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

759 lines
12 KiB
C++

//
// A grappling hook.
//
#include "game.h"
#include "hook.h"
#include "pap.h"
#include "fmatrix.h"
//
// Each point of the string.
//
typedef struct
{
SLONG x;
SLONG y;
SLONG z;
SWORD dx;
SWORD dy;
SWORD dz;
UWORD alive;
} HOOK_Point;
#define HOOK_NUM_POINTS 256
HOOK_Point HOOK_point[HOOK_NUM_POINTS];
//
// How far each hook point wants to be from its neighbours.
// The gravity acceleration on the points.
//
#define HOOK_POINT_DIST ( 0x800)
#define HOOK_POINT_GRAVITY (-0x80)
#define HOOK_POINT_FRICTION ( 2)
#define HOOK_POINT_ATTRACT ( 2)
#define HOOK_POINT_JUMP ( 0x1800)
#define HOOK_GRAPPLE_GRAVITY (-0x80)
//
// How far the string is unreeled to.
//
UBYTE HOOK_reeled;
//
// The current state
//
UBYTE HOOK_state;
//
// The pitch the grappling hook is currently being swung through.
//
SLONG HOOK_grapple_yaw;
SLONG HOOK_grapple_pitch;
SLONG HOOK_spin_x;
SLONG HOOK_spin_y;
SLONG HOOK_spin_z;
SLONG HOOK_spin_speed;
//
// Processing after we've gone still.
//
SLONG HOOK_countdown;
SLONG HOOK_get_state()
{
return HOOK_state;
}
//
// Put the string in a loop at (x,z).
//
#define HOOK_LOOP_RADIUS (0x2000)
#define HOOK_LOOP_ANGLE ((1436 * 2048) / 36000) // 14.36 degrees...
#define HOOK_LOOP_RAISE (0x0)
void HOOK_make_loop(SLONG x, SLONG z)
{
SLONG i;
SLONG y;
SLONG dy;
SLONG angle;
SLONG dx;
SLONG dz;
SLONG mx;
SLONG mz;
SLONG ground;
HOOK_Point *hp;
dy = 0;
angle = 0;
ground = PAP_calc_height_at(x,z) << 8;
mx = x << 8;
mz = z << 8;
for (i = HOOK_NUM_POINTS - 1; i >= 0; i--)
{
hp = &HOOK_point[i];
dx = SIN(angle) * HOOK_LOOP_RADIUS >> 16;
dz = COS(angle) * HOOK_LOOP_RADIUS >> 16;
hp->x = mx + dx;
hp->z = mz + dz;
hp->y = ground + dy;
hp->dx = 0;
hp->dy = 0;
hp->dz = 0;
hp->alive = FALSE;
mx += SIN(i << 2) >> 11;
mz += COS(i << 2) >> 11;
dy += HOOK_LOOP_RAISE;
angle += HOOK_LOOP_ANGLE;
angle &= 2047;
}
}
void HOOK_init(
SLONG x,
SLONG z)
{
//
// Loop up the string.
//
HOOK_make_loop(x,z);
//
// Good start angle.
//
HOOK_grapple_pitch = 512;
}
//
// Processes the points starting at the given point.
//
void HOOK_process_points(SLONG start_point)
{
SLONG i;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG ddx;
SLONG ddy;
SLONG ddz;
SLONG dist;
SLONG ddist;
SLONG ground;
HOOK_Point *hp;
HOOK_Point *hp_near;
//
// Process all the points.
//
i = start_point;
while(1)
{
ASSERT(WITHIN(i, 1, HOOK_NUM_POINTS - 1));
hp = &HOOK_point[i];
hp_near = &HOOK_point[i - 1];
//
// The link of this point to its predecessor.
//
dx = hp_near->x - hp->x;
dy = hp_near->y - hp->y;
dz = hp_near->z - hp->z;
dist = QDIST3(abs(dx),abs(dy),abs(dz)) + 1;
if (dist > HOOK_POINT_JUMP)
{
//
// Too far from the previous point- jump to nearer
// the previous point.
//
//
// Guard against overflows!
//
hp->x += (dx * (dist - HOOK_POINT_JUMP >> 8)) / (dist >> 8);
hp->y += (dy * (dist - HOOK_POINT_JUMP >> 8)) / (dist >> 8);
hp->z += (dz * (dist - HOOK_POINT_JUMP >> 8)) / (dist >> 8);
dx = hp_near->x - hp->x;
dy = hp_near->y - hp->y;
dz = hp_near->z - hp->z;
dist = QDIST3(abs(dx),abs(dy),abs(dz)) + 1;
}
ddist = dist;
ddist -= HOOK_POINT_DIST;
ddist >>= HOOK_POINT_ATTRACT;
ddx = (dx * ddist) / dist;
ddy = (dy * ddist) / dist;
ddz = (dz * ddist) / dist;
hp->dx += ddx;
hp->dy += ddy;
hp->dz += ddz;
if (i < HOOK_NUM_POINTS - 1)
{
hp_near = &HOOK_point[i + 1];
//
// The link of this point to its next point.
//
dx = hp_near->x - hp->x;
dy = hp_near->y - hp->y;
dz = hp_near->z - hp->z;
dist = QDIST3(abs(dx),abs(dy),abs(dz)) + 1;
ddist = dist;
ddist -= HOOK_POINT_DIST;
ddist >>= HOOK_POINT_ATTRACT;
if (dist < HOOK_POINT_JUMP)
{
ddx = dx * ddist / dist;
ddy = dy * ddist / dist;
ddz = dz * ddist / dist;
hp->dx += ddx;
hp->dy += ddy;
hp->dz += ddz;
}
}
//
// Gravity.
//
hp->dy += HOOK_POINT_GRAVITY;
//
// Friction.
//
hp->dx -= hp->dx >> HOOK_POINT_FRICTION;
hp->dy -= hp->dy >> HOOK_POINT_FRICTION;
hp->dz -= hp->dz >> HOOK_POINT_FRICTION;
//
// Actually move the point.
//
hp->x += hp->dx;
hp->y += hp->dy;
hp->z += hp->dz;
//
// Don't go underground.
//
ground = PAP_calc_map_height_at(hp->x >> 8, hp->z >> 8) << 8;
if (hp->y < ground)
{
dy = ground - hp->y;
if (dy < 0x2000)
{
hp->y = ground;
hp->dy = 0;
if (abs(hp->dx) + abs(hp->dz) < 256)
{
hp->dx >>= 2;
hp->dz >>= 2;
}
}
else
{
//
// Sliding along a wall?
//
ground = PAP_calc_map_height_at((hp->x - hp->dx) >> 8, hp->z >> 8) << 8;
if (hp->y > ground)
{
hp->x -= hp->dx;
hp->dx = 0;
}
else
{
hp->z -= hp->dz;
hp->dz = 0;
}
}
}
if (i == HOOK_reeled)
{
if (HOOK_reeled == HOOK_NUM_POINTS - 1)
{
break;
}
else
{
//
// Too far from the next point? Should it unreel?
//
if (ddist > 0)
{
HOOK_reeled += 1;
}
else
{
break;
}
}
}
i += 1;
}
}
//
// Spins the hook about the given point.
//
void HOOK_spin(
SLONG x,
SLONG y,
SLONG z,
SLONG yaw,
SLONG speed_or_minus_pitch)
{
SLONG i;
//
// The number of points from where darci hold the string
// to where the grappling hook is.
//
#define HOOK_NUM_HOLD 5
/*
if (HOOK_state != HOOK_STATE_SPINNING)
{
//
// Create a loop of string on the ground (x,z)
//
HOOK_make_loop(x,z);
//
// We've started using the hook.
//
HOOK_state = HOOK_STATE_SPINNING;
HOOK_reeled = HOOK_NUM_HOLD + 1;
//
// Put the string in the correct pose.
//
HOOK_point[HOOK_NUM_HOLD].x = x << 8;
HOOK_point[HOOK_NUM_HOLD].y = y << 8;
HOOK_point[HOOK_NUM_HOLD].z = z << 8;
for (i = 0; i < 16; i++)
{
HOOK_process_points(HOOK_NUM_HOLD + 1);
}
if (speed_or_minus_pitch > 0)
{
//
// Start with the grappling hook hanging down.
//
HOOK_grapple_pitch = 1536;
}
else
{
HOOK_grapple_pitch = -(speed_or_minus_pitch) & 2047;
}
//
// A remotely sensible value so that (old_x,old_y,old_z)
// wont have complete garbage in it the first time around.
//
HOOK_point[0].x = x << 8;
HOOK_point[0].y = y << 8;
HOOK_point[0].z = z << 8;
}
*/
HOOK_spin_x = x << 8;
HOOK_spin_y = y << 8;
HOOK_spin_z = z << 8;
HOOK_grapple_yaw = yaw;
HOOK_state = HOOK_STATE_SPINNING;
HOOK_reeled = HOOK_NUM_HOLD + 1;
if (speed_or_minus_pitch > 0)
{
HOOK_spin_speed = speed_or_minus_pitch;
}
else
{
HOOK_spin_speed = 0;
HOOK_grapple_pitch = -(speed_or_minus_pitch) & 2047;
}
}
void HOOK_release()
{
ASSERT(HOOK_state == HOOK_STATE_SPINNING);
HOOK_state = HOOK_STATE_FLYING;
}
void HOOK_process_flying()
{
SLONG speed;
SLONG ground;
SLONG odd;
SLONG dy;
//
// Process the grappling hook (point 0)
//
/*
HOOK_grapple_yaw += HOOK_spin_speed >> 1;
HOOK_grapple_pitch += HOOK_spin_speed >> 1;
if (HOOK_spin_speed > 0)
{
HOOK_spin_speed -= 1;
}
*/
HOOK_point[0].dy += HOOK_GRAPPLE_GRAVITY;
HOOK_point[0].x += HOOK_point[0].dx * TICK_RATIO >> TICK_SHIFT;
HOOK_point[0].y += HOOK_point[0].dy * TICK_RATIO >> TICK_SHIFT;
HOOK_point[0].z += HOOK_point[0].dz * TICK_RATIO >> TICK_SHIFT;
ground = PAP_calc_map_height_at(
HOOK_point[0].x >> 8,
HOOK_point[0].z >> 8) << 8;
if (HOOK_point[0].y < ground)
{
dy = ground - HOOK_point[0].y;
if (dy < 0x4000)
{
//
// A vertical bounce.
//
HOOK_point[0].y = ground;
HOOK_point[0].dy = abs(HOOK_point[0].dy);
HOOK_point[0].dx /= 2;
HOOK_point[0].dy /= 2;
HOOK_point[0].dz /= 2;
speed = abs(HOOK_point[0].dx);
speed += abs(HOOK_point[0].dy);
speed += abs(HOOK_point[0].dz);
speed >>= 4;
speed += 1;
odd = rand() % speed;
odd -= speed >> 1;
HOOK_point[0].dx += odd << 3;
odd = rand() % speed;
odd -= speed >> 1;
HOOK_point[0].dz += odd << 3;
HOOK_spin_speed = rand() & 0x1f;
}
else
{
//
// Bouncing off a wall.
//
SLONG check_x;
SLONG check_z;
SLONG check_height;
SLONG bounced_x = FALSE;
SLONG bounced_z = FALSE;
//
// Bounced in x?
//
check_x = HOOK_point[0].x - HOOK_point[0].dx >> 8;
check_z = HOOK_point[0].z >> 8;
check_height = PAP_calc_map_height_at(
check_x,
check_z) << 8;
bounced_x = (check_height < HOOK_point[0].y);
//
// Bounced in z?
//
check_x = HOOK_point[0].x >> 8;
check_z = HOOK_point[0].z - HOOK_point[0].dz >> 8;
check_height = PAP_calc_map_height_at(
check_x,
check_z) << 8;
bounced_z = (check_height < HOOK_point[0].y);
//
// Do the bouncing.
//
if (bounced_x)
{
HOOK_point[0].dx = -HOOK_point[0].dx;
HOOK_point[0].x += HOOK_point[0].dx;
}
if (bounced_z)
{
HOOK_point[0].dz = -HOOK_point[0].dz;
HOOK_point[0].z += HOOK_point[0].dz;
}
HOOK_spin_speed = rand() & 0x1f;
}
}
//
// Process all the points.
//
HOOK_process_points(1);
//
// Is the grapple moving?
//
speed = abs(HOOK_point[0].dx);
speed += abs(HOOK_point[0].dy);
speed += abs(HOOK_point[0].dz);
if (speed < 0x100)
{
//
// Hook has stopped moving.
//
HOOK_state = HOOK_STATE_STILL;
HOOK_countdown = 512;
}
}
void HOOK_process_spinning()
{
SLONG i;
SLONG vector[3];
SLONG old_x;
SLONG old_y;
SLONG old_z;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG px;
SLONG py;
SLONG pz;
//
// Set the grapple going.
//
old_x = HOOK_point[0].x;
old_y = HOOK_point[0].y;
old_z = HOOK_point[0].z;
//
// Work out the position of the grapple and the points connecting
// it to Darci's hand.
//
FMATRIX_vector(
vector,
HOOK_grapple_yaw,
HOOK_grapple_pitch);
vector[0] = vector[0] * HOOK_POINT_DIST >> 16;
vector[1] = vector[1] * HOOK_POINT_DIST >> 16;
vector[2] = vector[2] * HOOK_POINT_DIST >> 16;
px = HOOK_spin_x;
py = HOOK_spin_y;
pz = HOOK_spin_z;
for (i = HOOK_NUM_HOLD; i >= 0; i--)
{
HOOK_point[i].x = px;
HOOK_point[i].y = py;
HOOK_point[i].z = pz;
px += vector[0];
py += vector[1];
pz += vector[2];
}
dx = ((HOOK_point[0].x - old_x) * (1 << (TICK_SHIFT))) / TICK_RATIO;
dy = ((HOOK_point[0].y - old_y) * (1 << (TICK_SHIFT))) / TICK_RATIO;
dz = ((HOOK_point[0].z - old_z) * (1 << (TICK_SHIFT))) / TICK_RATIO;
HOOK_point[0].dx = dx;
HOOK_point[0].dy = dy;
HOOK_point[0].dz = dz;
//
// Process the points.
//
HOOK_process_points(HOOK_NUM_HOLD + 1);
//
// Spin!
//
HOOK_grapple_pitch -= HOOK_spin_speed;
HOOK_grapple_pitch &= 2047;
}
void HOOK_process()
{
switch(HOOK_state)
{
case HOOK_STATE_STILL:
//
// Process for a while after coming to a standstill-
// to give the string a chance to settle.
//
if (HOOK_countdown)
{
HOOK_process_flying();
HOOK_countdown -= 1;
}
break;
case HOOK_STATE_SPINNING:
HOOK_process_spinning();
break;
case HOOK_STATE_FLYING:
HOOK_process_flying();
HOOK_process_flying();
break;
default:
ASSERT(0);
break;
}
}
// ========================================================
//
// DRAWING THE HOOK.
//
// ========================================================
void HOOK_pos_grapple(
SLONG *x,
SLONG *y,
SLONG *z,
SLONG *yaw,
SLONG *pitch,
SLONG *roll)
{
*x = HOOK_point[0].x;
*y = HOOK_point[0].y + 0x1000;
*z = HOOK_point[0].z;
*yaw = HOOK_grapple_yaw;
*pitch = HOOK_grapple_pitch;
*roll = 0;
}
void HOOK_pos_point(SLONG point,
SLONG *x,
SLONG *y,
SLONG *z)
{
ASSERT(WITHIN(point, 0, HOOK_NUM_POINTS - 1));
*x = HOOK_point[point].x;
*y = HOOK_point[point].y;
*z = HOOK_point[point].z;
}