4865 lines
105 KiB
C++
4865 lines
105 KiB
C++
// vehicle.cpp
|
|
//
|
|
// vehicle physics
|
|
|
|
// tops of walls - colliding with them???
|
|
// need to properly handle coming out of skidding so bouncing off walls works better
|
|
// tune for each vehicle
|
|
// skidmarks
|
|
// new meshes ???
|
|
|
|
// include files
|
|
|
|
#define DUMP_COORDS 0
|
|
|
|
#ifndef PSX
|
|
#include <math.h>
|
|
#endif
|
|
|
|
#include "game.h"
|
|
#ifndef PSX
|
|
#include "c:\fallen\ddengine\headers\matrix.h"
|
|
#include "c:\fallen\ddengine\headers\poly.h"
|
|
#include "c:\fallen\ddengine\headers\oval.h"
|
|
#include "c:\fallen\ddlibrary\headers\ddlib.h"
|
|
#else
|
|
#include "c:\fallen\psxeng\headers\poly.h"
|
|
#endif
|
|
#include "pap.h"
|
|
#include "fmatrix.h"
|
|
#include "statedef.h"
|
|
#include "pcom.h"
|
|
|
|
#ifndef PSX
|
|
#include "c:\fallen\ddengine\headers\aeng.h"
|
|
#include "c:\fallen\ddengine\headers\mesh.h"
|
|
#else
|
|
#include "c:\fallen\psxeng\headers\mesh.h"
|
|
#endif
|
|
|
|
#include "pow.h"
|
|
|
|
#include "interfac.h"
|
|
#include "dirt.h"
|
|
#include "mist.h"
|
|
#include "c:\fallen\editor\headers\prim.h"
|
|
#include "animate.h"
|
|
#include "sound.h"
|
|
#include "barrel.h"
|
|
#include "interact.h"
|
|
#include "ob.h"
|
|
#include "night.h"
|
|
#include "c:\fallen\ddengine\headers\drawxtra.h"
|
|
#include "psystem.h"
|
|
|
|
#include "mfx.h"
|
|
|
|
#include "memory.h"
|
|
#include "road.h"
|
|
|
|
#ifndef PSX
|
|
#include "font2d.h"
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
#define ANNOYINGSCRIBBLECHECK ScribbleCheck()
|
|
static void ScribbleCheck ( void )
|
|
{
|
|
ASSERT ( prim_faces4[1].Points[0] >= 48 );
|
|
ASSERT ( prim_faces4[1].Points[0] < 62 );
|
|
ASSERT ( prim_faces4[1].Points[1] >= 48 );
|
|
ASSERT ( prim_faces4[1].Points[1] < 62 );
|
|
ASSERT ( prim_faces4[1].Points[2] >= 48 );
|
|
ASSERT ( prim_faces4[1].Points[2] < 62 );
|
|
ASSERT ( prim_faces4[1].Points[3] >= 48 );
|
|
ASSERT ( prim_faces4[1].Points[3] < 62 );
|
|
}
|
|
|
|
#else
|
|
#define ANNOYINGSCRIBBLECHECK
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Some externs
|
|
extern SLONG is_person_ko(Thing *p_person);
|
|
|
|
|
|
// constants for physics
|
|
|
|
#define MIN_COMPRESSION (13 << 8)
|
|
#define MAX_COMPRESSION (115 << 8)
|
|
|
|
#define UNITS_PER_METER 128
|
|
#define TICK_LOOP (4)
|
|
#define TICKS_PER_SECOND (20*TICK_LOOP)
|
|
#define GRAVITY (-(UNITS_PER_METER*10*256)/(TICKS_PER_SECOND*TICKS_PER_SECOND))
|
|
|
|
#define WHEELTIME 35 // time to full lock
|
|
#define WHEELRATIO 45 // WHEELRATIO/WHEELTIME = tan(max. steering angle)
|
|
#define SKID_START 3 // number of tics to slam the brakes on before we skid
|
|
#define SKID_FORCE 8500 // lateral force required to start a skid
|
|
#define NEAR_SKID_FORCE 5000 // not skidding, but enough to starsky the sound fx
|
|
|
|
#define STABLE_COUNT 16 // number of "stable" tics to actually be stable
|
|
|
|
#define CAR_VEL_SHIFT 4
|
|
|
|
#ifndef PSX
|
|
extern BOOL allow_debug_keys;
|
|
#endif
|
|
|
|
static void siren(Vehicle* veh, UBYTE play);
|
|
static inline void GetCarPoints(Thing* p_car, SLONG* x, SLONG* y, SLONG* z, SLONG step);
|
|
extern SLONG is_person_ko_and_lay_down(Thing *p_person);
|
|
//
|
|
// random vehicle types
|
|
//
|
|
UBYTE vehicle_random[] =
|
|
{
|
|
VEH_TYPE_VAN, VEH_TYPE_CAR, VEH_TYPE_TAXI, VEH_TYPE_JEEP,
|
|
VEH_TYPE_SEDAN, VEH_TYPE_VAN, VEH_TYPE_CAR, VEH_TYPE_TAXI,
|
|
VEH_TYPE_JEEP, VEH_TYPE_SEDAN, VEH_TYPE_VAN, VEH_TYPE_CAR,
|
|
VEH_TYPE_TAXI, VEH_TYPE_VAN, VEH_TYPE_CAR, VEH_TYPE_TAXI
|
|
};
|
|
|
|
// VehInfo
|
|
//
|
|
// vehicle information per type
|
|
|
|
struct VehInfo
|
|
{
|
|
SWORD DX[4],DZ[4];
|
|
|
|
SBYTE FwdAccel; // note, terminal velocity depends on this!
|
|
SBYTE BkAccel;
|
|
SBYTE SoftBrake;
|
|
SBYTE HardBrake;
|
|
|
|
UWORD Reserved;
|
|
SWORD BodyDy;
|
|
UWORD WheelPrim;
|
|
UWORD BodyPrim;
|
|
SWORD BodyOffset;
|
|
UWORD NumVertices;
|
|
UBYTE* VertexAssignments;
|
|
|
|
SWORD HLX; // headlights x,y,z
|
|
SWORD HLY;
|
|
SWORD HLZ;
|
|
|
|
SWORD BLX; // brakelights x,y,z; z = 0 ==> no brakelights
|
|
SWORD BLY;
|
|
SWORD BLZ;
|
|
|
|
SWORD FLX; // flashing lights x,y,z; z = 0 ==> no flashing lights
|
|
SWORD FLY;
|
|
SWORD FLZ;
|
|
SWORD FLRED; // if 1, the lights are both red (else blue & red)
|
|
|
|
UBYTE shad_size;
|
|
UBYTE shad_elongate; // In 6-bit fixed point!
|
|
};
|
|
|
|
|
|
#define WHEELBASE_VAN {-120,120,-120,120},{150,150,-165,-165}
|
|
#define WHEELBASE_CAR {- 85, 85,- 85, 85},{160,160,-120,-120}
|
|
|
|
#define ENGINE_LGV 17, 10, 4, 8 // light goods vehicle - slow in both directions, normal brakes
|
|
#define ENGINE_CAR 21, 10, 4, 8 // car - faster than LGV, same brakes
|
|
#define ENGINE_PIG 25, 15, 5, 10 // cop car - faster and better brakes
|
|
#define ENGINE_AMB 25, 10, 5, 8 // ambulance - cop car speed forwards; LGV speed backwards; better than average brakes
|
|
|
|
struct VehInfo veh_info[VEH_TYPE_NUMBER] =
|
|
{
|
|
{WHEELBASE_VAN, ENGINE_LGV, 0,30,PRIM_OBJ_VAN_WHEEL,PRIM_OBJ_VAN_BODY, 0x6800, 0, NULL, -248, 15, 90, 0, 0, 0, 0, 0, 0, 0, 185, 100},
|
|
{WHEELBASE_CAR, ENGINE_CAR, 0,30,PRIM_OBJ_CAR_WHEEL,PRIM_OBJ_CAR_BODY, 0x4000, 0, NULL, -205, 0, 60, 235, -8, 60, 0, 0, 0, 0, 125, 160},
|
|
{WHEELBASE_CAR, ENGINE_CAR, 0,30,PRIM_OBJ_CAR_WHEEL,PRIM_OBJ_TAXI_BODY, 0x4000, 0, NULL, -225, 0, 60, 245, -8, 60, 0, 0, 0, 0, 125, 160},
|
|
{WHEELBASE_CAR, ENGINE_PIG, 0,30,PRIM_OBJ_CAR_WHEEL,PRIM_OBJ_POLICE_BODY, 0x4000, 0, NULL, -225, 0, 60, 205,-24, 60, 0, 70, 40, 0, 125, 160},
|
|
{WHEELBASE_VAN, ENGINE_AMB, 0,30,PRIM_OBJ_VAN_WHEEL,PRIM_OBJ_AMBULANCE_BODY, 0x6800, 0, NULL, -248, 15, 90, 240, 15, 90, -40, 210, 50, 1, 185, 100},
|
|
{WHEELBASE_VAN, ENGINE_CAR, 0,30,PRIM_OBJ_VAN_WHEEL,PRIM_OBJ_JEEP_BODY, 0x6800, 0, NULL, -225,-10, 80, 240, -5, 90, 0, 0, 0, 0, 185, 100},
|
|
{WHEELBASE_VAN, ENGINE_AMB, 0,30,PRIM_OBJ_VAN_WHEEL,PRIM_OBJ_MEATWAGON_BODY, 0x6800, 0, NULL, -240,-10, 80, 240, 15, 90, 50, 140, 40, 0, 185, 100},
|
|
{WHEELBASE_CAR, ENGINE_PIG, 0,30,PRIM_OBJ_CAR_WHEEL,PRIM_OBJ_SEDAN_BODY, 0x4000, 0, NULL, -225,-20, 60, 205,-24, 70, 0, 0, 0, 0, 125, 160},
|
|
{WHEELBASE_VAN, ENGINE_LGV, 0,30,PRIM_OBJ_VAN_WHEEL,PRIM_OBJ_WILDCATVAN_BODY, 0x6800, 0, NULL, -248, 15, 90, 0, 0, 0, 0, 0, 0, 0, 185, 100},
|
|
};
|
|
|
|
|
|
// debug stuff
|
|
#if !defined(PSX) && !defined(TARGET_DC)
|
|
#ifndef NDEBUG
|
|
|
|
Thing* SelectedThing = NULL;
|
|
|
|
void LookForSelectedThing()
|
|
{
|
|
SLONG world_x;
|
|
SLONG world_y;
|
|
SLONG world_z;
|
|
|
|
SelectedThing = NULL;
|
|
|
|
RECT client;
|
|
|
|
GetClientRect(hDDLibWindow, &client);
|
|
|
|
float hitx = float(MouseX) * float(DisplayWidth) / float(client.right - client.left);
|
|
float hity = float(MouseY) * float(DisplayHeight) / float(client.bottom - client.top);
|
|
|
|
AENG_raytraced_position(
|
|
SLONG(hitx + 0.5f),
|
|
SLONG(hity + 0.5f),
|
|
&world_x,
|
|
&world_y,
|
|
&world_z);
|
|
|
|
if (WITHIN(world_x, 600, (PAP_SIZE_HI << PAP_SHIFT_HI) - 601) &&
|
|
WITHIN(world_z, 600, (PAP_SIZE_HI << PAP_SHIFT_HI) - 601))
|
|
{
|
|
UWORD found[8];
|
|
|
|
SLONG num = THING_find_sphere(world_x, world_y, world_z, 0x100, found, 8, 1 << CLASS_VEHICLE);
|
|
|
|
if (num == 1)
|
|
{
|
|
SelectedThing = TO_THING(found[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// I KNOW THIS IS NASTY!
|
|
//
|
|
|
|
UBYTE sneaky_do_it_for_positioning_a_person_to_do_the_enter_anim;
|
|
|
|
void get_car_door_offsets(SLONG type, SLONG door, SLONG *dx,SLONG *dz)
|
|
{
|
|
ASSERT(door == 0 || door == 1);
|
|
|
|
if (sneaky_do_it_for_positioning_a_person_to_do_the_enter_anim)
|
|
{
|
|
*dx=(veh_info[type].DX[door + 1]*450)>>8;
|
|
}
|
|
else
|
|
{
|
|
*dx=(veh_info[type].DX[door + 1]*300)>>8;
|
|
}
|
|
|
|
*dz=(veh_info[type].DZ[door + 1]*50)>>8;
|
|
}
|
|
|
|
SLONG VEH_collide_line_ignore_walls = 0;
|
|
|
|
UWORD get_vehicle_body_prim(SLONG type)
|
|
{
|
|
ASSERT(WITHIN(type, 0, VEH_TYPE_NUMBER - 1));
|
|
|
|
return(veh_info[type].BodyPrim);
|
|
}
|
|
|
|
SLONG get_vehicle_body_offset(SLONG type)
|
|
{
|
|
ASSERT(WITHIN(type, 0, VEH_TYPE_NUMBER - 1));
|
|
|
|
return(SLONG(veh_info[type].BodyOffset));
|
|
}
|
|
|
|
// state functions
|
|
|
|
StateFunction VEH_statefunctions[] =
|
|
{
|
|
{STATE_INIT, NULL},
|
|
{STATE_NORMAL, NULL},
|
|
{STATE_COLLISION, NULL},
|
|
{STATE_ABOUT_TO_REMOVE, NULL},
|
|
{STATE_REMOVE_ME, NULL},
|
|
{STATE_MOVEING, NULL},
|
|
{STATE_FDRIVING, VEH_driving},
|
|
{STATE_FDOOR, NULL}
|
|
};
|
|
|
|
// utilities
|
|
|
|
// make car matrix from yaw,tilt,roll
|
|
|
|
static SLONG car_matrix[9];
|
|
|
|
static void make_car_matrix(Vehicle* v)
|
|
{
|
|
//#ifndef PSX
|
|
FMATRIX_calc(car_matrix, v->Angle, v->Tilt, v->Roll);
|
|
//#else
|
|
// FMATRIX_calc(car_matrix, -v->Angle, v->Tilt, v->Roll);
|
|
//#endif
|
|
}
|
|
|
|
static void make_car_matrix_p(SLONG angle, SLONG tilt, SLONG roll)
|
|
{
|
|
FMATRIX_calc(car_matrix, angle, tilt, roll);
|
|
}
|
|
|
|
static void apply_car_matrix(SLONG *x,SLONG *y,SLONG *z)
|
|
{
|
|
SLONG tx,ty,tz;
|
|
|
|
tx=*x; ty=*y; tz=*z;
|
|
FMATRIX_MUL_BY_TRANSPOSE(car_matrix,tx,ty,tz);
|
|
*x=tx; *y=ty; *z=tz;
|
|
}
|
|
|
|
//
|
|
// Returns NULL if there isn't a driver...
|
|
//
|
|
|
|
Thing *get_vehicle_driver(Thing *p_vehicle)
|
|
{
|
|
Thing *p_driver;
|
|
|
|
ASSERT(p_vehicle->Class == CLASS_VEHICLE);
|
|
|
|
if (p_vehicle->Genus.Vehicle->Driver)
|
|
{
|
|
p_driver = TO_THING(p_vehicle->Genus.Vehicle->Driver);
|
|
}
|
|
else
|
|
{
|
|
p_driver = NULL;
|
|
}
|
|
|
|
return p_driver;
|
|
}
|
|
|
|
// are we driving a car?
|
|
|
|
inline bool is_driven_by_player(Thing* p_car)
|
|
{
|
|
if (p_car->Genus.Vehicle->Driver)
|
|
{
|
|
Thing* p_driver = TO_THING(p_car->Genus.Vehicle->Driver);
|
|
|
|
ASSERT(p_driver->Class == CLASS_PERSON);
|
|
|
|
if (p_driver->Genus.Person->PlayerID)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// find things we might run over
|
|
|
|
SLONG VEH_find_runover_things(Thing *p_vehicle, UWORD thing_index[], SLONG max_number, SLONG dangle)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG cx;
|
|
SLONG cy;
|
|
SLONG cz;
|
|
|
|
SLONG num;
|
|
SLONG angle;
|
|
SLONG infront;
|
|
|
|
//
|
|
// A vector pointing out in front of the car and how far infront
|
|
// of the car shall we look for things. The more the car is
|
|
// turning the closer in we check.
|
|
//
|
|
|
|
#define MAX_INFRONT 512
|
|
|
|
switch(p_vehicle->Class)
|
|
{
|
|
case CLASS_VEHICLE:
|
|
angle = p_vehicle->Genus.Vehicle->Angle & 2047;
|
|
infront = MAX_INFRONT;
|
|
if (!dangle) infront -= abs(p_vehicle->Genus.Vehicle->WheelAngle * 3);
|
|
break;
|
|
|
|
#ifdef BIKE
|
|
|
|
case CLASS_BIKE:
|
|
angle = p_vehicle->Draw.Mesh->Angle & 2047;
|
|
infront = MAX_INFRONT;
|
|
if (!dangle) infront -= abs(BIKE_control_get(p_vehicle).steer) * 8;
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
dx = -SIN(angle);
|
|
dz = -COS(angle);
|
|
|
|
SATURATE(infront, 256, MAX_INFRONT);
|
|
|
|
dx = dx * infront >> 8;
|
|
dz = dz * infront >> 8;
|
|
|
|
cx = p_vehicle->WorldPos.X + dx >> 8;
|
|
cy = p_vehicle->WorldPos.Y >> 8;
|
|
cz = p_vehicle->WorldPos.Z + dz >> 8;
|
|
|
|
//
|
|
// Draw a line to where we are checking for people.
|
|
//
|
|
#ifndef PSX
|
|
#ifndef TARGET_DC
|
|
if (ControlFlag&&allow_debug_keys)
|
|
{
|
|
AENG_world_line(
|
|
p_vehicle->WorldPos.X >> 8,
|
|
p_vehicle->WorldPos.Y >> 8,
|
|
p_vehicle->WorldPos.Z >> 8,
|
|
32,
|
|
0xffffff,
|
|
cx,
|
|
cy,
|
|
cz,
|
|
0,
|
|
0xff0000,
|
|
TRUE);
|
|
}
|
|
#endif
|
|
#endif
|
|
//
|
|
// Look for things at this position.
|
|
//
|
|
|
|
num = THING_find_sphere(
|
|
cx, cy, cz,
|
|
infront, // 0x140,
|
|
thing_index,
|
|
max_number,
|
|
(1 << CLASS_PERSON) | (1 << CLASS_VEHICLE));
|
|
|
|
//
|
|
// Look for things ahead
|
|
//
|
|
|
|
cx += dx >> 8;
|
|
cz += dz >> 8;
|
|
|
|
num += THING_find_sphere(cx, cy, cz, infront, thing_index + num, max_number - num, (1 << CLASS_PERSON) | (1 << CLASS_VEHICLE));
|
|
|
|
//
|
|
// Make sure that we are not in the list!
|
|
//
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
if (thing_index[i] == THING_NUMBER(p_vehicle))
|
|
{
|
|
thing_index[i] = thing_index[--num];
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
// find door position
|
|
|
|
void VEH_find_door(Thing *p_vehicle, SLONG i_am_a_passenger, SLONG *door_x, SLONG *door_z)
|
|
{
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG ix;
|
|
SLONG iz;
|
|
|
|
ASSERT(p_vehicle->Class == CLASS_VEHICLE);
|
|
|
|
dx = -SIN(p_vehicle->Genus.Vehicle->Angle);
|
|
dz = -COS(p_vehicle->Genus.Vehicle->Angle);
|
|
|
|
ix = p_vehicle->WorldPos.X >> 8;
|
|
iz = p_vehicle->WorldPos.Z >> 8;
|
|
|
|
if (i_am_a_passenger)
|
|
{
|
|
/*
|
|
switch(p_vehicle->Genus.Vehicle->Type)
|
|
{
|
|
case VEH_TYPE_VAN:
|
|
case VEH_TYPE_AMBULANCE:
|
|
case VEH_TYPE_JEEP:
|
|
case VEH_TYPE_MEATWAGON:
|
|
case VEH_TYPE_WILDCATVAN:
|
|
|
|
//
|
|
// Get in the back of the van.
|
|
//
|
|
|
|
ix -= dx >> 10;
|
|
iz -= dz >> 10;
|
|
|
|
break;
|
|
|
|
case VEH_TYPE_CAR:
|
|
case VEH_TYPE_TAXI:
|
|
case VEH_TYPE_POLICE:
|
|
case VEH_TYPE_SEDAN:
|
|
|
|
//
|
|
// The back seat on the same side as what Darci gets in.
|
|
//
|
|
|
|
ix -= dx >> 10;
|
|
iz -= dz >> 10;
|
|
|
|
ix += dz >> 9;
|
|
iz -= dx >> 9;
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
*/
|
|
|
|
ix += dx >> 10;
|
|
iz += dz >> 10;
|
|
|
|
ix -= dz >> 9;
|
|
iz += dx >> 9;
|
|
|
|
ix -= dz >> 11;
|
|
iz += dx >> 11;
|
|
|
|
}
|
|
else
|
|
{
|
|
ix += dx >> 10;
|
|
iz += dz >> 10;
|
|
|
|
ix += dz >> 9;
|
|
iz -= dx >> 9;
|
|
|
|
ix += dz >> 11;
|
|
iz -= dx >> 11;
|
|
}
|
|
|
|
*door_x = ix;
|
|
*door_z = iz;
|
|
}
|
|
|
|
//
|
|
// Returns TRUE if the given car is completely on the road.
|
|
//
|
|
|
|
SLONG VEH_on_road(Thing *p_vehicle, SLONG step)
|
|
{
|
|
SLONG x[4];
|
|
SLONG y[4];
|
|
SLONG z[4];
|
|
|
|
// get rotated points
|
|
GetCarPoints(p_vehicle, x, y, z, step);
|
|
|
|
// check 8 points along each edge
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
SLONG x1 = x[i];
|
|
SLONG z1 = z[i];
|
|
|
|
SLONG x2 = x[(i + 1) & 0x3];
|
|
SLONG z2 = z[(i + 1) & 0x3];
|
|
|
|
SLONG dx = (x2 - x1) >> 3;
|
|
SLONG dz = (z2 - z1) >> 3;
|
|
|
|
SLONG cx = x1;
|
|
SLONG cz = z1;
|
|
|
|
for (int j = 0; j < 7; j++)
|
|
{
|
|
if (!ROAD_is_road(cx >> 8, cz >> 8))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
cx += dx;
|
|
cz += dz;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// add damage to a specified area of a vehicle
|
|
|
|
void VEH_add_damage(Vehicle* vp, UBYTE area, UBYTE hp)
|
|
{
|
|
// max out at 2hp
|
|
if (hp > 2) hp = 2;
|
|
|
|
if (!hp)
|
|
{
|
|
// small amount of damage
|
|
if (vp->damage[area] < 2) vp->damage[area]++;
|
|
}
|
|
else
|
|
{
|
|
vp->damage[area] += hp;
|
|
if (vp->damage[area] > 4) vp->damage[area] = 4;
|
|
}
|
|
|
|
// even out damage across sides
|
|
if (vp->damage[2] < (vp->damage[0] + vp->damage[4])/2)
|
|
{
|
|
vp->damage[2] = (vp->damage[0] + vp->damage[4])/2;
|
|
}
|
|
if (vp->damage[3] < (vp->damage[1] + vp->damage[5])/2)
|
|
{
|
|
vp->damage[3] = (vp->damage[1] + vp->damage[5])/2;
|
|
}
|
|
|
|
// reduce damage at opposite corner
|
|
if ((vp->damage[0] + vp->damage[1] > 6) && (area == 0)) vp->damage[1]--;
|
|
if ((vp->damage[0] + vp->damage[1] > 6) && (area == 1)) vp->damage[0]--;
|
|
|
|
if ((vp->damage[4] + vp->damage[5] > 6) && (area == 4)) vp->damage[5]--;
|
|
if ((vp->damage[4] + vp->damage[5] > 6) && (area == 5)) vp->damage[4]--;
|
|
|
|
// randomly reduce damage at diagonally opposite corner
|
|
if ((hp == 2) && (Random() > 25000) && (vp->damage[5-area])) vp->damage[5-area]--;
|
|
}
|
|
|
|
// bounce a vehicle
|
|
|
|
void VEH_bounce(Vehicle* vp, UBYTE area, SLONG amount)
|
|
{
|
|
amount *= 2;
|
|
|
|
switch (area)
|
|
{
|
|
case 0:
|
|
vp->DY[1] -= amount;
|
|
break;
|
|
|
|
case 1:
|
|
vp->DY[0] -= amount;
|
|
break;
|
|
|
|
case 2:
|
|
vp->DY[1] -= amount;
|
|
vp->DY[3] -= amount;
|
|
break;
|
|
|
|
case 3:
|
|
vp->DY[0] -= amount;
|
|
vp->DY[2] -= amount;
|
|
break;
|
|
|
|
case 4:
|
|
vp->DY[3] -= amount;
|
|
break;
|
|
|
|
case 5:
|
|
vp->DY[2] -= amount;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// init the vehicle array
|
|
|
|
#ifndef PSX
|
|
void init_vehicles(void)
|
|
{
|
|
SLONG i;
|
|
|
|
for (i = 0; i < MAX_VEHICLES; i++)
|
|
{
|
|
TO_VEHICLE(i)->Spring[0].Compression = VEH_NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// VEH_init_vehinfo
|
|
//
|
|
// assign each vertex to a crumple zone and initialize the
|
|
// arctan table (for steering)
|
|
//
|
|
// prims must be loaded!
|
|
|
|
static void init_arctans(void);
|
|
|
|
|
|
|
|
void VEH_init_vehinfo()
|
|
{
|
|
int ii;
|
|
|
|
init_arctans();
|
|
|
|
#ifndef PSX
|
|
|
|
#ifdef TARGET_DC
|
|
// Just allocate these once.
|
|
static bool bAllocatedVertexAssignments = FALSE;
|
|
if ( !bAllocatedVertexAssignments )
|
|
{
|
|
for (ii = 0; ii < VEH_TYPE_NUMBER; ii++)
|
|
{
|
|
veh_info[ii].VertexAssignments = NULL;
|
|
}
|
|
}
|
|
|
|
#else
|
|
for (ii = 0; ii < VEH_TYPE_NUMBER; ii++)
|
|
{
|
|
if (veh_info[ii].VertexAssignments)
|
|
{
|
|
MemFree(veh_info[ii].VertexAssignments);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (ii = 0; ii < VEH_TYPE_NUMBER; ii++)
|
|
{
|
|
PrimObject* obj = &prim_objects[veh_info[ii].BodyPrim];
|
|
PrimInfo* inf = get_prim_info(veh_info[ii].BodyPrim);
|
|
SLONG px[6],pz[6];
|
|
|
|
// create 6 crumple points
|
|
px[0] = inf->minx; pz[0] = inf->minz;
|
|
px[1] = inf->maxx; pz[1] = inf->minz;
|
|
px[2] = inf->minx; pz[2] = (inf->minz + inf->maxz) / 2;
|
|
px[3] = inf->maxx; pz[3] = (inf->minz + inf->maxz) / 2;
|
|
px[4] = inf->minx; pz[4] = inf->maxz;
|
|
px[5] = inf->maxx; pz[5] = inf->maxz;
|
|
|
|
veh_info[ii].NumVertices = obj->EndPoint - obj->StartPoint;
|
|
|
|
#ifdef TARGET_DC
|
|
if ( !bAllocatedVertexAssignments )
|
|
{
|
|
ASSERT ( veh_info[ii].VertexAssignments == NULL );
|
|
veh_info[ii].VertexAssignments = (UBYTE*)MemAlloc(obj->EndPoint - obj->StartPoint);
|
|
}
|
|
else
|
|
{
|
|
ASSERT ( veh_info[ii].VertexAssignments != NULL );
|
|
}
|
|
#else
|
|
veh_info[ii].VertexAssignments = (UBYTE*)MemAlloc(obj->EndPoint - obj->StartPoint);
|
|
#endif
|
|
|
|
// assign each vertex to the nearest crumple point
|
|
for (int jj = obj->StartPoint; jj < obj->EndPoint; jj++)
|
|
{
|
|
SLONG maxdist = 0x7FFFFFFF;
|
|
SLONG best = -1;
|
|
|
|
SLONG x = prim_points[jj].X;
|
|
SLONG z = prim_points[jj].Z;
|
|
|
|
for (int kk = 0; kk < 6; kk++)
|
|
{
|
|
SLONG dist = (x - px[kk])*(x - px[kk]) + (z - pz[kk])*(z - pz[kk]);
|
|
|
|
if (dist < maxdist)
|
|
{
|
|
maxdist = dist;
|
|
best = kk;
|
|
}
|
|
}
|
|
|
|
ASSERT(best != -1);
|
|
|
|
veh_info[ii].VertexAssignments[jj - obj->StartPoint] = best;
|
|
}
|
|
}
|
|
|
|
#ifdef TARGET_DC
|
|
bAllocatedVertexAssignments = TRUE;
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
// find a free vehicle slot
|
|
|
|
Vehicle *VEH_alloc(void)
|
|
{
|
|
SLONG i;
|
|
|
|
for (i = 0; i < MAX_VEHICLES; i++)
|
|
{
|
|
if (TO_VEHICLE(i)->Spring[0].Compression == VEH_NULL)
|
|
{
|
|
Vehicle *ans = TO_VEHICLE(i);
|
|
|
|
ans->Spring[0].Compression = MIN_COMPRESSION;
|
|
|
|
return ans;
|
|
}
|
|
}
|
|
|
|
ASSERT(0);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// free a vehicle slot
|
|
|
|
void VEH_dealloc(Vehicle *veh)
|
|
{
|
|
veh->Spring[0].Compression = VEH_NULL;
|
|
if (veh->dlight) NIGHT_dlight_destroy(veh->dlight);
|
|
}
|
|
|
|
//
|
|
// Initialises a vehicles drawmesh structure.
|
|
//
|
|
|
|
static void set_vehicle_draw(Thing *p_thing)
|
|
{
|
|
DrawTween *draw;
|
|
ASSERT(0);
|
|
return;
|
|
|
|
draw=&p_thing->Genus.Vehicle->Draw;
|
|
|
|
draw->Angle = p_thing->Genus.Vehicle->Angle;
|
|
draw->Roll = 0;
|
|
draw->Tilt = 0;
|
|
draw->AnimTween = 0;
|
|
draw->TweenStage = 0;
|
|
draw->CurrentFrame = game_chunk[5].AnimList[2];
|
|
draw->NextFrame = draw->CurrentFrame->NextFrame;
|
|
draw->QueuedFrame = NULL;
|
|
draw->TheChunk = &game_chunk[5];
|
|
draw->FrameIndex = 0;
|
|
draw->Flags = 0;
|
|
}
|
|
THING_INDEX VEH_create(
|
|
SLONG x,
|
|
SLONG y,
|
|
SLONG z,
|
|
SLONG yaw,
|
|
SLONG pitch,
|
|
SLONG roll,
|
|
SLONG type,
|
|
UBYTE key,
|
|
UBYTE colour)
|
|
{
|
|
DrawMesh* dm;
|
|
THING_INDEX ans = NULL;
|
|
Thing* p_thing;
|
|
int ii;
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
ASSERT(WITHIN(type, 0, VEH_TYPE_NUMBER - 1));
|
|
|
|
//
|
|
// Looks like vehicles treat their yaw strangely...
|
|
//
|
|
|
|
yaw += 1024;
|
|
yaw &= 2047;
|
|
|
|
#if defined(FAST_EDDIE) && 0
|
|
|
|
//
|
|
// OK, now we'll ask the road system about our position
|
|
//
|
|
|
|
SLONG rn1,rn2;
|
|
|
|
ROAD_find(x >> 8, z >> 8, &rn1, &rn2);
|
|
|
|
SLONG rd = ROAD_signed_dist(rn1, rn2, x >> 8, z >> 8);
|
|
|
|
if (rd < 0)
|
|
{
|
|
SLONG tmp = rn1; rn1 = rn2; rn2 = tmp;
|
|
}
|
|
|
|
SLONG x1,z1;
|
|
SLONG x2,z2;
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
ROAD_node_pos(rn1, &x1, &z1);
|
|
ROAD_node_pos(rn2, &x2, &z2);
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
SLONG dx = -SIN(yaw);
|
|
SLONG dz = -COS(yaw);
|
|
|
|
if (dx * (x2 - x1) + dz * (z2 - z1) < 0)
|
|
{
|
|
// facing the wrong way
|
|
yaw ^= 1024;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Get a DrawMesh for this thing.
|
|
//
|
|
ANNOYINGSCRIBBLECHECK;
|
|
dm = alloc_draw_mesh();
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
if (dm)
|
|
{
|
|
ANNOYINGSCRIBBLECHECK;
|
|
ans = alloc_primary_thing(CLASS_VEHICLE);
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
if (ans)
|
|
{
|
|
Vehicle* vp;
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
p_thing = TO_THING(ans);
|
|
|
|
//
|
|
// Initialise the thing.
|
|
//
|
|
|
|
p_thing->Class = CLASS_VEHICLE;
|
|
p_thing->State = 0;
|
|
p_thing->SubState = 0;
|
|
p_thing->DrawType = DT_VEHICLE;
|
|
p_thing->Flags = 0;
|
|
p_thing->WorldPos.X = x;
|
|
p_thing->WorldPos.Y = y;
|
|
p_thing->WorldPos.Z = z;
|
|
|
|
p_thing->Draw.Mesh = dm;
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
dm->Angle = yaw;
|
|
|
|
vp = p_thing->Genus.Vehicle = VEH_alloc();
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
ASSERT(vp);
|
|
|
|
vp->Angle = yaw;
|
|
vp->Roll = roll;
|
|
vp->Tilt = pitch;
|
|
vp->Flags = 0;
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
vp->Spring[ii].Compression = MIN_COMPRESSION;
|
|
vp->DY[ii] = 0;
|
|
}
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
vp->Type = type;
|
|
|
|
if (!(NIGHT_flag & NIGHT_FLAG_DAYTIME)) {
|
|
ANNOYINGSCRIBBLECHECK;
|
|
vp->dlight = NIGHT_dlight_create( p_thing->WorldPos.X>>8, p_thing->WorldPos.Y>>8, p_thing->WorldPos.Z>>8, 200, 35, 32, 10);
|
|
ANNOYINGSCRIBBLECHECK;
|
|
} else {
|
|
ANNOYINGSCRIBBLECHECK;
|
|
vp->dlight=0;
|
|
ANNOYINGSCRIBBLECHECK;
|
|
}
|
|
|
|
// set extension parameters
|
|
ANNOYINGSCRIBBLECHECK;
|
|
reinit_vehicle(p_thing);
|
|
ANNOYINGSCRIBBLECHECK;
|
|
|
|
set_state_function(p_thing, STATE_FDRIVING);
|
|
// set_vehicle_draw(p_thing);
|
|
|
|
//
|
|
// Initialises more stuff...
|
|
//
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
p_thing->Genus.Vehicle->Roll = 0;
|
|
p_thing->Genus.Vehicle->Tilt = 0;
|
|
p_thing->Genus.Vehicle->key = key;
|
|
|
|
//
|
|
// Place on the mapwho.
|
|
//
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
add_thing_to_map(p_thing);
|
|
ANNOYINGSCRIBBLECHECK;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
|
|
ANNOYINGSCRIBBLECHECK;
|
|
return ans;
|
|
}
|
|
|
|
void reinit_vehicle(Thing* p_thing)
|
|
{
|
|
Vehicle* vp = p_thing->Genus.Vehicle;
|
|
|
|
vp->VelX = 0;
|
|
vp->VelY = 0;
|
|
vp->VelZ = 0;
|
|
vp->VelR = 0;
|
|
vp->Dir = 0;
|
|
vp->WheelAngle = 0;
|
|
vp->Skid = 0;
|
|
vp->Stable = 0;
|
|
vp->Smokin = 0;
|
|
vp->OnRoadFlags = 0;
|
|
vp->Health = 300;
|
|
vp->Siren = 0;
|
|
vp->GrabAction = 0;
|
|
|
|
for (int ii = 0; ii < 6; ii++)
|
|
{
|
|
vp->damage[ii] = 0;
|
|
}
|
|
|
|
SLONG height;
|
|
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
vp->DY[ii] = 0;
|
|
vp->Spring[ii].Compression = 4300;
|
|
}
|
|
|
|
vp->Tilt = 0;
|
|
vp->Roll = 0;
|
|
height = PAP_calc_map_height_at(p_thing->WorldPos.X >> 8, p_thing->WorldPos.Z >> 8);
|
|
//
|
|
// 107 is length of suspension 128 *(100-%compressed) =128*.84
|
|
//
|
|
p_thing->WorldPos.Y = (height + 107) << 8;
|
|
}
|
|
|
|
void free_vehicle(Thing *p_thing)
|
|
{
|
|
ASSERT(0);
|
|
if (p_thing->Genus.Vehicle)
|
|
{
|
|
VEH_dealloc(p_thing->Genus.Vehicle);
|
|
}
|
|
|
|
free_draw_mesh(p_thing->Draw.Mesh);
|
|
|
|
free_thing(p_thing);
|
|
}
|
|
|
|
|
|
|
|
// static functions
|
|
//
|
|
// (don't static functions have the "static" keyword?)
|
|
|
|
SLONG calc_car_collision_turn(Thing *p_car,SLONG angle,SLONG tilt,SLONG roll);
|
|
void calc_car_normal(SLONG *p,SLONG *dy,SLONG *nx,SLONG *ny,SLONG *nz);
|
|
void calc_car_vect(SLONG p1,SLONG p2,SLONG *dy,SLONG *vx,SLONG *vy,SLONG *vz);
|
|
void normalise_val256(SLONG *vx,SLONG *vy,SLONG *vz);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void animate_car(Thing *p_car)
|
|
{
|
|
SLONG tween_step;
|
|
DrawTween *draw_info;
|
|
ASSERT(0);
|
|
return;
|
|
|
|
|
|
draw_info=&p_car->Genus.Vehicle->Draw;
|
|
// tween_step=256/(draw_info->CurrentFrame->TweenStep+1);
|
|
tween_step=draw_info->CurrentFrame->TweenStep<<1;
|
|
|
|
tween_step = (tween_step*TICK_RATIO)>>TICK_SHIFT;
|
|
if(tween_step==0)
|
|
tween_step=1;
|
|
draw_info->AnimTween += tween_step; //256/(draw_info->CurrentFrame->TweenStep+1);
|
|
|
|
if(p_car->Genus.Vehicle->Draw.AnimTween>256)
|
|
{
|
|
p_car->Genus.Vehicle->Draw.AnimTween-=256;
|
|
|
|
SLONG advance_keyframe(DrawTween *draw_info);
|
|
advance_keyframe(&p_car->Genus.Vehicle->Draw);
|
|
}
|
|
}
|
|
|
|
|
|
void draw_car(Thing *p_car)
|
|
{
|
|
SLONG x[8],y[8],z[8];
|
|
SLONG vector[3];
|
|
SLONG dx,dy,dz;
|
|
SLONG c0=0;
|
|
struct VehInfo *info;
|
|
SLONG tilt;
|
|
Vehicle* vp;
|
|
|
|
vp = p_car->Genus.Vehicle;
|
|
info=&veh_info[p_car->Genus.Vehicle->Type];
|
|
|
|
make_car_matrix(p_car->Genus.Vehicle);
|
|
#ifdef PSX
|
|
/*
|
|
{
|
|
CBYTE str[30];
|
|
extern FONT2D_DrawString_3d(CBYTE*str, ULONG world_x, ULONG world_y,ULONG world_z, ULONG rgb, SLONG text_size, SWORD fade);
|
|
|
|
sprintf(str,"S%d W%d A%d d %d",p_car->Genus.Vehicle->Steering,p_car->Genus.Vehicle->Wheel,p_car->Genus.Vehicle->IsAnalog,(p_car->Genus.Vehicle->Flags & FLAG_FURN_DRIVING));
|
|
|
|
FONT2D_DrawString_3d(str,p_car->WorldPos.X>>8,p_car->WorldPos.Y>>8,p_car->WorldPos.Z>>8,0xffffff,512,0);
|
|
// CBYTE*str, ULONG world_x, ULONG world_y,ULONG world_z, ULONG rgb, SLONG text_size, SWORD fade);
|
|
|
|
}
|
|
*/
|
|
#endif
|
|
#if !defined(PSX) && !defined(TARGET_DC)
|
|
if (0)
|
|
{
|
|
//
|
|
// Draw the car as an animating prim.
|
|
//
|
|
|
|
extern void ANIM_obj_draw(Thing *p_thing,DrawTween *dt);
|
|
|
|
p_car->WorldPos.Y -= info->BodyOffset;
|
|
|
|
p_car->Genus.Vehicle->Draw.Angle = p_car->Genus.Vehicle->Angle;
|
|
p_car->Genus.Vehicle->Draw.Tilt = p_car->Genus.Vehicle->Tilt;
|
|
p_car->Genus.Vehicle->Draw.Roll = p_car->Genus.Vehicle->Roll;
|
|
|
|
ANIM_obj_draw(p_car,&p_car->Genus.Vehicle->Draw);
|
|
|
|
p_car->WorldPos.Y += info->BodyOffset;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
//
|
|
// Draw the car as a normal prim.
|
|
//
|
|
#ifndef PSX
|
|
// set crumples
|
|
MESH_set_crumple(info->VertexAssignments, p_car->Genus.Vehicle->damage);
|
|
#endif
|
|
|
|
if(MESH_draw_poly(
|
|
|
|
#ifdef PSX
|
|
info->BodyPrim|(1<<16), // The van,
|
|
#else
|
|
info->BodyPrim, // The van,
|
|
#endif
|
|
|
|
// info->BodyPrim, // The van,
|
|
p_car->WorldPos.X >> 8,
|
|
p_car->WorldPos.Y - get_vehicle_body_offset(p_car->Genus.Vehicle->Type) >> 8,
|
|
p_car->WorldPos.Z >> 8,
|
|
p_car->Genus.Vehicle->Angle,
|
|
p_car->Genus.Vehicle->Tilt,
|
|
p_car->Genus.Vehicle->Roll,
|
|
NULL,
|
|
#ifndef PSX
|
|
0xff,
|
|
-1)==NULL) // means use MESH_set_crumple parameters
|
|
#else
|
|
0)==NULL)
|
|
#endif
|
|
{
|
|
//
|
|
// nowt, one day skip drawing wheels if body fails to be drawn
|
|
//
|
|
|
|
}
|
|
|
|
|
|
#ifndef PSX
|
|
|
|
//
|
|
// Draw the car shadow...
|
|
//
|
|
if(!SOFTWARE)
|
|
{
|
|
OVAL_add(
|
|
p_car->WorldPos.X >> 8,
|
|
p_car->WorldPos.Y - get_vehicle_body_offset(p_car->Genus.Vehicle->Type) >> 8,
|
|
p_car->WorldPos.Z >> 8,
|
|
float(veh_info[p_car->Genus.Vehicle->Type].shad_size ) * 2.0F,
|
|
float(veh_info[p_car->Genus.Vehicle->Type].shad_elongate) * (1.0F / 64.0F),
|
|
float(p_car->Genus.Vehicle->Angle) * (2.0F * PI / 2048.0F),
|
|
OVAL_TYPE_SQUARE);
|
|
}
|
|
|
|
#endif
|
|
|
|
//#ifndef PSX
|
|
// transpose matrix for some reason
|
|
FMATRIX_TRANSPOSE(car_matrix);
|
|
//#endif
|
|
if (p_car->Genus.Vehicle->Flags & FLAG_FURN_DRIVING)
|
|
{
|
|
// headlights
|
|
#ifndef PSX
|
|
if ( (!(NIGHT_flag & NIGHT_FLAG_DAYTIME)) && (!SOFTWARE))
|
|
{
|
|
static SLONG xyz[5][3] = { -255,0,0, -254,12,16, -251,24,32, -247,36,48, -243,48,60 };
|
|
|
|
dz = xyz[p_car->Genus.Vehicle->damage[1]][0];
|
|
dy = xyz[p_car->Genus.Vehicle->damage[1]][1];
|
|
dx = xyz[p_car->Genus.Vehicle->damage[1]][2];
|
|
FMATRIX_MUL(car_matrix,dx,dy,dz);
|
|
|
|
vector[2] = info->HLX;
|
|
vector[1] = info->HLY;
|
|
vector[0] = info->HLZ;
|
|
FMATRIX_MUL(car_matrix,vector[0],vector[1],vector[2]);
|
|
BLOOM_draw( (p_car->WorldPos.X>>8)+vector[0],(p_car->WorldPos.Y>>8)+vector[1],(p_car->WorldPos.Z>>8)+vector[2],dx,dy,dz,0x606040,BLOOM_FLENSFLARE|BLOOM_BEAM);
|
|
|
|
dz = xyz[p_car->Genus.Vehicle->damage[0]][0];
|
|
dy = -xyz[p_car->Genus.Vehicle->damage[0]][1];
|
|
dx = -xyz[p_car->Genus.Vehicle->damage[0]][2];
|
|
FMATRIX_MUL(car_matrix,dx,dy,dz);
|
|
|
|
vector[2] = info->HLX;
|
|
vector[1] = info->HLY;
|
|
vector[0] = -info->HLZ;
|
|
FMATRIX_MUL(car_matrix,vector[0],vector[1],vector[2]);
|
|
BLOOM_draw( (p_car->WorldPos.X>>8)+vector[0],(p_car->WorldPos.Y>>8)+vector[1],(p_car->WorldPos.Z>>8)+vector[2],dx,dy,dz,0x606040,BLOOM_FLENSFLARE|BLOOM_BEAM);
|
|
}
|
|
#endif
|
|
// flashing lights
|
|
if (info->FLZ && (vp->Siren == 1))
|
|
{
|
|
SLONG rx, rz;
|
|
|
|
rx = SIN((SLONG(p_car)+(GAME_TURN<<7))&2047)>>8;
|
|
rz = COS((SLONG(p_car)+(GAME_TURN<<7))&2047)>>8;
|
|
|
|
vector[2] = info->FLX + (rx >> 6);
|
|
vector[1] = info->FLY;
|
|
vector[0] = info->FLZ + (rz >> 6);
|
|
FMATRIX_MUL(car_matrix,vector[0],vector[1],vector[2]);
|
|
|
|
SLONG colour = info->FLRED ? 0xDF0000 : 0x0000DF;
|
|
|
|
BLOOM_draw( (p_car->WorldPos.X>>8)+vector[0],(p_car->WorldPos.Y>>8)+vector[1],(p_car->WorldPos.Z>>8)+vector[2], rx, 0, rz, colour, 0);
|
|
|
|
vector[2] = info->FLX + (rx >> 6);
|
|
vector[1] = info->FLY;
|
|
vector[0] = -info->FLZ + (rz >> 6);
|
|
FMATRIX_MUL(car_matrix,vector[0],vector[1],vector[2]);
|
|
|
|
BLOOM_draw( (p_car->WorldPos.X>>8)+vector[0],(p_car->WorldPos.Y>>8)+vector[1],(p_car->WorldPos.Z>>8)+vector[2], rz, 0, rx, 0xDF0000, 0);
|
|
}
|
|
}
|
|
|
|
// smoke from bonnet
|
|
|
|
/*
|
|
|
|
if (vp->damage[0] || vp->damage[1] || vp->damage[2] || vp->damage[3] || vp->damage[4] || vp->damage[5])
|
|
{
|
|
UBYTE total = vp->damage[0] + vp->damage[1] + vp->damage[2] +
|
|
vp->damage[3] + vp->damage[4] + vp->damage[5];
|
|
|
|
total /= 6;
|
|
|
|
if ((Random()&7)<total)
|
|
{
|
|
*/
|
|
|
|
{
|
|
if ((Random()&0x7f)>p_car->Genus.Vehicle->Health)
|
|
{
|
|
vector[2] = info->HLX * 0.7f;
|
|
vector[1] = info->HLY;
|
|
vector[0] = 0;
|
|
FMATRIX_MUL(car_matrix,vector[0],vector[1],vector[2]);
|
|
PARTICLE_Add(
|
|
p_car->WorldPos.X+(vector[0]<<8) + (Random() & 0x3fff) - 0x1fff,
|
|
p_car->WorldPos.Y+(vector[1]<<8),
|
|
p_car->WorldPos.Z+(vector[2]<<8) + (Random() & 0x3fff) - 0x1fff,
|
|
vp->VelX+(Random()&0xff)-0x7f,
|
|
0x3ff+(Random()&0xff),
|
|
vp->VelZ+(Random()&0xff)-0x7f,
|
|
POLY_PAGE_SMOKECLOUD,2+((Random()&3)<<2),0x7FFFFFFF,
|
|
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FADE2|PFLAG_RESIZE|PFLAG_DAMPING,
|
|
75,40+(rand()&31),1,4,1);
|
|
|
|
if (p_car->Genus.Vehicle->Health <= 0)
|
|
{
|
|
//
|
|
// Smoke from out the back of the vehicle aswell...
|
|
//
|
|
|
|
PARTICLE_Add(
|
|
p_car->WorldPos.X-(vector[0]<<8) + (Random() & 0x7fff) - 0x3fff,
|
|
p_car->WorldPos.Y-(vector[1]<<8),
|
|
p_car->WorldPos.Z-(vector[2]<<8) + (Random() & 0x7fff) - 0x3fff,
|
|
vp->VelX+(Random()&0xff)-0x7f,
|
|
0x3ff+(Random()&0xff),
|
|
vp->VelZ+(Random()&0xff)-0x7f,
|
|
POLY_PAGE_SMOKECLOUD,2+((Random()&3)<<2),0x7FFFFFFF,
|
|
PFLAG_SPRITEANI|PFLAG_SPRITELOOP|PFLAG_FADE2|PFLAG_RESIZE|PFLAG_DAMPING,
|
|
75,40+(rand()&31),1,4,1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// brake/reversing lights are always shown, day or night
|
|
if (info->BLZ) // else no lights
|
|
{
|
|
SLONG colour = 0;
|
|
|
|
switch (p_car->Genus.Vehicle->Dir)
|
|
{
|
|
case -1:
|
|
case 1:
|
|
p_car->Genus.Vehicle->Brakelight = is_driven_by_player(p_car) ? 1 : 10; // timer for NPCs (non-player cars)
|
|
break;
|
|
|
|
case -2:
|
|
p_car->Genus.Vehicle->Brakelight = 0;
|
|
if (p_car->Genus.Vehicle->DControl & VEH_DECEL) // colour = 0x606060; // reversing light
|
|
colour=0x303030;
|
|
break;
|
|
|
|
case 2:
|
|
p_car->Genus.Vehicle->Brakelight = 0;
|
|
break;
|
|
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
if (p_car->Genus.Vehicle->Brakelight)
|
|
{
|
|
p_car->Genus.Vehicle->Brakelight--;
|
|
colour = 0x600000; // brake light
|
|
}
|
|
|
|
if (colour)
|
|
{
|
|
dx = 0; dy = 0; dz =255;
|
|
FMATRIX_MUL(car_matrix,dx,dy,dz);
|
|
|
|
vector[2] = info->BLX;
|
|
vector[1] = info->BLY;
|
|
vector[0] = info->BLZ;
|
|
FMATRIX_MUL(car_matrix,vector[0],vector[1],vector[2]);
|
|
BLOOM_draw( (p_car->WorldPos.X>>8)+vector[0],(p_car->WorldPos.Y>>8)+vector[1],(p_car->WorldPos.Z>>8)+vector[2],dx,dy,dz,colour,0);
|
|
|
|
vector[2] = info->BLX;
|
|
vector[1] = info->BLY;
|
|
vector[0] = -info->BLZ;
|
|
FMATRIX_MUL(car_matrix,vector[0],vector[1],vector[2]);
|
|
BLOOM_draw( (p_car->WorldPos.X>>8)+vector[0],(p_car->WorldPos.Y>>8)+vector[1],(p_car->WorldPos.Z>>8)+vector[2],dx,dy,dz,colour,0);
|
|
}
|
|
}
|
|
|
|
// return to original
|
|
FMATRIX_TRANSPOSE(car_matrix);
|
|
//#endif
|
|
|
|
}
|
|
|
|
tilt = p_car->Genus.Vehicle->Spin;
|
|
|
|
|
|
void AENG_set_bike_wheel_rotation(UWORD rot, UBYTE prim);
|
|
// I know it says "bike" on the box, but it doesn't mean "bike" in the box.
|
|
// Leave it! This is needed.
|
|
AENG_set_bike_wheel_rotation(tilt, info->WheelPrim);
|
|
|
|
|
|
#if !defined(PSX) && !defined(TARGET_DC)
|
|
#ifndef NDEBUG
|
|
if (p_car != SelectedThing) // don't draw wheels when selected
|
|
#endif
|
|
#endif
|
|
for(c0=0;c0<4;c0++)
|
|
{
|
|
SLONG wx,wy,wz;
|
|
SLONG angle;
|
|
|
|
wx = info->DX[c0];
|
|
wy = 51 - (((128 << 8) - p_car->Genus.Vehicle->Spring[c0].Compression) >> 8);
|
|
wz = info->DZ[c0];
|
|
|
|
apply_car_matrix(&wx,&wy,&wz);
|
|
|
|
if (c0>=2)
|
|
{
|
|
angle=p_car->Genus.Vehicle->Angle - p_car->Genus.Vehicle->WheelAngle;
|
|
angle=(angle+2048)&2047;
|
|
}
|
|
else
|
|
{
|
|
angle=p_car->Genus.Vehicle->Angle;
|
|
}
|
|
|
|
#ifndef TARGET_DC
|
|
MESH_draw_poly(
|
|
info->WheelPrim,
|
|
(p_car->WorldPos.X>>8)+wx,
|
|
(p_car->WorldPos.Y>>8)+wy,
|
|
(p_car->WorldPos.Z>>8)+wz,
|
|
angle,
|
|
0,
|
|
p_car->Genus.Vehicle->Roll,
|
|
NULL,
|
|
0);
|
|
#else
|
|
MESH_draw_poly(
|
|
info->WheelPrim,
|
|
(p_car->WorldPos.X>>8)+wx,
|
|
(p_car->WorldPos.Y>>8)+wy,
|
|
(p_car->WorldPos.Z>>8)+wz,
|
|
angle,
|
|
0,
|
|
p_car->Genus.Vehicle->Roll,
|
|
NULL,
|
|
0xff);
|
|
#endif
|
|
}
|
|
|
|
if (vp->Smokin)
|
|
{
|
|
vp->Smokin = 0; // must be set each time
|
|
|
|
for(c0=0;c0<4;c0++)
|
|
{
|
|
SLONG wx,wy,wz;
|
|
SLONG speed;
|
|
|
|
wx = info->DX[c0];
|
|
wy = 51 - (((128 << 8) - vp->Spring[c0].Compression) >> 8);
|
|
wz = info->DZ[c0];
|
|
|
|
apply_car_matrix(&wx,&wy,&wz);
|
|
|
|
wx = p_car->WorldPos.X + (wx << 8);
|
|
wy = p_car->WorldPos.Y + (wy << 8);
|
|
wz = p_car->WorldPos.Z + (wz << 8);
|
|
/*
|
|
PARTICLE_Add(wx,wy,wz,
|
|
(rand()&7)-3,20,(rand()&7)-3,
|
|
POLY_PAGE_STEAM,1+((rand()&3)<<2),
|
|
0x3FC9B7A3,PFLAG_FADE|PFLAG_RESIZE,
|
|
500, // life
|
|
60, // size
|
|
1,
|
|
20, // fade
|
|
4);*/
|
|
PARTICLE_Add(wx,wy,wz,
|
|
(rand()&7)-3,20,(rand()&7)-3,
|
|
POLY_PAGE_SMOKECLOUD2,2+((Random()&3)<<2),0x7Fc9B7A3,PFLAG_FADE|PFLAG_RESIZE|PFLAG_SPRITEANI|PFLAG_SPRITELOOP,30,60,1,16,10);
|
|
speed=((vp->VelX>>CAR_VEL_SHIFT)*(vp->VelX>>CAR_VEL_SHIFT))
|
|
+((vp->VelZ>>CAR_VEL_SHIFT)*(vp->VelZ>>CAR_VEL_SHIFT));
|
|
|
|
// TRACE("speed: %d\n",speed);
|
|
if ((speed>200)&&(vp->Skid==SKID_START)) {
|
|
#ifdef PSX
|
|
MFX_play_thing(THING_NUMBER(p_car),S_SKID_START,MFX_MOVING,p_car);
|
|
#else
|
|
if (speed>300000)
|
|
MFX_play_thing(THING_NUMBER(p_car),S_SKID_START,MFX_MOVING,p_car);
|
|
else
|
|
if (speed>100000)
|
|
MFX_play_thing(THING_NUMBER(p_car),S_SKID_START+1,MFX_MOVING,p_car);
|
|
else
|
|
MFX_play_thing(THING_NUMBER(p_car),S_SKID_END,MFX_MOVING,p_car);
|
|
|
|
#endif
|
|
}
|
|
|
|
if ((speed>200)&&(GAME_TURN&1)) {
|
|
#ifndef PSX
|
|
SLONG dx,dz;
|
|
if (vp->oldX[c0]&&vp->oldZ[c0]) {
|
|
dx=(vp->oldX[c0]-wx)>>8;
|
|
dz=(vp->oldZ[c0]-wz)>>8;
|
|
TRACKS_Add(wx,(PAP_calc_map_height_at(wx>>8,wz>>8)+5)<<8,wz,dx,0,dz,TRACK_TYPE_TYRE_SKID,0);
|
|
}
|
|
vp->oldX[c0]=wx;
|
|
vp->oldZ[c0]=wz;
|
|
#else
|
|
TRACKS_Add(wx,(PAP_calc_map_height_at(wx>>8,wz>>8)+5)<<8,wz,-vp->VelX>>7,0,-vp->VelZ>>7,TRACK_TYPE_TYRE_SKID,0);
|
|
#endif
|
|
}
|
|
}
|
|
} else {
|
|
#ifndef PSX
|
|
vp->oldX[0]=vp->oldX[1]=vp->oldX[2]=vp->oldX[3]=0;
|
|
vp->oldZ[0]=vp->oldZ[1]=vp->oldZ[2]=vp->oldZ[3]=0;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
#define WHEEL_GRAV (5<<8)
|
|
|
|
/*
|
|
Suspension is a damped spring connecting each wheel to the car
|
|
|
|
The extension of the spring goes from Min to Max
|
|
If the car is in mid air then the spring is free to extend to max extension.
|
|
|
|
the car at equilibrium, will exert a downward force on each spring of
|
|
|
|
F=Ma F= carMass*Gravity/4 (this assumes the mass is equaly dispersed)
|
|
|
|
The spring will exert an upwards force on the car of
|
|
|
|
Force = k/Extension, (the force is inversely proportional to the extension of the spring)
|
|
|
|
K is the strength of the spring
|
|
|
|
so at equilibrium
|
|
|
|
k/extension = carMass*Gravity/4
|
|
|
|
extension= (k*4)/(carmass*gravity)
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
Suggested Readings:
|
|
|
|
1 "Fundamental of Vehicle Dynamics," Thomas D. Gillespie, 1992, Society of Automotive Engineers, Inc.
|
|
|
|
2. "Theory of Ground Vehicles," J. Y. Wong, John Wiley & Sons, Inc, 1993.
|
|
|
|
3. MOTOR VEHICLE DYNAMICS: MODELING AND SIMULATION by G Genta (Poli. Torino, Italy)
|
|
|
|
*/
|
|
|
|
// ========================================================
|
|
//
|
|
// VEHICLE COLLISION
|
|
//
|
|
// ========================================================
|
|
|
|
extern SLONG there_is_a_los_car(SLONG x1, SLONG y1, SLONG z1,SLONG x2, SLONG y2, SLONG z2);
|
|
extern SLONG should_i_collide_against_this_anim_prim(Thing *p_animprim);
|
|
|
|
VEH_Col VEH_col[VEH_MAX_COL];
|
|
SLONG VEH_col_upto;
|
|
|
|
// VEH_collide_find_things
|
|
//
|
|
// Finds all the things that can possibly be collided with and stores
|
|
// their details int VEH_col[]
|
|
|
|
void VEH_collide_find_things(SLONG x, SLONG y, SLONG z, SLONG radius, SLONG ignore, SLONG ignore_prims)
|
|
{
|
|
static UWORD found[VEH_MAX_COL];
|
|
|
|
SLONG i;
|
|
SLONG num;
|
|
SLONG prim;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG dist;
|
|
|
|
Thing *p_found;
|
|
VEH_Col *vc;
|
|
PrimInfo *pi;
|
|
AnimPrimBbox *apb;
|
|
OB_Info *oi;
|
|
|
|
//
|
|
// Do we include bikes or not?
|
|
//
|
|
|
|
ULONG collide_types;
|
|
|
|
#if BIKE
|
|
if (ignore && TO_THING(ignore)->Class == CLASS_BIKE)
|
|
{
|
|
// This is collision for a bike - include bikes.
|
|
collide_types = (1 << CLASS_VEHICLE) | (1 << CLASS_ANIM_PRIM) | (1 << CLASS_BIKE);
|
|
}
|
|
else
|
|
#else
|
|
{
|
|
// This is collision for a van or car - ignore bikes.
|
|
collide_types = (1 << CLASS_VEHICLE) | (1 << CLASS_ANIM_PRIM) | (1 << CLASS_BAT);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Find everything in our sphere
|
|
//
|
|
|
|
num = THING_find_sphere(
|
|
x,y,z,
|
|
radius + 0x200,
|
|
found,
|
|
VEH_MAX_COL,
|
|
collide_types);
|
|
|
|
//
|
|
// Scan through
|
|
//
|
|
|
|
VEH_col_upto = 0;
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
if (found[i] == ignore) continue;
|
|
|
|
ASSERT(WITHIN(VEH_col_upto, 0, VEH_MAX_COL - 1));
|
|
|
|
p_found = TO_THING(found[i]);
|
|
|
|
switch(p_found->Class)
|
|
{
|
|
case CLASS_VEHICLE:
|
|
|
|
prim = get_vehicle_body_prim(p_found->Genus.Vehicle->Type);
|
|
pi = get_prim_info(prim);
|
|
|
|
// Simple bounding circle rejection.
|
|
|
|
dx = abs((p_found->WorldPos.X >> 8) - x);
|
|
dz = abs((p_found->WorldPos.Z >> 8) - z);
|
|
|
|
dist = QDIST2(dx,dz);
|
|
|
|
if (dist <= radius + pi->radius + 0x10)
|
|
{
|
|
vc = &VEH_col[VEH_col_upto++];
|
|
|
|
vc->type = VEH_COL_TYPE_BBOX;
|
|
vc->ob_index = NULL;
|
|
vc->veh = p_found;
|
|
vc->mid_x = p_found->WorldPos.X >> 8;
|
|
vc->mid_y = p_found->WorldPos.Y >> 8;
|
|
vc->mid_z = p_found->WorldPos.Z >> 8;
|
|
vc->height = pi->maxy;
|
|
vc->min_x = pi->minx;
|
|
vc->min_z = pi->minz;
|
|
vc->max_x = pi->maxx;
|
|
vc->max_z = pi->maxz;
|
|
|
|
vc->radius_or_yaw = p_found->Genus.Vehicle->Angle; // (= yaw for a BBOX)
|
|
}
|
|
|
|
break;
|
|
|
|
case CLASS_ANIM_PRIM:
|
|
#ifdef ANIM_PRIMS
|
|
if (should_i_collide_against_this_anim_prim(p_found))
|
|
{
|
|
apb = &anim_prim_bbox[p_found->Index];
|
|
pi = get_prim_info(p_found->Index);
|
|
|
|
// Simple bounding circle rejection.
|
|
|
|
dx = abs((p_found->WorldPos.X >> 8) - x);
|
|
dz = abs((p_found->WorldPos.Z >> 8) - z);
|
|
|
|
dist = QDIST2(dx,dz);
|
|
|
|
if (dist <= radius + pi->radius + 0x10)
|
|
{
|
|
vc = &VEH_col[VEH_col_upto++];
|
|
|
|
vc->type = VEH_COL_TYPE_BBOX;
|
|
vc->ob_index = NULL;
|
|
vc->veh = NULL;
|
|
vc->mid_x = p_found->WorldPos.X >> 8;
|
|
vc->mid_y = p_found->WorldPos.Y >> 8;
|
|
vc->mid_z = p_found->WorldPos.Z >> 8;
|
|
vc->height = apb->maxy;
|
|
vc->min_x = apb->minx;
|
|
vc->min_z = apb->minz;
|
|
vc->max_x = apb->maxx;
|
|
vc->max_z = apb->maxz;
|
|
|
|
vc->radius_or_yaw = p_found->Draw.Tweened->Angle; // (= yaw for a BBOX)
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
#if BIKE
|
|
|
|
case CLASS_BIKE:
|
|
|
|
vc = &VEH_col[VEH_col_upto++];
|
|
|
|
vc->type = VEH_COL_TYPE_CYLINDER;
|
|
vc->ob_index = NULL;
|
|
vc->veh = NULL;
|
|
vc->mid_x = p_found->WorldPos.X >> 8;
|
|
vc->mid_y = p_found->WorldPos.Y >> 8;
|
|
vc->mid_z = p_found->WorldPos.Z >> 8;
|
|
vc->height = 0x100;
|
|
|
|
vc->radius_or_yaw = 0x40; // (= radius for a cylinder)
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case CLASS_BAT:
|
|
|
|
if (p_found->Genus.Bat->type == BAT_TYPE_BALROG)
|
|
{
|
|
vc = &VEH_col[VEH_col_upto++];
|
|
|
|
vc->type = VEH_COL_TYPE_CYLINDER;
|
|
vc->ob_index = NULL;
|
|
vc->veh = p_found;
|
|
vc->mid_x = p_found->WorldPos.X >> 8;
|
|
vc->mid_y = p_found->WorldPos.Y >> 8;
|
|
vc->mid_z = p_found->WorldPos.Z >> 8;
|
|
vc->height = 0x100;
|
|
|
|
vc->radius_or_yaw = 0x40; // (= radius for a cylinder)
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now scan OBs
|
|
//
|
|
|
|
if (!ignore_prims)
|
|
{
|
|
SLONG mx1 = x - radius >> PAP_SHIFT_LO;
|
|
SLONG mz1 = z - radius >> PAP_SHIFT_LO;
|
|
SLONG mx2 = x + radius >> PAP_SHIFT_LO;
|
|
SLONG mz2 = z + radius >> PAP_SHIFT_LO;
|
|
|
|
SATURATE(mx1, 0, PAP_SIZE_LO - 1);
|
|
SATURATE(mz1, 0, PAP_SIZE_LO - 1);
|
|
SATURATE(mx2, 0, PAP_SIZE_LO - 1);
|
|
SATURATE(mz2, 0, PAP_SIZE_LO - 1);
|
|
|
|
for (SLONG mx = mx1; mx <= mx2; mx++)
|
|
{
|
|
for (SLONG mz = mz1; mz <= mz2; mz++)
|
|
{
|
|
for (oi = OB_find(mx,mz); oi->prim; oi++)
|
|
{
|
|
if (VEH_col_upto >= VEH_MAX_COL) return; // erk - out of room in array!
|
|
|
|
if (oi->y >= y + 0x180) continue; // it's above us
|
|
|
|
switch (prim_get_collision_model(oi->prim))
|
|
{
|
|
case PRIM_COLLIDE_BOX:
|
|
case PRIM_COLLIDE_SMALLBOX:
|
|
|
|
pi = get_prim_info(oi->prim);
|
|
|
|
// Simple bounding circle rejection.
|
|
|
|
dx = abs(oi->x - x);
|
|
dz = abs(oi->z - z);
|
|
|
|
dist = QDIST2(dx,dz);
|
|
|
|
if (dist <= radius + pi->radius + 0x10)
|
|
{
|
|
vc = &VEH_col[VEH_col_upto++];
|
|
|
|
vc->type = VEH_COL_TYPE_BBOX;
|
|
vc->ob_index = oi->index;
|
|
vc->veh = NULL;
|
|
vc->mid_x = oi->x;
|
|
vc->mid_y = oi->y;
|
|
vc->mid_z = oi->z;
|
|
vc->height = pi->maxy;
|
|
vc->min_x = pi->minx;
|
|
vc->min_z = pi->minz;
|
|
vc->max_x = pi->maxx;
|
|
vc->max_z = pi->maxz;
|
|
|
|
vc->radius_or_yaw = oi->yaw; // (= yaw for BBOXs)
|
|
}
|
|
|
|
break;
|
|
|
|
case PRIM_COLLIDE_NONE:
|
|
break;
|
|
|
|
case PRIM_COLLIDE_CYLINDER:
|
|
|
|
pi = get_prim_info(oi->prim);
|
|
|
|
// Simple bounding circle rejection.
|
|
|
|
dx = abs(oi->x - x);
|
|
dz = abs(oi->z - z);
|
|
|
|
dist = QDIST2(dx,dz);
|
|
|
|
if (dist <= radius + 0x40)
|
|
{
|
|
vc = &VEH_col[VEH_col_upto++];
|
|
|
|
vc->type = VEH_COL_TYPE_CYLINDER;
|
|
vc->ob_index = oi->index;
|
|
vc->veh = NULL;
|
|
vc->mid_x = oi->x;
|
|
vc->mid_y = oi->y;
|
|
vc->mid_z = oi->z;
|
|
vc->height = pi->maxy;
|
|
|
|
vc->radius_or_yaw = 0x30; // (= radius for cylinders)
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// VEH_shake_fences
|
|
//
|
|
// Shakes all the fence facets that lie on the edge of the given mapsquare.
|
|
|
|
void VEH_shake_fences(SLONG mx, SLONG mz)
|
|
{
|
|
SLONG exit;
|
|
SLONG facet;
|
|
SLONG f_list;
|
|
|
|
DFacet *df;
|
|
|
|
f_list = PAP_2LO(mx >> 2, mz >> 2).ColVectHead;
|
|
|
|
if (f_list)
|
|
{
|
|
exit = FALSE;
|
|
|
|
while(1)
|
|
{
|
|
ASSERT(WITHIN(f_list, 1, next_facet_link - 1));
|
|
|
|
facet = facet_links[f_list];
|
|
|
|
if (facet < 0)
|
|
{
|
|
facet = -facet;
|
|
exit = TRUE;
|
|
}
|
|
|
|
ASSERT(WITHIN(facet, 1, next_dfacet - 1));
|
|
|
|
df = &dfacets[facet];
|
|
|
|
if (df->FacetType == STOREY_TYPE_FENCE ||
|
|
df->FacetType == STOREY_TYPE_FENCE_FLAT ||
|
|
df->FacetType == STOREY_TYPE_FENCE_BRICK)
|
|
{
|
|
if (df->x[0] == df->x[1])
|
|
{
|
|
if (df->x[0] == mx || df->x[0] == mx + 1)
|
|
{
|
|
df->Shake = 255;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(df->z[0] == df->z[1]);
|
|
|
|
if (df->z[0] == mz || df->z[0] == mz + 1)
|
|
{
|
|
df->Shake = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
f_list += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// find closest car point to x,y,z
|
|
|
|
static UBYTE find_closest_car_point(SLONG x, SLONG y, SLONG z, Thing* car)
|
|
{
|
|
Vehicle* v = car->Genus.Vehicle;
|
|
|
|
make_car_matrix(v);
|
|
|
|
PrimInfo *inf;
|
|
SLONG xx[6],yy[6],zz[6];
|
|
|
|
inf = get_prim_info(veh_info[car->Genus.Vehicle->Type].BodyPrim);
|
|
|
|
xx[0]=inf->minx;
|
|
xx[1]=inf->maxx;
|
|
xx[2]=inf->minx;
|
|
xx[3]=inf->maxx;
|
|
xx[4]=inf->minx;
|
|
xx[5]=inf->maxx;
|
|
|
|
yy[0]=inf->miny;
|
|
yy[1]=inf->miny;
|
|
yy[2]=inf->miny;
|
|
yy[3]=inf->miny;
|
|
yy[4]=inf->miny;
|
|
yy[5]=inf->miny;
|
|
|
|
zz[0]=inf->minz;
|
|
zz[1]=inf->minz;
|
|
zz[2]=(inf->minz + inf->maxz)/2;
|
|
zz[3]=(inf->minz + inf->maxz)/2;
|
|
zz[4]=inf->maxz;
|
|
zz[5]=inf->maxz;
|
|
|
|
SLONG best_manhattan_distance = 0x7FFFFFFF;
|
|
SLONG nearest = -1;
|
|
|
|
for (int ii = 0; ii < 6; ii++)
|
|
{
|
|
apply_car_matrix(&xx[ii],&yy[ii],&zz[ii]);
|
|
SLONG manhattan = abs(xx[ii] + (car->WorldPos.X >> 8) - x) + abs(zz[ii] + (car->WorldPos.Z >> 8) - z);
|
|
if (manhattan < best_manhattan_distance)
|
|
{
|
|
best_manhattan_distance = manhattan;
|
|
nearest = ii;
|
|
}
|
|
}
|
|
|
|
ASSERT(nearest != -1);
|
|
|
|
return nearest;
|
|
}
|
|
|
|
// add car-car damage
|
|
//
|
|
// (only if one of the cars is driven by player)
|
|
|
|
void VEH_co_damage(Thing* v1, Thing* v2)
|
|
{
|
|
if (!is_driven_by_player(v1) && !is_driven_by_player(v2)) return;
|
|
|
|
UBYTE c1 = find_closest_car_point(v2->WorldPos.X >> 8, v2->WorldPos.Y >> 8, v2->WorldPos.Z >> 8, v1);
|
|
UBYTE c2 = find_closest_car_point(v1->WorldPos.X >> 8, v1->WorldPos.Y >> 8, v1->WorldPos.Z >> 8, v2);
|
|
SLONG damage;
|
|
|
|
MFX_play_thing(THING_NUMBER(v1),SOUND_Range(S_CAR_SMASH_START,S_CAR_SMASH_END),0,v1);
|
|
// give most damage to slower vehicle (faster vehicle gets caned already)
|
|
if (abs(v1->Velocity) > abs(v2->Velocity))
|
|
{
|
|
VEH_add_damage(v1->Genus.Vehicle, c1, (abs(v1->Velocity) + abs(v2->Velocity)) / 1024);
|
|
VEH_add_damage(v2->Genus.Vehicle, c2, abs(v1->Velocity) / 512);
|
|
VEH_bounce(v1->Genus.Vehicle, c1, abs(v1->Velocity));
|
|
VEH_bounce(v2->Genus.Vehicle, c2, abs(v1->Velocity));
|
|
}
|
|
else
|
|
{
|
|
VEH_add_damage(v1->Genus.Vehicle, c1, abs(v2->Velocity) / 512);
|
|
VEH_add_damage(v2->Genus.Vehicle, c2, (abs(v1->Velocity) + abs(v2->Velocity)) / 1024);
|
|
VEH_bounce(v1->Genus.Vehicle, c1, abs(v2->Velocity));
|
|
VEH_bounce(v2->Genus.Vehicle, c2, abs(v2->Velocity));
|
|
}
|
|
|
|
// set 2nd vehicle moving
|
|
Vehicle* vv1 = v1->Genus.Vehicle;
|
|
Vehicle* vv2 = v2->Genus.Vehicle;
|
|
|
|
vv2->VelX = -vv2->VelX/8 + vv1->VelX/4;
|
|
vv2->VelZ = -vv2->VelZ/8 + vv1->VelZ/4;
|
|
|
|
SLONG torque = abs(vv1->VelX) + abs(vv1->VelZ);
|
|
torque >>= 10;
|
|
|
|
switch (c2)
|
|
{
|
|
case 0:
|
|
case 5:
|
|
vv2->VelR -= torque;
|
|
break;
|
|
|
|
case 1:
|
|
case 4:
|
|
vv2->VelR += torque;
|
|
break;
|
|
}
|
|
|
|
vv2->Skid = SKID_START;
|
|
// make unstable
|
|
vv2->Stable = 0;
|
|
v2->StateFn = VEH_driving;
|
|
}
|
|
|
|
// GetCarPoints
|
|
//
|
|
// generate corner points of the car
|
|
|
|
static inline void GetCarPoints(Thing* p_car, SLONG* x, SLONG* y, SLONG* z, SLONG step)
|
|
{
|
|
Vehicle* veh;
|
|
PrimInfo* pinfo;
|
|
int ii;
|
|
|
|
veh = p_car->Genus.Vehicle;
|
|
pinfo = get_prim_info(veh_info[veh->Type].BodyPrim);
|
|
|
|
// get points in car frame
|
|
x[0] = pinfo->minx;
|
|
x[1] = pinfo->maxx;
|
|
x[2] = pinfo->maxx;
|
|
x[3] = pinfo->minx;
|
|
|
|
y[0] = pinfo->miny;
|
|
y[1] = pinfo->miny;
|
|
y[2] = pinfo->miny;
|
|
y[3] = pinfo->miny;
|
|
|
|
z[0] = pinfo->minz;
|
|
z[1] = pinfo->minz;
|
|
z[2] = pinfo->maxz;
|
|
z[3] = pinfo->maxz;
|
|
|
|
// make car matrix for new position (ignore tilt & roll)
|
|
make_car_matrix_p((veh->Angle + ((veh->VelR * step) >> TICK_SHIFT)) & 2047, 0, 0);
|
|
|
|
// transform to world coordinates
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
apply_car_matrix(&x[ii],&y[ii],&z[ii]);
|
|
|
|
x[ii] += (p_car->WorldPos.X + ((veh->VelX * step) >> TICK_SHIFT)) >> 8;
|
|
y[ii] += (p_car->WorldPos.Y + ((veh->VelY * step) >> TICK_SHIFT)) >> 8;
|
|
z[ii] += (p_car->WorldPos.Z + ((veh->VelZ * step) >> TICK_SHIFT)) >> 8;
|
|
}
|
|
}
|
|
|
|
// DoDamage
|
|
//
|
|
// do damage to a prim after a collision
|
|
|
|
static void DoDamage(Thing* p_car, VEH_Col* col)
|
|
{
|
|
// do car damage
|
|
if (col->veh && col->veh->Class == CLASS_VEHICLE) // Might be a Balrog!
|
|
{
|
|
// damage the other cars health
|
|
{
|
|
SLONG speed = p_car->Velocity >> 5;
|
|
|
|
speed -= 16;
|
|
|
|
if (speed > 0)
|
|
{
|
|
col->veh->Genus.Vehicle->Health -= speed>>1;
|
|
}
|
|
}
|
|
|
|
VEH_co_damage(p_car, col->veh);
|
|
}
|
|
// do ob damage
|
|
if (col->ob_index)
|
|
{
|
|
Vehicle* veh = p_car->Genus.Vehicle;
|
|
SLONG vel = Root(veh->VelX*veh->VelX + veh->VelZ*veh->VelZ);
|
|
|
|
if (vel > 20000) {
|
|
OB_damage(col->ob_index,
|
|
p_car->WorldPos.X >> 8, p_car->WorldPos.Y >> 8,
|
|
col->mid_x, col->mid_z,
|
|
get_vehicle_driver(p_car));
|
|
MFX_play_thing(THING_NUMBER(p_car),SOUND_Range(S_CAR_SMASH_START,S_CAR_SMASH_END),0,p_car);
|
|
} else {
|
|
if (vel > 10000)
|
|
MFX_play_thing(THING_NUMBER(p_car),SOUND_Range(S_CAR_SMASH_START,S_CAR_SMASH_END),0,p_car);
|
|
else // find a wimpy minor scrape?
|
|
if (vel>1000) // otherwise you get crunchy noises when 'stuck' against objects
|
|
MFX_play_thing(THING_NUMBER(p_car),SOUND_Range(S_CAR_SMASH_START,S_CAR_SMASH_START+1),0,p_car);
|
|
else
|
|
if (vel>250)
|
|
MFX_play_thing(THING_NUMBER(p_car),S_CAR_BUMP,0,p_car);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// CollideCar
|
|
//
|
|
// check car for collisions
|
|
|
|
#define COLL_NONE 0x00
|
|
|
|
#define COLL_FL 0x01 // front-left corner
|
|
#define COLL_FR 0x02 // front-right corner
|
|
#define COLL_BR 0x03 // back-right corner
|
|
#define COLL_BL 0x04 // back-left corner
|
|
#define COLL_FRONT 0x05 // front edge
|
|
#define COLL_BACK 0x06 // back edge
|
|
#define COLL_LEFT 0x07 // left edge
|
|
#define COLL_RIGHT 0x08 // right edge
|
|
#define COLL_ALL 0x09 // all edges
|
|
|
|
extern UBYTE last_mav_square_x;
|
|
extern UBYTE last_mav_square_z;
|
|
extern SBYTE last_mav_dx;
|
|
extern SBYTE last_mav_dz;
|
|
|
|
static void CollideWithKerb(Thing* p_car);
|
|
static void process_car(Thing *p_car);
|
|
|
|
// 1
|
|
// 0 1
|
|
// 8 2
|
|
// 3 2
|
|
// 4
|
|
void nudge_car(Thing* p_car,SLONG flags,SLONG *x,SLONG *z,SLONG neg)
|
|
{
|
|
SLONG dx=0,dz=0;
|
|
switch(flags&15)
|
|
{
|
|
case 1+2:
|
|
dx=x[3]-x[1];
|
|
dz=z[3]-z[1];
|
|
break;
|
|
case 2+4:
|
|
dx=x[0]-x[2];
|
|
dz=z[0]-z[2];
|
|
break;
|
|
case 4+8:
|
|
dx=x[1]-x[3];
|
|
dz=z[1]-z[3];
|
|
break;
|
|
case 8+1:
|
|
dx=x[2]-x[0];
|
|
dz=z[2]-z[0];
|
|
break;
|
|
|
|
case 1:
|
|
case 1+8+2:
|
|
//front
|
|
dx=x[2]-x[1];
|
|
dz=z[2]-z[1];
|
|
break;
|
|
|
|
case 2:
|
|
case 2+1+4:
|
|
//rhs
|
|
dx=x[0]-x[1];
|
|
dz=z[0]-z[1];
|
|
break;
|
|
|
|
case 4:
|
|
case 4+2+8:
|
|
//back
|
|
dx=x[1]-x[2];
|
|
dz=z[1]-z[2];
|
|
break;
|
|
|
|
case 8:
|
|
case 8+4+1:
|
|
//lhs
|
|
dx=x[1]-x[0];
|
|
dz=z[1]-z[0];
|
|
break;
|
|
}
|
|
|
|
// dx>>=4;
|
|
// dz>>=4;
|
|
#ifndef NDEBUG
|
|
if(ShiftFlag)
|
|
{
|
|
dx<<=8;
|
|
dz<<=8;
|
|
}
|
|
#endif
|
|
|
|
if(neg)
|
|
{
|
|
dx=-dx;
|
|
dz=-dz;
|
|
}
|
|
|
|
if(dx||dz)
|
|
{
|
|
GameCoord new_pos;
|
|
|
|
new_pos.X = p_car->WorldPos.X + dx;
|
|
new_pos.Y = p_car->WorldPos.Y;
|
|
new_pos.Z = p_car->WorldPos.Z + dz;
|
|
|
|
move_thing_on_map(p_car, &new_pos);
|
|
|
|
}
|
|
|
|
}
|
|
SLONG car_hit_flags;
|
|
static SLONG CollideCar(Thing* p_car, SLONG step)
|
|
{
|
|
Vehicle* veh = p_car->Genus.Vehicle;
|
|
VehInfo* vinfo = &veh_info[veh->Type];
|
|
|
|
SLONG x[4],y[4],z[4];
|
|
int ii;
|
|
|
|
car_hit_flags=0;
|
|
|
|
#ifdef _DEBUG
|
|
if (Keys[KB_H] && is_driven_by_player(p_car)) return 0;
|
|
#endif
|
|
|
|
// hit the kerb?
|
|
CollideWithKerb(p_car);
|
|
|
|
// run suspension now and save results
|
|
SLONG old_y = p_car->WorldPos.Y;
|
|
process_car(p_car);
|
|
veh->VelY = p_car->WorldPos.Y - old_y;
|
|
p_car->WorldPos.Y = old_y;
|
|
|
|
#if DUMP_COORDS
|
|
GetCarPoints(p_car, x, y, z, 0);
|
|
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
TRACE("Before move\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// generate corner points of the car, pushed out slightly
|
|
GetCarPoints(p_car, x, y, z, step);
|
|
|
|
#if DUMP_COORDS
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
TRACE("After move\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
UBYTE flags = 0,pflags=0;
|
|
static UBYTE flags_to_code[16] =
|
|
{
|
|
COLL_NONE, COLL_FRONT, COLL_NONE, COLL_FR,
|
|
COLL_BACK, COLL_LEFT, COLL_BR, COLL_RIGHT,
|
|
COLL_NONE, COLL_FL, COLL_FRONT, COLL_FRONT,
|
|
COLL_BL, COLL_LEFT, COLL_BACK, COLL_ALL
|
|
// COLL_NONE, COLL_FRONT, COLL_RIGHT, COLL_FR,
|
|
// COLL_BACK, COLL_LEFT, COLL_BR, COLL_RIGHT,
|
|
// COLL_LEFT, COLL_FL, COLL_FRONT, COLL_FRONT,
|
|
// COLL_BL, COLL_LEFT, COLL_BACK, COLL_ALL
|
|
};
|
|
|
|
// check each edge against the walls
|
|
if (!VEH_collide_line_ignore_walls)
|
|
{
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
int jj = (ii + 1) & 3;
|
|
int aa;
|
|
|
|
if (aa = there_is_a_los_car(x[ii],y[ii],z[ii], x[jj],y[jj],z[jj]))
|
|
{
|
|
// hit a wall or fence
|
|
flags |= 1 << ii;
|
|
|
|
if (aa == 1) flags |= 16; // X wall
|
|
if (aa == 2) flags |= 32; // Z wall
|
|
|
|
if (veh->Scrapin<10) veh->Scrapin++;
|
|
|
|
// if (is_driven_by_player(p_car))
|
|
// AENG_world_line((x[ii] + x[jj]) / 2, (y[ii] + y[jj]) / 2, (z[ii] + z[jj]) / 2, 32, 0xffffff,
|
|
// (x[ii] + x[jj]) / 2, (y[ii] + y[jj]) / 2 + 0xC00, (z[ii] + z[jj]) / 2, 0, 0xffffff,
|
|
// TRUE);
|
|
|
|
// shake fence
|
|
#ifndef PSX
|
|
VEH_shake_fences(last_mav_square_x, last_mav_square_z);
|
|
#endif
|
|
}
|
|
}
|
|
nudge_car(p_car,flags,x,z,0);
|
|
|
|
|
|
if (((flags & 15) == 8) || ((flags & 15) == 2) || ((flags & 15) == 10))
|
|
{
|
|
// if (is_driven_by_player(p_car)) TRACE("Facet my flags up [%2.2X]\n", flags);
|
|
flags |= 1; // front
|
|
}
|
|
}
|
|
if (veh->Scrapin>5) {
|
|
SLONG vel = (veh->VelX >> CAR_VEL_SHIFT) * (veh->VelX >> CAR_VEL_SHIFT) + (veh->VelZ >> CAR_VEL_SHIFT) * (veh->VelZ >> CAR_VEL_SHIFT);
|
|
if (vel>300)
|
|
MFX_play_thing(THING_NUMBER(p_car),SOUND_Range(S_CAR_SCRAPE_START,S_CAR_SCRAPE_END),MFX_MOVING,p_car);
|
|
if (veh->Scrapin>0) veh->Scrapin-=2;
|
|
|
|
if (flags & 15)
|
|
{
|
|
SLONG px = 0;
|
|
SLONG pz = 0;
|
|
SLONG div = 0;
|
|
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
int jj = (ii + 1) & 3;
|
|
|
|
if (flags & (1 << ii))
|
|
{
|
|
px += x[ii] + x[jj];
|
|
pz += z[ii] + z[jj];
|
|
div += 2;
|
|
}
|
|
}
|
|
|
|
px /= div;
|
|
pz /= div;
|
|
|
|
#ifndef PSX
|
|
DIRT_new_sparks(px,y[0],pz,2);
|
|
#endif
|
|
|
|
/* AENG_world_line(px, y[0], pz, 32, 0xFFFFFF,
|
|
(x[0] + x[1] + x[2] + x[3])/4, y[0], (z[0] + z[1] + z[2] + z[3])/4, 0, 0xFFFFFF,
|
|
TRUE);*/
|
|
}
|
|
}
|
|
|
|
// check against prims
|
|
for (ii = 0; ii < VEH_col_upto; ii++)
|
|
{
|
|
VEH_Col* vc = &VEH_col[ii];
|
|
int inside = 1;
|
|
|
|
if (vc->type == VEH_COL_TYPE_BBOX)
|
|
{
|
|
int jj;
|
|
|
|
for (jj = 0; jj < 4; jj++)
|
|
{
|
|
int kk = (jj + 1) & 3;
|
|
|
|
if (collide_box_with_line( vc->mid_x,
|
|
vc->mid_z,
|
|
vc->min_x,
|
|
vc->min_z,
|
|
vc->max_x,
|
|
vc->max_z,
|
|
vc->radius_or_yaw,
|
|
x[jj], z[jj],
|
|
x[kk], z[kk]))
|
|
{
|
|
pflags |= 1 << jj;
|
|
|
|
pflags |= 48; // just bounce
|
|
|
|
// if (is_driven_by_player(p_car))
|
|
// AENG_world_line((x[jj] + x[kk]) / 2, (y[jj] + y[kk]) / 2, (z[jj] + z[kk]) / 2, 32, 0xff0000,
|
|
// (x[jj] + x[kk]) / 2, (y[jj] + y[kk]) / 2 + 0xC00, (z[jj] + z[kk]) / 2, 0, 0xff0000,
|
|
// TRUE);
|
|
|
|
DoDamage(p_car, vc);
|
|
}
|
|
|
|
SLONG x1 = x[kk] - x[jj];
|
|
SLONG z1 = z[kk] - z[jj];
|
|
SLONG x2 = vc->mid_x - x[jj];
|
|
SLONG z2 = vc->mid_z - z[jj];
|
|
|
|
if (x2*z1 > x1*z2) inside = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int jj;
|
|
|
|
ASSERT(vc->type == VEH_COL_TYPE_CYLINDER);
|
|
|
|
for (jj = 0; jj < 4; jj++)
|
|
{
|
|
int kk = (jj + 1) & 3;
|
|
|
|
if (distance_to_line(x[jj], z[jj],
|
|
x[kk], z[kk],
|
|
vc->mid_x,
|
|
vc->mid_z) < vc->radius_or_yaw)
|
|
{
|
|
pflags |= 1 << jj;
|
|
|
|
pflags |= 48;
|
|
|
|
// if (is_driven_by_player(p_car))
|
|
// AENG_world_line((x[kk] + x[jj]) / 2, (y[kk] + y[jj]) / 2, (z[kk] + z[jj]) / 2, 32, 0xff00,
|
|
// (x[kk] + x[jj]) / 2, (y[kk] + y[jj]) / 2 + 0xC00, (z[kk] + z[jj]) / 2, 0, 0xff00,
|
|
// TRUE);
|
|
|
|
DoDamage(p_car, vc);
|
|
|
|
if (vc->veh && vc->veh->Class == CLASS_BAT)
|
|
{
|
|
//
|
|
// This car has it a Balrog! The driver is terrified!
|
|
//
|
|
|
|
if (veh->Driver)
|
|
{
|
|
//
|
|
// Make the car driver run away.
|
|
//
|
|
|
|
PCOM_make_driver_run_away(TO_THING(veh->Driver), vc->veh);
|
|
|
|
//
|
|
// Make the Balrog angry with the driver!
|
|
//
|
|
|
|
vc->veh->Genus.Bat->target = veh->Driver;
|
|
vc->veh->Genus.Bat->timer = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
SLONG x1 = x[kk] - x[jj];
|
|
SLONG z1 = z[kk] - z[jj];
|
|
SLONG x2 = vc->mid_x - x[jj];
|
|
SLONG z2 = vc->mid_z - z[jj];
|
|
|
|
if (x2*z1 > x1*z2) inside = 0;
|
|
}
|
|
}
|
|
|
|
if ((((flags|pflags) & 15) == 8) || (((flags|pflags) & 15) == 2) || (((flags|pflags) & 15) == 10))
|
|
{
|
|
// if (is_driven_by_player(p_car)) TRACE("Fix my flags up [%2.2X]\n", flags);
|
|
SLONG dsf = ((x[0] + x[1])/2 - vc->mid_x) * ((x[0] + x[1])/2 - vc->mid_x) + ((z[0] + z[1])/2 - vc->mid_z) * ((z[0] + z[1])/2 - vc->mid_z);
|
|
SLONG dsb = ((x[2] + x[3])/2 - vc->mid_x) * ((x[2] + x[3])/2 - vc->mid_x) + ((z[2] + z[3])/2 - vc->mid_z) * ((z[2] + z[3])/2 - vc->mid_z);
|
|
if (dsf < dsb) pflags |= 1; // front
|
|
else pflags |= 4; // back
|
|
}
|
|
|
|
// prim is inside car - bounce off
|
|
if (!(flags|pflags) && inside)
|
|
{
|
|
// if (is_driven_by_player(p_car)) TRACE("INSIDE\n");
|
|
pflags = 49;
|
|
DoDamage(p_car, vc);
|
|
}
|
|
}
|
|
|
|
if(pflags)
|
|
{
|
|
if(flags)
|
|
nudge_car(p_car,flags,x,z,1);
|
|
}
|
|
flags|=pflags;
|
|
|
|
//
|
|
// return if no collision
|
|
//
|
|
|
|
if (!flags) return 0;
|
|
|
|
//
|
|
// There has been a collision...
|
|
// Damage the car depending on how fast it is going
|
|
//
|
|
|
|
#if !defined(FAST_EDDIE) || !defined(_DEBUG)
|
|
{
|
|
SLONG speed = p_car->Velocity >> 5;
|
|
|
|
speed -= 16;
|
|
|
|
if (speed > 0)
|
|
{
|
|
veh->Health -= speed>>1; //miked cars were blowing up too easy so I've halved the damage, Ive increased cars health to 300 so they are harder to shoot too
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// set flag
|
|
//
|
|
|
|
UBYTE code = flags_to_code[flags & 15];
|
|
SLONG torque;
|
|
|
|
if (!is_driven_by_player(p_car)) veh->Wheel = 0;
|
|
|
|
torque = 0;
|
|
if (flags & 16) torque += abs(veh->VelX);
|
|
if (flags & 32) torque += abs(veh->VelZ);
|
|
torque >>= 9;
|
|
|
|
switch (code)
|
|
{
|
|
case COLL_FRONT:
|
|
case COLL_BACK:
|
|
p_car->Flags |= FLAGS_COLLIDED;
|
|
// Fallthrough!
|
|
|
|
case COLL_LEFT:
|
|
case COLL_RIGHT:
|
|
if (flags & 16) veh->VelX = -veh->VelX/4;
|
|
if (flags & 32) veh->VelZ = -veh->VelZ/4;
|
|
veh->Skid = SKID_START;
|
|
break;
|
|
|
|
case COLL_FL:
|
|
case COLL_BR:
|
|
p_car->Flags |= FLAGS_COLLIDED;
|
|
veh->VelR -= torque;
|
|
if (veh->VelR > -10) veh->VelR = -10;
|
|
if (flags & 16) veh->VelX = -veh->VelX/8;
|
|
if (flags & 32) veh->VelZ = -veh->VelZ/8;
|
|
// veh->Skid = SKID_START;
|
|
break;
|
|
|
|
case COLL_FR:
|
|
case COLL_BL:
|
|
p_car->Flags |= FLAGS_COLLIDED;
|
|
veh->VelR += torque;
|
|
if (veh->VelR < +10) veh->VelR = +10;
|
|
if (flags & 16) veh->VelX = -veh->VelX/8;
|
|
if (flags & 32) veh->VelZ = -veh->VelZ/8;
|
|
// veh->Skid = SKID_START;
|
|
break;
|
|
|
|
case COLL_ALL:
|
|
veh->VelX = 0;
|
|
veh->VelZ = 0;
|
|
veh->VelR = 0;
|
|
p_car->Velocity = 0;
|
|
veh->Skid = SKID_START;
|
|
break;
|
|
}
|
|
|
|
// reduce velocity to prevent bounce
|
|
p_car->Velocity >>= 1;
|
|
|
|
// regenerate car points for new position
|
|
GetCarPoints(p_car, x, y, z, step);
|
|
|
|
#if DUMP_COORDS
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
TRACE("After bounce\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// check each edge against the walls
|
|
if (!VEH_collide_line_ignore_walls)
|
|
{
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
int jj = (ii + 1) & 3;
|
|
|
|
if (there_is_a_los_car(x[ii],y[ii],z[ii], x[jj],y[jj],z[jj]))
|
|
{
|
|
// fucked it all up - don't move anywhere or we'll be in the shite
|
|
// if (is_driven_by_player(p_car)) TRACE("ARSE CUNT MOTHERFUCKER\n", flags);
|
|
veh->VelX = 0;
|
|
veh->VelY = 0;
|
|
veh->VelZ = 0;
|
|
veh->VelR = 0;
|
|
p_car->Velocity = 0;
|
|
veh->Skid = SKID_START;
|
|
|
|
#if DUMP_COORDS
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
GetCarPoints(p_car, x, y, z, step);
|
|
|
|
TRACE("After halt vs walls\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
int jj = (ii + 1) & 3;
|
|
ASSERT(!there_is_a_los_car(x[ii],y[ii],z[ii], x[jj],y[jj],z[jj]));
|
|
}
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check against prims
|
|
for (ii = 0; ii < VEH_col_upto; ii++)
|
|
{
|
|
VEH_Col* vc = &VEH_col[ii];
|
|
|
|
if (vc->type == VEH_COL_TYPE_BBOX)
|
|
{
|
|
int jj;
|
|
bool slide;
|
|
|
|
slide = false;
|
|
|
|
do
|
|
{
|
|
for (jj = 0; jj < 4; jj++)
|
|
{
|
|
int kk = (jj + 1) & 3;
|
|
|
|
if (collide_box_with_line( vc->mid_x,
|
|
vc->mid_z,
|
|
vc->min_x + 0x10,
|
|
vc->min_z + 0x10,
|
|
vc->max_x - 0x10,
|
|
vc->max_z - 0x10,
|
|
vc->radius_or_yaw,
|
|
x[jj], z[jj],
|
|
x[kk], z[kk]))
|
|
{
|
|
// fucked up
|
|
if (!slide)
|
|
{
|
|
// move us a *tiny* bit away from the other object
|
|
veh->VelX = ((p_car->WorldPos.X >> 8) - vc->mid_x) >> 4;
|
|
veh->VelZ = ((p_car->WorldPos.Z >> 8) - vc->mid_z) >> 4;
|
|
veh->VelR = 0;
|
|
slide = true;
|
|
veh->Skid = SKID_START;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
veh->VelX = 0;
|
|
veh->VelZ = 0;
|
|
veh->VelR = 0;
|
|
p_car->Velocity = 0;
|
|
#if DUMP_COORDS
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
GetCarPoints(p_car, x, y, z, step);
|
|
|
|
TRACE("After halt vs square prim\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
|
|
for (jj = 0; jj < 4; jj++)
|
|
{
|
|
int kk = (jj + 1) & 3;
|
|
|
|
ASSERT(!collide_box_with_line(vc->mid_x,
|
|
vc->mid_z,
|
|
vc->min_x + 0x10,
|
|
vc->min_z + 0x10,
|
|
vc->max_x - 0x10,
|
|
vc->max_z - 0x10,
|
|
vc->radius_or_yaw,
|
|
x[jj], z[jj],
|
|
x[kk], z[kk]));
|
|
}
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
if (jj == 4) break;
|
|
|
|
if (slide)
|
|
{
|
|
GetCarPoints(p_car, x, y, z, step);
|
|
|
|
#if DUMP_COORDS
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
TRACE("After slide (square prim)\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} while (slide);
|
|
}
|
|
else
|
|
{
|
|
int jj;
|
|
bool slide;
|
|
|
|
ASSERT(vc->type == VEH_COL_TYPE_CYLINDER);
|
|
|
|
slide = false;
|
|
|
|
do
|
|
{
|
|
for (jj = 0; jj < 4; jj++)
|
|
{
|
|
int kk = (jj + 1) & 3;
|
|
|
|
if (distance_to_line(x[jj], z[jj],
|
|
x[kk], z[kk],
|
|
vc->mid_x,
|
|
vc->mid_z) < vc->radius_or_yaw - 0x10)
|
|
{
|
|
// fucked up
|
|
if (!slide)
|
|
{
|
|
// try sliding car forwards
|
|
SLONG vel = Root((veh->VelX >> 4) * (veh->VelX >> 4) + (veh->VelZ >> 4) * (veh->VelZ >> 4));
|
|
veh->VelR = 0;
|
|
veh->VelX = (vel * SIN(veh->Angle & 2047)) >> 12;
|
|
veh->VelZ = (vel * COS(veh->Angle & 2047)) >> 12;
|
|
slide = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
veh->VelX = 0;
|
|
veh->VelZ = 0;
|
|
veh->VelR = 0;
|
|
p_car->Velocity = 0;
|
|
#if DUMP_COORDS
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
GetCarPoints(p_car, x, y, z, step);
|
|
|
|
TRACE("After halt vs round prim\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
|
|
for (jj = 0; jj < 4; jj++)
|
|
{
|
|
int kk = (jj + 1) & 3;
|
|
|
|
ASSERT(distance_to_line(x[jj],z[jj],x[kk],z[kk],vc->mid_x,vc->mid_z) >= vc->radius_or_yaw - 0x10);
|
|
}
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
if (jj == 4) break;
|
|
|
|
if (slide)
|
|
{
|
|
GetCarPoints(p_car, x, y, z, step);
|
|
|
|
#if DUMP_COORDS
|
|
if (is_driven_by_player(p_car))
|
|
{
|
|
TRACE("After slide (round prim)\n");
|
|
for (ii = 0; ii < 4; ii++)
|
|
{
|
|
TRACE("[%d] = %8.8X %8.8X %8.8X\n", ii, x[ii], y[ii], z[ii]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} while (slide);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// CollideWithKerb
|
|
//
|
|
// nudge the car away from the kerb
|
|
|
|
static void CollideWithKerb(Thing* p_car)
|
|
{
|
|
Vehicle* veh = p_car->Genus.Vehicle;
|
|
VehInfo* vinfo = &veh_info[veh->Type];
|
|
|
|
// make car matrix for new position (ignore tilt & roll)
|
|
make_car_matrix_p((veh->Angle + ((veh->VelR * TICK_RATIO) >> TICK_SHIFT)) & 2047, 0, 0);
|
|
|
|
// generate flags
|
|
UBYTE on_road = 0;
|
|
|
|
for (SLONG wheel = 0; wheel < 4; wheel++)
|
|
{
|
|
SLONG wx = vinfo->DX[wheel];
|
|
SLONG wy = 0;
|
|
SLONG wz = vinfo->DZ[wheel];
|
|
|
|
apply_car_matrix(&wx, &wy, &wz);
|
|
|
|
SLONG papx = wx + (p_car->WorldPos.X >> 8);
|
|
SLONG papz = wz + (p_car->WorldPos.Z >> 8);
|
|
|
|
if (ROAD_is_road(papx >> 8, papz >> 8)) on_road |= (1 << wheel);
|
|
}
|
|
|
|
UBYTE change = (on_road ^ veh->OnRoadFlags);
|
|
|
|
if (change && !veh->DControl)
|
|
{
|
|
#define KERB_TURN 16
|
|
|
|
// lower your shields and prepare to be boarded. your angle will be assimilated and merged with ours. resistance is futile.
|
|
static SLONG towards_table[8] = { 0x000, 0x200, 0x200, 0x400, 0x400, 0x600, 0x600, 0x800 }; // takes top 3 bits to nearest axis
|
|
SLONG towards = towards_table[veh->Angle >> 8];
|
|
|
|
if ((towards - veh->Angle) < -(KERB_TURN * TICK_RATIO) >> TICK_SHIFT)
|
|
{
|
|
veh->VelR -= KERB_TURN;
|
|
}
|
|
else if ((towards - veh->Angle) > (KERB_TURN * TICK_RATIO) >> TICK_SHIFT)
|
|
{
|
|
veh->VelR += KERB_TURN;
|
|
}
|
|
}
|
|
veh->OnRoadFlags = on_road;
|
|
}
|
|
|
|
// GetRunoverHP
|
|
//
|
|
// Get dot product of vehicle velocity with vector from vehicle centre
|
|
// to person - this gives an indication of the amount of HP the person
|
|
// must lose
|
|
|
|
SLONG GetRunoverHP(Thing* p_car, Thing* p_person)
|
|
{
|
|
SLONG tx = (p_person->WorldPos.X - p_car->WorldPos.X) >> 8;
|
|
SLONG tz = (p_person->WorldPos.Z - p_car->WorldPos.Z) >> 8;
|
|
|
|
SLONG tt = Root(tx*tx + tz*tz) * 200; // 200 units per HP
|
|
|
|
SLONG hp = abs(p_car->Genus.Vehicle->VelX * tx + p_car->Genus.Vehicle->VelZ * tz) / tt;
|
|
|
|
if (hp < 10) hp = 10;
|
|
|
|
return hp;
|
|
}
|
|
|
|
|
|
//
|
|
// Throws a person out of the car and knocks him over.
|
|
//
|
|
|
|
void VEH_throw_out_person(Thing *p_person, Thing *p_vehicle)
|
|
{
|
|
set_person_exit_vehicle(p_person);
|
|
|
|
knock_person_down(
|
|
p_person,
|
|
30,
|
|
p_vehicle->WorldPos.X >> 8,
|
|
p_vehicle->WorldPos.Z >> 8,
|
|
NULL);
|
|
}
|
|
|
|
|
|
|
|
//===============================================================
|
|
//
|
|
// Car state function
|
|
//
|
|
//===============================================================
|
|
|
|
// VEH_driving
|
|
//
|
|
// vehicle state handler
|
|
|
|
static void do_car_input(Thing *p_thing);
|
|
|
|
void VEH_driving(Thing *p_thing)
|
|
{
|
|
WaveParams car;
|
|
DrawMesh *dm = p_thing->Draw.Mesh;
|
|
Vehicle *veh = p_thing->Genus.Vehicle;
|
|
SLONG dx,dy,dz;
|
|
SLONG coltype;
|
|
SLONG dwheel;
|
|
|
|
dy = 0;
|
|
|
|
//
|
|
// Make sure that this is a furniture thing and everything is valid.
|
|
//
|
|
|
|
ASSERT(p_thing->Class == CLASS_VEHICLE);
|
|
|
|
//
|
|
// Forget you've been shot at every once in a while...
|
|
//
|
|
|
|
if ((GAME_TURN & 0x1f) == 0)
|
|
{
|
|
veh->Flags &= ~FLAG_VEH_SHOT_AT;
|
|
}
|
|
|
|
if (veh->Health <= 0)
|
|
{
|
|
veh->Steering = 0;
|
|
veh->DControl = 0;
|
|
|
|
if (p_thing->State != STATE_DEAD) // Magic number... we've already blown up!
|
|
{
|
|
//
|
|
// Blow up!
|
|
//
|
|
|
|
#ifdef PSX
|
|
POW_create(
|
|
POW_CREATE_LARGE_SEMI,
|
|
p_thing->WorldPos.X,
|
|
p_thing->WorldPos.Y,
|
|
p_thing->WorldPos.Z,0,0,0);
|
|
#else
|
|
{
|
|
Thing *pyro;
|
|
SLONG wave;
|
|
pyro=PYRO_create(p_thing->WorldPos,PYRO_FIREBOMB);
|
|
if (pyro)
|
|
pyro->Genus.Pyro->Flags|=PYRO_FLAGS_WAVE;
|
|
wave=S_EXPLODE_MEDIUM;
|
|
if (!(Random()&3)) wave++; // 25% chance of a bigger bang than usual
|
|
MFX_play_xyz(THING_NUMBER(p_thing),wave,0,p_thing->WorldPos.X,p_thing->WorldPos.Y,p_thing->WorldPos.Z);
|
|
}
|
|
#endif
|
|
VEH_bounce(veh, 0, 4000);
|
|
|
|
veh->damage[0] = 4;
|
|
veh->damage[1] = 4;
|
|
veh->damage[2] = 4;
|
|
veh->damage[3] = 4;
|
|
veh->damage[4] = 4;
|
|
veh->damage[5] = 4;
|
|
|
|
p_thing->State = STATE_DEAD;
|
|
p_thing->Genus.Person->Action = ACTION_DEAD;
|
|
veh->Health = 0;
|
|
|
|
//
|
|
// Anyone driving the car gets thrown out of the car and knocked over.
|
|
//
|
|
|
|
if (veh->Driver)
|
|
{
|
|
VEH_throw_out_person(TO_THING(veh->Driver), p_thing);
|
|
}
|
|
|
|
while(veh->Passenger)
|
|
{
|
|
//
|
|
// This function removes the person from the passenger list.
|
|
//
|
|
|
|
VEH_throw_out_person(TO_THING(veh->Passenger), p_thing);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use health as a countdown timer...
|
|
//
|
|
|
|
if (!(p_thing->Flags & FLAGS_IN_VIEW))
|
|
{
|
|
veh->Health -= 1;
|
|
|
|
if (veh->Health < -200)
|
|
{
|
|
//
|
|
// Get rid of the vehicle.
|
|
//
|
|
|
|
remove_thing_from_map(p_thing);
|
|
|
|
p_thing->StateFn = NULL;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (veh->Flags & FLAG_VEH_STALLED)
|
|
{
|
|
//
|
|
// Can't control this car no more...
|
|
//
|
|
|
|
veh->Steering = 0;
|
|
|
|
if (p_thing->Velocity > 0)
|
|
{
|
|
veh->DControl = VEH_DECEL;
|
|
}
|
|
else
|
|
{
|
|
veh->DControl = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Move it.
|
|
//
|
|
|
|
if (!(veh->Flags & FLAG_FURN_DRIVING) &&
|
|
!p_thing->Genus.Vehicle->VelX &&
|
|
!p_thing->Genus.Vehicle->VelZ &&
|
|
(veh->Stable >= STABLE_COUNT))
|
|
{
|
|
if (p_thing->State != STATE_DEAD)
|
|
{
|
|
//
|
|
// Dead cars need to execute the above code so they can dissapear
|
|
// from the map after a while.
|
|
//
|
|
|
|
siren(veh, 0);
|
|
p_thing->StateFn = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
do_car_input(p_thing);
|
|
|
|
//
|
|
// If this car is on the road, there is no need to collide with the walls.
|
|
//
|
|
|
|
VEH_collide_line_ignore_walls = VEH_on_road(p_thing, 0) && VEH_on_road(p_thing, TICK_RATIO);
|
|
|
|
//
|
|
// And maybe not prims to...
|
|
//
|
|
|
|
SLONG ignore_prims;
|
|
|
|
if (GAME_FLAGS & GF_CARS_WITH_ROAD_PRIMS)
|
|
{
|
|
ignore_prims = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ignore_prims = VEH_collide_line_ignore_walls;
|
|
}
|
|
|
|
{
|
|
Thing *p_driver;
|
|
p_driver=TO_THING(p_thing->Genus.Vehicle->Driver);
|
|
if(p_driver->Class==CLASS_PERSON)
|
|
{
|
|
if(p_driver->Genus.Person->PlayerID)
|
|
{
|
|
ignore_prims=0;
|
|
VEH_collide_line_ignore_walls=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark the vehicle as not having collided this frame.
|
|
//
|
|
|
|
p_thing->Flags &= ~FLAGS_COLLIDED;
|
|
|
|
//
|
|
// Find all the things this vehicle can collide with.
|
|
//
|
|
|
|
SLONG vel = Root((veh->VelX >> 4) * (veh->VelX >> 4) + (veh->VelZ >> 4) * (veh->VelZ >> 4)) >> 4;
|
|
|
|
VEH_collide_find_things(
|
|
p_thing->WorldPos.X >> 8,
|
|
p_thing->WorldPos.Y >> 8,
|
|
p_thing->WorldPos.Z >> 8,
|
|
0x180 + vel,
|
|
THING_NUMBER(p_thing),
|
|
ignore_prims);
|
|
|
|
//
|
|
// Collide with them
|
|
//
|
|
|
|
coltype = CollideCar(p_thing, TICK_RATIO);
|
|
|
|
|
|
|
|
//
|
|
// set new position / angle
|
|
//
|
|
|
|
GameCoord new_pos;
|
|
|
|
new_pos.X = p_thing->WorldPos.X + ((veh->VelX * TICK_RATIO) >> TICK_SHIFT);
|
|
new_pos.Y = p_thing->WorldPos.Y + veh->VelY;
|
|
new_pos.Z = p_thing->WorldPos.Z + ((veh->VelZ * TICK_RATIO) >> TICK_SHIFT);
|
|
|
|
move_thing_on_map(p_thing, &new_pos);
|
|
veh->Angle += (veh->VelR * TICK_RATIO) >> TICK_SHIFT;
|
|
veh->Angle &= 2047;
|
|
|
|
//
|
|
// Are there any people inside our bounding box? If there is
|
|
// then we should run them over.
|
|
//
|
|
|
|
if ((veh->VelX || veh->VelZ) && veh->Driver) // make sure we don't run ourselves over when we get out
|
|
{
|
|
SLONG i;
|
|
|
|
#define MAX_RUNOVER 8
|
|
|
|
UWORD people[MAX_RUNOVER];
|
|
SLONG num;
|
|
|
|
SLONG box_valid = FALSE;
|
|
SLONG miny;
|
|
SLONG maxy;
|
|
SLONG prim;
|
|
SLONG useangle;
|
|
SLONG sin_yaw;
|
|
SLONG cos_yaw;
|
|
SLONG matrix[4];
|
|
|
|
SLONG tx;
|
|
SLONG tz;
|
|
|
|
SLONG rx;
|
|
SLONG rz;
|
|
|
|
PrimInfo *pi;
|
|
Thing *p_found;
|
|
|
|
num = THING_find_sphere(
|
|
p_thing->WorldPos.X >> 8,
|
|
p_thing->WorldPos.Y >> 8,
|
|
p_thing->WorldPos.Z >> 8,
|
|
0x200,
|
|
people,
|
|
MAX_RUNOVER,
|
|
1 << CLASS_PERSON);
|
|
|
|
if (num)
|
|
{
|
|
prim = get_vehicle_body_prim(p_thing->Genus.Vehicle->Type);
|
|
pi = get_prim_info(prim);
|
|
miny = (p_thing->WorldPos.Y - get_vehicle_body_offset(p_thing->Genus.Vehicle->Type) >> 8) + pi->miny - 0x80;
|
|
maxy = (p_thing->WorldPos.Y - get_vehicle_body_offset(p_thing->Genus.Vehicle->Type) >> 8) + pi->maxy - 0x80;
|
|
}
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
p_found = TO_THING(people[i]);
|
|
|
|
if (p_found->Genus.Person->Flags & FLAG_PERSON_DRIVING)
|
|
{
|
|
// You can't run yourself over!
|
|
}
|
|
/*
|
|
else if (p_found->State == STATE_DEAD || p_found->State == STATE_DYING)
|
|
{
|
|
// Ignore dead people.
|
|
}
|
|
*/
|
|
else if (p_found->OnFace)
|
|
{
|
|
// Ignore people on faces... they might be dangling on this car!
|
|
}
|
|
else if (!WITHIN((p_found->WorldPos.Y >> 8), miny, maxy))
|
|
{
|
|
// TRACE("foundy = %d (%d-%d)\n", p_found->WorldPos.Y, miny, maxy);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This person is a candidate for being run over.
|
|
//
|
|
|
|
if (!box_valid)
|
|
{
|
|
useangle = -p_thing->Genus.Vehicle->Angle;
|
|
useangle &= 2047;
|
|
|
|
sin_yaw = SIN(useangle);
|
|
cos_yaw = COS(useangle);
|
|
|
|
matrix[0] = cos_yaw;
|
|
matrix[1] = sin_yaw;
|
|
matrix[2] = -sin_yaw;
|
|
matrix[3] = cos_yaw;
|
|
|
|
box_valid = TRUE;
|
|
}
|
|
|
|
//
|
|
// Rotate this person into the space of the prim.
|
|
//
|
|
|
|
tx = p_found->WorldPos.X - p_thing->WorldPos.X >> 8;
|
|
tz = p_found->WorldPos.Z - p_thing->WorldPos.Z >> 8;
|
|
|
|
rx = MUL64(tx, matrix[0]) + MUL64(tz, matrix[1]);
|
|
rz = MUL64(tx, matrix[2]) + MUL64(tz, matrix[3]);
|
|
|
|
if (WITHIN(rx, pi->minx - 0x18, pi->maxx + 0x18) &&
|
|
WITHIN(rz, pi->minz - 0x18, pi->maxz + 0x18))
|
|
{
|
|
Thing* p_driver = get_vehicle_driver(p_thing);
|
|
|
|
//
|
|
// Run this person over.
|
|
//
|
|
|
|
if (p_found->State == STATE_DEAD || p_found->State == STATE_DYING)
|
|
{
|
|
//
|
|
// ran over a corpse or someone lay down
|
|
//
|
|
if(is_person_ko_and_lay_down(p_found))
|
|
{
|
|
SLONG anim;
|
|
|
|
//
|
|
// Is this person on their front or back?
|
|
//
|
|
|
|
switch(person_is_lying_on_what(p_found))
|
|
{
|
|
case PERSON_ON_HIS_FRONT:
|
|
anim = ANIM_FIGHT_STOMPED_BACK;
|
|
break;
|
|
|
|
case PERSON_ON_HIS_BACK:
|
|
anim = ANIM_FIGHT_STOMPED_FRONT;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
if(p_found->Draw.Tweened->CurrentAnim!=anim || (p_found->Draw.Tweened->CurrentFrame->Flags&ANIM_FLAG_LAST_FRAME))
|
|
set_person_ko_recoil(p_found,anim,0);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// if(version_censored)
|
|
if((!VIOLENCE)&&(p_found->Genus.Person->PersonType==PERSON_COP||p_found->Genus.Person->PersonType==PERSON_CIV))
|
|
{
|
|
if(p_found->SubState!=SUB_STATE_FLIPING)
|
|
{
|
|
set_person_flip(p_found, Random() & 0x1);
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
knock_person_down(p_found,
|
|
GetRunoverHP(p_thing, p_found),
|
|
p_thing->WorldPos.X >> 8,
|
|
p_thing->WorldPos.Z >> 8,
|
|
p_driver);
|
|
|
|
//
|
|
// A nice sound!
|
|
//
|
|
|
|
MFX_play_thing(THING_NUMBER(p_found),S_THUMP_SQUISH,MFX_REPLACE,p_found);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Rustle up some leaves.
|
|
//
|
|
|
|
DIRT_gust(
|
|
p_thing,
|
|
p_thing->WorldPos.X>>8,
|
|
p_thing->WorldPos.Z>>8,
|
|
new_pos.X>>8,
|
|
new_pos.Z>>8);
|
|
|
|
//
|
|
// Swirl the mist.
|
|
//
|
|
#ifndef PSX
|
|
MIST_gust(
|
|
p_thing->WorldPos.X>>8,
|
|
p_thing->WorldPos.Z>>8,
|
|
new_pos.X>>8,
|
|
new_pos.Z>>8);
|
|
#endif
|
|
//
|
|
// Knock down barrels.
|
|
//
|
|
|
|
{
|
|
SLONG prim = get_vehicle_body_prim(p_thing->Genus.Vehicle->Type);
|
|
|
|
BARREL_hit_with_prim(
|
|
prim,
|
|
p_thing->WorldPos.X >> 8,
|
|
p_thing->WorldPos.Y - get_vehicle_body_offset(p_thing->Genus.Vehicle->Type) >> 8,
|
|
p_thing->WorldPos.Z >> 8,
|
|
p_thing->Genus.Vehicle->Angle);
|
|
}
|
|
|
|
// Do the engine noise.
|
|
|
|
car.Priority = 0;
|
|
car.Flags = WAVE_CARTESIAN|WAVE_LOOP|WAVE_SET_LOOP_POINTS;
|
|
car.LoopStart = 282624/2;
|
|
car.LoopEnd = 408574/2;
|
|
car.Mode.Cartesian.Scale = (128<<8);
|
|
car.Mode.Cartesian.X = p_thing->WorldPos.X;
|
|
car.Mode.Cartesian.Y = p_thing->WorldPos.Y;
|
|
car.Mode.Cartesian.Z = p_thing->WorldPos.Z;
|
|
|
|
if (p_thing->Velocity > 500)
|
|
{
|
|
dx = -SIN(p_thing->Genus.Vehicle->Angle) << 1;
|
|
dz = -COS(p_thing->Genus.Vehicle->Angle) << 1;
|
|
|
|
//
|
|
// Scare nearby people.
|
|
//
|
|
|
|
PCOM_oscillate_tympanum(
|
|
PCOM_SOUND_VAN,
|
|
NULL,
|
|
p_thing->WorldPos.X + dx >> 8,
|
|
p_thing->WorldPos.Y >> 8,
|
|
p_thing->WorldPos.Z + dz >> 8);
|
|
}
|
|
}
|
|
|
|
//===============================================================
|
|
//
|
|
// Handle input from car
|
|
//
|
|
//===============================================================
|
|
|
|
// init_arctans
|
|
//
|
|
// init arctan table (for steering)
|
|
|
|
static SLONG arctan_table[2*WHEELTIME + 1];
|
|
static SLONG arctan_table_ok = 0;
|
|
|
|
static void init_arctans(void)
|
|
{
|
|
int ii;
|
|
|
|
if (arctan_table_ok) return;
|
|
|
|
for (ii = 0; ii <= WHEELTIME; ii++)
|
|
{
|
|
arctan_table[WHEELTIME - ii] = -Arctan(ii, -WHEELRATIO) & 2047;
|
|
arctan_table[WHEELTIME + ii] = +Arctan(ii, -WHEELRATIO) & 2047;
|
|
}
|
|
|
|
for (ii = 0; ii <= 2*WHEELTIME + 1; ii++)
|
|
{
|
|
if (arctan_table[ii] > 1024) arctan_table[ii] -= 2048;
|
|
}
|
|
|
|
arctan_table_ok = 1;
|
|
}
|
|
|
|
// steering_wheel
|
|
//
|
|
// run the steering wheel
|
|
|
|
//
|
|
// changed fro analogue input by MikeD dx has a scalar value, really the wheel should equal dx in analogue mode
|
|
//
|
|
|
|
extern SLONG analogue;
|
|
|
|
void steering_wheel(Vehicle* veh, SLONG velocity, bool player)
|
|
{
|
|
SLONG inc = TICK_RATIO;
|
|
|
|
if (!(veh->Flags & FLAG_FURN_DRIVING) || (veh->Flags & FLAG_VEH_IN_AIR))
|
|
{
|
|
// bring wheel in
|
|
if (veh->Wheel > inc)
|
|
veh->Wheel -= inc;
|
|
else
|
|
if (veh->Wheel < -inc)
|
|
veh->Wheel += inc;
|
|
else
|
|
veh->Wheel = 0;
|
|
}
|
|
else
|
|
{
|
|
if (player)
|
|
{
|
|
velocity = abs(velocity);
|
|
|
|
if (velocity > 1000)
|
|
velocity = 1000;
|
|
|
|
if (veh->IsAnalog)
|
|
{
|
|
#if 0
|
|
inc = (abs(veh->Steering)*inc * (256 - velocity / 8)) >> (6+7);
|
|
#else
|
|
// The steering says where the steering wheel is directly.
|
|
// It is damped by the input routine.
|
|
//veh->Wheel = veh->Steering << 5;
|
|
|
|
// Actually, I'd love to do that, but it becomes unmanageable at high speed,
|
|
// and doesn't give a decent turning circle at low speed.
|
|
// So inversely to speed it is...
|
|
veh->Wheel = ( veh->Steering * ( 700 - ( velocity >> 1 ) ) ) >> ( 3 );
|
|
|
|
if (veh->Wheel > (WHEELTIME << TICK_SHIFT))
|
|
{
|
|
veh->Wheel = WHEELTIME << TICK_SHIFT;
|
|
}
|
|
else if (veh->Wheel < -(WHEELTIME << TICK_SHIFT))
|
|
{
|
|
veh->Wheel = -(WHEELTIME << TICK_SHIFT);
|
|
}
|
|
goto steering_done;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
inc = (inc * (256 - velocity / 8)) >> 7;
|
|
}
|
|
}
|
|
|
|
{
|
|
// scale inc according to velocity
|
|
|
|
if (veh->Steering > 0)
|
|
{
|
|
// steer right
|
|
if (veh->Wheel < 0) veh->Wheel = 0;
|
|
else veh->Wheel += inc;
|
|
|
|
if (veh->Wheel > (WHEELTIME << TICK_SHIFT)) veh->Wheel = WHEELTIME << TICK_SHIFT;
|
|
}
|
|
else if (veh->Steering < 0)
|
|
{
|
|
// steer left
|
|
if (veh->Wheel > 0) veh->Wheel = 0;
|
|
else veh->Wheel -= inc;
|
|
|
|
if (veh->Wheel < -(WHEELTIME << TICK_SHIFT)) veh->Wheel = -(WHEELTIME << TICK_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
// let go of wheel
|
|
if (veh->Wheel > 0) veh->Wheel >>= 1;
|
|
else if (veh->Wheel < 0) veh->Wheel = (veh->Wheel + 1) >> 1;
|
|
}
|
|
}
|
|
steering_done:;
|
|
}
|
|
|
|
// lookup in table
|
|
veh->WheelAngle = arctan_table[(veh->Wheel >> TICK_SHIFT) + WHEELTIME];
|
|
}
|
|
|
|
// siren
|
|
//
|
|
// play/stop the siren
|
|
|
|
static void siren(Vehicle* veh, UBYTE play)
|
|
{
|
|
if (veh->Siren == play) return;
|
|
|
|
if ((veh->Type==VEH_TYPE_POLICE)||
|
|
(veh->Type==VEH_TYPE_AMBULANCE)||
|
|
(veh->Type==VEH_TYPE_MEATWAGON))
|
|
{
|
|
if (play)
|
|
MFX_play_ambient(VEHICLE_NUMBER(veh),S_CAR_SIREN1,MFX_LOOPED);
|
|
else
|
|
MFX_stop(VEHICLE_NUMBER(veh),S_CAR_SIREN1);
|
|
}
|
|
|
|
veh->Siren = play;
|
|
}
|
|
|
|
// pedals
|
|
//
|
|
// run the pedals
|
|
|
|
static inline void pedals(Vehicle* veh, VehInfo* vinfo, SLONG velocity, UBYTE& friction, UWORD& move_cancel, SWORD& accel,Thing *p_thing)
|
|
{
|
|
|
|
if (veh->DControl & VEH_ACCEL) veh->Skid = 0;
|
|
|
|
// set GrabAction flag for preventing Darci getting out of the car
|
|
if (veh->DControl & (VEH_ACCEL | VEH_DECEL))
|
|
{
|
|
veh->GrabAction = 1;
|
|
}
|
|
else if (!(veh->DControl & VEH_FASTER))
|
|
{
|
|
veh->GrabAction = 0;
|
|
}
|
|
|
|
if (!(veh->Flags & FLAG_FURN_DRIVING))
|
|
{
|
|
// no-one in the car - slow down with a big friction (engine braking + braking)
|
|
siren(veh,0);
|
|
veh->Dir = 0;
|
|
friction -= 4;
|
|
}
|
|
else if (veh->DControl & VEH_ACCEL)
|
|
{
|
|
|
|
if (veh->Dir < 0)
|
|
{
|
|
// applying brakes while in reverse
|
|
veh->Dir = -1;
|
|
friction -= 4;
|
|
accel = vinfo->SoftBrake;
|
|
move_cancel = INPUT_CAR_ACCELERATE;
|
|
}
|
|
else
|
|
{
|
|
// accelerating forwards (terminal velocity is an emergent property
|
|
// of constant acceleration and velocity-based friction)
|
|
veh->Dir = +2;
|
|
accel = vinfo->FwdAccel;
|
|
if (veh->DControl & VEH_FASTER)
|
|
{
|
|
if (velocity < VEH_SPEED_LIMIT) accel <<= 1;
|
|
else if (velocity < VEH_SPEED_LIMIT*2) accel += (accel >> 1);
|
|
}
|
|
else
|
|
{
|
|
if (velocity >= VEH_SPEED_LIMIT) accel = 0;
|
|
}
|
|
|
|
#ifdef FAST_EDDIE
|
|
if (Keys[KB_T]) accel <<= 1; // !$$! we're fucking Batman!
|
|
#endif
|
|
|
|
if ((velocity < -200) || ((veh->DControl & VEH_FASTER) && (velocity < 400)))
|
|
{
|
|
// do wheelspin smoke
|
|
veh->Smokin = 1;
|
|
}
|
|
}
|
|
}
|
|
else if (veh->DControl & VEH_DECEL)
|
|
{
|
|
if ((veh->Dir > 0) || (velocity > 200)) // 2nd check so you don't bust the engine going into reverse at 50mph
|
|
{
|
|
// applying brakes while going forwards
|
|
veh->Dir = +1;
|
|
friction -= 4;
|
|
accel = -vinfo->SoftBrake;
|
|
move_cancel = INPUT_CAR_DECELERATE;
|
|
|
|
if (!veh->Skid)
|
|
{
|
|
if ((veh->DControl & VEH_FASTER) && (velocity > 1600)) veh->Skid = 1;
|
|
}
|
|
else
|
|
{
|
|
veh->Skid++;
|
|
if (veh->Skid == SKID_START)
|
|
{
|
|
// skid in a 60s style
|
|
if (!veh->VelR)
|
|
{
|
|
// Justin wants these lines removed
|
|
// I don't, so what I've done is make sure they're included
|
|
// in the final builds but not in the release build
|
|
// Aren't I cunning? ;)
|
|
//#ifdef FINAL
|
|
if (Random() & 128) veh->VelR = velocity >> 6;
|
|
else veh->VelR = -velocity >> 6;
|
|
//#endif
|
|
}
|
|
else
|
|
{
|
|
if (veh->VelR > 0) veh->VelR = velocity >> 5;
|
|
else veh->VelR = -velocity >> 5;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// accelerating backwards
|
|
veh->Dir = -2;
|
|
accel = -vinfo->BkAccel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// slow down with a little friction (engine braking)
|
|
friction -= 1;
|
|
|
|
// reset state
|
|
if ((veh->Dir == -1) || (veh->Dir == +1)) veh->Dir = 0;
|
|
}
|
|
|
|
// operate siren
|
|
if (veh->DControl & VEH_SIREN)
|
|
{
|
|
siren(veh, !veh->Siren);
|
|
move_cancel = INPUT_CAR_SIREN;
|
|
}
|
|
}
|
|
|
|
// do_car_input
|
|
//
|
|
// handle inputs from the player
|
|
|
|
static void do_car_input(Thing *p_thing)
|
|
{
|
|
Vehicle* veh = p_thing->Genus.Vehicle;
|
|
VehInfo* vinfo = &veh_info[p_thing->Genus.Vehicle->Type];
|
|
|
|
#ifdef _DEBUG
|
|
if (is_driven_by_player(p_thing))
|
|
{
|
|
if (Keys[KB_H] && ControlFlag)
|
|
{
|
|
Keys[KB_H] = 0;
|
|
ASSERT(0);
|
|
}
|
|
|
|
if (Keys[KB_Y]) veh->Skid = SKID_START;
|
|
}
|
|
#endif
|
|
|
|
if (!(veh->Flags & FLAG_FURN_DRIVING) && !veh->VelX && !veh->VelZ)
|
|
{
|
|
siren(veh, 0);
|
|
veh->VelR = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// move steering wheel
|
|
//
|
|
|
|
steering_wheel(veh, p_thing->Velocity, is_driven_by_player(p_thing));
|
|
|
|
//
|
|
// modify engine velocity
|
|
//
|
|
|
|
if (veh->Flags & FLAG_VEH_IN_AIR)
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (veh->Skid < SKID_START)
|
|
{
|
|
UBYTE friction; // amount of friction (lower number = more friction)
|
|
UWORD move_cancel; // move to cancel if velocity becomes 0
|
|
SWORD accel; // acceleration
|
|
|
|
//
|
|
// get input from controls
|
|
//
|
|
|
|
|
|
pedals(veh, vinfo, p_thing->Velocity, friction = 7, move_cancel = 0, accel = 0,p_thing);
|
|
|
|
if (move_cancel == INPUT_CAR_SIREN && p_thing->Genus.Vehicle->Driver)
|
|
{
|
|
// always cancel this key immediately
|
|
Thing *p_driver;
|
|
p_driver=TO_THING(p_thing->Genus.Vehicle->Driver);
|
|
if (p_driver->Genus.Person->PlayerID)
|
|
{
|
|
Thing *p_player;
|
|
p_player = NET_PLAYER(p_driver->Genus.Person->PlayerID-1);
|
|
p_player->Genus.Player->InputDone = move_cancel;
|
|
}
|
|
}
|
|
|
|
//
|
|
// apply acceleration & friction
|
|
//
|
|
|
|
SLONG oldvel = p_thing->Velocity;
|
|
SLONG oldmag = abs(oldvel);
|
|
|
|
p_thing->Velocity = ((SLONG(p_thing->Velocity) << friction) - SLONG(p_thing->Velocity)) >> friction;
|
|
p_thing->Velocity += accel;
|
|
|
|
SLONG newvel = p_thing->Velocity;
|
|
SLONG newmag = abs(newvel);
|
|
|
|
SLONG realacc = newvel - oldvel;
|
|
|
|
//
|
|
// apply forces to suspension from acceleration
|
|
//
|
|
|
|
if (realacc > 0)
|
|
{
|
|
if (abs(veh->DY[0]) < (realacc << 4)) veh->DY[0] -= realacc << 2;
|
|
if (abs(veh->DY[1]) < (realacc << 4)) veh->DY[1] -= realacc << 2;
|
|
}
|
|
else
|
|
{
|
|
if (abs(veh->DY[0]) < (-realacc << 2)) veh->DY[0] += -realacc;
|
|
if (abs(veh->DY[1]) < (-realacc << 2)) veh->DY[1] += -realacc;
|
|
}
|
|
|
|
//
|
|
// detect if we've stopped
|
|
//
|
|
|
|
bool stopped = false;
|
|
|
|
if ((veh->Dir == +2) || (veh->Dir == -2))
|
|
{
|
|
if (!accel && (newmag < 10)) stopped = true; // coast to a halt
|
|
}
|
|
else if ((veh->Dir == +1) || (veh->Dir == -1))
|
|
{
|
|
if ((newmag < 10) || (newmag > oldmag)) stopped = true; // brake to a halt
|
|
}
|
|
else
|
|
{
|
|
ASSERT(accel == 0);
|
|
if (newmag < 10) stopped = true; // coast to a halt
|
|
}
|
|
|
|
//
|
|
// deal with stopping
|
|
//
|
|
|
|
if (stopped)
|
|
{
|
|
p_thing->Velocity = 0;
|
|
veh->Dir = 0;
|
|
|
|
if (move_cancel && p_thing->Genus.Vehicle->Driver)
|
|
{
|
|
// cancel keypress
|
|
Thing *p_driver;
|
|
p_driver=TO_THING(p_thing->Genus.Vehicle->Driver);
|
|
if (p_driver->Genus.Person->PlayerID)
|
|
{
|
|
Thing *p_player;
|
|
p_player = NET_PLAYER(p_driver->Genus.Person->PlayerID-1);
|
|
p_player->Genus.Player->InputDone = move_cancel;
|
|
}
|
|
}
|
|
if (!(veh->Flags & FLAG_FURN_DRIVING))
|
|
{
|
|
// kill velocity, reset wheel
|
|
p_thing->Velocity = 0;
|
|
veh->Wheel = 0;
|
|
veh->WheelAngle = 0;
|
|
}
|
|
|
|
veh->VelX = 0;
|
|
veh->VelZ = 0;
|
|
veh->VelR = 0;
|
|
|
|
return;
|
|
}
|
|
/*
|
|
if (is_driven_by_player(p_thing))
|
|
{
|
|
TRACE("Velocity = %d\n", p_thing->Velocity);
|
|
}
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
// skidding - slow car down
|
|
veh->VelX = ((SLONG(veh->VelX) << 4) - SLONG(veh->VelX)) >> 4;
|
|
veh->VelZ = ((SLONG(veh->VelZ) << 4) - SLONG(veh->VelZ)) >> 4;
|
|
if (veh->VelR > 0) veh->VelR = ((SLONG(veh->VelR) << 4) - SLONG(veh->VelR)) >> 4;
|
|
else veh->VelR = veh->VelR - (veh->VelR >> 4);
|
|
|
|
if (veh->Steering < 0)
|
|
{
|
|
if (veh->VelR < 32) veh->VelR += (abs(veh->VelX) + abs(veh->VelZ)) >> 12;
|
|
}
|
|
else if (veh->Steering > 0)
|
|
{
|
|
if (veh->VelR > -32) veh->VelR -= (abs(veh->VelX) + abs(veh->VelZ)) >> 12;
|
|
}
|
|
|
|
// stop car if too slow
|
|
if (abs(veh->VelX) < 2048) veh->VelX = 0;
|
|
if (abs(veh->VelZ) < 2048) veh->VelZ = 0;
|
|
|
|
if (!veh->VelX && !veh->VelZ) veh->VelR = (veh->VelR + 1) >> 1;
|
|
|
|
// slow engine down rapidly
|
|
p_thing->Velocity = (p_thing->Velocity + 1) >> 1;
|
|
|
|
if (abs(p_thing->Velocity) < 10) p_thing->Velocity = 0;
|
|
|
|
if (abs(veh->VelR) < 16)
|
|
{
|
|
// see how the skid angle compares to the car's direction
|
|
SLONG dx = veh->VelX >> (8 - CAR_VEL_SHIFT);
|
|
SLONG dz = veh->VelZ >> (8 - CAR_VEL_SHIFT);
|
|
|
|
SLONG mvx = dx * dx + dz * dz;
|
|
SLONG dp = (dx * -SIN(veh->Angle) + dz * COS(veh->Angle)) >> 16;
|
|
|
|
#if 0
|
|
if (is_driven_by_player(p_thing))
|
|
{
|
|
TRACE("dx = %d, dz = %d\n", dx, dz);
|
|
TRACE("SIN = %d, COS = %d\n", -SIN(veh->Angle), COS(veh->Angle));
|
|
TRACE("MVX = %d, DP = %d, DP*DP = %d\n", mvx, dp, dp*dp);
|
|
|
|
float cossq = float(dp*dp) / float(mvx);
|
|
|
|
char str[32];
|
|
sprintf(str, "*cos2 = %f", cossq);
|
|
char *sp;
|
|
if ((dp > 0) && (dp * dp > (mvx - (mvx >> 2))))
|
|
{
|
|
sp = str;
|
|
}
|
|
else
|
|
{
|
|
sp = str + 1;
|
|
}
|
|
CONSOLE_text(str,1000);
|
|
}
|
|
#endif
|
|
|
|
if ((dp > 0) && (dp * dp > (mvx - (mvx >> 2)))) // cos^2 angle > 15/16 => cos angle > 3/4
|
|
{
|
|
p_thing->Velocity = Root(mvx);
|
|
// if (is_driven_by_player(p_thing)) TRACE("Set Velocity = %d\n", p_thing->Velocity);
|
|
|
|
// come out of skid
|
|
// if (p_thing->Velocity < 800) veh->Skid = 0;
|
|
veh->Skid = 0;
|
|
}
|
|
}
|
|
|
|
if (!veh->VelX && !veh->VelZ && !p_thing->Velocity)
|
|
{
|
|
veh->Skid = 0;
|
|
}
|
|
|
|
// still connect brake lights to brake pedal
|
|
veh->Dir = (veh->DControl & VEH_DECEL) ? +1 : 0;
|
|
|
|
// smokin!
|
|
veh->Smokin = 1;
|
|
|
|
#ifdef PSX
|
|
if (is_driven_by_player(p_thing))
|
|
{
|
|
SLONG shock=p_thing->Velocity>>1;
|
|
SATURATE(shock,64,192);
|
|
PSX_SetShock((shock>128)?1:0,shock);
|
|
}
|
|
#endif
|
|
#ifdef TARGET_DC
|
|
if (is_driven_by_player(p_thing))
|
|
{
|
|
Vibrate ( 30.0f, (float)( p_thing->Velocity ) * 0.01f, 2.0f );
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// steer the car
|
|
//
|
|
|
|
SLONG dangle; // delta angle, nothing rude
|
|
SLONG dx,dz; // delta position
|
|
|
|
if (veh->WheelAngle && !(veh->Flags & FLAG_VEH_IN_AIR))
|
|
{
|
|
SLONG tcx,tcz,tcr; // turning circle x,y, radius
|
|
SLONG dx1,dz1; // delta position (in car frame)
|
|
|
|
//
|
|
// find the car's turning circle
|
|
//
|
|
|
|
if (veh->WheelAngle < 0)
|
|
{
|
|
// turning circle centre is to left of car
|
|
SLONG angle = -veh->WheelAngle;
|
|
SLONG wheelbase = vinfo->DZ[0] - vinfo->DZ[2];
|
|
|
|
// radius = wheelbase / sin(angle)
|
|
tcr = DIV64(wheelbase, SIN(angle));
|
|
|
|
// x offset = radius * cos(angle)
|
|
tcx = vinfo->DX[0] - (tcr * COS(angle) >> 16);
|
|
tcz = vinfo->DZ[0];
|
|
}
|
|
else
|
|
{
|
|
// turning circle centre is to right of car
|
|
SLONG angle = veh->WheelAngle;
|
|
SLONG wheelbase = vinfo->DZ[1] - vinfo->DZ[3];
|
|
|
|
// radius = wheelbase / sin(angle)
|
|
tcr = DIV64(wheelbase, SIN(angle));
|
|
|
|
// x offset = radius * cos(angle)
|
|
tcx = vinfo->DX[1] + (tcr * COS(angle) >> 16);
|
|
tcz = vinfo->DZ[1];
|
|
}
|
|
|
|
//
|
|
// get angle turned
|
|
//
|
|
|
|
// get turn angle in radians
|
|
SLONG radangle = DIV64(p_thing->Velocity<<(8 - CAR_VEL_SHIFT), tcr << 8);
|
|
|
|
// convert to fraction of 1.0 (divide by 2*PI = multiply by 1/2*PI = multiply by 10430)
|
|
SLONG angle = MUL64(radangle, 10430);
|
|
|
|
// convert to angle 0 to 2047
|
|
angle >>= 5;
|
|
|
|
// angle = (angle * TICK_RATIO) >> TICK_SHIFT;
|
|
if (!angle) angle = 1;
|
|
|
|
//
|
|
// apply forces to suspension
|
|
//
|
|
|
|
if (veh->WheelAngle > 0)
|
|
{
|
|
if (abs(veh->DY[1]) < (angle << 2)) veh->DY[1] += angle << 1;
|
|
if (abs(veh->DY[3]) < (angle << 2)) veh->DY[3] += angle << 1;
|
|
|
|
dangle = -angle;
|
|
}
|
|
else
|
|
{
|
|
if (abs(veh->DY[0]) < (angle << 2)) veh->DY[0] += angle << 1;
|
|
if (abs(veh->DY[2]) < (angle << 2)) veh->DY[2] += angle << 1;
|
|
|
|
dangle = +angle;
|
|
}
|
|
|
|
dangle &= 2047;
|
|
|
|
//
|
|
// Get the vector of the vehicle's movement, in the vehicle's own frame
|
|
//
|
|
|
|
if (angle > 2)
|
|
{
|
|
if (veh->WheelAngle < 0)
|
|
{
|
|
SLONG c = COS(dangle) - 65536;
|
|
SLONG s = SIN(dangle);
|
|
|
|
dx1 = (-tcx * c - -tcz * s) >> (16 - CAR_VEL_SHIFT);
|
|
dz1 = -(-tcx * s + -tcz * c) >> (16 - CAR_VEL_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
SLONG c = COS(dangle) - 65536;
|
|
SLONG s = -SIN(dangle);
|
|
|
|
dx1 = -(tcx * c - -tcz * s) >> (16 - CAR_VEL_SHIFT);
|
|
dz1 = -(tcx * s + -tcz * c) >> (16 - CAR_VEL_SHIFT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// use simpler approximation to avoid the asymptote
|
|
dx1 = 0;
|
|
// dz1 = -SLONG(p_thing->Velocity * TICK_RATIO) >> TICK_SHIFT;
|
|
dz1 = -SLONG(p_thing->Velocity);
|
|
}
|
|
|
|
//
|
|
// convert to world frame
|
|
//
|
|
|
|
dx = (SIN(veh->Angle) * dz1 - COS(veh->Angle) * dx1) >> (8 + CAR_VEL_SHIFT);
|
|
dz = (COS(veh->Angle) * dz1 + SIN(veh->Angle) * dx1) >> (8 + CAR_VEL_SHIFT);
|
|
dangle = (dangle & 1024) ? dangle - 2048 : dangle;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// not steering
|
|
//
|
|
|
|
SLONG dx1 = 0;
|
|
// SLONG dz1 = -SLONG(p_thing->Velocity * TICK_RATIO) >> TICK_SHIFT;
|
|
SLONG dz1 = -SLONG(p_thing->Velocity);
|
|
|
|
dx = (SIN(veh->Angle) * dz1 - COS(veh->Angle) * dx1) >> (8 + CAR_VEL_SHIFT);
|
|
dz = (COS(veh->Angle) * dz1 + SIN(veh->Angle) * dx1) >> (8 + CAR_VEL_SHIFT);
|
|
dangle = 0;
|
|
}
|
|
|
|
if ((veh->VelX || veh->VelZ) && (veh->Skid < SKID_START))
|
|
{
|
|
SLONG ax,az; // acceleration
|
|
SLONG vx,vz,vv; // velocity
|
|
SLONG av; // |v x a|
|
|
|
|
// get acceleration
|
|
ax = dx - veh->VelX;
|
|
az = dz - veh->VelZ;
|
|
|
|
// get normalized velocity
|
|
vx = veh->VelX;
|
|
vz = veh->VelZ;
|
|
vv = vx*vx + vz*vz;
|
|
if (vv)
|
|
{
|
|
// get speed
|
|
vv = Root(vv);
|
|
|
|
// get acceleration component
|
|
av = (ax * vz) - (az * vx);
|
|
|
|
// if abs((av * 256 / vv) >> 8) > SKID_FORCE
|
|
if (abs(av) > SKID_FORCE * vv)
|
|
veh->Skid = SKID_START;
|
|
else
|
|
if (abs(av) > ((NEAR_SKID_FORCE*vinfo->FwdAccel)>>5) * vv)
|
|
MFX_play_thing(THING_NUMBER(p_thing), SOUND_Range(S_SKID_SLOW_START,S_SKID_SLOW_END),MFX_MOVING,p_thing);
|
|
}
|
|
}
|
|
|
|
if (veh->Skid < SKID_START)
|
|
{
|
|
veh->VelX = dx;
|
|
veh->VelZ = dz;
|
|
veh->VelR = dangle;
|
|
}
|
|
|
|
// spin the wheels
|
|
veh->Spin = (veh->Spin + (p_thing->Velocity >> 2)) & 2047;
|
|
|
|
#define VEH_FWD_ACCEL (1)
|
|
#define VEH_FWD_DECEL (2)
|
|
#define VEH_REV_ACCEL (3)
|
|
#define VEH_REV_DECEL (4)
|
|
|
|
if (is_driven_by_player(p_thing))
|
|
{
|
|
UBYTE state=0;
|
|
|
|
if (veh->Dir>=0)
|
|
{
|
|
if ((veh->DControl & VEH_ACCEL) && !veh->Skid)
|
|
state=VEH_FWD_ACCEL;
|
|
else
|
|
state=VEH_FWD_DECEL;
|
|
} else {
|
|
if ((veh->DControl & VEH_DECEL) && !veh->Skid)
|
|
state=VEH_REV_ACCEL;
|
|
else
|
|
state=VEH_REV_DECEL;
|
|
}
|
|
|
|
if (state!=veh->LastSoundState)
|
|
{
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_START);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_CRUISE);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_DECEL);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_IDLE);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CAR_REVERSE_START);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CAR_REVERSE_LOOP);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CAR_REVERSE_END);
|
|
switch(state)
|
|
{
|
|
case VEH_FWD_ACCEL:
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_START,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_CRUISE,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
break;
|
|
case VEH_FWD_DECEL:
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_DECEL,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_IDLE,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
break;
|
|
case VEH_REV_ACCEL:
|
|
#ifdef PSX
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_START,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_CRUISE,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
#else
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CAR_REVERSE_START,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CAR_REVERSE_LOOP,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
#endif
|
|
break;
|
|
case VEH_REV_DECEL:
|
|
#ifdef PSX
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_DECEL,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_IDLE,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
#else
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CAR_REVERSE_END,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_IDLE,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
veh->LastSoundState=state;
|
|
|
|
|
|
/* UBYTE state=(veh->DControl==VEH_ACCEL)|((veh->DControl==VEH_DECEL)<<1)|((veh->Dir==-2)<<3);
|
|
|
|
if ((veh->DControl == VEH_ACCEL) && !veh->Skid)
|
|
{
|
|
if (!(veh->Flags & FLAG_VEH_FX_STATE))
|
|
{
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_DECEL);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_IDLE);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CAR_REVERSE_START);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CAR_REVERSE_LOOP);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_START,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_CRUISE,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
|
|
veh->Flags|=FLAG_VEH_FX_STATE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((veh->DControl == VEH_DECEL))
|
|
{
|
|
if ((veh->Dir==-2)&&!(veh->Flags & FLAG_VEH_FX_STATE))
|
|
{
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_DECEL);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_IDLE);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CAR_REVERSE_START,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CAR_REVERSE_LOOP,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
veh->Flags|=FLAG_VEH_FX_STATE;
|
|
}
|
|
|
|
} else
|
|
|
|
if (veh->Flags & FLAG_VEH_FX_STATE)
|
|
{
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_START);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CARX_CRUISE);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CAR_REVERSE_START);
|
|
MFX_stop(THING_NUMBER(p_thing),S_CAR_REVERSE_LOOP);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_DECEL,MFX_MOVING|MFX_EARLY_OUT);
|
|
MFX_play_ambient(THING_NUMBER(p_thing),S_CARX_IDLE,MFX_MOVING|MFX_QUEUED|MFX_SHORT_QUEUE|MFX_LOOPED);
|
|
|
|
veh->Flags^=FLAG_VEH_FX_STATE;
|
|
}
|
|
} */
|
|
}
|
|
|
|
// TRACE("dx: %d dz: %d dir: %d \n",veh->dx,veh->dz, veh->Dir);
|
|
}
|
|
|
|
//===============================================================
|
|
//
|
|
// Run suspension physics
|
|
//
|
|
//===============================================================
|
|
|
|
// apply_thrust_to_suspension
|
|
//
|
|
// work out effect on car of the suspension parameters given
|
|
|
|
inline static SLONG apply_thrust_to_suspension(Suspension *p_sus, SLONG velocity, SLONG penetrate_dist)
|
|
{
|
|
SLONG acc;
|
|
SLONG compression;
|
|
|
|
// damp the velocity
|
|
velocity = ((velocity << 4) - velocity) >> 4;
|
|
|
|
// subtract velocity from compression, but if we're penetrating, reduce velocity to
|
|
// handle the collision
|
|
compression = p_sus->Compression - (velocity - penetrate_dist);
|
|
|
|
// clamp
|
|
if (compression < MIN_COMPRESSION) compression = MIN_COMPRESSION;
|
|
else if (compression > MAX_COMPRESSION) compression = MAX_COMPRESSION;
|
|
|
|
// get restoring acceleration (mass = 256)
|
|
// NOTE: this is *totally* non-physical, but it works (linear is *very* unstable)
|
|
acc = (compression >> 5) * (compression >> 5) >> 9;
|
|
|
|
// add acceleration due to spring & gravity
|
|
velocity += GRAVITY + acc;
|
|
|
|
// if really compressed, we're going up a step, so make sure the
|
|
// wheel stays above the surface
|
|
if (compression == MAX_COMPRESSION) velocity += penetrate_dist;
|
|
|
|
p_sus->Compression = compression;
|
|
|
|
return velocity;
|
|
}
|
|
|
|
// expand_suspension
|
|
//
|
|
// allow suspension to expand (when in air)
|
|
|
|
inline static void expand_suspension(Suspension *p_sus, SLONG size)
|
|
{
|
|
ASSERT(size >= 0);
|
|
|
|
size -= size >> 2; // size = size * 200 / 256
|
|
|
|
if (p_sus->Compression - size < MIN_COMPRESSION) p_sus->Compression = MIN_COMPRESSION;
|
|
else p_sus->Compression -= size;
|
|
}
|
|
|
|
// process_car
|
|
//
|
|
// given where it is, work out the wheel's positions, the suspension
|
|
// action and the car's orientation
|
|
|
|
static void do_car_fall_and_tilt(Thing* p_car, SLONG *wx, SLONG *wy, SLONG *wz, SLONG *dy);
|
|
|
|
static void process_car(Thing *p_car)
|
|
{
|
|
SLONG count;
|
|
SLONG wheel;
|
|
SLONG c0;
|
|
SLONG wx[4],wy[4],wz[4];
|
|
SLONG dy[4];
|
|
VehInfo* info;
|
|
Vehicle* vp;
|
|
BOOL squeaky=0;
|
|
BOOL crunchy=0;
|
|
/*
|
|
{
|
|
SLONG door;
|
|
extern SLONG in_right_place_for_car(Thing *p_person, Thing *p_vehicle, SLONG *door);
|
|
in_right_place_for_car(NET_PERSON(0)
|
|
,p_car, &door);
|
|
}
|
|
*/
|
|
|
|
vp = p_car->Genus.Vehicle;
|
|
info = &veh_info[vp->Type];
|
|
|
|
if (vp->Flags & FLAG_VEH_ANIMATING)
|
|
{
|
|
// animate_car(p_car);
|
|
}
|
|
|
|
make_car_matrix(vp);
|
|
|
|
UBYTE on_road = 0;
|
|
|
|
// if((p_car->Flags & FLAGS_IN_VIEW) && !ShiftFlag)
|
|
//if(!ShiftFlag)
|
|
{
|
|
int in_air = 0;
|
|
|
|
for (wheel = 0; wheel < 4; wheel++)
|
|
{
|
|
SLONG y;
|
|
SLONG height;
|
|
|
|
y = p_car->WorldPos.Y;
|
|
|
|
dy[wheel]=0;
|
|
|
|
ASSERT(p_car->Genus.Vehicle->Spring[wheel].Compression >= MIN_COMPRESSION);
|
|
|
|
// Calculate wheel's position relative to origin of car
|
|
// given that the car has an angle,tilt and roll
|
|
//
|
|
wx[wheel] = info->DX[wheel];
|
|
wy[wheel] = 0;
|
|
wz[wheel] = info->DZ[wheel];
|
|
|
|
apply_car_matrix(&wx[wheel],&wy[wheel],&wz[wheel]);
|
|
|
|
SLONG papx = wx[wheel] + (p_car->WorldPos.X >> 8);
|
|
SLONG papz = wz[wheel] + (p_car->WorldPos.Z >> 8);
|
|
|
|
height = PAP_calc_map_height_at(papx, papz) << 8;
|
|
|
|
if (ROAD_is_road(papx >> 8, papz >> 8)) on_road |= (1 << wheel);
|
|
#ifndef FINAL
|
|
#ifndef TARGET_DC
|
|
if (Keys[KB_Q] && is_driven_by_player(p_car))
|
|
{
|
|
if (wheel < 2) height += 0x4000;
|
|
else height += 0x8000;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// iterate the suspension algorithm
|
|
//
|
|
|
|
SLONG y_pos;
|
|
|
|
count = TICK_LOOP;
|
|
while (--count)
|
|
{
|
|
SLONG size;
|
|
|
|
// saturate the DY on the wheels to stop the car going mad!
|
|
if (vp->DY[wheel] > 1536) vp->DY[wheel] = 1536;
|
|
|
|
// get size of spring
|
|
size = (128 << 8) - vp->Spring[wheel].Compression;
|
|
|
|
// get position of wheel
|
|
y_pos = y + (wy[wheel] << 8) - size;
|
|
|
|
if (y_pos <= height)
|
|
{
|
|
// wheel on the floor - push car upwards
|
|
vp->DY[wheel] = apply_thrust_to_suspension(&vp->Spring[wheel], vp->DY[wheel], height - y_pos);
|
|
// TRACE("s-floor: %d\n",vp->DY[wheel]);
|
|
squeaky+=abs(vp->DY[wheel]);
|
|
crunchy+=vp->DY[wheel];
|
|
}
|
|
else
|
|
{
|
|
// in air
|
|
if (vp->Spring[wheel].Compression > MIN_COMPRESSION)
|
|
{
|
|
// expand suspension
|
|
expand_suspension(&vp->Spring[wheel], y_pos - height);
|
|
|
|
if (vp->Spring[wheel].Compression > MIN_COMPRESSION)
|
|
{
|
|
// if still compressed then do an upthrust
|
|
vp->DY[wheel] = apply_thrust_to_suspension(&vp->Spring[wheel], vp->DY[wheel], 0);
|
|
// TRACE("s-up: %d\n",vp->DY[wheel]);
|
|
// squeaky=1;
|
|
}
|
|
}
|
|
// falling
|
|
vp->DY[wheel] += GRAVITY;
|
|
}
|
|
|
|
y += vp->DY[wheel];
|
|
dy[wheel] += vp->DY[wheel];
|
|
}
|
|
|
|
// get size of spring
|
|
SLONG size = (128 << 8) - vp->Spring[wheel].Compression;
|
|
|
|
// get final position of wheel
|
|
y_pos = y + (wy[wheel] << 8) - size;
|
|
|
|
if (y_pos - height > 1024) in_air++;
|
|
}
|
|
#ifndef PSX
|
|
if (squeaky>1600) MFX_play_thing(THING_NUMBER(p_car),SOUND_Range(S_CAR_SUSPENSION_START,S_CAR_SUSPENSION_END),MFX_MOVING,p_car);
|
|
#endif
|
|
if (crunchy<-4000) MFX_play_thing(THING_NUMBER(p_car),S_CAR_SMASH_START+1,MFX_MOVING,p_car);
|
|
// TRACE("crunchy: %d\n",crunchy);
|
|
|
|
do_car_fall_and_tilt(p_car, &wx[0], &wy[0], &wz[0], &dy[0]);
|
|
|
|
if (in_air == 4) vp->Flags |= FLAG_VEH_IN_AIR;
|
|
else vp->Flags &= ~FLAG_VEH_IN_AIR;
|
|
}
|
|
#if 0
|
|
else
|
|
{
|
|
//
|
|
// Back in from sourcesafe version 118
|
|
//
|
|
//
|
|
// car's that aren't drawn have ultra cheap suspension
|
|
//
|
|
SLONG height;
|
|
for(c0=0;c0<4;c0++)
|
|
{
|
|
p_car->Genus.Vehicle->DY[c0]=0;
|
|
p_car->Genus.Vehicle->Spring[c0].Compression=4300; //16%/84% compression
|
|
}
|
|
p_car->Genus.Vehicle->Tilt=0;
|
|
p_car->Genus.Vehicle->Roll=0;
|
|
height=PAP_calc_map_height_at((p_car->WorldPos.X>>8),(p_car->WorldPos.Z>>8));
|
|
//
|
|
// 107 is length of suspension 128 *(100-%compressed) =128*.84
|
|
//
|
|
p_car->WorldPos.Y=(height+107)<<8;
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
if (vp->dlight)
|
|
{
|
|
SLONG dx = -car_matrix[6] << 1;
|
|
SLONG dy = 0x2000;
|
|
SLONG dz = -car_matrix[8] << 1;
|
|
|
|
SLONG lx;
|
|
SLONG ly;
|
|
SLONG lz;
|
|
|
|
lx = p_car->WorldPos.X + dx >> 8;
|
|
ly = p_car->WorldPos.Y + dy >> 8;
|
|
lz = p_car->WorldPos.Z + dz >> 8;
|
|
|
|
NIGHT_dlight_move(p_car->Genus.Vehicle->dlight, lx, ly, lz);
|
|
}
|
|
}
|
|
|
|
//===============================================================
|
|
//
|
|
// Calculate car orientation
|
|
//
|
|
//===============================================================
|
|
|
|
static void calc_tilt_and_roll(SLONG *tilt, SLONG *roll, SLONG *whx, SLONG *why, SLONG *whz, SLONG angle);
|
|
|
|
static void do_car_fall_and_tilt(Thing* car, SLONG *wx, SLONG *wy, SLONG *wz, SLONG *dy)
|
|
{
|
|
SLONG min_dy,max_dy;
|
|
SLONG c0,pos_count,neg_count;
|
|
SLONG remove;
|
|
SLONG tilt,roll;
|
|
|
|
// find min,max dy and number of positive vectors
|
|
min_dy = 0x7FFFFFFF;
|
|
max_dy = 0x80000000;
|
|
pos_count = 0;
|
|
neg_count = 0;
|
|
|
|
for (c0 = 0; c0 < 4; c0++)
|
|
{
|
|
if (dy[c0] > 0) pos_count++;
|
|
if (dy[c0] < 0) neg_count++;
|
|
if (dy[c0] > max_dy) max_dy = dy[c0];
|
|
if (dy[c0] < min_dy) min_dy = dy[c0];
|
|
}
|
|
|
|
if (pos_count == 4) // all positive, whole car is rising
|
|
{
|
|
car->WorldPos.Y += max_dy;
|
|
remove = max_dy;
|
|
}
|
|
else if (pos_count == 0) // all negative, whole car is falling
|
|
{
|
|
car->WorldPos.Y += min_dy;
|
|
remove = min_dy;
|
|
}
|
|
else
|
|
{
|
|
remove = 0;
|
|
}
|
|
|
|
// remove aggregate vector
|
|
dy[0] -= remove;
|
|
dy[1] -= remove;
|
|
dy[2] -= remove;
|
|
dy[3] -= remove;
|
|
|
|
// add dy into wy
|
|
wy[0] += dy[0] >> 8;
|
|
wy[1] += dy[1] >> 8;
|
|
wy[2] += dy[2] >> 8;
|
|
wy[3] += dy[3] >> 8;
|
|
|
|
// calculate tilt and roll for car
|
|
calc_tilt_and_roll(&tilt, &roll, wx, wy, wz, car->Genus.Vehicle->Angle);
|
|
|
|
// check stability
|
|
if ((abs(car->Genus.Vehicle->Tilt - tilt) < 16) && (abs(car->Genus.Vehicle->Roll - roll) < 16) && !remove)
|
|
{
|
|
if (car->Genus.Vehicle->Stable != STABLE_COUNT) car->Genus.Vehicle->Stable++;
|
|
}
|
|
else
|
|
{
|
|
car->Genus.Vehicle->Stable = 0;
|
|
}
|
|
|
|
car->Genus.Vehicle->Tilt = tilt;
|
|
car->Genus.Vehicle->Roll = roll;
|
|
}
|
|
|
|
// fast_root
|
|
//
|
|
// perform a proper fast squareroot
|
|
|
|
#ifndef PSX
|
|
static inline SLONG fast_root(SLONG num)
|
|
{
|
|
#if 0
|
|
SLONG sh;
|
|
SLONG ans;
|
|
SLONG ans_sq;
|
|
|
|
ASSERT(num >= 0);
|
|
|
|
// do an approximate BSR-style thingy
|
|
if (num & 0xFF000000) sh = 15;
|
|
else if (num & 0x00FF0000) sh = 11;
|
|
else if (num & 0x0000FF00) sh = 7;
|
|
else sh = 3;
|
|
|
|
// calculate using a bit iteration (much faster than
|
|
// using a bloody DIVIDE in Newton-Raphson!)
|
|
ans = 0;
|
|
ans_sq = 0;
|
|
|
|
do
|
|
{
|
|
// work out (ans + bit)^2 = (ans*ans + 2*ans*bit + bit*bit) where bit = (1 << sh)
|
|
SLONG newans = ans_sq + (ans << (sh + 1)) + (1 << (sh + sh));
|
|
|
|
if (newans <= num)
|
|
{
|
|
ans_sq = newans;
|
|
ans |= (1 << sh);
|
|
}
|
|
} while (sh--);
|
|
|
|
return ans;
|
|
#else
|
|
// OK, I've done it now ... but to be honest, I reckon this is the
|
|
// fastest way on current Intel chips ...
|
|
return (SLONG)sqrt((double)num);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
// normalise_val256
|
|
//
|
|
// normalize a vector to magnitude 256
|
|
|
|
static inline void normalise_val256(SLONG *vx, SLONG *vy, SLONG *vz)
|
|
{
|
|
SLONG len;
|
|
|
|
len = *vx * *vx + *vy * *vy + *vz * *vz;
|
|
|
|
#ifndef PSX
|
|
len = fast_root(len);
|
|
#else
|
|
len = Root(len);
|
|
#endif
|
|
|
|
if (len) len = 65536 / len;
|
|
else len = 65536;
|
|
|
|
*vx = (*vx * len) >> 8;
|
|
*vy = (*vy * len) >> 8;
|
|
*vz = (*vz * len) >> 8;
|
|
}
|
|
|
|
// calc_tilt_n_roll_with_matrix
|
|
//
|
|
// calculate tilt & roll from 3 vectors
|
|
|
|
static inline void calc_tilt_n_roll_with_matrix(SLONG across_x,SLONG across_y,SLONG across_z,SLONG nose_x,SLONG nose_y,SLONG nose_z,SLONG nx,SLONG ny,SLONG nz,SLONG *angle,SLONG *tilt,SLONG *roll)
|
|
{
|
|
SLONG matrix[9];
|
|
|
|
//
|
|
// assumes nx,ny,nz is normalised 256 & is the normal out of the top of the car
|
|
//
|
|
|
|
matrix[0]=(across_x)<<8;
|
|
matrix[1]=(across_y)<<8;
|
|
matrix[2]=(across_z)<<8;
|
|
|
|
matrix[3]=(nx)<<8;
|
|
matrix[4]=(ny)<<8;
|
|
matrix[5]=(nz)<<8;
|
|
|
|
matrix[6]=(nose_x)<<8;
|
|
matrix[7]=(nose_y)<<8;
|
|
matrix[8]=(nose_z)<<8;
|
|
|
|
FMATRIX_find_angles(matrix,angle,tilt,roll);
|
|
}
|
|
|
|
// calc_tilt_and_roll
|
|
//
|
|
// calculate tilt and roll for the car
|
|
|
|
static void calc_tilt_and_roll(SLONG *tilt, SLONG *roll, SLONG *whx, SLONG *why, SLONG *whz, SLONG angle)
|
|
{
|
|
SLONG nx,ny,nz;
|
|
|
|
SLONG vx,vy,vz;
|
|
SLONG wx,wy,wz;
|
|
SLONG wheel;
|
|
|
|
SLONG x02,y02,z02;
|
|
SLONG x10,y10,z10;
|
|
SLONG x31,y31,z31;
|
|
SLONG x23,y23,z23;
|
|
|
|
SLONG tt = 0;
|
|
SLONG tr = 0;
|
|
|
|
// precalculate the normal vectors
|
|
x02 = whx[0] - whx[2];
|
|
y02 = -(why[0] - why[2]);
|
|
z02 = whz[0] - whz[2];
|
|
|
|
normalise_val256(&x02, &y02, &z02);
|
|
|
|
x10 = whx[1] - whx[0];
|
|
y10 = -(why[1] - why[0]);
|
|
z10 = whz[1] - whz[0];
|
|
|
|
normalise_val256(&x10, &y10, &z10);
|
|
|
|
x31 = whx[3] - whx[1];
|
|
y31 = -(why[3] - why[1]);
|
|
z31 = whz[3] - whz[1];
|
|
|
|
normalise_val256(&x31, &y31, &z31);
|
|
|
|
x23 = whx[2] - whx[3];
|
|
y23 = -(why[2] - why[3]);
|
|
z23 = whz[2] - whz[3];
|
|
|
|
normalise_val256(&x23, &y23, &z23);
|
|
|
|
for (wheel = 0; wheel < 4; wheel++)
|
|
{
|
|
switch (wheel)
|
|
{
|
|
case 0:
|
|
vx = x02; vy = y02; vz = z02;
|
|
wx = x10; wy = y10; wz = z10;
|
|
|
|
nx = (vy * wz - vz * wy) >> 8;
|
|
ny = (vz * wx - vx * wz) >> 8;
|
|
nz = (vx * wy - vy * wx) >> 8;
|
|
|
|
calc_tilt_n_roll_with_matrix( wx, wy, wz, vx, vy, vz,nx,ny,nz,&angle,tilt,roll);
|
|
break;
|
|
|
|
case 1:
|
|
vx = x10; vy = y10; vz = z10;
|
|
wx = x31; wy = y31; wz = z31;
|
|
|
|
nx = (vy * wz - vz * wy) >> 8;
|
|
ny = (vz * wx - vx * wz) >> 8;
|
|
nz = (vx * wy - vy * wx) >> 8;
|
|
|
|
calc_tilt_n_roll_with_matrix( vx, vy, vz,-wx,-wy,-wz,nx,ny,nz,&angle,tilt,roll);
|
|
break;
|
|
|
|
case 2:
|
|
vx = x23; vy = y23; vz = z23;
|
|
wx = x02; wy = y02; wz = z02;
|
|
|
|
nx = (vy * wz - vz * wy) >> 8;
|
|
ny = (vz * wx - vx * wz) >> 8;
|
|
nz = (vx * wy - vy * wx) >> 8;
|
|
|
|
calc_tilt_n_roll_with_matrix(-vx,-vy,-vz, wx, wy, wz,nx,ny,nz,&angle,tilt,roll);
|
|
break;
|
|
|
|
case 3:
|
|
vx = x31; vy = y31; vz = z31;
|
|
wx = x23; wy = y23; wz = z23;
|
|
|
|
nx = (vy * wz - vz * wy) >> 8;
|
|
ny = (vz * wx - vx * wz) >> 8;
|
|
nz = (vx * wy - vy * wx) >> 8;
|
|
|
|
calc_tilt_n_roll_with_matrix(-wx,-wy,-wz,-vx,-vy,-vz,nx,ny,nz,&angle,tilt,roll);
|
|
break;
|
|
}
|
|
|
|
if (*roll > 1024) *roll = *roll - 2048;
|
|
if (*tilt > 1024) *tilt = *tilt - 2048;
|
|
|
|
tt += *tilt;
|
|
tr += *roll;
|
|
}
|
|
|
|
// take average
|
|
tt /= 4;
|
|
tr /= 4;
|
|
|
|
// max out
|
|
if (tt < -312) tt = -312;
|
|
else if (tt > 312) tt = 312;
|
|
|
|
if (tr < -312) tr = -312;
|
|
if (tr > 312) tr = 312;
|
|
|
|
*tilt = -tt & 2047;
|
|
*roll = -tr & 2047;
|
|
}
|
|
|
|
|
|
void VEH_reduce_health(
|
|
Thing *p_car,
|
|
Thing *p_person,
|
|
SLONG damage)
|
|
{
|
|
ASSERT(p_car->Class == CLASS_VEHICLE);
|
|
|
|
p_car->Genus.Vehicle->Health -= damage >> 1;
|
|
|
|
if (p_car->Genus.Vehicle->Health <= 0)
|
|
{
|
|
if (p_car->Genus.Vehicle->Driver)
|
|
{
|
|
//
|
|
// The person has blown up a car with someone inside it. So
|
|
// they have killed that person!
|
|
//
|
|
|
|
if (p_person)
|
|
{
|
|
p_person->Genus.Person->Flags2 |= FLAG2_PERSON_IS_MURDERER;
|
|
}
|
|
}
|
|
|
|
if (p_car->Genus.Vehicle->Type == VEH_TYPE_POLICE ||
|
|
p_car->Genus.Vehicle->Type == VEH_TYPE_MEATWAGON)
|
|
{
|
|
//
|
|
// This is a police car that has blown up.
|
|
//
|
|
|
|
if (p_person && p_person->Class == CLASS_PERSON && p_person->Genus.Person->PlayerID)
|
|
{
|
|
//
|
|
// Give the player a red mark!
|
|
//
|
|
|
|
NET_PLAYER(0)->Genus.Player->RedMarks += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure the car processes this health info...
|
|
//
|
|
|
|
p_car->StateFn = VEH_driving;
|
|
}
|
|
|
|
|
|
Thing *vehicle_wheel_pos_vehicle;
|
|
VehInfo *vehicle_wheel_pos_info;
|
|
|
|
|
|
void vehicle_wheel_pos_init(Thing *p_vehicle)
|
|
{
|
|
vehicle_wheel_pos_vehicle = p_vehicle;
|
|
vehicle_wheel_pos_info = &veh_info[p_vehicle->Genus.Vehicle->Type];;
|
|
|
|
make_car_matrix(p_vehicle->Genus.Vehicle);
|
|
}
|
|
|
|
void vehicle_wheel_pos_get(
|
|
SLONG which,
|
|
SLONG *wx,
|
|
SLONG *wy,
|
|
SLONG *wz)
|
|
{
|
|
SLONG wheel_x;
|
|
SLONG wheel_y;
|
|
SLONG wheel_z;
|
|
|
|
wheel_x = vehicle_wheel_pos_info->DX[which];
|
|
wheel_y = 51 - (((128 << 8) - vehicle_wheel_pos_vehicle->Genus.Vehicle->Spring[which].Compression) >> 8);
|
|
wheel_z = vehicle_wheel_pos_info->DZ[which];
|
|
|
|
apply_car_matrix(
|
|
&wheel_x,
|
|
&wheel_y,
|
|
&wheel_z);
|
|
|
|
*wx = wheel_x + (vehicle_wheel_pos_vehicle->WorldPos.X >> 8);
|
|
*wy = wheel_y + (vehicle_wheel_pos_vehicle->WorldPos.Y >> 8);
|
|
*wz = wheel_z + (vehicle_wheel_pos_vehicle->WorldPos.Z >> 8);
|
|
}
|