// // Puddles! // #include #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