MuckyFoot-UrbanChaos/fallen/Source/chopper.cpp
2017-05-20 11:14:17 +10:00

805 lines
17 KiB
C++

//
// Helicopters
// Wockawockawockawocka
//
#include "game.h"
#include "animate.h"
#include "chopper.h"
#include "statedef.h"
#include "dirt.h"
#include "pap.h"
#include "mav.h"
#include "sound.h"
#include "mfx.h"
#include "eway.h"
#include "memory.h"
#include <stdio.h>
#define HARDWIRED_RADIUS 8192
//#define DETECTION_RADIUS 4096
#define DETECTION_RADIUS 1024
#define IGNORAMUS_RADIUS 6144
#define PRIM_OBJ_CHOPPER 74
#define PRIM_OBJ_CHOPPER_BLADES 75
/***********************************************
*
* Generic helicopter infrastructure stuff
*
*/
GenusFunctions CHOPPER_functions[CHOPPER_NUMB] =
{
{CHOPPER_NONE, NULL},
{CHOPPER_CIVILIAN, CIVILIAN_state_function}
};
//
// Zeros out the choppers in the 'the_game' structure.
//
#ifndef PSX
void init_choppers(void)
{
memset((UBYTE*)CHOPPERS, 0, sizeof(Chopper) * MAX_CHOPPERS);
CHOPPER_COUNT = 0;
}
//
// Creates a new chopper of the given type.
//
#endif
Thing *alloc_chopper(UBYTE type)
{
SLONG i;
Thing *p_thing;
Chopper *p_chopper;
DrawMesh *dm;
THING_INDEX t_index;
SLONG a_index;
ASSERT(WITHIN(type, 1, CHOPPER_NUMB - 1));
//
// Look for an unused chopper structure.
//
for (i = 1; i < MAX_CHOPPERS; i++)
{
if (CHOPPERS[i].ChopperType == CHOPPER_NONE)
{
a_index = i;
goto found_chopper;
}
}
//
// There are no spare chopper structures.
//
TRACE("Run out of chopper structures.");
return NULL;
found_chopper:
//
// Find the drawmesh structure for the chopper.
//
dm = alloc_draw_mesh();
if (dm == NULL)
{
ASSERT(0);
//
// Could not allocate a drawmesh structure.
//
TRACE("Run out of drawmesh structures.");
//
// Free up the chopper structure we allocated.
//
TO_CHOPPER(a_index)->ChopperType = CHOPPER_NONE;
return NULL;
}
t_index = alloc_primary_thing(CLASS_CHOPPER);
if (t_index)
{
p_thing = TO_THING(t_index);
p_chopper = TO_CHOPPER(a_index);
//
// Link the chopper to the thing and back again.
//
p_thing->Class = CLASS_CHOPPER;
p_thing->Genus.Chopper = p_chopper;
p_chopper->ChopperType = type;
p_chopper->thing = p_thing;
p_chopper->speed = 2048;
p_chopper->radius = HARDWIRED_RADIUS;
p_chopper->substate = CHOPPER_substate_landed;
//
// Set the draw type and the drawmesh stuff.
//
p_thing->DrawType = DT_CHOPPER;
p_thing->Draw.Mesh = dm;
dm->Angle = 0;
dm->Roll = 0;
dm->Tilt = 0;
// dm->ObjectId = PRIM_OBJ_TRAFFIC_CONE;
dm->ObjectId = PRIM_OBJ_CHOPPER;
p_chopper->rotorprim=PRIM_OBJ_CHOPPER_BLADES;
return p_thing;
}
else
{
//
// Free up the chopper structure and the
// drawmesh structure we allocated.
//
TO_CHOPPER(a_index)->ChopperType = CHOPPER_NONE;
free_draw_mesh(dm);
}
}
#ifndef PSX
void free_chopper(Thing *p_thing)
{
Chopper *chopper = CHOPPER_get_chopper(p_thing);
//
// Free the chopper structure and the thing structure.
//
chopper->ChopperType = CHOPPER_NONE;
CHOPPER_COUNT -= 1;
free_thing(p_thing);
}
#endif
Thing *CHOPPER_create(GameCoord pos, UBYTE type)
{
Thing *p_thing = alloc_chopper(type);
if (p_thing != NULL)
{
p_thing->WorldPos = pos;
add_thing_to_map(p_thing);
//
// Make it initialise itself.
//
set_state_function(p_thing, STATE_INIT);
}
return p_thing;
}
Chopper *CHOPPER_get_chopper(struct Thing *chopper_thing)
{
Chopper *chopper;
ASSERT(WITHIN(chopper_thing, TO_THING(1), TO_THING(MAX_THINGS)));
ASSERT(chopper_thing->Class == CLASS_CHOPPER);
chopper = chopper_thing->Genus.Chopper;
ASSERT(WITHIN(chopper, TO_CHOPPER(1), TO_CHOPPER(MAX_CHOPPERS - 1)));
return chopper;
}
DrawMesh *CHOPPER_get_drawmesh(struct Thing *chopper_thing)
{
DrawMesh *dm;
ASSERT(WITHIN(chopper_thing, TO_THING(1), TO_THING(MAX_THINGS)));
ASSERT(chopper_thing->Class == CLASS_CHOPPER);
dm = chopper_thing->Draw.Mesh;
ASSERT(WITHIN(dm, &DRAW_MESHES[0], &DRAW_MESHES[MAX_DRAW_MESHES - 1]));
return dm;
}
/*************************************************************
*
* Chopper Utility Belt
*
*/
SLONG CHOPPER_altitude(Thing *thing) {
// return thing->WorldPos.Y-(250<<8);
// return (thing->WorldPos.Y-(250<<8))-(PAP_calc_height_at(thing->WorldPos.X>>8,thing->WorldPos.Z>>8)<<8);
// return (thing->WorldPos.Y-(250<<8))-(PAP_calc_map_height_at(thing->WorldPos.X>>8,thing->WorldPos.Z>>8)<<8);
return (thing->WorldPos.Y-(250<<8))-(PAP_calc_map_height_at(thing->WorldPos.X>>8,thing->WorldPos.Z>>8));
}
void CHOPPER_home(Thing *thing, GameCoord new_pos) {
Chopper *chopper = CHOPPER_get_chopper(thing);
DrawMesh *dm = CHOPPER_get_drawmesh(thing);
SLONG dx,dz,angle,dangle;
// CBYTE msg[300];
UBYTE accel;
UWORD speed;
dx = new_pos.X - thing->WorldPos.X;
dz = new_pos.Z - thing->WorldPos.Z;
// chopper->dist = Root( dx*dx + dz*dz );
// chopper->dist = QDIST2(dx,dz);
chopper->dist = SDIST2((dx>>8),(dz>>8));
accel=0;
if (chopper->dist>(128*128)) {
if (chopper->dist>(512*512)) {
//speed=8192;
accel=96;
} else {
//speed=2048;
accel=64;
}
}
if (chopper->since_takeoff < 255)
{
accel = chopper->since_takeoff >> 2;
chopper->since_takeoff += 5;
}
// speed = QDIST2((dx>>8),(dz>>8))*16;
speed = chopper->dist/64;
if (speed<2048) speed=2048;
if (speed>12288) speed=12288;
chopper->speed=speed;
if (accel) {
if (thing->WorldPos.X > new_pos.X)
chopper->dx-=accel;
if (thing->WorldPos.X < new_pos.X)
chopper->dx+=accel;
if (thing->WorldPos.Z > new_pos.Z)
chopper->dz-=accel;
if (thing->WorldPos.Z < new_pos.Z)
chopper->dz+=accel;
}
if ((chopper->dist>(512*512))||chopper->counter) {
angle=-Arctan(dx,dz);
angle&=2047;
dangle = angle - dm->Angle;
if (dangle > 1024) {dangle -= 2048;}
if (dangle < -1024) {dangle += 2048;}
dangle >>= 3;
SATURATE(dangle, -10, +10);
chopper->ry=dangle;
if (chopper->dist>(512*512)) chopper->counter=128;
}
if (chopper->counter) chopper->counter--;
dm->Angle += chopper->ry;
dm->Angle &= 2047;
chopper->ry=(chopper->ry*204)>>8; //float removed MD
// sprintf(msg,"dist:%d counter:%d spd: %d\n",chopper->dist,chopper->counter,chopper->speed);
// TRACE(msg);
//
// Avoid buildings.
//
{
SLONG dist;
SLONG mid_x = thing->WorldPos.X >> 8;
SLONG mid_y = thing->WorldPos.Y >> 8;
SLONG mid_z = thing->WorldPos.Z >> 8;
// mid_y -= 0x80;
mid_y -= 0xff;
#define CHOPPER_AVOID_SPEED 64
for (dist = 128; dist <= 0x400; dist += 128)
{
if (MAV_inside(mid_x + dist, mid_y, mid_z)) {chopper->dx -= CHOPPER_AVOID_SPEED;}
if (MAV_inside(mid_x - dist, mid_y, mid_z)) {chopper->dx += CHOPPER_AVOID_SPEED;}
if (MAV_inside(mid_x, mid_y, mid_z + dist)) {chopper->dz -= CHOPPER_AVOID_SPEED;}
if (MAV_inside(mid_x, mid_y, mid_z - dist)) {chopper->dz += CHOPPER_AVOID_SPEED;}
}
}
}
void CHOPPER_limit(Chopper *chopper) {
if (chopper->dx > chopper->speed) chopper->dx= (chopper->dx+chopper->dx+chopper->dx+chopper->speed)>>2;
if (chopper->dz > chopper->speed) chopper->dz= (chopper->dz+chopper->dz+chopper->dz+chopper->speed)>>2;
if (chopper->dx < -chopper->speed) chopper->dx= (chopper->dx+chopper->dx+chopper->dx-chopper->speed)>>2; //*0.25;
if (chopper->dz < -chopper->speed) chopper->dz= (chopper->dz+chopper->dz+chopper->dz-chopper->speed)>>2; //*0.25;
/* if (chopper->dx> chopper->speed) chopper->dx= chopper->speed;
if (chopper->dz> chopper->speed) chopper->dz= chopper->speed;
if (chopper->dx<-chopper->speed) chopper->dx=-chopper->speed;
if (chopper->dz<-chopper->speed) chopper->dz=-chopper->speed;*/
}
void CHOPPER_damp(Chopper *chopper, UBYTE factor) {
UBYTE i;
/*
for (i=0;i<factor;i++) {
if (chopper->dx>32) chopper->dx-=31;
if (chopper->dz>32) chopper->dz-=31;
if (chopper->dx<32) chopper->dx+=31;
if (chopper->dz<32) chopper->dz+=31;
if (chopper->dx>0) chopper->dx--;
if (chopper->dz>0) chopper->dz--;
if (chopper->dx<0) chopper->dx++;
if (chopper->dz<0) chopper->dz++;
}*/
// chopper->dx*=0.90; //bloody floats
//chopper->dz*=0.90;
chopper->dx=(chopper->dx*230)>>8;
chopper->dz=(chopper->dz*230)>>8;
}
UBYTE CHOPPER_radius_broken(GameCoord pnt, GameCoord ctr, SLONG radius) {
SLONG dist,dx,dz;
dx = (pnt.X-ctr.X)>>8;
dz = (pnt.Z-ctr.Z)>>8;
return (((dx*dx)+(dz*dz))>(radius*radius));
}
void CHOPPER_predict_altitude(Thing *thing, Chopper *chopper)
{
SLONG tx,tz,dx,dz,altitude,gnd;
SLONG dist;
if ((chopper->dx==0)&&(chopper->dz==0)) return;
tx=thing->WorldPos.X>>8;
tz=thing->WorldPos.Z>>8;
dx=chopper->dx>>3;
dz=chopper->dz>>3;
tx+=dx; tz+=dz;
gnd=PAP_calc_height_at(tx,tz)+0x10;
// DIRT_new_water(tx, gnd, tz, -1, 28, 0);
// altitude=(750<<8)+(thing->WorldPos.Y-(250<<8))-(PAP_calc_map_height_at(tx,tz)<<8);
altitude=(675<<8)+(PAP_calc_map_height_at(tx,tz)<<8); // was 750 - lower so we can see it more often
// try and be higher than our target
if (chopper->target&&(altitude<chopper->target->WorldPos.Y+(50<<8))) altitude=chopper->target->WorldPos.Y+(50<<8);
altitude-=CHOPPER_altitude(thing);
// altitude/=Root(SDIST2((dx>>8),(dz>>8)));
dist=Root( (dx*dx)+(dz*dz) );
dist = dist ? dist : 1;
altitude/=dist;
/*
if ((altitude>20)&&(MAV_inside(tx,gnd,tz))) {
TRACE("panic!\n");
CHOPPER_damp(chopper,1);
CHOPPER_damp(chopper,1);
}
*/
//chopper->dy=altitude;
altitude /= 32;
if (chopper->since_takeoff != 255)
{
//
// Still taking off.
//
SATURATE(altitude, -6, +6);
}
chopper->dy += altitude;
chopper->dy -= chopper->dy / 32;
}
/*************************************************************
*
* Specific helicopter behaviour stuff
*
*/
//
// The functions called when a chopper is in each State.
//
void CHOPPER_fn_init (Thing *);
void CHOPPER_fn_normal(Thing *);
StateFunction CIVILIAN_state_function[] =
{
{STATE_INIT, CHOPPER_fn_init },
{STATE_NORMAL, CHOPPER_fn_normal}
};
// ========================================================
//
// The state functions.
//
// ========================================================
void CHOPPER_fn_init(Thing *thing)
{
Chopper *chopper = CHOPPER_get_chopper(thing);
DrawMesh *dm = CHOPPER_get_drawmesh(thing);
chopper->home.X = thing->WorldPos.X;
chopper->home.Y = thing->WorldPos.Y;
chopper->home.Z = thing->WorldPos.Z;
//
// Start being a normal chopper.
//
set_state_function(thing, STATE_NORMAL);
}
void CHOPPER_fn_normal(Thing *thing)
{
GameCoord new_pos;
SLONG mag, rpos, altitude;
CBYTE msg[300];
// blatant ects hack
Thing *darci = NET_PERSON(0);
Chopper *chopper = CHOPPER_get_chopper(thing);
if (chopper->substate!=CHOPPER_substate_landed) {
// play_quick_wave(thing,S_TUNE_SEWER+1,WAVE_PLAY_NO_INTERUPT);
MFX_play_thing(THING_NUMBER(thing),S_HELI,MFX_LOOPED|MFX_QUEUED|MFX_MOVING,thing);
/* if (!chopper->channel) chopper->channel=play_object_wave(chopper->channel,thing,S_HELI,WAVE_PLAY_NO_INTERUPT|WAVE_LOOP);
#ifdef USE_A3D
if (chopper->channel) A3DPosition(chopper->channel,thing->WorldPos.X,thing->WorldPos.Y,thing->WorldPos.Z);
#endif
*/
}
// General stuff -- happens regardless of state
chopper->rotors+=(chopper->rotorspeed>>2);
new_pos=thing->WorldPos;
new_pos.X+=chopper->dx;
new_pos.Y+=chopper->dy;
new_pos.Z+=chopper->dz;
move_thing_on_map(thing, &new_pos);
altitude=CHOPPER_altitude(thing);
if (chopper->rotorspeed>0) {
// Are we low enough to cause ground-level turbulence?
if (altitude<(1200<<8)) {
// rotor speed and altitude affect the strength of the turbulence
mag=chopper->rotorspeed-(altitude>>10);
if (mag>0) {
rpos=chopper->rotors;
rpos&=2047;
new_pos.X=(mag*SIN(rpos))>>10;
new_pos.Z=(mag*COS(rpos))>>10;
new_pos.X+=thing->WorldPos.X;
new_pos.Z+=thing->WorldPos.Z;
DIRT_gust(
thing,
thing->WorldPos.X>>8,thing->WorldPos.Z>>8,
new_pos.X>>8, new_pos.Z>>8);
rpos+=1024;
rpos&=2047;
new_pos.X=(mag*SIN(rpos))>>10;
new_pos.Z=(mag*COS(rpos))>>10;
new_pos.X+=thing->WorldPos.X;
new_pos.Z+=thing->WorldPos.Z;
DIRT_gust(
thing,
thing->WorldPos.X>>8,thing->WorldPos.Z>>8,
new_pos.X>>8, new_pos.Z>>8);
}
}
}
// State specific stuff
switch(chopper->substate) {
case CHOPPER_substate_idle:
CHOPPER_damp(chopper,1);
if (chopper->victim)
CHOPPER_init_state(thing,CHOPPER_substate_tracking);
else
if (!CHOPPER_radius_broken(darci->WorldPos,thing->WorldPos,DETECTION_RADIUS)) {
if ((!chopper->target)&&!chopper->victim) chopper->target=darci;
CHOPPER_init_state(thing,CHOPPER_substate_tracking);
}
break;
case CHOPPER_substate_takeoff:
CHOPPER_damp(chopper,1);
if (chopper->rotorspeed < 500)
{
chopper->rotorspeed+=10; // was 2
if (chopper->rotorspeed > 150)
{
chopper->dy += chopper->rotorspeed - 150 >> 6;
chopper->dy += 2;
}
}
else
if (altitude<(170<<8)) {
chopper->dy+=10; // was 2
if (chopper->light < 250)
{
chopper->light += 5;
}
//chopper->light=255;
}
else
/*
if (chopper->dy>0)
chopper->dy--;
else
*/
{
//
// Don't slow down before you start chasing Darci!
//
CHOPPER_init_state(thing,CHOPPER_substate_idle);
}
break;
case CHOPPER_substate_landing:
CHOPPER_damp(chopper,4);
if (altitude>0) {
chopper->dy=((chopper->dy+chopper->dy+chopper->dy-(altitude>>6))*0.25f)-1.0f;
} else {
chopper->dy=0;
if (chopper->light>3) chopper->light-=3;
if (chopper->light) chopper->light--;
if (chopper->rotorspeed > 200)
chopper->rotorspeed--;
if (chopper->rotorspeed > 0)
chopper->rotorspeed--;
else
CHOPPER_init_state(thing,CHOPPER_substate_landed);
}
break;
/* if (altitude>(40<<8)) {
chopper->dy--;
} else
if (chopper->dy<0) {
chopper->dy+=2;
if (chopper->dy>0) chopper->dy=0;
} else {
if (chopper->light>3) chopper->light-=3;
if (chopper->light) chopper->light--;
if (altitude>0) thing->WorldPos.Y--;
if (chopper->rotorspeed > 200)
chopper->rotorspeed--;
if (chopper->rotorspeed > 0)
chopper->rotorspeed--;
else
CHOPPER_init_state(thing,CHOPPER_substate_landed);
}
break;*/
case CHOPPER_substate_landed:
if (chopper->victim||!CHOPPER_radius_broken(darci->WorldPos,thing->WorldPos,DETECTION_RADIUS)) {
// chopper->target=darci;
CHOPPER_init_state(thing,CHOPPER_substate_takeoff);
}
// heh.
break;
case CHOPPER_substate_tracking:
if (chopper->victim) {
chopper->victim=EWAY_get_person(chopper->victim);
chopper->target=TO_THING(chopper->victim);
chopper->victim=0;
}
/* // no idea what this does y'know
if (altitude<(200<<8)) {
chopper->dy*=1.5;
}
// Safety thing -- always ramp over buildings:
if (altitude<(100<<8)) {
SLONG alt_wanted;
alt_wanted=(750<<8)+(PAP_calc_map_height_at(thing->WorldPos.X>>8,thing->WorldPos.Z>>8)<<8);
thing->WorldPos.Y=(thing->WorldPos.Y+thing->WorldPos.Y+thing->WorldPos.Y+alt_wanted)*0.25;
}
// nor this. i bet it's important tho.
if (altitude>(600<<8)) {
chopper->dy*=0.6;
}
*/
CHOPPER_predict_altitude(thing,chopper);
CHOPPER_home(thing,chopper->target->WorldPos);
CHOPPER_limit(chopper);
if (chopper->target->Class==CLASS_PERSON) {
if ((chopper->target->Genus.Person->PlayerID>0)&&
(CHOPPER_radius_broken(thing->WorldPos,chopper->home,chopper->radius)))
CHOPPER_init_state(thing,CHOPPER_substate_homing);
}
break;
case CHOPPER_substate_patrolling:
GameCoord targ;
SLONG rot;
chopper->patrol++;
// rot=chopper->patrol>>2;
rot=chopper->patrol;
rot&=2047;
targ.X = chopper->home.X + (SIN(rot)*5);
targ.Z = chopper->home.Z + (COS(rot)*5);
targ.Y = thing->WorldPos.Y;
CHOPPER_home(thing,targ);
// DIRT_new_water(targ.X>>8, PAP_calc_height_at(targ.X>>8,targ.Z>>8)+0x10, targ.Z>>8, -1, 28, 0);
break;
case CHOPPER_substate_homing:
CHOPPER_predict_altitude(thing,chopper);
CHOPPER_home(thing,chopper->home);
CHOPPER_limit(chopper);
// blatant ects hack
if (!CHOPPER_radius_broken(thing->WorldPos,chopper->home,IGNORAMUS_RADIUS)) {
if (!CHOPPER_radius_broken(darci->WorldPos,thing->WorldPos,DETECTION_RADIUS)) {
chopper->target=darci;
CHOPPER_init_state(thing,CHOPPER_substate_tracking);
}
}
break;
default:
ASSERT(0);
break;
}
// sprintf(msg,"X:%d Y:%d Z:%d MWHO:%d\n",thing->WorldPos.X,thing->WorldPos.Y,thing->WorldPos.Z,thing->State&FLAGS_ON_MAPWHO);
// TRACE(msg);
}
/*************************************************************
*
* Helicopter substates -- individual actions
*
*/
void CHOPPER_init_state(Thing *chopper_thing, UBYTE new_state) {
Chopper *chopper = CHOPPER_get_chopper(chopper_thing);
TRACE("Chopper: ");
switch (new_state) {
case CHOPPER_substate_idle:
TRACE("Idle\n");
break;
case CHOPPER_substate_takeoff:
TRACE("Takeoff\n");
chopper->counter = 0;
chopper->since_takeoff = 0;
break;
case CHOPPER_substate_landing:
TRACE("Landing\n");
chopper->counter=0;
break;
case CHOPPER_substate_landed:
TRACE("Landed\n");
chopper->counter=0;
break;
case CHOPPER_substate_tracking:
if (chopper->victim) {
chopper->victim=EWAY_get_person(chopper->victim);
chopper->target=TO_THING(chopper->victim);
chopper->victim=0;
}
if (!(chopper->target)) {
TRACE("Tracking failed, no target\n");
CHOPPER_init_state(chopper_thing, CHOPPER_substate_idle);
} else {
TRACE("Tracking\n");
}
chopper->counter=0;
break;
case CHOPPER_substate_homing:
chopper->target=0;
chopper->counter=0;
TRACE("Homing\n");
break;
case CHOPPER_substate_patrolling:
chopper->target=0;
chopper->patrol=0;
TRACE("Patrolling\n");
break;
}
chopper->substate = new_state;
}