REDRIVER2/src_rebuild/Game/C/event.c

4036 lines
70 KiB
C

#include "driver2.h"
#include "event.h"
#include "cars.h"
#include "players.h"
#include "mc_snd.h"
#include "camera.h"
#include "convert.h"
#include "draw.h"
#include "models.h"
#include "mission.h"
#include "job_fx.h"
#include "bomberman.h"
#include "debris.h"
#include "main.h"
#include "system.h"
#include "spool.h"
#include "texture.h"
#include "civ_ai.h"
#include "pedest.h"
#include "shadow.h"
#include "gamesnd.h"
#include "dr2roads.h"
#include "ASM/rndrasm.h"
#include "cutrecorder.h"
struct FixedEvent // same as EVENT but different fields
{
VECTOR position;
short rotation;
short active;
u_short initialRotation;
u_short finalRotation;
u_short minSpeed;
u_short maxSpeed;
short flags;
short radius;
int model;
EVENT* next;
char* modelName;
};
struct MissionTrain
{
EVENT* engine;
int* node;
int cornerSpeed;
int initialStraightSpeed;
int finalStraightSpeed;
int start;
int startDir;
};
struct Foam
{
MODEL* model;
int rotate;
};
struct EventCarriage
{
short rotation;
short vel;
};
struct MultiCar
{
EVENT* event;
int count;
};
struct Helicopter
{
int speed;
short pitch;
short dp;
short roll;
short dr;
int lastX;
int lastZ;
TEXTURE_DETAILS rotorTexture;
short rotorrot;
short rotorvel;
int cleanModel;
int deadModel;
};
struct Detonator
{
int timer;
int count;
};
struct CameraDelay
{
int delay;
int type;
};
enum VisType
{
VIS_INIT = 0,
VIS_SORT = 1,
VIS_ADD = 2,
VIS_NEXT = 3,
};
struct EventCamera
{
VECTOR position;
short yAng;
MATRIX matrix;
int rotate;
};
enum Station
{
EVENT_NO_STATION = 0,
EVENT_APPROACHING = 1,
EVENT_LEAVING = 2,
};
#define PATH_NODE_WRAP 0x80000000 // go back to first node without interpolation
#define PATH_NODE_CYCLE 0x80000001 // cycle nodes with interpolation
#define PATH_NODE_STATION 0x80000002 // stop point
#define PATH_NODE_REVERSE 0x80000003
// TODO: put more meaning into those arrays
int ElTrainData[83] = {
6, // train count
// train 1 (n 1)
80, 130, 0x8000, // speed 1, 2, start direction
336284, -220364, 283420, PATH_NODE_STATION,
-204500, -158924, 247580, -123084, 188624, -158924, 73520, -138444, 17200, -124148, -39120, PATH_NODE_STATION,
-109276, -82131, -80103, -17628, -203568, -124712, -39728, -265000, 129620, -386012, PATH_NODE_WRAP,
// train 2 (n 31)
80, 130, 0,
-158928, 189219, -123684, 246995, PATH_NODE_CYCLE,
// train 3 (n 39)
0, 90, 0x8000,
188402, -425768, 354291, PATH_NODE_WRAP,
// train 4 (n 46)
0, 90, 0x8000,
354291, -425168, 188402, PATH_NODE_WRAP,
// train 5 (n 53)
60, 110, 0,
-386012, 130215, -264404, -39132, -124688, 16619, -139048, 72943, -159520, 282863, PATH_NODE_STATION,
-204991, -220964, 336284, PATH_NODE_WRAP,
// train 6 (n 71)
70, 120, 0,
-82719, -39712, PATH_NODE_STATION,
-115487, -124120, -202968,-18216, -80683, PATH_NODE_CYCLE
};
int VegasTrainData[7] = {
0, 123, 0x8000,
982000, -68855, 762717, PATH_NODE_WRAP
};
int VegasParkedTrains[3] = {
-68855, 975520, 792317
};
// [A] monorail
int VegasMonorailData[] = {
20, 70, 0x18000, // cycle flag. Cannot be used for event itself
-70500, -67510, -111000, PATH_NODE_REVERSE, -70500, PATH_NODE_WRAP,
};
int HavanaFerryData[12] = {
40, 0, 64, 425555, -452, 20000, // Ferry 1
25, 0, 64, 315750, 130740, 135960 // Ferry 2
};
int RioFerryData[6] = {
6, 0, 0, 200384, 387424, 410000
};
int HavanaMiniData[4] = {
50, 3995, 4605, 0
};
int LiftingBridges[55] = {
8, // bridge count
// 1
-182784, -175616, -168448,
7, -227328, -162304, -141824, -121344, -100864, -80384, -59904,
0x100, // goose island start
// 2
-312832, -305664, -298496,
1, 324096,
// 3
-311808, -304640, -297472,
1, 247296,
// 4
-256512, -249344, -242176,
1, 247296,
// 5
-262656, -255488, -248320,
1, 324096,
0x8000, // goose island end
// 6
170496, 177664, 184832,
1, -271360,
// 7
-12800, -5632, 1536,
5, -162304, -102912, -82432, -61952, -39936,
// 8
-6656, 512, 7680,
3, 4137, 27648, 128000
};
int ChicagoCameraHack[3] = {
-208006, 198, 657039
};
int HavanaCameraHack[9] = {
-491431, 3568, -139048,
-453981, 409, -128009,
-453975, 429, -127175
};
int VegasCameraHack[16] = {
124092, 3988, 3494,
-11285, 1559, 843824,
-38801, 2681, 273148,
-36925, 2048, 269647, 273935,
-65503, 1617, 796775
};
int RioCameraHack[6] = {
-276620, 3072, -321920,
-40920, 3623, 382170
};
MissionTrain missionTrain[2] =
{
{ 0, ElTrainData + 4, 100, 150, 200, 289051, 32768 },
{ 0, ElTrainData + 7, 125, 145, 215, -204500, 0 }
};
FixedEvent chicagoDoor[3] =
{
{
{ -207616, 0, 659706, 0 },
0,
0,
800u,
0u,
25u,
50u,
80,
0,
0,
0,
"WRGFLDDOOR"
},
{
{ -209152, -512, 668928, 0 },
0,
0,
2496u,
4096u,
25u,
50u,
64,
0,
0,
0,
"DOOR01"
},
{
{ 195264, -3728, 74752, 0 },
0,
0,
0u,
0u,
0u,
0u,
1088,
0,
0,
0,
"FRAME"
}
};
FixedEvent havanaFixed[3] =
{
{
{ -183296, -273, -41720, 0 },
0,
0,
800,
0,
25,
50,
64,
0,
0,
0,
"DOOR"
},
{
{ -455168, 1529, -125440, 0 },
0,
0,
0u,
0u,
0u,
0u,
1536,
0,
0,
0,
"LIFT_SHAFT"
},
{
{ -487936, 0, -136689, 0 },
0,
0,
1152u,
0u,
10u,
20u,
80,
0,
0,
0,
"BASEDOOR"
}
};
FixedEvent vegasDoor[5] =
{
{
{ -11851, 0, 844163, 0 },
0,
0,
1700u,
3072u,
25u,
50u,
80,
0,
0,
0,
"DOOR"
},
{
{ -106242, -239, -216960, 0 },
0,
0,
800u,
0u,
25u,
50u,
96,
0,
0,
0,
"DOOR01"
},
{
{ 63058, -231, 501184, 0 },
0,
0,
3200u,
4096u,
25u,
50u,
96,
0,
0,
0,
"DOOR02"
},
{
{ -159356, -176, 647024, 0 },
0,
0,
3200u,
4096u,
25u,
50u,
64,
0,
0,
0,
"DOOR03"
},
{
{ 123936, -170, 4928, 0 },
0,
0,
3200u,
4096u,
25u,
50u,
80,
0,
0,
0,
"GATE01"
}
};
FixedEvent rioDoor[6] =
{
{
{ -123328, -177, -254720, 0 },
0,
0,
3200u,
4096u,
25u,
50u,
96,
0,
0,
0,
"DOOR1"
},
{
{ -125248, -17, -256208, 0 },
0,
0,
1600u,
0u,
25u,
50u,
80,
0,
0,
0,
"DOOR2"
},
{
{ -274000, -17, -321408, 0 },
0,
0,
1748u,
3072u,
25u,
50u,
80,
0,
0,
0,
"GATE01"
},
{
{ -274000, -17, -322432, 0 },
0,
0,
2348u,
1024u,
25u,
50u,
80,
0,
0,
0,
"GATE01"
},
{
{ -40432, -17, 383328, 0 },
0,
0,
700u,
2048u,
25u,
50u,
80,
0,
0,
0,
"GATE03"
},
{
{ -39424, -17, 383328, 0 },
0,
0,
900u,
0u,
25u,
50u,
80,
0,
0,
0,
"GATE03"
}
};
Helicopter HelicopterData =
{
400,
0,
0,
0,
0,
0,
0,
{ { 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u }, 0u, 0u, 0, 0 },
0,
0,
0,
0
};
static Foam foam;
EventGlobal events;
CELL_OBJECT* EventCop;
int event_models_active = 0;
static EVENT(*trackingEvent[2]);
static MS_TARGET(*carEvent[8]);
static int cameraEventsActive = 0;
static CameraDelay cameraDelay;
static Detonator detonator;
static int eventHaze = 0;
static int doneFirstHavanaCameraHack = 0;
static SVECTOR boatOffset;
static FixedEvent* fixedEvent = NULL;
int carsOnBoat = 0;
MultiCar multiCar;
EVENT* firstEvent;
EVENT* firstMissionEvent;
EVENT* event;
static EventCamera eventCamera;
void MakeEventTrackable(EVENT* ev);
// [D] [T]
int GetVisValue(int index, int zDir)
{
int camera;
int radius;
EVENT* ev;
VECTOR pos;
if (index & 0xC000)
{
camera = (index >> 0xf ^ 1) & 1;
if (zDir == 0)
{
pos.vx = player[camera].cameraPos.vx;
radius = 16000;
}
else
{
pos.vx = player[camera].cameraPos.vz;
radius = 16000;
}
}
else
{
int multiple;
if (index & 0x80)
ev = (EVENT*)&fixedEvent[index & 0x7f];
else
ev = &event[index & 0x7f];
if (index & 0xf00)
{
if ((ev->flags & 0x30) && zDir == 0 || !(ev->flags & 0x30) && zDir != 0)
multiple = 1;
else
multiple = 0;
}
else
{
multiple = 1;
}
if (multiple)
{
pos.vx = ev->position.vx;
pos.vz = ev->position.vz;
if ((ev->flags & 0xCC0) == 0x80)
{
pos.vx -= boatOffset.vx;
pos.vz -= boatOffset.vz;
}
if (zDir != 0)
pos.vx = pos.vz;
}
else
{
pos.vx = ev->node[(index & 0xf00U) >> 8];
}
radius = ev->radius;
}
if (index & 0x2000)
pos.vx -= radius;
if (index & 0x1000)
pos.vx += radius;
return pos.vx;
}
static unsigned short* xVis;
static unsigned short* zVis;
// [D] [T]
void VisibilityLists(VisType type, int i)
{
static unsigned short xList[128]; // offset 0x0
static unsigned short zList[128]; // offset 0x100
static unsigned short(*firstX[2]); // offset 0x0
static unsigned short(*firstZ[2]); // offset 0x8
static int count; // offset 0x10
static unsigned short(*list[2]) = {
xList, zList
};
int k;
EVENT* ev;
int tempList;
int num;
int n, j, vis;
int camera;
int table[128];
//debugCount = &count.10;
if (type == VIS_SORT)
{
num = 0;
do {
j = 0;
while (j < count)
{
vis = GetVisValue(list[num][j], num);
table[j] = vis;
// add sorted
// code is similar to sorting code in DrawAllTheCars
if (j > 0 && vis < table[j - 1])
{
tempList = list[num][j];
k = j - 1;
do {
n = k;
table[k + 1] = table[k];
list[num][k + 1] = list[num][k];
if (k == 0)
break;
k--;
} while (vis < table[k]);
table[n] = vis;
list[num][n] = tempList;
}
j++;
}
num++;
} while (num < 2);
num = 0;
while (num < NumPlayers)
{
if (num != 0)
camera = 0x4000;
else
camera = 0x8000;
firstX[num] = xList;
firstZ[num] = zList;
do {
} while ((*(firstX[num]++) & camera) == 0);
do {
} while ((*(firstZ[num]++) & camera) == 0);
num++;
}
}
else if (type == VIS_INIT)
{
zList[0] = -0x6000;
xList[0] = -0x6000;
zList[1] = -0x7000;
xList[1] = -0x7000;
count = 2;
if (NumPlayers == 2)
{
zList[2] = 0x6000;
xList[2] = 0x6000;
zList[3] = 0x5000;
xList[3] = 0x5000;
count = 4;
}
num = 0;
while (num < NumPlayers)
{
firstX[num] = xList;
firstZ[num] = zList;
num++;
}
}
else if (type == VIS_ADD)
{
if (i & 0x80)
ev = (EVENT*)&fixedEvent[i & 0x7f];
else
ev = &event[i & 0x7f];
if (ev->radius == 0)
{
xList[count] = i;
zList[count] = i;
count++;
}
else
{
xList[count] = i | 0x2000;
zList[count] = i | 0x2000;
xList[count + 1] = i | 0x1000;
zList[count + 1] = i | 0x1000;
count += 2;
}
}
else if (type == VIS_NEXT)
{
ev = firstEvent;
while (ev != NULL)
{
ev->flags &= ~4;
ev = ev->next;
}
xVis = firstX[i];
zVis = firstZ[i];
}
}
// [D] [T]
void SetElTrainRotation(EVENT* ev)
{
if (ev->flags & 0x8000)
ev->rotation = 1024;
else
ev->rotation = 0;
if (ev->node[0] < ev->node[2])
ev->rotation += 2048;
}
// [D] [T]
void InitTrain(EVENT* ev, int count, int type)
{
int mv;
int* to;
int length;
int height;
ev->node = &ev->data[3];
ev->timer = 0;
ev->flags = ev->data[2] | 0x2;
if (type == 0)
{
height = -1773;
length = 1600;
SetElTrainRotation(ev);
}
else if (type == 1)
{
if(ev->data == VegasMonorailData) // [A]
{
length = 1850;
height = -1100;
}
else
{
length = 3700;
height = -734;
}
ev->rotation = 0;
}
ev->position.vy = height;
ev->flags &= ~0x7000;
if (ev->node[3] != PATH_NODE_WRAP && ev->node[3] != PATH_NODE_REVERSE)
ev->flags |= 0x3000;
to = ev->node;
// get direction sign
if (to[2] == PATH_NODE_STATION)
mv = to[3] - to[0] >> 0x1f;
else
mv = to[2] - to[0] >> 0x1f;
if (ev->flags & 0x8000)
{
ev->position.vz = to[0] + ((count * length ^ mv) - mv);
ev->position.vx = to[1];
}
else
{
ev->position.vx = to[0] + ((count * length ^ mv) - mv);
ev->position.vz = to[1];
}
}
// [D] [T]
void InitDoor(FixedEvent* ev, EVENT*** e, int* cEvents)
{
ev->active = 0;
ev->rotation = ev->finalRotation;
ev->flags |= 0x200;
*(FixedEvent**)*e = ev; // [A] is that gonna work?
*e = &(**e)->next;
VisibilityLists(VIS_ADD, (ev - fixedEvent) | 0x80);
}
// [D] [T]
void InitEvents(void)
{
events.camera = 0;
events.track = trackingEvent;
events.cameraEvent = NULL;
detonator.timer = 0;
detonator.count = 0;
trackingEvent[0] = NULL;
carEvent[0] = NULL;
cameraEventsActive = 0;
event_models_active = 0;
cameraDelay.delay = 0;
eventHaze = 0;
carsOnBoat = 0;
doneFirstHavanaCameraHack = 0;
boatOffset.vx = 0;
boatOffset.vy = 0;
boatOffset.vz = 0;
VisibilityLists(VIS_INIT, 0);
TriggerEvent(-1);
}
// [D] [T]
void BoatOffset(SVECTOR* offset, EVENT* ev)
{
offset->vx = 0;
offset->vy = -ev->data[2];
offset->vz = ev->data[4] - ev->position.vz;
}
// [D] [T]
int OnBoat(VECTOR* pos, EVENT* ev, int* dist)
{
int halfBoatLength;
int halfBoatWidth;
if (GameLevel == 1)
{
halfBoatWidth = 853;
halfBoatLength = 2431;
}
else
{
halfBoatWidth = 1600;
halfBoatLength = 5376;
}
if (ev->position.vx - halfBoatWidth < pos->vx &&
ev->position.vx + halfBoatWidth > pos->vx)
{
*dist = pos->vz - ev->position.vz;
if (*dist > -halfBoatLength && *dist < halfBoatLength)
{
return 1;
}
}
return 0;
}
#ifdef PSX
#define EVENT_RADIUS 4096
#else
#define EVENT_RADIUS 12000
#endif
// [D] [T] [A] long function, please debug more
void SetUpEvents(int full)
{
int direction;
int i, n;
int* p;
EVENT* evt;
int detonatorModel;
int trainModel;
EVENT** e;
int cEvents;
int ElTrackModel;
int carModel;
firstEvent = NULL;
e = &firstEvent;
// Multiplayer level loaded?
if (!doSpooling)
{
firstEvent = NULL;
EventCop = NULL;
return;
}
D_MALLOC_BEGIN()
if (full)
{
EventCop = (CELL_OBJECT*)D_MALLOC(sizeof(CELL_OBJECT) * 16);
event = (EVENT*)mallocptr;
}
evt = event;
cEvents = 0; // TODO: use D_MALLOC for each event?
if (GameLevel == 0)
{
int count;
int cBridges;
cBridges = LiftingBridges[0];
direction = 0;
p = &LiftingBridges[1];
// make lifting bridges
n = 0;
while (n < cBridges)
{
int timeOffset;
cameraEventsActive = 1;
// goose island bridges start/end?
if (p[0] == 0x100)
{
firstMissionEvent = &event[cEvents];
p++;
}
else if (p[0] == 0x8000)
{
direction = true;
p++;
}
evt = &event[cEvents];
evt[0].data = &p[0];
evt[1].data = &p[2];
if (direction)
{
evt[0].position.vz = (p[0] + p[1]) / 2;
evt[1].position.vz = (p[1] + p[2]) / 2;
}
else
{
evt[0].position.vx = (p[0] + p[1]) / 2;
evt[1].position.vx = (p[1] + p[2]) / 2;
}
count = p[3];
p += 4;
// randomize the timer of bridges
timeOffset = (Random2(0) >> (n & 31) & 255) * 32;
evt = &event[cEvents];
for (i = 0; i < 2; i++)
{
if (direction)
{
evt[i].flags = 0x1;
evt[i].position.vx = *p;
}
else
{
evt[i].flags = 0x21;
evt[i].position.vz = *p;
}
evt[i].node = p;
evt[i].radius = EVENT_RADIUS;
evt[i].timer = timeOffset % 8000;
evt[i].model = n * 2 + i;
}
while (--count >= 0)
{
VisibilityLists(VIS_ADD, cEvents | count * 256);
VisibilityLists(VIS_ADD, cEvents + 1 | count * 256);
p++;
}
*e = &event[cEvents];
evt = *e;
evt->next = &event[cEvents + 1];
cEvents += 2;
e = &evt->next->next;
n++;
}
if (full)
ElTrackModel = FindModelIdxWithName("ELTRAIN");
count = ElTrainData[0];
p = ElTrainData;
n = 0;
missionTrain[0].engine = &event[cEvents];
missionTrain[1].engine = missionTrain[0].engine;
// add trains
while (n < count-1)
{
// randomize carriage count
if (n != 0)
i = (Random2(0) >> (n & 0x1f) & 3U) + 2;
else
i = 5;
direction = 1;
n++;
p++;
while (--i >= 0)
{
evt = &event[cEvents];
evt->radius = 0;
evt->data = p;
evt->position.pad = p - ElTrainData;
InitTrain(evt, i, 0);
if (direction)
{
direction = 0;
evt->flags |= 0x400;
}
if (full)
evt->model = ElTrackModel;
*e = evt;
e = &(*e)->next;
VisibilityLists(VIS_ADD, cEvents);
cEvents++;
}
//i = *p;
do
{
} while (*++p + PATH_NODE_WRAP > 1);
}
fixedEvent = chicagoDoor;
InitDoor(&chicagoDoor[0], &e, &cEvents);
InitDoor(&chicagoDoor[1], &e, &cEvents);
*e = (EVENT*)&chicagoDoor[2];
e = &(*e)->next;
VisibilityLists(VIS_ADD, 130);
if (full)
{
// these models are for ferris wheel
chicagoDoor[2].initialRotation = FindModelIdxWithName("HUB");
chicagoDoor[2].minSpeed = FindModelIdxWithName("CAR");
chicagoDoor[2].model = FindModelIdxWithName("FRAME");
chicagoDoor[0].model = FindModelIdxWithName(chicagoDoor[0].modelName);
chicagoDoor[1].model = FindModelIdxWithName(chicagoDoor[1].modelName);
// Caine's compund semi model
carModel = FindModelIdxWithName("LORRY");
}
// Caine's compound trucks
multiCar.count = 0;
multiCar.event = &event[cEvents];
evt = multiCar.event;
for (i = 0; i < 7; i++)
{
evt[i].flags = 0x680;
if (full)
evt[i].model = carModel;
evt[i].radius = 0;
evt[i].next = &evt[i + 1];
}
cEvents += 7;
}
else if (GameLevel == 1)
{
evt = event;
cameraEventsActive = 1;
evt->radius = 0;
evt->position.vy = 111;
evt->position.vx = HavanaFerryData[3];
evt->position.vz = HavanaFerryData[4];;
evt->rotation = 3072;
evt->flags = -0x77ad;
evt->timer = -1;
evt->node = HavanaFerryData + 4;
evt->data = HavanaFerryData;
// [A] reset Ferry angles
evt->data[1] = RSIN(CameraCnt * 32) >> 9;
evt->data[2] = RCOS(CameraCnt * 16) + 4096 >> 7;
VisibilityLists(VIS_ADD, 0);
MakeEventTrackable(evt);
*e = evt;
fixedEvent = havanaFixed;
e = &(*e)->next;
InitDoor(havanaFixed, &e, &cEvents);
InitDoor(havanaFixed + 2, &e, &cEvents);
*e = (EVENT*)(&havanaFixed[1]);
e = &(*e)->next;
VisibilityLists(VIS_ADD, 129);
evt[1].flags = 0x4212;
evt[1].node = HavanaMiniData + 1;
evt[1].position.vy = 3995;
evt[1].position.vx = -455167;
evt[1].position.vz = -125439;
evt[1].rotation = 0;
evt[1].timer = -1;
evt[1].radius = 0;
evt[1].data = HavanaMiniData;
VisibilityLists(VIS_ADD, 1);
*e = event + 1;
e = &(*e)->next;
cEvents = 2;
if (full != 0)
{
event->model = FindModelIdxWithName("FERRY");
event[1].model = FindModelIdxWithName("LIFT");
havanaFixed[1].model = FindModelIdxWithName(havanaFixed[1].modelName);
havanaFixed[2].model = FindModelIdxWithName(havanaFixed[2].modelName);
havanaFixed[0].model = FindModelIdxWithName(havanaFixed[0].modelName);
foam.model = FindModelPtrWithName("FOAM");
}
}
else if (GameLevel == 2)
{
int cCarriages;
if (full)
trainModel = FindModelIdxWithName("TRAIN");
evt = event;
// in Beat the train we spawn more train cars
if (gCurrentMissionNumber == 22)
cCarriages = 9;
else
cCarriages = 2;
for (i = 0; i < cCarriages; i++)
{
evt[i].radius = EVENT_RADIUS;
if (i < 2)
{
evt[i].flags = 0x302;
evt[i].position.vx = VegasParkedTrains[0];
evt[i].position.vz = VegasParkedTrains[i + 1];
evt[i].timer = 2;
VisibilityLists(VIS_ADD, i);
}
evt[i].position.vy = -734;
evt[i].rotation = 0;
if (full)
evt[i].model = trainModel;
*e = &evt[i];
e = &(*e)->next;
}
// zero first and last train links
evt[cCarriages - 1].next = NULL;
evt[1].next = NULL;
cEvents = cCarriages;
fixedEvent = vegasDoor;
for (i = 0; i < 5; i++)
{
InitDoor(&vegasDoor[i], &e, &cEvents);
if (full)
vegasDoor[i].model = FindModelIdxWithName(vegasDoor[i].modelName);
}
// [A] Init cut Vegas monorail
if (full)
trainModel = FindModelIdxWithName("MONORAIL");
evt = &event[cEvents];
// [A] add monorail. More than two for some unknown reason are not visible
cCarriages = 2;
for (i = 0; i < cCarriages; i++)
{
evt[i].radius = EVENT_RADIUS;
evt[i].position.vx = VegasMonorailData[4];
evt[i].position.vz = VegasMonorailData[3];
evt[i].data = VegasMonorailData;
InitTrain(&evt[i], i, 1);
VisibilityLists(VIS_ADD, cEvents+i);
if (full)
evt[i].model = trainModel;
*e = &evt[i];
e = &(*e)->next;
}
evt->flags |= 0x500;
// zero first and last train links
evt[cCarriages - 1].next = NULL;
evt[1].next = NULL;
cEvents += cCarriages;
evt = &event[cEvents];
if (gCurrentMissionNumber == 30)
{
// Destroy the yard
evt[0].position.vx = -12283;
evt[0].position.vy = -275;
evt[0].position.vz = 841243;
evt[1].position.vx = -13482;
evt[1].position.vy = -250;
evt[1].position.vz = 841184;
evt[2].position.vx = -14380;
evt[2].position.vy = -276;
evt[2].position.vz = 840579;
i = 0;
for (i = 0; i < 3; i++)
{
if (i == 2)
evt[i].rotation = 0;
else
evt[i].rotation = 2048;
evt[i].flags = 0x400;
evt[i].radius = 0;
}
if (full)
{
detonatorModel = FindModelIdxWithName("DETONATOR");
evt[0].model = detonatorModel;
evt[1].model = detonatorModel;
evt[2].model = detonatorModel;
}
firstMissionEvent = &event[cEvents];
cEvents += 3;
}
}
else if (GameLevel == 3)
{
evt = event;
cameraEventsActive = 1;
if (full != 0)
evt->model = FindModelIdxWithName("BOAT01");
evt->radius = 0;
evt->position.vy = 256;
evt->position.vx = RioFerryData[3];
evt->position.vz = RioFerryData[4];
evt->flags = -0x77ad;
evt->rotation = 0;
evt->timer = -1;
evt->node = RioFerryData + 4;
evt->data = RioFerryData;
VisibilityLists(VIS_ADD, 0);
MakeEventTrackable(event);
*e = event;
cEvents = 1;
fixedEvent = rioDoor;
e = &(*e)->next;
for (i = 0; i < 6; i++)
{
InitDoor(&rioDoor[i], &e, &cEvents);
if (full)
rioDoor[i].model = FindModelIdxWithName(rioDoor[i].modelName);
}
if (full)
{
foam.model = FindModelPtrWithName("FOAM");
detonatorModel = FindModelIdxWithName("DETONATOR");
}
evt = event;
if (gCurrentMissionNumber == 35)
{
// setup bombs on Boat Jump
int dz, dy;
evt[cEvents].position.vx = 201520;
evt[cEvents].position.vy = -177;
evt[cEvents].position.vz = 385248;
evt[cEvents + 1].position.vx = 201392;
evt[cEvents + 1].position.vy = -177;
evt[cEvents + 1].position.vz = 389200;
evt[cEvents + 2].position.vx = 199376;
evt[cEvents + 2].position.vy = -177;
evt[cEvents + 2].position.vz = 389200;
for (i = 0; i < 3; i++)
{
if (i == 2)
evt[cEvents + i].rotation = 3072;
else
evt[cEvents + i].rotation = 1024;
evt[cEvents + i].flags = 0x80;
evt[cEvents + i].radius = 0;
evt[cEvents + i].data = &evt->data[1];
dz = evt->position.vz - evt[cEvents + i].position.vz;
dy = evt->position.vy - evt[cEvents + i].position.vy;
evt[cEvents + i].node = (int*)ABS(SquareRoot0(dz * dz + dy * dy));
}
if (full != 0)
{
evt[cEvents].model = detonatorModel;
evt[cEvents + 1].model = detonatorModel;
evt[cEvents + 2].model = detonatorModel;
}
firstMissionEvent = &event[cEvents];
cEvents += 3;
}
else if (gCurrentMissionNumber - 39U < 2)
{
// setup Lenny helicopter
evt[cEvents].flags = 0xC0;
if (gCurrentMissionNumber == 39)
{
evt[cEvents].position.vx = 65223;
evt[cEvents].position.vy = -774;
evt[cEvents].position.vz = -518866;
evt[cEvents].rotation = 2624;
evt[cEvents].timer = -1;
HelicopterData.pitch = 0;
HelicopterData.roll = 0;
}
else
{
evt[cEvents].position.vy = -1000;
evt[cEvents].rotation = -1;
evt[cEvents].timer = 0;
evt[cEvents].flags |= 0x100;
}
HelicopterData.rotorrot = Random2(0) & 0xff;
HelicopterData.rotorvel = 1;
evt[cEvents].node = (int*)&MissionTargets[4];
evt[cEvents].radius = 0;
if (full)
{
HelicopterData.cleanModel = FindModelIdxWithName("CHOPPERCLEAN");
HelicopterData.deadModel = FindModelIdxWithName("CHOPPERDAM");
GetTextureDetails("ROTOR", &HelicopterData.rotorTexture);
}
evt[cEvents].model = HelicopterData.cleanModel;
VisibilityLists(VIS_ADD, cEvents);
MakeEventTrackable(event + cEvents);
*e = &evt[cEvents++];
e = &(*e)->next;
}
}
*e = NULL;
if (full)
mallocptr += cEvents * sizeof(EVENT);
D_MALLOC_END();
}
// [D] [T]
void InitEventCamera(void)
{
eventCamera.position = camera_position;
eventCamera.yAng = camera_angle.vy;
eventCamera.matrix = inv_camera_matrix;
eventCamera.rotate = 1;
events.camera = 1;
}
// [D] [T]
void ResetEventCamera(void)
{
camera_position = eventCamera.position;
camera_angle.vy = eventCamera.yAng;
inv_camera_matrix = eventCamera.matrix;
events.camera = 0;
}
// [D] [T]
void SetCamera(EVENT* ev)
{
int iPivot;
int axis;
int rotation;
VECTOR pivot;
SVECTOR offset;
MATRIX matrix;
SVECTOR temp;
offset.vx = 0;
offset.vy = 0;
offset.vz = 0;
if (eventCamera.rotate != 0)
inv_camera_matrix = eventCamera.matrix;
pivot.vy = 0;
if (ev->flags & 0x800)
{
iPivot = ev->position.vz;
rotation = ev->data[1];
offset = boatOffset;
if (GameLevel == 1)
pivot.vy = 111;
else
pivot.vy = 256;
axis = 0;
}
else
{
axis = ev->flags & 0x30;
rotation = ev->rotation;
iPivot = *ev->data;
}
if (axis == 0)
{
rotation = -rotation;
pivot.vx = camera_position.vx;
pivot.vz = iPivot;
}
else
{
pivot.vz = camera_position.vz;
pivot.vx = iPivot;
}
if (rotation == 0)
{
camera_position.vx = eventCamera.position.vx + offset.vx;
camera_position.vy = eventCamera.position.vy + offset.vy;
camera_position.vz = eventCamera.position.vz + offset.vz;
camera_angle.vy = eventCamera.yAng;
}
else
{
camera_position = eventCamera.position;
InitMatrix(matrix);
if (axis == 0x10)
{
camera_angle.vy = camera_angle.vy - rotation;
_RotMatrixY(&matrix, rotation);
}
else if (axis == 0)
{
_RotMatrixX(&matrix, rotation);
}
else if (axis == 0x20)
{
_RotMatrixZ(&matrix, rotation);
}
temp.vx = camera_position.vx - pivot.vx;
temp.vy = camera_position.vy - pivot.vy;
temp.vz = camera_position.vz - pivot.vz;
gte_SetRotMatrix(&matrix);
gte_SetTransVector(&pivot);
gte_ldv0(&temp);
gte_rtv0tr();
gte_stlvnl(&camera_position);
// this seems to reflect matrix by Y axis
short v1, v0, a1, a2;
v1 = matrix.m[0][1];
v0 = matrix.m[1][0];
a1 = matrix.m[0][2];
a2 = matrix.m[2][0];
v1 = v1 ^ v0;
v0 = v0 ^ v1;
v1 = v1 ^ v0;
matrix.m[1][0] = v0;
matrix.m[0][1] = v1;
a1 = a1 ^ a2;
a2 = a2 ^ a1;
a1 = a1 ^ a2;
matrix.m[2][0] = a2;
matrix.m[0][2] = a1;
v0 = matrix.m[2][1];
v1 = matrix.m[1][2];
v1 = v1 ^ v0;
v0 = v0 ^ v1;
v1 = v1 ^ v0;
matrix.m[2][1] = v0;
matrix.m[1][2] = v1;
// OLD
//gte_SetRotMatrix(&inv_camera_matrix);
//MulRotMatrix(&matrix);
//inv_camera_matrix = matrix;
// NEW
gte_MulMatrix0(&inv_camera_matrix, &matrix, &inv_camera_matrix);
camera_position.vx += offset.vx;
camera_position.vy += offset.vy;
camera_position.vz += offset.vz;
}
SetCameraVector();
if (rotation != 0 || eventCamera.rotate != 0)
{
Set_Inv_CameraMatrix();
SetCameraVector();
SetupDrawMapPSX();
}
eventCamera.rotate = rotation;
if (ev->flags & 0x800)
{
ev->flags &= ~0x1;
events.draw = 0;
}
else
{
events.draw = ev->model;
}
}
// [D] [T]
void EventCollisions(CAR_DATA* cp, int type)
{
if (carsOnBoat >> CAR_INDEX(cp) == 0)
return;
if (type == 0)
{
events.draw = 0;
events.camera = 1;
cp->hd.where.t[0] += boatOffset.vx;
cp->hd.where.t[1] += boatOffset.vy;
cp->hd.where.t[2] += boatOffset.vz;
return;
}
cp->hd.where.t[0] -= boatOffset.vx;
cp->hd.where.t[1] -= boatOffset.vy;
cp->hd.where.t[2] -= boatOffset.vz;
events.camera = 0;
}
// [D] [T]
void NextNode(EVENT* ev)
{
if (ev->node[2] == PATH_NODE_STATION || ev->node[2] == PATH_NODE_REVERSE)
{
ev->node = &ev->node[2];
}
else
{
ev->node = &ev->node[1];
ev->flags ^= 0x8000;
}
if (*ev->node == PATH_NODE_CYCLE)
{
ev->node = &ev->data[3];
}
else if (ev->node[3] == PATH_NODE_WRAP || ev->node[3] == PATH_NODE_REVERSE)
{
ev->flags &= ~0x7000;
return;
}
ev->flags &= ~0x7000;
ev->flags |= 0x3000;
SetElTrainRotation(ev);
}
// [D] [T]
void StepFromToEvent(EVENT* ev)
{
int df;
int md;
long* curr;
int* to;
int direction;
int d, d2;
if (ev->timer > 0)
{
ev->timer--;
return;
}
if (ev->timer != 0)
return;
direction = ev->flags & 0xc000;
to = &ev->node[1];
if (direction == 16384)
{
curr = &ev->position.vy;
}
else if (direction <= 16384)
{
if (direction != 0)
curr = &ev->position.vx;
else
curr = &ev->position.vz;
}
else if (direction == 32768)
{
curr = &ev->position.vz;
}
d = *curr - *ev->node;
d2 = *to - *curr;
if (ABS(d2) < ABS(d))
md = ABS(d2);
else
md = ABS(d);
df = *to - ev->node[0] >> 31;
// movement speed
if ((ev->flags & 0xC000) == 0x4000)
{
if (md > 1024)
md = ev->data[0] ^ df;
else
md = ((ev->data[0] - 1) * RSIN(md)) / 4096 + 1 ^ df;
}
else
{
if (md > 2048)
md = ev->data[0] ^ df;
else
md = ((ev->data[0] - 1) * RSIN(md >> 1)) / 4096 + 1U ^ df;
}
*curr += (md - df);
if ((d2 ^ *to - *curr) < 0)
{
*curr = *to;
ev->timer = -1;
if (ev == events.cameraEvent)
{
SetSpecialCamera(SPECIAL_CAMERA_RESET, 0);
if(direction == 16384)
{
SetMSoundVar(3, NULL);
}
}
}
}
// [D] [T]
void StepPathEvent(EVENT* ev)
{
static int speed;
int d1;
int* i;
int d;
int* from;
int* to;
Station station;
int direction;
long* curr;
int dir;
int turn[4];
XZPAIR centre;
XZPAIR offset;
short flags;
if (ev->timer != 0)
{
if (ev->timer == 1)
{
ev->timer--;
do
{
if(!(ev->data[2] & 0x10000))
NextNode(ev);
flags = ev->flags;
ev = ev->next;
if (ev == NULL)
{
speed = 0;
return;
}
} while (ev->flags == (flags & ~0x400));
}
else
{
if (!(ev->flags & 0x100))
ev->timer--;
}
speed = 0;
return;
}
flags = ev->flags;
if (speed == 0 && (flags & 0x400) == 0)
return;
//
// [A] completely custom code for Luxor Monorail because OG code for handling trains SUCKS!!!
if(ev->data[2] & 0x10000)
{
from = ev->node;
to = &from[2];
if (flags & 0x8000)
curr = &ev->position.vz;
else
curr = &ev->position.vx;
i = &from[0];
d = *to - *curr;
if (d < 0)
dir = -1;
else
dir = 1;
d = ABS(d);
d1 = ABS(*curr - *i);
if (d1 > d)
d1 = d;
d = ABS(*curr - *i);
if (d <= d1)
d1 = d;
if (ev->flags & 0x400)
speed = ev->data[1];
if ((d1 < 6000 && d1 < 8048) && (ev->flags & 0x400))
{
speed = ev->data[0] + (speed - ev->data[0]) * RSIN(((d1 - 2048) * 1024) / 8048) / 4096;
}
*curr += speed * dir;
int dist = (*to - *curr) * dir;
if(dist < (-dir)*2048 && ev->flags & 0x400)
{
EVENT* _evit = ev;
do
{
_evit->flags &= ~0x100;
_evit->timer = 300;
// manually advance node
do
{
_evit->node += 1;
if (_evit->node[2] == PATH_NODE_WRAP)
{
// reinitialize if we reached WRAP
_evit->node = &ev->data[3];
break;
}
} while (_evit->node[2] == PATH_NODE_WRAP || _evit->node[2] == PATH_NODE_REVERSE);
_evit = _evit->next;
} while (_evit);
}
return;
}
from = ev->node;
to = &from[2];
if (*from == PATH_NODE_STATION)
{
station = EVENT_LEAVING;
i = &from[1];
}
else
{
station = EVENT_APPROACHING;
i = &from[0];
if (from[2] == PATH_NODE_STATION)
{
to = &from[3];
}
else
{
station = EVENT_NO_STATION;
if (from[-1] == PATH_NODE_STATION)
i = &from[-2];
else if (from[2] == PATH_NODE_CYCLE)
to = &ev->data[3];
else if (from[1] == PATH_NODE_CYCLE && &ev->data[3] < to)
to = &ev->data[4];
}
}
// move train
if ((flags & 0x7000) == 0x1000)
{
u_int loop; // unsigned on purspose
if (flags & 0x8000)
{
direction = -1;
loop = 3;
}
else
{
direction = 1;
loop = 0;
}
if (from[0] == PATH_NODE_STATION)
i = &from[-2];
while (loop < 4)
{
if (*i == PATH_NODE_CYCLE)
i = &ev->data[3];
else if (*i == PATH_NODE_STATION)
i += 2;
turn[loop] = *i;
i++;
loop += direction;
}
if (turn[0] - turn[2] > -1)
centre.x = turn[2] + 2048;
else
centre.x = turn[2] - 2048;
if (turn[3] - turn[1] > -1)
centre.z = turn[1] + 2048;
else
centre.z = turn[1] - 2048;
if (ev->flags & 0x400)
speed = ev->data[0];
offset.x = (ev->position.vz - centre.z) * speed / 2048;
offset.z = (centre.x - ev->position.vx) * speed / 2048;
if (((turn[2] - turn[0]) * offset.x + (turn[3] - turn[1]) * offset.z) * direction < 0)
{
offset.x = -offset.x;
offset.z = -offset.z;
}
ev->position.vx += offset.x;
ev->position.vz += offset.z;
ev->rotation = ratan2(offset.x, offset.z) + 1024U & 0xfff;
if (ev->flags & 0x8000)
{
centre.x = ev->position.vx - centre.x;
if (turn[0] - turn[2] < 0)
{
if (centre.x > -1)
return;
}
else
{
if (centre.x < 1)
return;
}
ev->position.vz = turn[1];
NextNode(ev);
}
else
{
centre.z = ev->position.vz - centre.z;
if (turn[3] - turn[1] < 0)
{
if (centre.z > -1)
return;
}
else
{
if (centre.z < 1)
return;
}
ev->position.vx = turn[2];
NextNode(ev);
}
return;
}
if (flags & 0x8000)
curr = &ev->position.vz;
else
curr = &ev->position.vx;
d = *to - *curr;
if (d < 0)
dir = -1;
else
dir = 1;
d = ABS(d);
d1 = ABS(*curr - *i);
if (d1 > d)
{
if (station == EVENT_LEAVING)
station = EVENT_NO_STATION;
d1 = d;
}
else
{
if (station == EVENT_APPROACHING)
station = EVENT_NO_STATION;
}
d = ABS(*curr - *i);
if (d <= d1)
d1 = d;
if (ev->flags & 0x400)
{
if (ev->flags & 0x80)
{
if (CameraCnt < 0x1000)
speed = (ev->data[1] * (4096 - CameraCnt) + ev->data[2] * CameraCnt) / 4096;
else
speed = ev->data[2];
//debugSpeed = speed;
}
speed = ev->data[1];
}
if ((d1 < 6000 || station == EVENT_NO_STATION && d1 < 8048) && (ev->flags & 0x400))
{
if (station == EVENT_NO_STATION)
{
if ((ev->flags & 0x7000U) == 0x3000)
{
speed = ev->data[0] + (speed - ev->data[0]) * RSIN(((d1 - 2048) * 1024) / 8048) / 4096;
}
}
else
{
// acceleration or slowdown
speed = 5 + (speed - 5) * RSIN((d1 << 10) / 6000) / 4096;
}
}
*curr += speed * dir;
int dist = (*to - *curr) * dir;
if (station == EVENT_NO_STATION && (ev->flags & 0x7000))
{
if (dist < 2048)
{
if ((ev->flags & 0x7000) == 0x3000)
{
ev->flags &= ~0x7000;
ev->flags |= 0x1000;
}
}
return;
}
if (dist < 0)
{
if (station == EVENT_NO_STATION)
{
// [A] preserve direction flag or train will get stuck
// i might have been decompiled it wrong but now it works
dir = ev->flags & 0x400;
InitTrain(ev, 0, GameLevel == 0 ? 0 : 1);
if (dir)
ev->flags |= 0x400;
}
else if (ev->flags & 0x400)
{
ev->timer = 300;
}
}
}
// [D] [T]
int GetBridgeRotation(int timer)
{
if (_CutRec_IsOn())
return 0;
if (gDisableChicagoBridges)
return 0;
if (timer > 2600)
return 0;
if (timer > 1600)
timer = 2600 - timer;
else if (timer > 1000)
timer = 1000;
return (4096 - RCOS((timer * 2048) / 1000)) * 800 / 8192;
}
// [D] [T]
void StepHelicopter(EVENT* ev)
{
static int rotating = 1;
short sign;
int rot;
int pitch, roll, d2p, d2r;
int direction;
int vx, vz;
XZPAIR vel;
VECTOR pos;
VECTOR drift = { 2,3,2 };
if (ev->timer >= 0)
{
int b, c, d, t, tt1, tt2;
t = ev->timer >> 3;
tt1 = FIXEDH(t * t);
tt2 = FIXEDH(tt1 * t);
b = (tt2 * 3 - tt1 * 5) / 2 + 4096;
c = tt1 * 2 + (t - tt2 * 3) / 2;
d = (tt2 - tt1) / 2;
ev->position.vx = ev->node[0] + FIXEDH((ev->node[2] - ev->node[0]) * b + (ev->node[4] - ev->node[0]) * c + (ev->node[6] - ev->node[0]) * d);
ev->position.vz = ev->node[1] + FIXEDH((ev->node[3] - ev->node[1]) * b + (ev->node[5] - ev->node[1]) * c + (ev->node[7] - ev->node[1]) * d);
ev->timer += HelicopterData.speed;
if (ev->timer < 0)
{
if (ev->node[9] == (ev->node[8] == 0))
{
ev->timer = -1;
}
else
{
ev->timer = 0;
ev->node += 2;
}
}
if (ev->rotation == -1)
{
vel.x = (ev->node[4] - ev->node[0]) / 2;
vel.z = (ev->node[5] - ev->node[1]) / 2;
ev->rotation = ratan2(vel.x, vel.z);
HelicopterData.pitch = 0;
HelicopterData.roll = 0;
HelicopterData.dp = 0;
HelicopterData.dr = 0;
}
else
{
vel.x = ev->position.vx - HelicopterData.lastX;
vel.z = ev->position.vz - HelicopterData.lastZ;
}
rot = ev->rotation;
vz = vel.z * RCOS(rot) + vel.x * RSIN(rot);
vx = vel.x * RCOS(rot) - vel.z * RSIN(rot);
pitch = HelicopterData.pitch;
if (ABS(vz) <= 900000)
pitch = -HelicopterData.pitch - (RSIN((vz * 1024) / 900000) >> 3);
else if (vz < 1)
pitch = 512 - pitch;
else
pitch = -512 - pitch;
roll = HelicopterData.roll;
if (ABS(vx) <= 150000)
roll = (RSIN((vx * 1024) / 150000) >> 3) - roll;
else if (vx < 1)
roll = -512 - roll;
else
roll = 512 - roll;
d2p = pitch - HelicopterData.dp;
if (ABS(d2p) < 5)
{
HelicopterData.dp = pitch;
}
else
{
sign = (d2p >> 0x1f);
HelicopterData.dp += (sign ^ 5) - sign;
}
d2r = roll - HelicopterData.dr;
if (ABS(d2r) < 5)
{
HelicopterData.dr = roll;
}
else
{
sign = (d2r >> 0x1f);
HelicopterData.dr += (sign ^ 5) - sign;
}
HelicopterData.pitch = DIFF_ANGLES(-HelicopterData.dp, HelicopterData.pitch);// (HelicopterData.pitch + HelicopterData.dp + 2048U & 0xfff) - 2048;
HelicopterData.roll = DIFF_ANGLES(-HelicopterData.dr, HelicopterData.roll); //(HelicopterData.roll + HelicopterData.dr + 2048U & 0xfff) - 2048;
rot = DIFF_ANGLES(ev->rotation, ratan2(vel.x, vel.z)); //(ratan2(vel.x, vel.z) - ev->rotation + 2048U & 0xfff) - 2048;
if (ABS(rot) > 512)
{
if (rot > 0)
direction = 1024;
else
direction = -1024;
}
else
{
direction = RSIN(rot * 2) >> 2;
}
ev->rotation += FIXEDH(FIXEDH(direction * direction) * direction);
ev->rotation &= 0xfff;
if (GetSurfaceIndex(&ev->position) == -23)
{
if (ev->position.vy < -50)
{
ev->position.vy += 10;
}
else
{
pos.vy = 0;
pos.vx = ev->position.vx;
pos.vz = ev->position.vz;
Setup_Smoke(&pos, 100, 500, SMOKE_WHITE, 0, &dummy, 0);
}
}
else
{
if (ev->position.vy > -1000)
ev->position.vy -= 10;
}
HelicopterData.lastX = ev->position.vx;
HelicopterData.lastZ = ev->position.vz;
SetMSoundVar((intptr_t)&ev->position, NULL);
if ((ev->flags & 0x100) && (Random2(0) & 3) == (CameraCnt & 3U))
{
Setup_Smoke(&ev->position, 100, 500, SMOKE_BLACK, 0, &dummy, 0);
}
}
if (ev->model == HelicopterData.deadModel)
{
pos.vy = -200;
pos.vx = ev->position.vx + (rand() & 0xff) - 128;
pos.vz = ev->position.vz + (rand() & 0xff) - 128;
Setup_Smoke(&pos, 50, 100, SMOKE_FIRE, 0, &dummy, 0);
Setup_Smoke(&pos, 100, 500, SMOKE_BLACK, 0, &drift, 0);
SetMSoundVar(0, NULL);
}
else
{
HelicopterData.rotorrot += HelicopterData.rotorvel;
HelicopterData.rotorvel += rotating;
if (ABS(HelicopterData.rotorvel) > 256)
rotating = -rotating;
}
}
// [D] [T]
void StepEvents(void)
{
EVENT* ev;
int i;
VECTOR old;
XZPAIR speed;
VECTOR* pos;
EVENT* evt;
VECTOR* vel;
CELL_OBJECT* cop;
CAR_DATA* cp;
int onBoatLastFrame;
int dist;
int thisCamera, otherCamera;
onBoatLastFrame = carsOnBoat;
ev = firstEvent;
if (detonator.timer)
DetonatorTimer();
while (ev)
{
//carsOnBoat = onBoatLastFrame;
if (ev->flags & 2)
{
if (ev->flags & 0x40)
{
i = 0;
cp = car_data;
carsOnBoat = 0;
do {
if (cp->controlType != CONTROL_TYPE_NONE &&
OnBoat((VECTOR*)cp->hd.where.t, ev, &dist))
{
carsOnBoat |= 1 << i;
}
i++;
cp++;
} while (i < MAX_CARS && i < 32);
// make Tanner on boat also
if (player[0].playerType == 2 && OnBoat((VECTOR*)player[0].pos, ev, &dist))
carsOnBoat |= (1 << TANNER_COLLIDER_CARID) | 0x200000;// 0x300000;
BoatOffset(&boatOffset, ev);
old.vx = ev->position.vx;
old.vz = ev->position.vz;
}
if (ev->flags & 0x10)
StepFromToEvent(ev);
else
StepPathEvent(ev);
if (ev->flags & 0x800)
{
ev->data[1] = RSIN(CameraCnt * 32) >> 9;
ev->data[2] = RCOS(CameraCnt * 16) + 4096 >> 7;
if (detonator.timer - 1U < 159) // HMM?
{
int tmSqr;
tmSqr = detonator.timer * detonator.timer;
ev->data[1] -= RSIN(detonator.timer * 64) * tmSqr >> 18; //rcossin_tbl[(detonator.timer & 0x3fU) * 128] * tmSqr >> 0x12;
ev->data[2] -= RSIN(detonator.timer * 64) * tmSqr >> 16; //rcossin_tbl[(detonator.timer & 0x3fU) * 128] * tmSqr >> 0x10;
}
if (foam.rotate & 0xffff7fffU) // HMMMMMM?
foam.rotate--;
else
foam.rotate ^= ((Random2(0) & 0xf00) >> 8) + 8U | 0x8000;
}
// move cars on boats
if ((ev->flags & 0x40) && (carsOnBoat != 0 || onBoatLastFrame != 0))
{
int bit;
speed.x = ev->position.vx - old.vx;
speed.z = ev->position.vz - old.vz;
// go thru cars
for (i = 0; i < MAX_CARS + 1 && i < 32; i++)
{
bit = (1 << i);
if (i == TANNER_COLLIDER_CARID)
{
pos = (VECTOR*)player[0].pos;
vel = NULL;
}
else
{
pos = (VECTOR*)car_data[i].hd.where.t;
vel = (VECTOR*)car_data[i].st.n.linearVelocity;
}
// update position and add velocity
if (carsOnBoat & bit)
{
pos->vx += speed.x;
pos->vz += speed.z;
if (i == TANNER_COLLIDER_CARID)
{
SetTannerPosition(pos);
carsOnBoat &= ~(1 << TANNER_COLLIDER_CARID);
}
else if ((onBoatLastFrame & bit) == 0)
{
vel->vx -= speed.x * 4096;
vel->vz -= speed.z * 4096;
}
// [A] this causes drifting, StepOneCar already does better job at updating this
// car_data[i].st.n.fposition[0] = pos->vx << 4;
// car_data[i].st.n.fposition[2] = pos->vz << 4;
}
else if (vel && (onBoatLastFrame & bit))
{
vel->vx += speed.x * 4096;
vel->vz += speed.z * 4096;
}
}
}
}
else
{
ushort flags;
flags = ev->flags & 0xcc0;
if (flags == 0x40)
{
// perform rotation of doors
if (ev->timer != 0)
{
FixedEvent* door;
int sign, rotAngle;
unsigned short* target;
door = (FixedEvent*)ev;
if (door->active == 1)
target = &door->finalRotation;
else
target = &door->initialRotation;
rotAngle = ABS(door->rotation - door->initialRotation) * 2048 / ABS(door->finalRotation - door->initialRotation);
sign = (*target - door->rotation) >> 0x1f;
door->rotation += (door->minSpeed + (RSIN(rotAngle) * (door->maxSpeed - door->minSpeed) >> 0xc) ^ sign) - sign;
// check if complete
if (((*target - door->rotation) ^ sign) - sign < 0)
{
door->rotation = *target;
door->active = 0;
if (gCurrentMissionNumber != 30)
SetMSoundVar(3, NULL);
if (door == (FixedEvent*)events.cameraEvent)
SetSpecialCamera(SPECIAL_CAMERA_RESET, 0);
}
}
}
else if (flags < 0x41)
{
// perform bridge rotation
if ((ev->flags & 0xcc0) == 0)
{
ev->rotation = GetBridgeRotation(ev->timer);
if (ev->model & 1)
ev->rotation = -ev->rotation;
if ((ev->flags & 0x100) == 0 || ev->timer <= 1000)
{
ev->timer++;
ev->timer %= 8000;
}
}
}
else if (flags == 0xc0)
{
StepHelicopter(ev);
}
else if (flags == 0x440)
{
// rotate ferris wheel
if ((CameraCnt & 0x1fU) == 0)
{
if ((chicagoDoor[2].active & 1U) == 0)
{
chicagoDoor[2].active += 2;
if (chicagoDoor[2].active == 30)
chicagoDoor[2].active = 31;
}
else
{
chicagoDoor[2].active -= 2;
if (chicagoDoor[2].active == -1)
chicagoDoor[2].active = 0;
}
}
chicagoDoor[2].rotation -= chicagoDoor[2].active;
chicagoDoor[2].rotation &= 0xfff;
}
}
onBoatLastFrame = carsOnBoat;
ev = ev->next;
}
VisibilityLists(VIS_SORT, 0);
if (EventCop != NULL)
{
event_models_active = 0;
for (i = 0; i < NumPlayers; i++)
{
unsigned short* x;
unsigned short* z;
if (i == 0)
{
thisCamera = 0x8000;
otherCamera = 0x4000;
}
else
{
thisCamera = 0x4000;
otherCamera = 0x8000;
}
VisibilityLists(VIS_NEXT, i);
x = xVis;
while ((*x & thisCamera) == 0)
{
if ((*x & otherCamera) == 0)
{
if ((*x & 0x80) == 0)
evt = &event[*x & 0x7f];
else
evt = (EVENT*)&fixedEvent[*x & 0x7f];
// events that enable drawing
if ((evt->flags & 0x204) == 0x200)
{
z = zVis;
while ((*z & thisCamera) == 0)
{
if ((*z & otherCamera) == 0 && (*x & 0xfff) == (*z & 0xfff))
{
cop = &EventCop[event_models_active++];
cop->pos.vx = evt->position.vx;
cop->pos.vy = evt->position.vy;
cop->pos.vz = evt->position.vz;
cop->yang = (evt->rotation >> 6);
cop->type = evt->model;
// [A] train should be only dangerous in "Beat the train"
if ((evt->flags & 0x12) == 2 && evt->data == VegasTrainData)
cop->pad = 1;
else
cop->pad = 0;
evt->flags |= 0x4;
}
z++;
}
}
}
x++;
}
}
}
if (cameraDelay.delay != 0 && --cameraDelay.delay == 0)
{
SetSpecialCamera((SpecialCamera)cameraDelay.type, 1);
}
}
// [D] [T]
void DrawFerrisWheel(MATRIX* matrix, VECTOR* pos)
{
MODEL* model;
int loop;
int sn, cs, angle;
int rotation;
VECTOR offset;
VECTOR carPos;
if (chicagoDoor[2].model == -1)
return;
RenderModel(modelpointers[chicagoDoor[2].model], matrix, pos, 0, PLOT_NO_CULL, 1, 0);
if (chicagoDoor[2].model != -1)
RenderModel(modelpointers[chicagoDoor[2].initialRotation], NULL, NULL, 0, 0, 1, 0);
matrix->m[0][0] = -matrix->m[0][0];
matrix->m[1][0] = -matrix->m[1][0];
matrix->m[2][0] = -matrix->m[2][0];
RenderModel(modelpointers[chicagoDoor[2].model], matrix, pos, 0, PLOT_NO_CULL, 1, 0);
if (chicagoDoor[2].minSpeed == -1)
return;
VECTOR spoke[2] = {
{0, 0, 2677},
{0, 2677, 0}
};
rotation = 0;
model = modelpointers[chicagoDoor[2].minSpeed];
SetRotMatrix(&inv_camera_matrix);
_MatrixRotate(&spoke[0]);
_MatrixRotate(&spoke[1]);
for (loop = 0; loop < 5; loop++)
{
angle = chicagoDoor[2].rotation + rotation;
sn = RSIN(angle);
cs = RCOS(angle);
offset.vx = FIXEDH(spoke[0].vx * sn + spoke[1].vx * cs);
offset.vy = FIXEDH(spoke[0].vy * sn + spoke[1].vy * cs);
offset.vz = FIXEDH(spoke[0].vz * sn + spoke[1].vz * cs);
carPos.vx = pos->vx + offset.vx;
carPos.vy = pos->vy + offset.vy;
carPos.vz = pos->vz + offset.vz;
gte_SetTransVector(&carPos);
RenderModel(model, NULL, NULL, 0, 0, 1, 0);
carPos.vx = pos->vx - offset.vx;
carPos.vy = pos->vy - offset.vy;
carPos.vz = pos->vz - offset.vz;
gte_SetTransVector(&carPos);
RenderModel(model, NULL, NULL, 0, 0, 1, 0);
rotation += 410;
}
}
// [D] [T]
void DrawRotor(VECTOR pos, MATRIX* matrix)
{
POLY_FT4* poly;
MATRIX localMat;
int z;
SVECTOR v[5] = {
{0,-470,-120},
{1024,0,0 },
{0,0,1024 },
{0,0,0},
{0,0,0},
};
InitMatrix(localMat);
pos.vx -= camera_position.vx;
pos.vy -= camera_position.vy;
pos.vz -= camera_position.vz;
_RotMatrixY(&localMat, HelicopterData.rotorrot & 0xfff);
ApplyMatrixSV(matrix, v, v);
MulMatrix0(matrix, &localMat, &localMat);
ApplyMatrixSV(&localMat, v + 1, v + 1);
ApplyMatrixSV(&localMat, v + 2, v + 2);
v[0].vx += pos.vx;
v[0].vy += pos.vy;
v[0].vz += pos.vz;
v[1].vx += v[0].vx;
v[1].vy += v[0].vy;
v[1].vz += v[0].vz;
v[2].vx += v[0].vx;
v[2].vy += v[0].vy;
v[2].vz += v[0].vz;
v[4].vx = v[0].vx - v[2].vx;
v[4].vy = v[0].vy - v[2].vy;
v[4].vz = v[0].vz - v[2].vz;
v[3].vx = v[0].vx + v[4].vx;
v[3].vy = v[0].vy + v[4].vy;
v[3].vz = v[0].vz + v[4].vz;
gte_SetRotMatrix(&inv_camera_matrix);
gte_SetTransVector(&dummy);
gte_ldv3(&v[0], &v[1], &v[2]);
gte_rtpt();
poly = (POLY_FT4*)current->primptr;
*(ushort*)&poly->u0 = *(ushort*)&HelicopterData.rotorTexture.coords.u0;
*(ushort*)&poly->u1 = *(ushort*)&HelicopterData.rotorTexture.coords.u1;
*(ushort*)&poly->u2 = ((*(ushort*)&HelicopterData.rotorTexture.coords.u0 & 0xfefe) >> 1) + ((*(ushort*)&HelicopterData.rotorTexture.coords.u3 & 0xfefe) >> 1);
*(ushort*)&poly->u3 = *(ushort*)&HelicopterData.rotorTexture.coords.u3;
poly->tpage = HelicopterData.rotorTexture.tpageid | 0x40;
poly->clut = HelicopterData.rotorTexture.clutid;
setPolyFT4(poly);
setSemiTrans(poly, 1);
poly->r0 = 127;
poly->g0 = 127;
poly->b0 = 127;
gte_avsz3();
gte_stsxy3(&poly->x2, &poly->x1, &poly->x0);
gte_stsz(&z);
z = z + 1000 >> 3;
gte_ldv0(&v[3]);
gte_rtps();
gte_stsxy(&poly->x3);
addPrim(current->ot + z, poly);
current->primptr += sizeof(POLY_FT4);
*(poly + 1) = *poly;
poly++;
v[3].vz = v[0].vz * 2 - v[1].vz;
v[3].vy = v[0].vy * 2 - v[1].vy;
v[3].vx = v[0].vx * 2 - v[1].vx;
gte_ldv0(&v[3]);
gte_rtps();
gte_stsxy(&poly->x1);
addPrim(current->ot + z, poly);
*(ushort*)&poly->u1 = *(ushort*)&HelicopterData.rotorTexture.coords.u2;
current->primptr += sizeof(POLY_FT4);
}
// [D] [T]
void DrawEvents(int camera)
{
static EVENT* nearestTrain; // offset 0x28
static int distanceFromNearestTrain; // offset 0x2c
int reflection;
int i;
unsigned short* x;
unsigned short* z;
EVENT* ev;
MATRIX matrix;
VECTOR pos;
VECTOR shadow[4];
XZPAIR offset;
int thisCamera;
int otherCamera;
int temp;
#ifndef PSX
if (gDemoLevel)
{
firstEvent = NULL;
EventCop = NULL;
return;
}
#endif
if (CurrentPlayerView == 0)
{
nearestTrain = NULL;
thisCamera = 0x8000;
otherCamera = 0x4000;
}
else
{
thisCamera = 0x4000;
otherCamera = 0x8000;
}
if (camera == 0)
{
SetFrustrumMatrix();
}
else
{
if (cameraEventsActive == 0)
return;
InitEventCamera();
}
VisibilityLists(VIS_NEXT, CurrentPlayerView);
x = xVis;
while ((*x & thisCamera) == 0)
{
if ((*x & otherCamera) == 0)
{
if ((*x & 0x80) == 0)
ev = &event[*x & 0x7f];
else
ev = (EVENT*)&fixedEvent[*x & 0x7f];
z = zVis;
while ((*z & thisCamera) == 0)
{
if ((*z & otherCamera) == 0 && (*x & 0xfff) == (*z & 0xfff))
{
if ((ev->flags & 4) == 0 && (ev->flags & 1) == camera)
{
ev->flags |= 4;
if (camera == 0)
{
if (es_mobile[0] != -1 && (ev->flags & 2))
{
int dist;
dist = ABS(ev->position.vx - camera_position.vx) + ABS(ev->position.vz - camera_position.vz);
if (nearestTrain == NULL || dist < distanceFromNearestTrain)
{
nearestTrain = ev;
distanceFromNearestTrain = dist;
}
}
if (FrustrumCheck(&ev->position, modelpointers[ev->model]->bounding_sphere) != -1)
{
pos.vx = ev->position.vx - camera_position.vx;
pos.vy = ev->position.vy - camera_position.vy;
pos.vz = ev->position.vz - camera_position.vz;
matrix.m[0][0] = ONE;
matrix.m[1][0] = 0;
matrix.m[2][0] = 0;
matrix.m[0][1] = 0;
matrix.m[1][1] = ONE;
matrix.m[2][1] = 0;
matrix.m[0][2] = 0;
matrix.m[1][2] = 0;
matrix.m[2][2] = ONE;
reflection = 0;
if ((ev->flags & 2U) == 0)
{
int type;
type = ev->flags & 0xcc0;
if (type == 0x80)
{
_RotMatrixY(&matrix, ev->rotation);
pos.vx = pos.vx - boatOffset.vx;
pos.vz = pos.vz - boatOffset.vz;
pos.vy = (pos.vy - boatOffset.vy) + FIXEDH((int)ev->node * RSIN(*ev->data));
}
else if (type == 0x400)
{
_RotMatrixY(&matrix, ev->rotation);
}
else if (type == 0x440)
{
_RotMatrixX(&matrix, ev->rotation);
}
else if (type == 0x480)
{
int rot;
for (i = 0; i < 4; i++)
{
if ((i & 1) == 0)
offset.x = -201;
else
offset.x = 201;
if ((i & 2) == 0)
offset.z = -1261;
else
offset.z = 1261;
rot = ev->rotation + 0x200U & 0x400;
shadow[i].vx = ev->position.vx + (rot ? offset.z : offset.x);
shadow[i].vy = 0;
shadow[i].vz = ev->position.vz + (rot ? offset.x : offset.z);
}
PlaceShadowForCar(shadow, 0, 35, 4);
_RotMatrixY(&matrix, ev->rotation);
}
else if (type != 0xc0)
{
type = ev->flags & 0x30;
if (type == 0x20)
{
_RotMatrixZ(&matrix, ev->rotation);
}
else if (type == 0x10)
{
_RotMatrixY(&matrix, ev->rotation);
}
else if (type == 0)
{
_RotMatrixX(&matrix, ev->rotation);
}
}
}
else
{
_RotMatrixY(&matrix, ev->rotation);
}
gte_SetRotMatrix(&inv_camera_matrix);
_MatrixRotate(&pos);
if ((ev->flags & 0xcc0) == 0xc0)
{
if (ev->model == HelicopterData.cleanModel)
{
_RotMatrixZ(&matrix, HelicopterData.roll & 0xfff);
_RotMatrixX(&matrix, HelicopterData.pitch & 0xfff);
_RotMatrixY(&matrix, ev->rotation & 0xfff);
DrawRotor(ev->position, &matrix);
RenderModel(modelpointers[ev->model], &matrix, &pos, 0, 0, 1, 0);
pos.vx = ev->position.vx - camera_position.vx;
pos.vy = -camera_position.vy - ev->position.vy;
pos.vz = ev->position.vz - camera_position.vz;
gte_SetRotMatrix(&inv_camera_matrix);
_MatrixRotate(&pos);
reflection = 1;
}
else
{
// scale matrix lil bit
matrix.m[0][0] = matrix.m[0][0] * 5 >> 2;
matrix.m[1][1] = matrix.m[1][1] * 5 >> 2;
matrix.m[2][2] = matrix.m[2][2] * 5 >> 2;
_RotMatrixY(&matrix, 2813);
RenderModel(modelpointers[ev->model], &matrix, &pos, 0, 0, 1, 0);
}
}
if (ev->flags & 0x800)
{
_RotMatrixX(&matrix, ev->data[1]);
pos.vy -= ev->data[2];
temp = combointensity;
if (ev->flags & 0x20)
{
SetupPlaneColours(0x80a0c);
}
else
{
ev->flags |= 1;
reflection = 1; // [A] fix bug with ferry reflection at night
}
}
if (reflection)
{
matrix.m[1][0] = -matrix.m[1][0];
matrix.m[1][1] = -matrix.m[1][1];
matrix.m[1][2] = -matrix.m[1][2];
SetupPlaneColours(0x00464a40);
RenderModel(modelpointers[ev->model], &matrix, &pos, 400, PLOT_INV_CULL, 1, ev->rotation >> 6);
}
else
{
if ((ev->flags & 0xcc0U) == 0x440)
DrawFerrisWheel(&matrix, &pos);
else
RenderModel(modelpointers[ev->model], &matrix, &pos, 0, 0, 1, ev->rotation >> 6);
}
if (ev->flags & 0x800)
{
combointensity = temp;
matrix.m[0][0] = 0;
matrix.m[0][1] = 0;
matrix.m[0][2] = (foam.rotate & 0x8000) ? ONE : -ONE;
matrix.m[1][0] = 0;
matrix.m[1][1] = ONE;
matrix.m[1][2] = 0;
matrix.m[2][0] = ONE;
matrix.m[2][1] = 0;
matrix.m[2][2] = 0;
pos.vy += ev->data[2];
if (gTimeOfDay != 1)
SetupPlaneColours(0x00282828);
RenderModel(foam.model, &matrix, &pos, 200, (foam.rotate & 0x8000) ? 0x3 : 0x1, 1, 0);
SetupPlaneColours(combointensity);
}
}
}
else
{
SetCamera(ev);
DrawMapPSX(&ObjectDrawnValue);
}
}
}
z++;
}
}
x++;
}
if (camera)
{
ResetEventCamera();
}
else if (eventHaze != 0)
{
add_haze(eventHaze, eventHaze, 7);
}
if (camera == 0 && (CurrentPlayerView == NumPlayers - 1) && es_mobile[0] != -1)
{
if (nearestTrain == NULL)
{
SetEnvSndVol(es_mobile[0], -10000);
}
else
{
SetEnvSndVol(es_mobile[0], 2000);
SetEnvSndPos(es_mobile[0], nearestTrain->position.vx, nearestTrain->position.vz);
}
}
}
// [A] camera offset for events
void EventCameraOffset(SVECTOR* offset)
{
offset->vx = 0;
offset->vy = 0;
offset->vz = 0;
if (gCurrentMissionNumber == 22)
{
offset->vy = 650;
offset->vz = -850;
}
}
int debugOffset = 0;
// [D] [T]
sdPlane* EventSurface(VECTOR* pos, sdPlane* plane)
{
int d;
EVENT* ev;
int d1, d2;
int dist;
int sin;
int cos;
int i;
i = plane->surface & 0xffef;
ev = &event[i];
// chicago bridge plane
if (GameLevel == 0)
{
int dist;
int end;
if ((ev->flags & 0x30U) == 0)
dist = pos->vz;
else
dist = pos->vx;
if ((i & 1) == 0)
end = ev->data[1];
else
end = ev->data[-1];
d = ev->rotation;
sin = RSIN(d);
cos = RCOS(d);
d1 = (end - ev->data[0]) * cos;
d2 = dist - ev->data[0];
if (ABS(d1) < ABS(d2) * 4096)
return GetSeaPlane();
plane->d = d2 * sin / cos;
if (d2 > 1024)
{
if (pos->vy + 200 < plane->d)
{
return GetSeaPlane();
}
plane->b = cos * 4;
}
else
{
plane->b = cos * 4;
}
if ((ev->flags & 0x30U) == 0)
{
plane->a = 0;
plane->c = sin * -4;
//plane->d = (sin * -0x40000) >> 0x10; // [A] this is unused and incorrect!
}
else
{
plane->c = 0;
plane->a = sin * -4;
}
plane->d ^= 0x40000000;
}
else if (GameLevel == 1 || GameLevel == 3)
{
// make secret base solid due to we use surface Ids
// Havana 3D occlusion was made simpler in Rev 1.1
// this change was very sneaky
if (i > 8)
return plane;
// Havana and Rio boats floating
if (ev->flags & 0x800)
{
int height;
int offset;
if (OnBoat(pos, ev, &dist) == 0)
return GetSeaPlane();
if (GameLevel == 3)
height = 256;
else
height = 200;
d = ev->data[1];
sin = RSIN(d);
cos = RCOS(d);
offset = dist * -4096 + cos * -3328;
if (GameLevel == 3 && offset > 0)
{
int sin2;
d = 160 - ev->data[1];
cos = RCOS(d);
sin2 = RSIN(d);
if (offset > cos * 2048)
{
debugOffset = offset;
return GetSeaPlane();
}
plane->d = height - (FIXEDH(sin * 3328) + ev->data[2] + ev->position.vy) + (FIXEDH(offset) * sin2 / cos) ^ 0x40000000;
plane->b = cos * 4;
plane->a = 0;
plane->c = sin2 * 4;
}
else
{
plane->b = cos * 4;
plane->a = 0;
plane->c = sin * 4;
plane->d = (height + (sin * dist / cos - ev->data[2] - ev->position.vy)) ^ 0x40000000;
}
}
else
{
if (pos->vy < -ev->position.vy - 100)
{
plane->d = 0xbfffee02;
}
else
{
plane->d = ev->position.vy;
}
}
}
else
{
return GetSeaPlane();
}
return plane;
}
// [D] [T]
void MakeEventTrackable(EVENT* ev)
{
EVENT** p;
p = trackingEvent;
while (*p)
p++;
*p = ev;
p[1] = NULL; // WTF?
}
// [D] [T]
void TriggerDoor(FixedEvent* door, int* stage, int sound)
{
if (*stage == 0)
{
if ((door->flags & 0x30) != 0x10)
door->flags &= ~0x200;
door->active = 2;
}
else
{
door->active = 1;
door->flags |= 0x200;
*stage = -1;
}
if (sound != 0)
SetMSoundVar(1, &door->position);
}
// [D] [T]
VECTOR* TriggerEvent(int i)
{
static int stage[10];
EVENT* ev;
int loop;
VECTOR* pos;
pos = NULL;
if (i == -1) // initialize
{
for (loop = 0; loop < 10; loop++)
stage[loop] = 0;
return NULL;
}
if (GameLevel >= 2 && i > 0 && i < 4) // Vegas and Rio detonators
{
if (stage[i] == 0)
{
ev = &firstMissionEvent[i - 1];
ev->next = firstEvent->next;
firstEvent->next = ev;
VisibilityLists(VIS_ADD, ev - event);
SetMSoundVar(i, NULL);
detonator.count++;
}
}
else
{
if (GameLevel == 0)
{
switch (i)
{
case 0:
case 1:
if (stage[i] == 0)
{
int offset, nodePos;
ev = missionTrain[i].engine;
MakeEventTrackable(ev);
ev->flags |= 0x180;
if (*missionTrain[i].node - missionTrain[i].start > -1)
offset = 1600;
else
offset = -1600;
loop = 0;
do {
if (missionTrain[i].node[0] == PATH_NODE_STATION)
nodePos = missionTrain[i].node[-1];
else
nodePos = missionTrain[i].node[1];
if (missionTrain[i].startDir == 0x8000)
{
ev->flags |= 0x8000;
ev->position.vx = nodePos;
ev->position.vz = missionTrain[i].start + loop * offset;
}
else
{
ev->flags &= ~0x8000;
ev->position.vz = nodePos;
ev->position.vx = missionTrain[i].start + loop * offset;
}
ev->node = missionTrain[i].node;
ev->data = &missionTrain[i].cornerSpeed;
ev->timer = 0;
ev->flags &= ~0x7000;
ev->flags |= 0x3000;
SetElTrainRotation(ev);
loop++;
ev = ev->next;
} while (ev && (ev->flags & 0x400U) == 0);
}
else
{
ev = missionTrain[i].engine;
pos = &ev->position;
if (ev->timer != 0)
ev->timer = 1;
}
break;
case 2:
case 3:
case 4:
if (stage[i] == 0)
{
// start bridges raised
ev = firstMissionEvent;
for (loop = 0; loop < 10; loop++)
{
ev->flags |= 0x100;
if (i == 4)
ev->timer = 2600;
else
ev->timer = 1000;
ev++;
}
if (i == 2)
{
firstMissionEvent[9].timer = 2600;
firstMissionEvent[8].timer = 2600;
}
}
else
{
// raise bridges
firstMissionEvent[8].timer = 0;
firstMissionEvent[9].timer = 0;
}
break;
case 5:
PrepareSecretCar();
events.cameraEvent = (EVENT*)&chicagoDoor[0];
case 6:
TriggerDoor(&chicagoDoor[i - 5], &stage[i], 1);
}
}
else if (GameLevel == 1)
{
switch (i)
{
case 0:
event->timer = 1;
break;
case 1:
event->position.vx = HavanaFerryData[9];
event->position.vz = HavanaFerryData[10];
event->timer = 1;
event->node = &HavanaFerryData[10];
event->data = &HavanaFerryData[6];
// [A] reset Ferry angles
event->data[1] = RSIN(CameraCnt * 32) >> 9;
event->data[2] = RCOS(CameraCnt * 16) + 4096 >> 7;
break;
case 2:
TriggerDoor(&havanaFixed[0], &stage[i], 1);
break;
case 3:
PrepareSecretCar();
events.cameraEvent = (EVENT*)&havanaFixed[2];
TriggerDoor(&havanaFixed[2], &stage[i], 0);
break;
case 4:
if (stage[i] != 0)
{
SetSpecialCamera(SPECIAL_CAMERA_WAIT, 0);
event[1].node++;
}
SetMSoundVar(1, &event[1].position);
event[1].timer = 0;
events.cameraEvent = &event[1];
}
}
else if (GameLevel == 2)
{
switch (i)
{
case 0:
loop = 0;
// start train
do {
ev = &event[loop];
ev->data = VegasTrainData;
InitTrain(ev, loop, 1);
ev->flags |= 0x200;
if (loop > 1)
VisibilityLists(VIS_ADD, loop);
loop++;
} while (loop < 9);
event->flags |= 0x500;
MakeEventTrackable(ev);
event[1].next = &event[2];
break;
case 4:
TriggerDoor(&vegasDoor[i - 4], &stage[i], 0);
break;
case 8:
events.cameraEvent = (EVENT*)&vegasDoor[4];
PrepareSecretCar();
case 5:
case 6:
case 7:
TriggerDoor(&vegasDoor[i - 4], &stage[i], 1);
break;
case 9:
SetMSoundVar(5, NULL);
}
}
else if (GameLevel == 3)
{
switch (i)
{
case 0:
event->timer = 1;
break;
case 4:
// open race track gates
TriggerDoor(&rioDoor[2], &stage[i], 0);
TriggerDoor(&rioDoor[3], &stage[i], 0);
events.cameraEvent = (EVENT*)&rioDoor[2];
break;
case 5:
case 6:
TriggerDoor(&rioDoor[i - 5], &stage[i], (i == 5));
break;
case 7:
if (stage[i] == 0)
{
pos = &event[1].position;
}
else
{
event[1].position.vy = -17;
event[1].position.vx = -241619;
event[1].position.vz = -212638;
event[1].timer = -2;
event[1].model = HelicopterData.deadModel;
}
break;
case 8:
// open gate to secret car
PingOutAllSpecialCivCars();
TriggerDoor(&rioDoor[4], &stage[i], 0);
TriggerDoor(&rioDoor[5], &stage[i], 0);
events.cameraEvent = (EVENT*)&rioDoor[4];
}
}
}
if (i < 10)
stage[i]++;
return pos;
}
// [D] [T]
void OffsetTarget(VECTOR* target)
{
target->vx -= boatOffset.vx;
target->vy -= boatOffset.vy;
target->vz -= boatOffset.vz;
}
//[D] [T]
void SetSpecialCamera(SpecialCamera type, int change)
{
static int rememberCamera[3]; // offset 0x230
static short boatCamera[6] = {
-1000, 2100, 6000,
-5000, 3584, -5000
};
int* hackCamera;
short* boat;
if (change == 0 && type != SPECIAL_CAMERA_WAIT)
{
cameraDelay.delay = 1;
cameraDelay.type = type;
camera_change = 1;
return;
}
if (type == SPECIAL_CAMERA_RESET)
{
events.cameraEvent = NULL;
gStopPadReads = 0;
camera_position.vx = rememberCamera[0];
camera_angle.vy = (short)rememberCamera[1];
camera_position.vz = rememberCamera[2];
player[0].cameraPos.vx = rememberCamera[0];
player[0].cameraPos.vy = camera_position.vy;
player[0].cameraPos.vz = rememberCamera[2];
player[0].cameraPos.pad = camera_position.pad;
}
else if (type == SPECIAL_CAMERA_WAIT)
{
cameraDelay.delay = 100;
cameraDelay.type = 1;
gStopPadReads = 1;
}
else
{
boat = NULL;
rememberCamera[0] = camera_position.vx;
rememberCamera[1] = camera_angle.vy;
rememberCamera[2] = camera_position.vz;
if (GameLevel == 0)
{
hackCamera = &ChicagoCameraHack[0];
}
else if (GameLevel == 1)
{
if (gCurrentMissionNumber == 15)
{
boat = boatCamera;
events.cameraEvent = (EVENT*)0x1;
}
else
{
if (type == SPECIAL_CAMERA_SET2)
{
hackCamera = &HavanaCameraHack[6];
camera_position.vy = -500;
}
else if (doneFirstHavanaCameraHack)
{
hackCamera = &HavanaCameraHack[3];
}
else
{
hackCamera = &HavanaCameraHack[0];
doneFirstHavanaCameraHack = 1;
}
}
}
else if (GameLevel == 2)
{
hackCamera = &VegasCameraHack[0];
if (gCurrentMissionNumber == 23)
{
if (type != SPECIAL_CAMERA_SET2)
{
hackCamera = &VegasCameraHack[6];
events.cameraEvent = (EVENT*)0x1;
}
else
{
hackCamera = &VegasCameraHack[9];
}
}
else if (gCurrentMissionNumber == 22)
{
hackCamera = &VegasCameraHack[13];
events.cameraEvent = (EVENT*)0x1;
camera_position.vy = -1800;
}
else if (gCurrentMissionNumber == 30)
{
hackCamera += 3;
}
}
else if (GameLevel == 3)
{
if (gCurrentMissionNumber == 35)
{
boat = &boatCamera[3];
}
else if (camera_position.vz > 0)
{
hackCamera = &RioCameraHack[3];
}
else
{
hackCamera = &RioCameraHack[0];
}
}
if (boat == NULL)
{
camera_position.vx = hackCamera[0];
camera_angle.vy = hackCamera[1];
camera_position.vz = hackCamera[2];
}
else
{
camera_position.vx = event->position.vx + boat[0];
camera_angle.vy = boat[1];
camera_position.vz = event->position.vz + boat[2];
}
player[0].cameraPos = camera_position;
if (events.cameraEvent != NULL)
{
gStopPadReads = 1;
}
}
if (type != SPECIAL_CAMERA_WAIT)
{
Havana3DLevelDraw = -1;
camera_change = 1;
VisibilityLists(VIS_SORT, 0);
}
}
// [D] [T]
void ScreenShake(int count, SVECTOR* ang)
{
eventHaze = count * 4;
camera_angle.vx = (ang->vx + (Random2(0) % (eventHaze / 2))) - eventHaze;
camera_angle.vy = (ang->vy + (Random2(0) % (eventHaze * 2))) - eventHaze;
eventHaze *= 3;
}
// [D] [T]
int DetonatorTimer(void)
{
static SVECTOR rememberCameraAngle; // offset 0x30
static int count = 0; // offset 0x38
int cnt;
EVENT* ev;
VECTOR pos;
if (gCurrentMissionNumber == 23)
{
if (detonator.timer - 3U < 17)
{
ScreenShake(detonator.timer - 2, &rememberCameraAngle);
}
else if (detonator.timer - 31U > 8)
{
if (detonator.timer == 21)
{
count++;
if ((count & 7) == 0)
{
AddExplosion(camera_position, BIG_BANG);
}
camera_position.vz += 150;
if (VegasCameraHack[12] < camera_position.vz)
{
pos.vx = camera_position.vx;
pos.vy = camera_position.vy;
pos.vz = 271695;
AddExplosion(pos, HEY_MOMMA);
rememberCameraAngle = camera_angle;
Mission.timer[0].flags &= ~TIMER_FLAG_BOMB_COUNTDOWN;
SetMissionComplete();
}
else
{
detonator.timer = detonator.timer + 1;
}
player[0].cameraPos.vz = camera_position.vz;
}
else if (detonator.timer == 0)
{
SetSpecialCamera(SPECIAL_CAMERA_SET, 0);
detonator.timer = 70;
}
else if (detonator.timer == 22)
{
SetSpecialCamera(SPECIAL_CAMERA_SET2, 0);
}
else if (detonator.timer == 40)
{
BombThePlayerToHellAndBack(gCarWithABerm);
car_data[gCarWithABerm].st.n.linearVelocity[1] += 200000;
rememberCameraAngle = camera_angle;
}
}
else
{
ScreenShake(detonator.timer - 30, &rememberCameraAngle);
}
}
else
{
if (detonator.timer - 141U < 19)
{
ScreenShake(detonator.timer - 140, &rememberCameraAngle);
Setup_Smoke(&firstMissionEvent[0].position, 100, 500, SMOKE_BLACK, 0, &dummy, 0);
}
else
{
if (detonator.timer == 167)
{
ev = &firstMissionEvent[1];
ev++;
}
else
{
if (detonator.timer < 168)
{
if (detonator.timer == 0)
{
cnt = detonator.count - 1;
ev = &firstMissionEvent[0];
if (detonator.count < 3)
{
while (detonator.count = cnt, detonator.count != -1)
{
AddExplosion(ev->position, BIG_BANG);
cnt = detonator.count - 1;
ev++;
}
return 0;
}
detonator.timer = 200;
SetSpecialCamera(SPECIAL_CAMERA_SET, 0);
events.cameraEvent = (EVENT*)0x1;
rememberCameraAngle = camera_angle;
}
else if (detonator.timer == 160)
{
if (GameLevel == 3)
{
event->flags &= ~0x1;
event->flags |= 0x20;
AddExplosion(event[0].position, HEY_MOMMA);
}
else
{
AddExplosion(firstMissionEvent[1].position, HEY_MOMMA);
}
}
detonator.timer--;
return 1;
}
ev = &firstMissionEvent[0];
if (detonator.timer == 180)
{
ev++;
}
else if (detonator.timer != 190)
{
detonator.timer--;
return 1;
}
}
pos.vx = ev->position.vx;
pos.vy = ev->position.vy;
pos.vz = ev->position.vz;
if (GameLevel == 3)
{
pos.vx -= boatOffset.vx;
pos.vy -= boatOffset.vy;
pos.vz -= boatOffset.vz;
}
AddExplosion(pos, BIG_BANG);
}
}
detonator.timer--;
return 1;
}
// [D] [T]
void MultiCarEvent(MS_TARGET* target)
{
EVENT* first;
int n;
EVENT* ev;
MULTICAR_DATA* mcd;
int i;
first = firstEvent;
// [A] validate
if (target->type != Target_MultiCar)
return;
firstEvent = multiCar.event + multiCar.count;
for (i = 0; i < 5; i++)
{
mcd = &target->multiCar[i];
if (mcd->x == 0x80000000)
break;
n = (multiCar.event - event) + multiCar.count;
ev = event + n;
ev->position.vx = mcd->x;
ev->position.vz = mcd->z;
ev->position.vy = -312;
ev->rotation = mcd->rot;
VisibilityLists(VIS_ADD, n);
multiCar.count++;
}
//firstEvent->next = first; // [A] bug fix
multiCar.event[multiCar.count - 1].next = first;
}