MuckyFoot-UrbanChaos/fallen/Source/puddle.cpp

936 lines
15 KiB
C++
Raw Permalink Normal View History

2017-05-20 03:14:17 +02:00
//
// Puddles!
//
#include <MFStdLib.h>
#include "game.h" // :-(
#include "puddle.h"
#include "mav.h"
#include "pap.h"
#include "memory.h"
#ifndef TARGET_DC
//
// The puddles.
//
#define PUDDLE_TYPE_STRIP1 0
#define PUDDLE_TYPE_STRIP2 1
#define PUDDLE_TYPE_CORNER 2
#define PUDDLE_TYPE_WHOLE 3
#define PUDDLE_TYPE_NUMBER 4
typedef struct
{
UWORD x1;
UWORD z1;
UWORD x2;
UWORD z2;
SWORD y;
UBYTE type;
UBYTE rotate_uvs;
UBYTE map_x;
UBYTE next;
UBYTE y1;
UBYTE y2;
UBYTE g1;
UBYTE g2;
UBYTE s1;
UBYTE s2;
} PUDDLE_Puddle;
#define PUDDLE_MAX_PUDDLES 256
PUDDLE_Puddle PUDDLE_puddle[PUDDLE_MAX_PUDDLES];
SLONG PUDDLE_puddle_upto;
//
// The puddle mapwho system.
//
#define PUDDLE_MAPWHO_SIZE 128
UBYTE PUDDLE_mapwho[PUDDLE_MAPWHO_SIZE];
//
// The uv coordinates of each puddle type.
//
typedef struct
{
SLONG u1;
SLONG v1;
SLONG u2;
SLONG v2;
} PUDDLE_Texture;
PUDDLE_Texture PUDDLE_texture[PUDDLE_TYPE_NUMBER] =
{
{ 0, 0, 256, 62},
{ 0, 64, 256, 126},
{ 0, 128, 128, 256},
{128, 128, 256, 256}
};
void PUDDLE_init()
{
SLONG i;
//
// Clear the puddles and the puddle mapwho.
//
memset(PUDDLE_puddle, 0, sizeof(PUDDLE_puddle));
memset(PUDDLE_mapwho, 0, sizeof(PUDDLE_mapwho));
PUDDLE_puddle_upto = 1;
//
// Clear the REFLECTIVE bit in the mapwho square.
//
{
SLONG x;
SLONG z;
MapElement *me;
for (x = 0; x < MAP_WIDTH; x++)
for (z = 0; z < MAP_HEIGHT; z++)
{
PAP_2HI(x,z).Flags &= ~PAP_FLAG_REFLECTIVE;
}
}
}
void PUDDLE_create_do(
UWORD x1,
UWORD z1,
UWORD x2,
UWORD z2,
SWORD y,
UBYTE type,
UBYTE rotate_uvs)
{
SLONG x;
SLONG z;
PUDDLE_Puddle *pp;
UBYTE next;
UBYTE *prev;
UBYTE map_x = x1 + x2 >> 9;
UBYTE map_z = z1 + z2 >> 9;
if (!WITHIN(map_z, 0, PUDDLE_MAPWHO_SIZE - 1))
{
//
// Off map.
//
return;
}
if (!WITHIN(PUDDLE_puddle_upto, 1, PUDDLE_MAX_PUDDLES - 1))
{
//
// No more puddles.
//
return;
}
if (!PAP_is_flattish(x1, z1, x2, z2))
{
//
// On a hill!
//
return;
}
pp = &PUDDLE_puddle[PUDDLE_puddle_upto];
pp->x1 = x1;
pp->z1 = z1;
pp->x2 = x2;
pp->z2 = z2;
pp->y = y;
pp->map_x = map_x;
pp->rotate_uvs = rotate_uvs;
pp->type = type;
//
// Put this puddle into the correct place in the mapwho.
//
prev = &PUDDLE_mapwho[map_z];
next = PUDDLE_mapwho[map_z];
while(1)
{
ASSERT(WITHIN(next, 0, PUDDLE_puddle_upto - 1));
if (next == NULL || PUDDLE_puddle[next].map_x >= map_x)
{
//
// This is the correct place to insert the puddle.
//
*prev = PUDDLE_puddle_upto;
pp->next = next;
PUDDLE_puddle_upto += 1;
break;
}
prev = &PUDDLE_puddle[next].next;
next = PUDDLE_puddle[next].next;
}
//
// Set the relfective bit in the mapsquares around this puddle.
//
{
SLONG mx1;
SLONG mz1;
SLONG mx2;
SLONG mz2;
#define PUDDLE_EXTEND_REFLECTION 128
mx1 = pp->x1;
mz1 = pp->z1;
mx2 = pp->x2;
mz2 = pp->z2;
if (mx1 > mx2) {SWAP(mx1,mx2);}
if (mz1 > mz2) {SWAP(mz1,mz2);}
mx1 = mx1 - PUDDLE_EXTEND_REFLECTION >> ELE_SHIFT;
mz1 = mz1 - PUDDLE_EXTEND_REFLECTION >> ELE_SHIFT;
mx2 = mx2 + PUDDLE_EXTEND_REFLECTION >> ELE_SHIFT;
mz2 = mz2 + PUDDLE_EXTEND_REFLECTION >> ELE_SHIFT;
SATURATE(mx1, 0, MAP_WIDTH - 1);
SATURATE(mx2, 0, MAP_WIDTH - 1);
SATURATE(mz1, 0, MAP_HEIGHT - 1);
SATURATE(mz2, 0, MAP_HEIGHT - 1);
for (x = mx1; x <= mx2; x++)
for (z = mz1; z <= mz2; z++)
{
PAP_2HI(x,z).Flags |= PAP_FLAG_REFLECTIVE;
}
}
}
void PUDDLE_create(
UWORD x,
SWORD y,
UWORD z)
{
SLONG x1;
SLONG z1;
SLONG x2;
SLONG z2;
#define PUDDLE_WHOLE_SIZE 0x140
x1 = x - PUDDLE_WHOLE_SIZE;
z1 = z - PUDDLE_WHOLE_SIZE;
x2 = x + PUDDLE_WHOLE_SIZE;
z2 = z + PUDDLE_WHOLE_SIZE;
//
// A randomish rotation of the puddle.
//
if (rand() & 0x1) {SWAP(x1, x2);}
if (rand() & 0x1) {SWAP(z1, z2);}
PUDDLE_create_do(
x1, z1,
x2, z2,
y,
PUDDLE_TYPE_WHOLE,
FALSE);
}
void PUDDLE_precalculate()
{
SLONG i;
SLONG j;
SLONG mx;
SLONG mz;
SLONG dx;
SLONG dz;
SLONG cx;
SLONG cz;
SLONG type;
SLONG vec1x;
SLONG vec1z;
SLONG vec2x;
SLONG vec2z;
SLONG midx;
SLONG midz;
SLONG size;
SLONG px1, px2;
SLONG pz1, pz2;
SLONG py;
struct
{
SLONG dx;
SLONG dz;
} order[4] =
{
{-1,0},
{+1,0},
{0,-1},
{0,+1}
};
for (mx = 1; mx < MAP_WIDTH - 2; mx++)
for (mz = 1; mz < MAP_HEIGHT - 2; mz++)
{
//
// Look for corners of buildings.
//
if (PAP_2HI(mx,mz).Flags & PAP_FLAG_HIDDEN)
{
for (i = 0; i < 4; i++)
{
dx = (i & 0x1) ? +1 : -1;
dz = (i & 0x2) ? +1 : -1;
for (j = 1; j < 4; j++)
{
cx = mx + ((j & 0x1) ? dx : 0);
cz = mz + ((j & 0x2) ? dz : 0);
if (PAP_2HI(cx,cz).Flags & (PAP_FLAG_HIDDEN | PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER))
{
goto not_a_corner_of_a_building;
}
if (MAVHEIGHT(cx,cz) > MAVHEIGHT(mx,mz))
{
goto not_a_corner_of_a_building;
}
if(MAV_SPARE(cx,cz) & MAV_SPARE_FLAG_WATER)
{
goto not_a_corner_of_a_building;
}
}
if ((rand() & 0x3) == ((mx + mz) & 0x3))
{
//
// Create a corner puddle.
//
midx = (mx << 8) + 0x80 + dx * 0x80;
midz = (mz << 8) + 0x80 + dz * 0x80;
size = 0xa0 + (rand() & 0x3f);
px1 = midx - dx * size;
pz1 = midz - dz * size;
px2 = midx + dx * size;
pz2 = midz + dz * size;
py = PAP_calc_height_at(px2, pz2);
py += 0x8;
PUDDLE_create_do(
px1, pz1,
px2, pz2,
py,
PUDDLE_TYPE_CORNER,
FALSE);
}
not_a_corner_of_a_building:;
}
}
//
// Corners of roads.
//
if (!(PAP_2HI(mx,mz).Flags & PAP_FLAG_SINK_SQUARE))
{
for (i = 0; i < 4; i++)
{
dx = (i & 0x1) ? +1 : -1;
dz = (i & 0x2) ? +1 : -1;
for (j = 1; j < 4; j++)
{
cx = mx + ((j & 0x1) ? dx : 0);
cz = mz + ((j & 0x2) ? dz : 0);
if (PAP_2HI(cx,cz).Flags & (PAP_FLAG_HIDDEN | PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER))
{
goto not_a_corner_of_a_road;
}
if (!(PAP_2HI(cx,cz).Flags & PAP_FLAG_SINK_SQUARE))
{
goto not_a_corner_of_a_road;
}
}
if(MAV_SPARE(cx,cz) & MAV_SPARE_FLAG_WATER)
goto not_a_corner_of_a_road;
if ((rand() & 0x3) == ((mx + mz) & 0x3))
{
//
// Create a corner puddle.
//
midx = (mx << 8) + 0x80 + dx * 0x80;
midz = (mz << 8) + 0x80 + dz * 0x80;
size = 0xa0 + (rand() & 0x3f);
px1 = midx - dx * size;
pz1 = midz - dz * size;
px2 = midx + dx * size;
pz2 = midz + dz * size;
py = PAP_calc_height_at(px2, pz2);
py += 0x2;
PUDDLE_create_do(
px1, pz1,
px2, pz2,
py,
PUDDLE_TYPE_CORNER,
FALSE);
}
not_a_corner_of_a_road:;
}
}
//
// Edges of buildings.
//
if (!(PAP_2HI(mx,mz).Flags & (PAP_FLAG_HIDDEN | PAP_FLAG_REFLECTIVE)))
{
for (i = 0; i < 4; i++)
{
dx = order[i].dx;
dz = order[i].dz;
vec1x = dx;
vec1z = dz;
vec2x = dz;
vec2z = -dx;
cx = mx + vec1x;
cz = mz + vec1z;
if (!(PAP_2HI(cx,cz).Flags & PAP_FLAG_HIDDEN) ||
(PAP_2HI(cx,cz).Flags & (PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER)))
{
goto not_a_building_edge;
}
if (MAVHEIGHT(cx,cz) < MAVHEIGHT(mx,mz))
{
goto not_a_building_edge;
}
if(MAV_SPARE(cx,cz) & MAV_SPARE_FLAG_WATER)
goto not_a_building_edge;
cx = mx + vec1x + vec2x;
cz = mz + vec1z + vec2z;
if (!(PAP_2HI(cx,cz).Flags & PAP_FLAG_HIDDEN) ||
(PAP_2HI(cx,cz).Flags & (PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER)))
{
goto not_a_building_edge;
}
if (MAVHEIGHT(cx,cz) < MAVHEIGHT(mx,mz))
{
goto not_a_building_edge;
}
cx = mx + vec2x;
cz = mz + vec2z;
if (PAP_2HI(cx,cz).Flags & (PAP_FLAG_HIDDEN | PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER))
{
goto not_a_building_edge;
}
if ((rand() & 0x7) == ((mx + mz) & 0x7))
{
//
// Create a puddle along the edge of this building.
//
size = 0xa0 + (rand() & 0x3f);
px1 = (mx << 8) + 0x80 + vec1x * 0x80 - vec2x * 0x80;
pz1 = (mz << 8) + 0x80 + vec1z * 0x80 - vec2z * 0x80;
px2 = px1 - vec1x * size + vec2x * 0x200;
pz2 = pz1 - vec1z * size + vec2z * 0x200;
py = PAP_calc_height_at(px2,pz2);
py += 0x4;
type = PUDDLE_TYPE_STRIP1 + (rand() & 0x1);
PUDDLE_create_do(
px1, pz1,
px2, pz2,
py,
type,
(i == 0 || i == 1));
}
not_a_building_edge:;
}
}
//
// Edges of roads.
//
if ( (PAP_2HI(mx,mz).Flags & PAP_FLAG_SINK_SQUARE) &&
!(PAP_2HI(mx,mz).Flags & (PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER)))
{
for (i = 0; i < 4; i++)
{
dx = order[i].dx;
dz = order[i].dz;
vec1x = dx;
vec1z = dz;
vec2x = dz;
vec2z = -dx;
cx = mx + vec1x;
cz = mz + vec1z;
if (PAP_2HI(cx,cz).Flags & (PAP_FLAG_SINK_SQUARE | PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER))
{
goto not_a_road_edge;
}
if(MAV_SPARE(cx,cz) & MAV_SPARE_FLAG_WATER)
goto not_a_road_edge;
cx = mx + vec1x + vec2x;
cz = mz + vec1z + vec2z;
if (PAP_2HI(cx,cz).Flags & (PAP_FLAG_SINK_SQUARE | PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER))
{
goto not_a_road_edge;
}
cx = mx + vec2x;
cz = mz + vec2z;
if (!(PAP_2HI(cx,cz).Flags & PAP_FLAG_SINK_SQUARE) ||
(PAP_2HI(cx,cz).Flags & (PAP_FLAG_REFLECTIVE | PAP_FLAG_WATER)))
{
goto not_a_road_edge;
}
if ((rand() & 0x3) == ((mx + mz) & 0x3))
{
//
// Create a puddle along the edge of this road.
//
size = 0xa0 + (rand() & 0x3f);
px1 = (mx << 8) + 0x80 + vec1x * 0x80 - vec2x * 0x80;
pz1 = (mz << 8) + 0x80 + vec1z * 0x80 - vec2z * 0x80;
px2 = px1 - vec1x * size + vec2x * 0x200;
pz2 = pz1 - vec1z * size + vec2z * 0x200;
py = PAP_calc_height_at(px2,pz2);
py += 0x2;
type = PUDDLE_TYPE_STRIP1 + (rand() & 0x1);
PUDDLE_create_do(
px1, pz1,
px2, pz2,
py,
type,
(i == 0 || i == 1));
}
not_a_road_edge:;
}
}
}
}
//
// Each type of ripple.
//
#define PUDDLE_NUM_RIPPLES 1
struct
{
UBYTE y1;
UBYTE y2;
UBYTE g1;
UBYTE g2;
UBYTE s1;
UBYTE s2;
} PUDDLE_ripple[PUDDLE_NUM_RIPPLES] =
{
// {182, 251, 235, 82, 128, 64},
{ 62, 137, 17, 178, 40, 45},
// {161, 74, 215, 6, 32, 64},
// {172, 222, 55, 206, 128, 128}
};
SLONG PUDDLE_in(
SLONG x,
SLONG z)
{
SLONG lz;
SLONG pz;
SLONG px1, px2;
SLONG pz1, pz2;
UBYTE next;
PUDDLE_Puddle *pp;
//
// Only bother looking if this is in a reflective place.
//
SLONG mx = x >> 8;
SLONG mz = z >> 8;
if (!WITHIN(mx, 0, MAP_WIDTH - 1) ||
!WITHIN(mz, 0, MAP_HEIGHT - 1))
{
return FALSE;
}
if(MAV_SPARE(mx,mz) & MAV_SPARE_FLAG_WATER)
return(TRUE);
if (PAP_2HI(mx,mz).Flags & PAP_FLAG_REFLECTIVE)
{
//
// Look for a puddle this splash could be in.
//
for (lz = -1; lz <= 1; lz++)
{
pz = mz + lz;
if (WITHIN(pz, 0, PUDDLE_MAPWHO_SIZE - 1))
{
for (next = PUDDLE_mapwho[pz]; next; next = PUDDLE_puddle[next].next)
{
ASSERT(WITHIN(next, 1, PUDDLE_puddle_upto - 1));
pp = &PUDDLE_puddle[next];
//
// Is the splash in this puddle?
//
px1 = pp->x1;
pz1 = pp->z1;
px2 = pp->x2;
pz2 = pp->z2;
if (px1 > px2) {SWAP(px1, px2);}
if (pz1 > pz2) {SWAP(pz1, pz2);}
if (WITHIN(x, px1, px2) &&
WITHIN(z, pz1, pz2))
{
return TRUE;
}
}
}
}
}
return FALSE;
}
void PUDDLE_splash(
SLONG x,
SLONG y,
SLONG z)
{
SLONG mx;
SLONG mz;
SLONG lz;
SLONG pz;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG dist;
SLONG ripple;
UBYTE next;
PUDDLE_Puddle *pp;
//
// Only bother looking if this is in a reflective place.
//
mx = x >> 8;
mz = z >> 8;
if (!WITHIN(mx, 0, MAP_WIDTH - 1) ||
!WITHIN(mz, 0, MAP_HEIGHT - 1))
{
return;
}
if ((PAP_2HI(mx,mz).Flags & PAP_FLAG_REFLECTIVE)||(MAV_SPARE(mx,mz) & MAV_SPARE_FLAG_WATER))
{
//
// Look for a puddle this splash could be in.
//
for (lz = -1; lz <= 1; lz++)
{
pz = mz + lz;
if (WITHIN(pz, 0, PUDDLE_MAPWHO_SIZE - 1))
{
for (next = PUDDLE_mapwho[pz]; next; next = PUDDLE_puddle[next].next)
{
ASSERT(WITHIN(next, 1, PUDDLE_puddle_upto - 1));
pp = &PUDDLE_puddle[next];
//
// Is the splash in this puddle?
//
dy = pp->y - y;
#define PUDDLE_SPLASH_Y_RANGE 32
if (WITHIN(dy, -PUDDLE_SPLASH_Y_RANGE, +PUDDLE_SPLASH_Y_RANGE))
{
SLONG px1, px2;
SLONG pz1, pz2;
px1 = pp->x1;
pz1 = pp->z1;
px2 = pp->x2;
pz2 = pp->z2;
if (px1 > px2) {SWAP(px1, px2);}
if (pz1 > pz2) {SWAP(pz1, pz2);}
if (WITHIN(x, px1, px2) &&
WITHIN(z, pz1, pz2))
{
//
// Splash this puddle.
//
ripple = rand() & (PUDDLE_NUM_RIPPLES - 1);
pp->y1 = PUDDLE_ripple[ripple].y1;
pp->y2 = PUDDLE_ripple[ripple].y2;
pp->g1 = PUDDLE_ripple[ripple].g1;
pp->g2 = PUDDLE_ripple[ripple].g2;
pp->s1 = PUDDLE_ripple[ripple].s1;
pp->s2 = PUDDLE_ripple[ripple].s2;
}
}
}
}
}
}
}
void PUDDLE_process()
{
SLONG i;
SLONG s1;
SLONG s2;
PUDDLE_Puddle *pp;
for (i = 1; i < PUDDLE_puddle_upto; i++)
{
pp = &PUDDLE_puddle[i];
if (pp->s1 | pp->s2)
{
s1 = pp->s1;
s2 = pp->s2;
if (s1 > 0) {s1 -= 1;}
if (s2 > 0) {s2 -= 1;}
pp->s1 = s1;
pp->s2 = s2;
}
}
}
UBYTE PUDDLE_get_upto;
UBYTE PUDDLE_get_z;
UBYTE PUDDLE_get_x_min;
UBYTE PUDDLE_get_x_max;
PUDDLE_Info PUDDLE_get_info;
void PUDDLE_get_start(UBYTE z_map, UBYTE x_map_min, UBYTE x_map_max)
{
PUDDLE_get_z = z_map;
PUDDLE_get_x_min = x_map_min - 1;
PUDDLE_get_x_max = x_map_max + 1;
if (!WITHIN(z_map, 0, PUDDLE_MAPWHO_SIZE - 1))
{
PUDDLE_get_upto = 0;
}
else
{
//
// Find the first puddle in the linked list for this z-line, which
// lies on an x-square >= x_map_min.
//
PUDDLE_get_upto = PUDDLE_mapwho[z_map];
while(1)
{
if (PUDDLE_get_upto == NULL)
{
return;
}
else
{
ASSERT(WITHIN(PUDDLE_get_upto, 1, PUDDLE_puddle_upto - 1));
if (PUDDLE_puddle[PUDDLE_get_upto].map_x >= x_map_min)
{
return;
}
else
{
PUDDLE_get_upto = PUDDLE_puddle[PUDDLE_get_upto].next;
}
}
}
}
}
PUDDLE_Info *PUDDLE_get_next()
{
PUDDLE_Puddle *pp;
if (PUDDLE_get_upto == NULL)
{
return NULL;
}
ASSERT(WITHIN(PUDDLE_get_upto, 1, PUDDLE_puddle_upto - 1));
pp = &PUDDLE_puddle[PUDDLE_get_upto];
if (pp->map_x > PUDDLE_get_x_max)
{
return NULL;
}
PUDDLE_get_info.x1 = pp->x1;
PUDDLE_get_info.z1 = pp->z1;
PUDDLE_get_info.x2 = pp->x2;
PUDDLE_get_info.z2 = pp->z2;
PUDDLE_get_info.y = pp->y;
PUDDLE_get_info.puddle_y1 = pp->y1;
PUDDLE_get_info.puddle_y2 = pp->y2;
PUDDLE_get_info.puddle_g1 = pp->g1;
PUDDLE_get_info.puddle_g2 = pp->g2;
PUDDLE_get_info.puddle_s1 = pp->s1;
PUDDLE_get_info.puddle_s2 = pp->s2;
ASSERT(WITHIN(pp->type, 0, PUDDLE_TYPE_NUMBER - 1));
PUDDLE_get_info.u1 = PUDDLE_texture[pp->type].u1;
PUDDLE_get_info.v1 = PUDDLE_texture[pp->type].v1;
PUDDLE_get_info.u2 = PUDDLE_texture[pp->type].u2;
PUDDLE_get_info.v2 = PUDDLE_texture[pp->type].v2;
PUDDLE_get_info.rotate_uvs = pp->rotate_uvs;
PUDDLE_get_upto = pp->next;
return &PUDDLE_get_info;
}
#endif //#ifndef TARGET_DC