9310 lines
153 KiB
C++
9310 lines
153 KiB
C++
#include "game.h"
|
|
#include "id.h"
|
|
#include "maths.h"
|
|
#include "thing.h"
|
|
#include "ob.h"
|
|
|
|
#ifndef PSX
|
|
|
|
|
|
//
|
|
// Do you want thin walls
|
|
//
|
|
|
|
#define YOU_WANT_THIN_WALLS 0
|
|
|
|
|
|
//
|
|
// The height of the ceiling
|
|
//
|
|
|
|
#define ID_CEILING_HEIGHT (0x100)
|
|
#define ID_WALL_WIDTH (0x8)
|
|
|
|
//
|
|
// Points.
|
|
//
|
|
|
|
typedef struct
|
|
{
|
|
UWORD x; // 8-bits per mapsquare.
|
|
UWORD z;
|
|
UWORD y;
|
|
UWORD index; // A special field. It is NOT the index of the point, but a place where
|
|
// the engine can store a value (the index of the transformed point.)
|
|
|
|
} ID_Point;
|
|
|
|
#define ID_MAX_POINTS 1024
|
|
|
|
ID_Point ID_point[ID_MAX_POINTS];
|
|
SLONG ID_point_upto;
|
|
|
|
//
|
|
// Faces.
|
|
//
|
|
|
|
#define ID_FACE_FLAG_QUAD (1 << 0) // Otherwise it is a triange and point 3 is unused.
|
|
|
|
//
|
|
// Whether the n'th point is a packed (x,z) coordinate
|
|
// or an index into the point array.
|
|
//
|
|
|
|
#define ID_FACE_FLAG_ONFLOOR0 (1 << 4)
|
|
#define ID_FACE_FLAG_ONFLOOR1 (1 << 5)
|
|
#define ID_FACE_FLAG_ONFLOOR2 (1 << 6)
|
|
#define ID_FACE_FLAG_ONFLOOR3 (1 << 7)
|
|
|
|
typedef struct
|
|
{
|
|
UWORD point[4]; // Each point is either an index into the point array,
|
|
// If the corresponding flag is set, it is and (x,z)
|
|
// coordinate of the floor point. x is in the LBS, z is
|
|
// in the MSB.
|
|
UWORD texture;
|
|
UWORD flag;
|
|
UWORD next;
|
|
UWORD shit;
|
|
|
|
} ID_Face;
|
|
|
|
#ifdef PSX
|
|
#define ID_MAX_FACES 512
|
|
#else
|
|
#define ID_MAX_FACES 2048
|
|
#endif
|
|
|
|
ID_Face ID_face[ID_MAX_FACES];
|
|
SLONG ID_face_upto;
|
|
|
|
|
|
//
|
|
// The floorplan a square can be both inside and outside at the
|
|
// same time!
|
|
//
|
|
|
|
#define ID_FLOOR_FLAG_OUTSIDE (1 << 0)
|
|
#define ID_FLOOR_FLAG_INSIDE (1 << 1)
|
|
#define ID_FLOOR_FLAG_ON_WALL (1 << 2) // The bottom left hand corner of the square is on a wall.
|
|
#define ID_FLOOR_FLAG_P_IN (1 << 3) // The bottom left hand corner of the square is inside.
|
|
|
|
#define ID_FLOOR_FLAG_WALL_XS (1 << 4)
|
|
#define ID_FLOOR_FLAG_WALL_XL (1 << 5)
|
|
#define ID_FLOOR_FLAG_WALL_ZS (1 << 6)
|
|
#define ID_FLOOR_FLAG_WALL_ZL (1 << 7)
|
|
|
|
#define ID_FLOOR_FLAG_DOORMAT (1 << 8) // An outside door opens onto this square.
|
|
#define ID_FLOOR_FLAG_INMAT (1 << 9) // This square connects to an inside-door.
|
|
|
|
#define ID_FLOOR_FLAG_FURNISHED (1 << 10) // There is an item of furniture on this square.
|
|
#define ID_FLOOR_FLAG_STAIR (1 << 11) // There is a staircase here.
|
|
|
|
|
|
|
|
#define ID_FLOOR_ROOM_NULL 0
|
|
|
|
typedef struct
|
|
{
|
|
UWORD flag;
|
|
UBYTE room; // The unique room number to which this floorsquare belongs.
|
|
UBYTE texid;
|
|
UWORD next; // The index into the face array of the first face in the linked list.
|
|
|
|
} ID_Square;
|
|
|
|
ID_Square ID_floor[ID_PLAN_SIZE][ID_PLAN_SIZE];
|
|
|
|
//
|
|
// The bounding box of the floorplan.
|
|
//
|
|
|
|
SLONG ID_floor_x1;
|
|
SLONG ID_floor_z1;
|
|
SLONG ID_floor_x2;
|
|
SLONG ID_floor_z2;
|
|
|
|
//
|
|
// How to access the floor squares indirectly, so that
|
|
// you can pretend the array is 'ID_FLOOR_SIZE' big.
|
|
// You must only ask for squares within the bounding
|
|
// box of the floorplan.
|
|
//
|
|
|
|
#ifndef NDEBUG
|
|
#define ID_FLOOR(x,z) (ID_floor_slow((x),(z)))
|
|
#else
|
|
#define ID_FLOOR(x,z) (&ID_floor[(x) - ID_floor_x1][(z) - ID_floor_z1])
|
|
#endif
|
|
|
|
ID_Square *ID_floor_slow(SLONG x, SLONG z)
|
|
{
|
|
SLONG sx;
|
|
SLONG sz;
|
|
|
|
ASSERT(WITHIN(x, ID_floor_x1, ID_floor_x2));
|
|
ASSERT(WITHIN(z, ID_floor_z1, ID_floor_z2));
|
|
|
|
sx = x - ID_floor_x1;
|
|
sz = z - ID_floor_z1;
|
|
|
|
ASSERT(WITHIN(sx, 0, ID_PLAN_SIZE - 1));
|
|
ASSERT(WITHIN(sz, 0, ID_PLAN_SIZE - 1));
|
|
|
|
return &ID_floor[sx][sz];
|
|
}
|
|
|
|
//
|
|
// The number of inside squares... the area of the building.
|
|
//
|
|
|
|
SLONG ID_floor_area;
|
|
|
|
//
|
|
// The walls
|
|
//
|
|
|
|
#define ID_WALL_T_OUTSIDE 1
|
|
#define ID_WALL_T_INSIDE 2
|
|
|
|
typedef struct
|
|
{
|
|
UBYTE type;
|
|
UBYTE door[3]; // For inside walls only. 255 => NULL
|
|
|
|
UBYTE x1, z1;
|
|
UBYTE x2, z2;
|
|
SLONG id;
|
|
SLONG num_blocks;
|
|
|
|
} ID_Wall;
|
|
|
|
#define ID_MAX_WALLS 64
|
|
|
|
ID_Wall ID_wall[ID_MAX_WALLS];
|
|
SLONG ID_wall_upto;
|
|
|
|
//
|
|
// The 'get_type' function.
|
|
//
|
|
|
|
SLONG (*ID_get_type)(SLONG id, SLONG block);
|
|
|
|
//
|
|
// For working out the inside squares.
|
|
//
|
|
|
|
#define ID_LINK_T_ENTER 1
|
|
#define ID_LINK_T_LEAVE 2
|
|
|
|
typedef struct
|
|
{
|
|
UBYTE type;
|
|
UBYTE next;
|
|
UWORD pos; // 8-bit fixed point.
|
|
|
|
} ID_Link;
|
|
|
|
#define ID_MAX_LINKS 128
|
|
|
|
ID_Link ID_link[ID_MAX_LINKS];
|
|
SLONG ID_link_upto;
|
|
|
|
//
|
|
// One linked list per z-row of floor square.
|
|
// 0 is the NULL index
|
|
//
|
|
|
|
UBYTE ID_edge[ID_PLAN_SIZE];
|
|
|
|
//
|
|
// How to access the EDGE list. It is relative to
|
|
// the bounding box, so it takes up less memory.
|
|
//
|
|
|
|
#ifndef NDEBUG
|
|
#define ID_EDGE(z) (ID_check_edge((z)), ID_edge[(z) - ID_floor_z1])
|
|
#else
|
|
#define ID_EDGE(z) (ID_edge[(z) - ID_floor_z1])
|
|
#endif
|
|
|
|
void ID_check_edge(SLONG z)
|
|
{
|
|
SLONG sz;
|
|
|
|
ASSERT(WITHIN(z, ID_floor_z1, ID_floor_z2));
|
|
|
|
sz = z - ID_floor_z1;
|
|
|
|
ASSERT(WITHIN(sz, 0, ID_PLAN_SIZE - 1));
|
|
}
|
|
|
|
//
|
|
// Info for each room.
|
|
//
|
|
|
|
#define ID_ROOM_TYPE_NONE 0
|
|
#define ID_ROOM_TYPE_LOO 1
|
|
#define ID_ROOM_TYPE_KITCHEN 2
|
|
#define ID_ROOM_TYPE_LOUNGE 3
|
|
#define ID_ROOM_TYPE_LOBBY 4
|
|
#define ID_ROOM_TYPE_DINING 5
|
|
#define ID_ROOM_TYPE_WAREHOUSE 6
|
|
#define ID_ROOM_TYPE_OFFICE 7
|
|
#define ID_ROOM_TYPE_MEETING 8
|
|
#define ID_ROOM_TYPE_CORRIDOR 9
|
|
#define ID_ROOM_TYPE_BEDROOM 10
|
|
#define ID_ROOM_TYPE_NUMBER 11
|
|
|
|
#define ID_CORRIDOR_LENGTH 4
|
|
|
|
#define ID_ROOM_FLAG_RECTANGULAR (1 << 0) // If the room is a perfect rectangle.
|
|
#define ID_ROOM_FLAG_CORRIDOR (1 << 1) // Long and thin. 1 by ID_CORRIDOR_LENGTH or more.
|
|
#define ID_ROOM_FLAG_LOBBY (1 << 2) // Connect to the outside by a door.
|
|
#define ID_ROOM_FLAG_STAIRWAY (1 << 3) // Room contains stairs.
|
|
#define ID_ROOM_FLAG_SEEABLE (1 << 4)
|
|
|
|
|
|
typedef struct
|
|
{
|
|
UBYTE type;
|
|
UBYTE flag;
|
|
|
|
UWORD cam_x;
|
|
UWORD cam_z;
|
|
|
|
UBYTE num_doors; // The number of internal doors this room has.
|
|
UBYTE flat; // Which flat this room is part of.
|
|
|
|
//
|
|
// The bounding box of the room. The are mapsquares
|
|
// not points so it is inclusive.
|
|
//
|
|
|
|
UBYTE x1;
|
|
UBYTE z1;
|
|
UBYTE x2;
|
|
UBYTE z2;
|
|
|
|
} ID_Room;
|
|
|
|
#define ID_MAX_ROOMS 32
|
|
|
|
ID_Room ID_room[ID_MAX_ROOMS]; // Room 0 is unused, it is the NULL room.
|
|
SLONG ID_room_upto;
|
|
SLONG ID_flat_upto;
|
|
|
|
//
|
|
// The floor textures for each room type.
|
|
//
|
|
|
|
#define ID_MAX_FLOOR_TEXTURES ID_ROOM_TYPE_NUMBER
|
|
|
|
UWORD ID_floor_texture[ID_MAX_FLOOR_TEXTURES];
|
|
|
|
//
|
|
// The stairs...
|
|
//
|
|
|
|
ID_Stair *ID_stair;
|
|
SLONG ID_num_stairs;
|
|
|
|
//
|
|
// The current type of storey
|
|
//
|
|
|
|
SLONG ID_storey_type;
|
|
|
|
|
|
//
|
|
// The furniture placed down indoors.
|
|
//
|
|
|
|
typedef struct
|
|
{
|
|
UWORD x;
|
|
UWORD z;
|
|
UBYTE prim;
|
|
UBYTE yaw;
|
|
|
|
} ID_Furn;
|
|
|
|
#define ID_MAX_FURN 128
|
|
|
|
ID_Furn ID_furn[ID_MAX_FURN];
|
|
SLONG ID_furn_upto;
|
|
|
|
|
|
|
|
//
|
|
// Hardcoded size of the doorframes.
|
|
//
|
|
|
|
#define ID_FRAME_START (0x30)
|
|
#define ID_FRAME_END (0xd0)
|
|
#define ID_FRAME_HEIGHT (0xd0)
|
|
|
|
|
|
|
|
//
|
|
// Returns the texture for the given room.
|
|
//
|
|
// 'poly_number' refers to the hard-coded door frame. It is which
|
|
// quad in the door frame is needed.
|
|
//
|
|
// +-----------+
|
|
// |\ 1 /|
|
|
// | \ / |
|
|
// | +-----+ |
|
|
// | || || |
|
|
// |2 || || 3|
|
|
// | || || |
|
|
// 4 5
|
|
//
|
|
// It is ignored for other texture types.
|
|
//
|
|
|
|
#define ID_TEXTURE_TYPE_FLOOR 0
|
|
#define ID_TEXTURE_TYPE_WALL 1
|
|
#define ID_TEXTURE_TYPE_DOOR 2
|
|
#define ID_TEXTURE_TYPE_WINDOW 3
|
|
#define ID_TEXTURE_TYPE_WALLTOP 4
|
|
|
|
//
|
|
// The size of a square texture.
|
|
//
|
|
|
|
#define ID_TEXTURE_SIZE (1.0F)
|
|
#define ID_TEXTURE_FRAME_START (ID_TEXTURE_SIZE * 3.0F * 0.0625F)
|
|
#define ID_TEXTURE_FRAME_END (ID_TEXTURE_SIZE * 12.0F * 0.0625F)
|
|
#define ID_TEXTURE_FRAME_HEIGHT (ID_TEXTURE_SIZE * 3.0F * 0.0625F)
|
|
#define ID_TEXTURE_FRAME_DEPTH (ID_TEXTURE_SIZE * 3.0F * 0.0625F)
|
|
|
|
//
|
|
// The texture page for inside buildings.
|
|
//
|
|
|
|
#define ID_TEXTURE_PAGE 6
|
|
|
|
typedef struct
|
|
{
|
|
float u0, v0;
|
|
float u1, v1;
|
|
float u2, v2;
|
|
float u3, v3;
|
|
|
|
} ID_Texture;
|
|
|
|
#define ID_MAX_TEXTURES 6
|
|
|
|
ID_Texture ID_texture[ID_MAX_TEXTURES] =
|
|
{
|
|
{0.0, 0.0, ID_TEXTURE_SIZE, 0.0, 0.0, ID_TEXTURE_SIZE, ID_TEXTURE_SIZE, ID_TEXTURE_SIZE}, // Square.
|
|
|
|
{0.0, 0.0, ID_TEXTURE_SIZE, 0.0, ID_TEXTURE_FRAME_START, ID_TEXTURE_FRAME_HEIGHT, ID_TEXTURE_FRAME_END, ID_TEXTURE_FRAME_HEIGHT},
|
|
{0.0, 0.0, ID_TEXTURE_FRAME_START, ID_TEXTURE_FRAME_HEIGHT, 0.0, ID_TEXTURE_SIZE, ID_TEXTURE_FRAME_START, ID_TEXTURE_SIZE},
|
|
{ID_TEXTURE_FRAME_END, ID_TEXTURE_FRAME_HEIGHT, ID_TEXTURE_SIZE, 0.0, ID_TEXTURE_FRAME_END, ID_TEXTURE_SIZE, ID_TEXTURE_SIZE, ID_TEXTURE_SIZE},
|
|
{ID_TEXTURE_FRAME_START, ID_TEXTURE_FRAME_HEIGHT, ID_TEXTURE_FRAME_START + ID_TEXTURE_FRAME_DEPTH, ID_TEXTURE_FRAME_HEIGHT, ID_TEXTURE_FRAME_START, ID_TEXTURE_SIZE, ID_TEXTURE_FRAME_START + ID_TEXTURE_FRAME_DEPTH, ID_TEXTURE_SIZE},
|
|
{ID_TEXTURE_FRAME_END - ID_TEXTURE_FRAME_DEPTH, ID_TEXTURE_FRAME_HEIGHT, ID_TEXTURE_FRAME_END, ID_TEXTURE_FRAME_HEIGHT, ID_TEXTURE_FRAME_END - ID_TEXTURE_FRAME_DEPTH, ID_TEXTURE_SIZE, ID_TEXTURE_FRAME_END, ID_TEXTURE_SIZE}
|
|
};
|
|
|
|
//
|
|
// The texture UWORD is a packed (baseu, basev, poly_number)
|
|
//
|
|
|
|
#define ID_GET_TEXTURE_UWORD(baseu, basev, poly_number) (((baseu) << 12) | ((basev) << 8) | (poly_number))
|
|
#define ID_GET_TEXTURE_BASEU(texture) (((texture) >> 12) & 0x0f)
|
|
#define ID_GET_TEXTURE_BASEV(texture) (((texture) >> 8) & 0x0f)
|
|
#define ID_GET_TEXTURE_POLYNUMBER(texture) (((texture) >> 0) & 0xff)
|
|
|
|
UWORD ID_get_texture(SLONG room_type, SLONG texture_type, SLONG poly_number)
|
|
{
|
|
UWORD ans;
|
|
|
|
SLONG base_u;
|
|
SLONG base_v;
|
|
|
|
switch(room_type)
|
|
{
|
|
case ID_ROOM_TYPE_LOO: base_u = 0; base_v = 0; break;
|
|
case ID_ROOM_TYPE_KITCHEN: base_u = 4; base_v = 0; break;
|
|
case ID_ROOM_TYPE_LOUNGE: base_u = 0; base_v = 1; break;
|
|
case ID_ROOM_TYPE_LOBBY: base_u = 4; base_v = 1; break;
|
|
case ID_ROOM_TYPE_DINING: base_u = 0; base_v = 2; break;
|
|
case ID_ROOM_TYPE_WAREHOUSE:
|
|
case ID_ROOM_TYPE_OFFICE:
|
|
case ID_ROOM_TYPE_MEETING:
|
|
case ID_ROOM_TYPE_CORRIDOR:
|
|
case ID_ROOM_TYPE_BEDROOM: base_u = 4; base_v = 2; break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
if (texture_type == ID_TEXTURE_TYPE_DOOR)
|
|
{
|
|
ASSERT(WITHIN(poly_number, 1, 5));
|
|
}
|
|
else
|
|
{
|
|
poly_number = 0; // The normal square.
|
|
}
|
|
|
|
if (texture_type != ID_TEXTURE_TYPE_WALLTOP)
|
|
{
|
|
//
|
|
// The different texture types are in an ordered line
|
|
// on the texture
|
|
//
|
|
|
|
base_u += texture_type;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For walltops, always use texture (7,7).
|
|
//
|
|
|
|
base_u = 7;
|
|
base_v = 7;
|
|
}
|
|
|
|
ans = ID_GET_TEXTURE_UWORD(base_u, base_v, poly_number);
|
|
|
|
return ans;
|
|
|
|
}
|
|
|
|
//
|
|
// Returns the page.
|
|
//
|
|
|
|
SLONG ID_get_texture_uvs(
|
|
UWORD texture,
|
|
float *u0, float *v0,
|
|
float *u1, float *v1,
|
|
float *u2, float *v2,
|
|
float *u3, float *v3)
|
|
{
|
|
float u;
|
|
float v;
|
|
|
|
SLONG base_u;
|
|
SLONG base_v;
|
|
SLONG poly_number;
|
|
SLONG page = 0;
|
|
#ifndef PSX
|
|
|
|
ID_Texture *it;
|
|
|
|
base_u = ID_GET_TEXTURE_BASEU(texture);
|
|
base_v = ID_GET_TEXTURE_BASEV(texture);
|
|
poly_number = ID_GET_TEXTURE_POLYNUMBER(texture);
|
|
|
|
ASSERT(WITHIN(poly_number, 0, ID_MAX_TEXTURES - 1));
|
|
|
|
it = &ID_texture[poly_number];
|
|
|
|
page = ID_TEXTURE_PAGE * 64;
|
|
page += base_v * 8;
|
|
page += base_u * 1;
|
|
|
|
*u0 = it->u0;
|
|
*v0 = it->v0;
|
|
*u1 = it->u1;
|
|
*v1 = it->v1;
|
|
*u2 = it->u2;
|
|
*v2 = it->v2;
|
|
*u3 = it->u3;
|
|
*v3 = it->v3;
|
|
|
|
#endif
|
|
return page;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Random numbers for the ID module.
|
|
//
|
|
|
|
ULONG ID_rand_seed;
|
|
|
|
inline void ID_srand(ULONG seed)
|
|
{
|
|
ID_rand_seed = seed;
|
|
}
|
|
|
|
inline ULONG ID_grand(void)
|
|
{
|
|
return ID_rand_seed;
|
|
}
|
|
|
|
inline UWORD ID_rand(void)
|
|
{
|
|
UWORD ans;
|
|
|
|
ID_rand_seed *= 328573;
|
|
ID_rand_seed += 123456789;
|
|
|
|
ans = ID_rand_seed >> 7;
|
|
|
|
return ans;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Returns the index of a point at (x,y,z). This function
|
|
// will fail if the point could be used as packed (x,z).
|
|
//
|
|
|
|
UWORD ID_get_point_index(UWORD x, UWORD y, UWORD z)
|
|
{
|
|
SLONG i;
|
|
UWORD ans;
|
|
|
|
//
|
|
// Make sure that this point could not have been
|
|
// used as a packed (x,z).
|
|
//
|
|
|
|
ASSERT(!((x & 0xff) == 0 && (z & 0xff) == 0 && y == 0));
|
|
|
|
//
|
|
// Look for a point in the array already at this position.
|
|
//
|
|
|
|
for (i = ID_point_upto - 1; i >= 0; i--)
|
|
{
|
|
if (ID_point[i].x == x &&
|
|
ID_point[i].y == y &&
|
|
ID_point[i].z == z)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have to create a new point.
|
|
//
|
|
|
|
ASSERT(WITHIN(ID_point_upto, 0, ID_MAX_POINTS - 1));
|
|
|
|
ans = ID_point_upto++;
|
|
|
|
ID_point[ans].x = x;
|
|
ID_point[ans].y = y;
|
|
ID_point[ans].z = z;
|
|
|
|
return ans;
|
|
}
|
|
|
|
|
|
//
|
|
// Adds faces to the floorplan. If 'quad' then it is a quad otherwise
|
|
// it is a triangle. It adds it to square (mx, mz)
|
|
//
|
|
|
|
void ID_add_face_to_square(
|
|
SLONG mx,
|
|
SLONG mz,
|
|
UWORD x[4],
|
|
UWORD y[4],
|
|
UWORD z[4],
|
|
UWORD texture,
|
|
UBYTE quad)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG num_points;
|
|
|
|
UWORD point[4];
|
|
UWORD flag;
|
|
|
|
ID_Square *is;
|
|
|
|
ASSERT(WITHIN(ID_face_upto, 1, ID_MAX_FACES - 1));
|
|
|
|
|
|
//
|
|
// How many points?
|
|
//
|
|
|
|
if (quad)
|
|
{
|
|
flag = ID_FACE_FLAG_QUAD;
|
|
num_points = 4;
|
|
}
|
|
else
|
|
{
|
|
flag = 0;
|
|
num_points = 3;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
|
|
//
|
|
// Make sure that no two points are the same.
|
|
//
|
|
|
|
for (i = 1; i < num_points; i++)
|
|
{
|
|
for (j = i - 1; j >= 0; j--)
|
|
{
|
|
if (x[i] == x[j] &&
|
|
y[i] == y[j] &&
|
|
z[i] == z[j])
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// The points.
|
|
//
|
|
|
|
for (i = 0; i < num_points; i++)
|
|
{
|
|
if ((x[i] & 0xff) == 0 && (z[i] & 0xff) == 0 && y[i] == 0)
|
|
{
|
|
//
|
|
// We can use this point index as packed (x,z)
|
|
//
|
|
|
|
flag |= ID_FACE_FLAG_ONFLOOR0 << i;
|
|
point[i] = (x[i] >> 8) | (z[i] >> 0);
|
|
}
|
|
else
|
|
{
|
|
point[i] = ID_get_point_index(x[i], y[i], z[i]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The mapsquare we must add this face to.
|
|
//
|
|
|
|
is = ID_FLOOR(mx, mz);
|
|
|
|
if (!(is->flag & ID_FLOOR_FLAG_INSIDE))
|
|
{
|
|
TRACE("Warning: Adding face to square that is not inside.\n");
|
|
}
|
|
|
|
ASSERT(WITHIN(ID_face_upto, 1, ID_MAX_FACES - 1));
|
|
|
|
ID_face[ID_face_upto].point[0] = point[0];
|
|
ID_face[ID_face_upto].point[1] = point[1];
|
|
ID_face[ID_face_upto].point[2] = point[2];
|
|
ID_face[ID_face_upto].point[3] = point[3];
|
|
ID_face[ID_face_upto].flag = flag;
|
|
ID_face[ID_face_upto].texture = texture;
|
|
ID_face[ID_face_upto].next = is->next;
|
|
|
|
is->next = ID_face_upto++;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void ID_clear_floorplan(void)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
//
|
|
// Mark the whole floor as outside.
|
|
//
|
|
|
|
for (x = 0; x < ID_PLAN_SIZE; x++)
|
|
{
|
|
for (z = 0; z < ID_PLAN_SIZE; z++)
|
|
{
|
|
ID_floor[x][z].flag = ID_FLOOR_FLAG_OUTSIDE;
|
|
ID_floor[x][z].room = 0;
|
|
ID_floor[x][z].texid = 0;
|
|
ID_floor[x][z].next = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear the bounding box.
|
|
//
|
|
|
|
ID_floor_x1 = +INFINITY;
|
|
ID_floor_z1 = +INFINITY;
|
|
ID_floor_x2 = -INFINITY;
|
|
ID_floor_z2 = -INFINITY;
|
|
|
|
//
|
|
// Clear all the walls and the 'get_type' function.
|
|
//
|
|
|
|
ID_wall_upto = 0;
|
|
ID_get_type = NULL;
|
|
|
|
//
|
|
// Clear all points and faces.
|
|
//
|
|
|
|
ID_point_upto = 0;
|
|
ID_face_upto = 1;
|
|
|
|
//
|
|
// Initialise the floor textures.
|
|
//
|
|
|
|
for (i = 1; i < ID_ROOM_TYPE_NUMBER; i++)
|
|
{
|
|
ID_floor_texture[i] = ID_get_texture(i, ID_TEXTURE_TYPE_FLOOR, 0);
|
|
}
|
|
}
|
|
|
|
void ID_set_outline(SLONG x1, SLONG z1, SLONG x2, SLONG z2, SLONG id, SLONG num_blocks)
|
|
{
|
|
ASSERT(WITHIN(ID_wall_upto, 0, ID_MAX_WALLS - 1));
|
|
|
|
ASSERT(WITHIN(x1, 0, ID_FLOOR_SIZE - 1));
|
|
ASSERT(WITHIN(z1, 0, ID_FLOOR_SIZE - 1));
|
|
ASSERT(WITHIN(x2, 0, ID_FLOOR_SIZE - 1));
|
|
ASSERT(WITHIN(z2, 0, ID_FLOOR_SIZE - 1));
|
|
|
|
//
|
|
// Add this wall to the array of walls.
|
|
//
|
|
|
|
ID_wall[ID_wall_upto].type = ID_WALL_T_OUTSIDE;
|
|
ID_wall[ID_wall_upto].door[0] = 255;
|
|
ID_wall[ID_wall_upto].door[1] = 255;
|
|
ID_wall[ID_wall_upto].door[2] = 255;
|
|
ID_wall[ID_wall_upto].x1 = x1;
|
|
ID_wall[ID_wall_upto].z1 = z1;
|
|
ID_wall[ID_wall_upto].x2 = x2;
|
|
ID_wall[ID_wall_upto].z2 = z2;
|
|
ID_wall[ID_wall_upto].id = id;
|
|
ID_wall[ID_wall_upto].num_blocks = num_blocks;
|
|
|
|
ID_wall_upto += 1;
|
|
|
|
//
|
|
// Update the bounding box of the walls.
|
|
//
|
|
|
|
if (x1 < ID_floor_x1) {ID_floor_x1 = x1;}
|
|
if (x2 < ID_floor_x1) {ID_floor_x1 = x2;}
|
|
if (x1 > ID_floor_x2) {ID_floor_x2 = x1;}
|
|
if (x2 > ID_floor_x2) {ID_floor_x2 = x2;}
|
|
|
|
if (z1 < ID_floor_z1) {ID_floor_z1 = z1;}
|
|
if (z2 < ID_floor_z1) {ID_floor_z1 = z2;}
|
|
if (z1 > ID_floor_z2) {ID_floor_z2 = z1;}
|
|
if (z2 > ID_floor_z2) {ID_floor_z2 = z2;}
|
|
}
|
|
|
|
void ID_set_get_type_func(SLONG (*get_type)(SLONG id, SLONG block))
|
|
{
|
|
ID_get_type = get_type;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Collision stuff we should know about...
|
|
//
|
|
|
|
|
|
extern UWORD next_col_vect;
|
|
extern UWORD next_col_vect_link;
|
|
|
|
//void insert_collision_vect(SLONG x1,SLONG y1,SLONG z1,SLONG x2,SLONG y2,SLONG z2,UBYTE prim_type,UBYTE prim_extra,SWORD face);
|
|
void remove_collision_vect(UWORD vect);
|
|
|
|
|
|
//
|
|
// These two functions must be called in pairs.
|
|
//
|
|
// Inserts collision vectors for the walls.
|
|
// Removes collision vectros for the walls.
|
|
//
|
|
|
|
SLONG ID_colvect_old_next_col_vect;
|
|
SLONG ID_colvect_old_next_col_vect_link;
|
|
SLONG ID_colvect_stuff_valid;
|
|
|
|
void ID_wall_colvects_insert()
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG dist;
|
|
SLONG changed;
|
|
|
|
SLONG doorway_x;
|
|
SLONG doorway_z;
|
|
|
|
UBYTE wall_height_inside;
|
|
UBYTE wall_height_outside;
|
|
|
|
ID_Wall *iw;
|
|
|
|
#define MAX_POINTS 12
|
|
|
|
struct
|
|
{
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG dist;
|
|
|
|
} point[MAX_POINTS];
|
|
SLONG point_upto;
|
|
|
|
//
|
|
// Remember the end of the col_vect and col_vect_link arrays for
|
|
// when we get rid of these colvects.
|
|
//
|
|
|
|
ID_colvect_old_next_col_vect = next_col_vect;
|
|
ID_colvect_old_next_col_vect_link = next_col_vect_link;
|
|
ID_colvect_stuff_valid = TRUE;
|
|
|
|
if (ID_storey_type == ID_STOREY_TYPE_WAREHOUSE)
|
|
{
|
|
wall_height_inside = 1;
|
|
wall_height_outside = 2;
|
|
}
|
|
else
|
|
{
|
|
wall_height_inside = 1;
|
|
wall_height_outside = 1;
|
|
}
|
|
|
|
//
|
|
// Insert colvects for each wall.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
switch(iw->type)
|
|
{
|
|
case ID_WALL_T_OUTSIDE:
|
|
|
|
insert_collision_vect(
|
|
iw->x2 << 8, INDOORS_HEIGHT_FLOOR, iw->z2 << 8,
|
|
iw->x1 << 8, INDOORS_HEIGHT_FLOOR, iw->z1 << 8,
|
|
STOREY_TYPE_NOT_REALLY_A_STOREY_TYPE_BUT_A_VALUE_TO_PUT_IN_THE_PRIM_TYPE_FIELD_OF_COLVECTS_GENERATED_BY_INSIDE_BUILDINGS,
|
|
wall_height_outside, 0);
|
|
|
|
break;
|
|
|
|
case ID_WALL_T_INSIDE:
|
|
|
|
point[0].x = iw->x1 << 8;
|
|
point[0].z = iw->z1 << 8;
|
|
|
|
point[1].x = iw->x2 << 8;
|
|
point[1].z = iw->z2 << 8;
|
|
|
|
point_upto = 2;
|
|
|
|
dx = SIGN(iw->x2 - iw->x1);
|
|
dz = SIGN(iw->z2 - iw->z1);
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (iw->door[j] != 255)
|
|
{
|
|
doorway_x = iw->x1 + dx * iw->door[j] << 8;
|
|
doorway_z = iw->z1 + dz * iw->door[j] << 8;
|
|
|
|
point[point_upto + 0].x = doorway_x + dx * ID_FRAME_START;
|
|
point[point_upto + 0].z = doorway_z + dz * ID_FRAME_START;
|
|
|
|
point[point_upto + 1].x = doorway_x + dx * ID_FRAME_END;
|
|
point[point_upto + 1].z = doorway_z + dz * ID_FRAME_END;
|
|
|
|
point_upto += 2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the distance of each point from point 1.
|
|
//
|
|
|
|
for (j = 0; j < point_upto; j++)
|
|
{
|
|
dx = point[j].x - (iw->x1 << 8);
|
|
dz = point[j].z - (iw->z1 << 8);
|
|
|
|
dist = abs(dx) + abs(dz);
|
|
|
|
point[j].dist = dist;
|
|
}
|
|
|
|
//
|
|
// Sort the points in order of distance from the first point.
|
|
//
|
|
|
|
do
|
|
{
|
|
changed = FALSE;
|
|
|
|
for (j = 0; j < point_upto - 1; j++)
|
|
{
|
|
if (point[j + 0].dist > point[j + 1].dist)
|
|
{
|
|
SWAP(point[j + 0].x, point[j + 1].x);
|
|
SWAP(point[j + 0].z, point[j + 1].z);
|
|
SWAP(point[j + 0].dist, point[j + 1].dist);
|
|
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
|
|
} while(changed);
|
|
|
|
for (j = 0; j < point_upto - 1; j += 2)
|
|
{
|
|
insert_collision_vect(
|
|
point[j + 0].x, INDOORS_HEIGHT_FLOOR, point[j + 0].z,
|
|
point[j + 1].x, INDOORS_HEIGHT_FLOOR, point[j + 1].z,
|
|
STOREY_TYPE_NOT_REALLY_A_STOREY_TYPE_BUT_A_VALUE_TO_PUT_IN_THE_PRIM_TYPE_FIELD_OF_COLVECTS_GENERATED_BY_INSIDE_BUILDINGS,
|
|
wall_height_inside, 0);
|
|
|
|
insert_collision_vect(
|
|
point[j + 1].x, INDOORS_HEIGHT_FLOOR, point[j + 1].z,
|
|
point[j + 0].x, INDOORS_HEIGHT_FLOOR, point[j + 0].z,
|
|
STOREY_TYPE_NOT_REALLY_A_STOREY_TYPE_BUT_A_VALUE_TO_PUT_IN_THE_PRIM_TYPE_FIELD_OF_COLVECTS_GENERATED_BY_INSIDE_BUILDINGS,
|
|
wall_height_inside, 0);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ID_wall_colvects_remove()
|
|
{
|
|
SLONG i;
|
|
|
|
ASSERT(ID_colvect_stuff_valid);
|
|
|
|
for (i = ID_colvect_old_next_col_vect; i < next_col_vect; i++)
|
|
{
|
|
remove_collision_vect(i);
|
|
}
|
|
|
|
//
|
|
// We are not using these any more...
|
|
//
|
|
|
|
next_col_vect = ID_colvect_old_next_col_vect;
|
|
next_col_vect_link = ID_colvect_old_next_col_vect_link;
|
|
ID_colvect_stuff_valid = FALSE;
|
|
}
|
|
|
|
//
|
|
// Returns TRUE if the given wall lies on the perimeter of the given
|
|
// room. If it does then the function gives back the section of wall
|
|
// that lies on the edge of the room. The sense of the wall segment
|
|
// is such that the room lies of the right of the wall.
|
|
//
|
|
|
|
SLONG ID_is_wall_on_room_perim(
|
|
SLONG wall,
|
|
SLONG room,
|
|
SLONG *x1,
|
|
SLONG *z1,
|
|
SLONG *x2,
|
|
SLONG *z2)
|
|
{
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG mx;
|
|
SLONG mz;
|
|
|
|
SLONG dmx;
|
|
SLONG dmz;
|
|
|
|
SLONG wx1;
|
|
SLONG wz1;
|
|
SLONG wx2;
|
|
SLONG wz2;
|
|
|
|
SLONG length;
|
|
|
|
SLONG found_start;
|
|
SLONG flip_ends;
|
|
|
|
ID_Wall *iw;
|
|
|
|
ASSERT(WITHIN(wall, 0, ID_wall_upto - 1));
|
|
|
|
iw = &ID_wall[wall];
|
|
|
|
dx = iw->x2 - iw->x1;
|
|
dz = iw->z2 - iw->z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// Only outside walls should be non-orthogonal.
|
|
//
|
|
|
|
ASSERT(iw->type == ID_WALL_T_OUTSIDE);
|
|
|
|
//
|
|
// This line should only lie on the perimeter of
|
|
// one room.
|
|
//
|
|
|
|
length = abs(dx) + abs(dz);
|
|
|
|
mx = iw->x1 << 8;
|
|
mz = iw->z1 << 8;
|
|
|
|
mx += (dx << 7) / length;
|
|
mz += (dz << 7) / length;
|
|
|
|
dx = SIGN(dx);
|
|
dz = SIGN(dz);
|
|
|
|
dmx = -SIGN(dz);
|
|
dmz = SIGN(dx);
|
|
|
|
mx += dmx;
|
|
mz += dmz;
|
|
|
|
mx >>= 8;
|
|
mz >>= 8;
|
|
|
|
//
|
|
// This square should be inside and part
|
|
// of a room.
|
|
//
|
|
|
|
ASSERT(ID_FLOOR(mx,mz)->flag & ID_FLOOR_FLAG_INSIDE);
|
|
ASSERT(ID_FLOOR(mx,mz)->room != 0);
|
|
|
|
if (ID_FLOOR(mx,mz)->room == room)
|
|
{
|
|
//
|
|
// Outside lines are always in clockwise order.
|
|
//
|
|
|
|
*x1 = iw->x1;
|
|
*z1 = iw->z1;
|
|
*x2 = iw->x2;
|
|
*z2 = iw->z2;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
wx1 = iw->x1;
|
|
wz1 = iw->z1;
|
|
wx2 = iw->x2;
|
|
wz2 = iw->z2;
|
|
|
|
if (wx1 > wx2) {SWAP(wx1, wx2);}
|
|
if (wz1 > wz2) {SWAP(wz1, wz2);}
|
|
|
|
dx = SIGN(wx2 - wx1);
|
|
dz = SIGN(wz2 - wz1);
|
|
|
|
ASSERT(!dx || !dz);
|
|
ASSERT(dx >= 0 && dz >= 0);
|
|
|
|
found_start = FALSE;
|
|
flip_ends = FALSE;
|
|
|
|
for (x = wx1, z = wz1; x != wx2 || z != wz2; x += dx, z += dz)
|
|
{
|
|
#define ID_ROOM_SQUARE(x,z,r) (WITHIN((x), ID_floor_x1, ID_floor_x2) && WITHIN((z), ID_floor_z1, ID_floor_z2) && ID_FLOOR((x),(z))->room == (r))
|
|
|
|
if (dx == 1 && dz == 0)
|
|
{
|
|
if (ID_ROOM_SQUARE(x, z, room))
|
|
{
|
|
if (!found_start)
|
|
{
|
|
*x1 = x;
|
|
*z1 = z;
|
|
|
|
found_start = TRUE;
|
|
flip_ends = FALSE;
|
|
}
|
|
|
|
*x2 = x + 1;
|
|
*z2 = z;
|
|
}
|
|
|
|
if (ID_ROOM_SQUARE(x, z - 1, room))
|
|
{
|
|
if (!found_start)
|
|
{
|
|
*x1 = x;
|
|
*z1 = z;
|
|
|
|
found_start = TRUE;
|
|
flip_ends = TRUE;
|
|
}
|
|
|
|
*x2 = x + 1;
|
|
*z2 = z;
|
|
}
|
|
}
|
|
else
|
|
if (dx == 0 && dz == 1)
|
|
{
|
|
if (ID_ROOM_SQUARE(x, z, room))
|
|
{
|
|
if (!found_start)
|
|
{
|
|
*x1 = x;
|
|
*z1 = z;
|
|
|
|
found_start = TRUE;
|
|
flip_ends = TRUE;
|
|
}
|
|
|
|
*x2 = x;
|
|
*z2 = z + 1;
|
|
}
|
|
|
|
if (ID_ROOM_SQUARE(x - 1, z, room))
|
|
{
|
|
if (!found_start)
|
|
{
|
|
*x1 = x;
|
|
*z1 = z;
|
|
|
|
found_start = TRUE;
|
|
flip_ends = FALSE;
|
|
}
|
|
|
|
*x2 = x;
|
|
*z2 = z + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flip_ends)
|
|
{
|
|
//
|
|
// We have to flip the direction of the line segment,
|
|
// to make the room be on the right-hand-side of
|
|
// the line.
|
|
//
|
|
|
|
SWAP(*x1, *x2);
|
|
SWAP(*z1, *z2);
|
|
}
|
|
|
|
return found_start;
|
|
}
|
|
|
|
|
|
//
|
|
// Creates faces for a mapsquare.
|
|
//
|
|
|
|
#define ID_WALL_FACES_OUT_WINDOW 1 // A window to the outside.
|
|
#define ID_WALL_FACES_OUT_FRAME 2 // A door to the outside.
|
|
#define ID_WALL_FACES_NORMAL 3
|
|
#define ID_WALL_FACES_FRAME 4 // A doorframe without a door.
|
|
#define ID_WALL_FACES_UPPER 5 // The upper storey of a warehouse.
|
|
|
|
//
|
|
// The function needs four control points.
|
|
//
|
|
// 0O--------O1 Given in this order.
|
|
// |\ |
|
|
// |2O------O3
|
|
// | |
|
|
// | |
|
|
// | |
|
|
// +--------+
|
|
//
|
|
// It fits the faces around the control points. Control points 1 and
|
|
// 2 are normally be on the corners of mapsquares.
|
|
//
|
|
|
|
void ID_create_mapsquare_faces(
|
|
UBYTE map_x,
|
|
UBYTE map_z,
|
|
UWORD cpx[4],
|
|
UWORD cpz[4],
|
|
SLONG room_type,
|
|
SLONG face_type,
|
|
SLONG generate_top_wall_quad)
|
|
{
|
|
SLONG i;
|
|
|
|
UWORD x[4];
|
|
UWORD y[4];
|
|
UWORD z[4];
|
|
|
|
UWORD texture;
|
|
|
|
SLONG tu;
|
|
SLONG tv;
|
|
SLONG page;
|
|
|
|
if (generate_top_wall_quad)
|
|
{
|
|
//
|
|
// The top of the wall quad.
|
|
//
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
x[i] = cpx[i];
|
|
z[i] = cpz[i];
|
|
|
|
y[i] = INDOORS_HEIGHT_FLOOR + ID_CEILING_HEIGHT;
|
|
|
|
if (face_type == ID_WALL_FACES_UPPER)
|
|
{
|
|
y[i] += 256;
|
|
}
|
|
}
|
|
|
|
ID_add_face_to_square(map_x, map_z, x, y, z, ID_get_texture(room_type, ID_TEXTURE_TYPE_WALLTOP, 0), TRUE);
|
|
}
|
|
|
|
if (face_type == ID_WALL_FACES_FRAME ||
|
|
face_type == ID_WALL_FACES_OUT_FRAME)
|
|
{
|
|
UWORD dpx[4];
|
|
UWORD dpz[4];
|
|
|
|
//
|
|
// Hard-code a simple door-frame.
|
|
//
|
|
|
|
dpx[0] = cpx[0] + (((cpx[1] - cpx[0]) * ID_FRAME_START) >> 8);
|
|
dpz[0] = cpz[0] + (((cpz[1] - cpz[0]) * ID_FRAME_START) >> 8);
|
|
|
|
dpx[1] = cpx[0] + (((cpx[1] - cpx[0]) * ID_FRAME_END) >> 8);
|
|
dpz[1] = cpz[0] + (((cpz[1] - cpz[0]) * ID_FRAME_END) >> 8);
|
|
|
|
dpx[2] = cpx[2] + (((cpx[3] - cpx[2]) * ID_FRAME_START) >> 8);
|
|
dpz[2] = cpz[2] + (((cpz[3] - cpz[2]) * ID_FRAME_START) >> 8);
|
|
|
|
dpx[3] = cpx[2] + (((cpx[3] - cpx[2]) * ID_FRAME_END) >> 8);
|
|
dpz[3] = cpz[2] + (((cpz[3] - cpz[2]) * ID_FRAME_END) >> 8);
|
|
|
|
//
|
|
// Here goes!
|
|
//
|
|
|
|
//
|
|
// Sides of door frame.
|
|
//
|
|
|
|
x[0] = dpx[2];
|
|
z[0] = dpz[2];
|
|
y[0] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
x[1] = dpx[0];
|
|
z[1] = dpz[0];
|
|
y[1] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
x[2] = dpx[2];
|
|
z[2] = dpz[2];
|
|
y[2] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
x[3] = dpx[0];
|
|
z[3] = dpz[0];
|
|
y[3] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
ID_add_face_to_square(map_x, map_z, x, y, z, ID_get_texture(room_type, ID_TEXTURE_TYPE_DOOR, 4), TRUE);
|
|
|
|
x[0] = dpx[1];
|
|
z[0] = dpz[1];
|
|
y[0] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
x[1] = dpx[3];
|
|
z[1] = dpz[3];
|
|
y[1] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
x[2] = dpx[1];
|
|
z[2] = dpz[1];
|
|
y[2] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
x[3] = dpx[3];
|
|
z[3] = dpz[3];
|
|
y[3] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
ID_add_face_to_square(map_x, map_z, x, y, z, ID_get_texture(room_type, ID_TEXTURE_TYPE_DOOR, 5), TRUE);
|
|
|
|
//
|
|
// Top of door frame.
|
|
//
|
|
|
|
x[0] = cpx[2];
|
|
z[0] = cpz[2];
|
|
y[0] = INDOORS_HEIGHT_FLOOR + ID_CEILING_HEIGHT;
|
|
|
|
x[1] = cpx[3];
|
|
z[1] = cpz[3];
|
|
y[1] = INDOORS_HEIGHT_FLOOR + ID_CEILING_HEIGHT;
|
|
|
|
x[2] = dpx[2];
|
|
z[2] = dpz[2];
|
|
y[2] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
x[3] = dpx[3];
|
|
z[3] = dpz[3];
|
|
y[3] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
ID_add_face_to_square(map_x, map_z, x, y, z, ID_get_texture(room_type, ID_TEXTURE_TYPE_DOOR, 1), TRUE);
|
|
|
|
x[0] = cpx[2];
|
|
z[0] = cpz[2];
|
|
y[0] = INDOORS_HEIGHT_FLOOR + ID_CEILING_HEIGHT;
|
|
|
|
x[1] = dpx[2];
|
|
z[1] = dpz[2];
|
|
y[1] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
x[2] = cpx[2];
|
|
z[2] = cpz[2];
|
|
y[2] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
x[3] = dpx[2];
|
|
z[3] = dpz[2];
|
|
y[3] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
ID_add_face_to_square(map_x, map_z, x, y, z, ID_get_texture(room_type, ID_TEXTURE_TYPE_DOOR, 2), TRUE);
|
|
|
|
x[0] = dpx[3];
|
|
z[0] = dpz[3];
|
|
y[0] = INDOORS_HEIGHT_FLOOR + ID_FRAME_HEIGHT;
|
|
|
|
x[1] = cpx[3];
|
|
z[1] = cpz[3];
|
|
y[1] = INDOORS_HEIGHT_FLOOR + ID_CEILING_HEIGHT;
|
|
|
|
x[2] = dpx[3];
|
|
z[2] = dpz[3];
|
|
y[2] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
x[3] = cpx[3];
|
|
z[3] = cpz[3];
|
|
y[3] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
ID_add_face_to_square(map_x, map_z, x, y, z, ID_get_texture(room_type, ID_TEXTURE_TYPE_DOOR, 3), TRUE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The front of the wall.
|
|
//
|
|
|
|
x[0] = cpx[2];
|
|
z[0] = cpz[2];
|
|
y[0] = INDOORS_HEIGHT_FLOOR + ID_CEILING_HEIGHT;
|
|
|
|
x[1] = cpx[3];
|
|
z[1] = cpz[3];
|
|
y[1] = INDOORS_HEIGHT_FLOOR + ID_CEILING_HEIGHT;
|
|
|
|
x[2] = cpx[2];
|
|
z[2] = cpz[2];
|
|
y[2] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
x[3] = cpx[3];
|
|
z[3] = cpz[3];
|
|
y[3] = INDOORS_HEIGHT_FLOOR + 0;
|
|
|
|
if (face_type == ID_WALL_FACES_UPPER)
|
|
{
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
y[i] += 256;
|
|
}
|
|
}
|
|
|
|
if (face_type == ID_WALL_FACES_OUT_WINDOW)
|
|
{
|
|
texture = ID_get_texture(room_type, ID_TEXTURE_TYPE_WINDOW, 0);
|
|
}
|
|
else
|
|
{
|
|
texture = ID_get_texture(room_type, ID_TEXTURE_TYPE_WALL, 0);
|
|
}
|
|
|
|
ID_add_face_to_square(map_x, map_z, x, y, z, texture, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Returns the perimeter of the given room. The first
|
|
// point appears again at the end of the array. Returns FALSE if
|
|
// it fails to find a perimeter!
|
|
//
|
|
|
|
typedef struct
|
|
{
|
|
UBYTE x;
|
|
UBYTE z;
|
|
|
|
} ID_Perim;
|
|
|
|
#define ID_MAX_PERIMS 32
|
|
|
|
ID_Perim ID_perim[ID_MAX_PERIMS];
|
|
SLONG ID_perim_upto;
|
|
|
|
SLONG ID_calc_room_perim(SLONG room)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG sx1, sz1;
|
|
SLONG sx2, sz2;
|
|
|
|
SLONG tries;
|
|
|
|
#define MAX_SEGS 32
|
|
|
|
struct
|
|
{
|
|
UBYTE x1, z1;
|
|
UBYTE x2, z2;
|
|
|
|
} seg[MAX_SEGS];
|
|
|
|
SLONG seg_upto = 0;
|
|
|
|
//
|
|
// Find all the lines that lie on the edge of the given room.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
if (ID_is_wall_on_room_perim(i, room,
|
|
&sx1, &sz1,
|
|
&sx2, &sz2))
|
|
{
|
|
ASSERT(WITHIN(seg_upto, 0, MAX_SEGS - 1));
|
|
|
|
//
|
|
// Remember all the segments.
|
|
//
|
|
|
|
seg[seg_upto].x1 = sx1;
|
|
seg[seg_upto].z1 = sz1;
|
|
seg[seg_upto].x2 = sx2;
|
|
seg[seg_upto].z2 = sz2;
|
|
|
|
seg_upto += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// There must be at least three lines!
|
|
//
|
|
|
|
ASSERT(seg_upto >= 3);
|
|
|
|
//
|
|
// Build the perimeter.
|
|
//
|
|
|
|
ID_perim[0].x = seg[0].x1;
|
|
ID_perim[0].z = seg[0].z1;
|
|
|
|
ID_perim[1].x = seg[0].x2;
|
|
ID_perim[1].z = seg[0].z2;
|
|
|
|
ID_perim_upto = 1;
|
|
|
|
tries = 0;
|
|
|
|
while(
|
|
ID_perim[ID_perim_upto].x != ID_perim[0].x ||
|
|
ID_perim[ID_perim_upto].z != ID_perim[0].z)
|
|
{
|
|
//
|
|
// Look for the next segment.
|
|
//
|
|
|
|
for (i = 0; i < seg_upto; i++)
|
|
{
|
|
if (seg[i].x1 == ID_perim[ID_perim_upto].x &&
|
|
seg[i].z1 == ID_perim[ID_perim_upto].z)
|
|
{
|
|
ID_perim_upto += 1;
|
|
|
|
if (!WITHIN(ID_perim_upto, 0, ID_MAX_PERIMS - 1))
|
|
{
|
|
//
|
|
// ERROR! FAILURE!
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ID_perim[ID_perim_upto].x = seg[i].x2;
|
|
ID_perim[ID_perim_upto].z = seg[i].z2;
|
|
}
|
|
}
|
|
|
|
if (tries++ > ID_MAX_PERIMS)
|
|
{
|
|
//
|
|
// ERROR! FAILURE!
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ID_perim_upto += 1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Returns TRUE if it can find the perimeter for all the rooms!
|
|
//
|
|
|
|
SLONG ID_i_can_find_the_room_perims(void)
|
|
{
|
|
SLONG i;
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
if (!ID_calc_room_perim(i))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Given the segment of wall on the perimeter of a room, this function returns
|
|
// the wall it is a part of, and two numbers (bwmul, bwadd) that you can use to map
|
|
// a part of the segment to the corresponding part of the wall.
|
|
//
|
|
// Wallsegment number = (blocksegment number) * bwmul + bwadd
|
|
//
|
|
// It returns number of blocks in the segment. If the segment is
|
|
// orthogonal, that is easy. It the segment is non-othogonal, it must
|
|
// be a whole outside wall. (Non-orthogonal outside walls are never
|
|
// split up.)
|
|
//
|
|
|
|
void ID_find_segment_wall(
|
|
SLONG x1,
|
|
SLONG z1,
|
|
SLONG x2,
|
|
SLONG z2,
|
|
SLONG *wall,
|
|
SLONG *bwmul,
|
|
SLONG *bwadd,
|
|
SLONG *length)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG wx1;
|
|
SLONG wz1;
|
|
|
|
SLONG wx2;
|
|
SLONG wz2;
|
|
|
|
ID_Wall *iw;
|
|
|
|
//
|
|
// Find the wall that this segment belongs to. If the wall
|
|
// is an outside wall, we fill in block_start and block_dir
|
|
// that let us work out which block of the wall corresponds
|
|
// to each block of the segment.
|
|
//
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// This is a non-orthogonal so it must be an outside wall.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
ASSERT(ID_wall[i].type == ID_WALL_T_OUTSIDE);
|
|
|
|
if ((iw->x1 == x1 && iw->z1 == z1 && iw->x2 == x2 && iw->z2 == z2) ||
|
|
(iw->x1 == x1 && iw->z1 == z1 && iw->x2 == x2 && iw->z2 == z2))
|
|
{
|
|
//
|
|
// The length of the segment is the length of the wall.
|
|
//
|
|
|
|
*wall = i;
|
|
*bwmul = 1;
|
|
*bwadd = 0;
|
|
*length = iw->num_blocks;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Failed to find a wall.
|
|
//
|
|
|
|
ASSERT(0);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
if (dx == 0 && iw->x1 == x1 && iw->x2 == x2)
|
|
{
|
|
wz1 = iw->z1;
|
|
wz2 = iw->z2;
|
|
|
|
if (wz1 > wz2) {SWAP(wz1, wz2);}
|
|
|
|
if (WITHIN(z1, wz1, wz2) &&
|
|
WITHIN(z2, wz1, wz2))
|
|
{
|
|
//
|
|
// This is the wall we want.
|
|
//
|
|
|
|
*bwmul = SIGN(dz) * SIGN(iw->z2 - iw->z1);
|
|
|
|
if (*bwmul == 1)
|
|
{
|
|
*bwadd = abs(z1 - iw->z1);
|
|
}
|
|
else
|
|
{
|
|
*bwadd = abs(z1 - iw->z1) - 1;
|
|
}
|
|
|
|
goto found_wall;
|
|
}
|
|
}
|
|
|
|
if (dz == 0 && iw->z1 == z1 && iw->z2 == z2)
|
|
{
|
|
wx1 = iw->x1;
|
|
wx2 = iw->x2;
|
|
|
|
if (wx1 > wx2) {SWAP(wx1, wx2);}
|
|
|
|
if (WITHIN(x1, wx1, wx2) &&
|
|
WITHIN(x2, wx1, wx2))
|
|
{
|
|
//
|
|
// This is the wall we want.
|
|
//
|
|
|
|
*bwmul = SIGN(dx) * SIGN(iw->x2 - iw->x1);
|
|
|
|
if (*bwmul == 1)
|
|
{
|
|
*bwadd = abs(x1 - iw->x1);
|
|
}
|
|
else
|
|
{
|
|
*bwadd = abs(x1 - iw->x1) - 1;
|
|
}
|
|
|
|
goto found_wall;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Failed to find a wall.
|
|
//
|
|
|
|
ASSERT(0);
|
|
|
|
found_wall:;
|
|
|
|
//
|
|
// The length of this segment.
|
|
//
|
|
|
|
*wall = i;
|
|
*length = MAX(abs(dx), abs(dz));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if YOU_WANT_THIN_WALLS
|
|
|
|
//
|
|
// Adds faces for the given wall.
|
|
//
|
|
|
|
void ID_add_wall_faces(SLONG wall)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG x1, y1, z1;
|
|
SLONG x2, y2, z2;
|
|
|
|
SLONG dmx;
|
|
SLONG dmz;
|
|
|
|
SLONG mx;
|
|
SLONG mz;
|
|
|
|
UWORD px[4];
|
|
UWORD py[4];
|
|
UWORD pz[4];
|
|
UWORD texture;
|
|
UBYTE quad;
|
|
|
|
ID_Wall *iw;
|
|
|
|
ASSERT(WITHIN(wall, 0, ID_wall_upto - 1));
|
|
|
|
iw = &ID_wall[wall];
|
|
|
|
//
|
|
// Each block in turn.
|
|
//
|
|
|
|
dx = iw->x2 - iw->x1 << 16;
|
|
dz = iw->z2 - iw->z1 << 16;
|
|
|
|
dx /= iw->num_blocks;
|
|
dz /= iw->num_blocks;
|
|
|
|
x = iw->x1 << 16;
|
|
z = iw->z1 << 16;
|
|
|
|
y1 = 0;
|
|
y2 = ID_CEILING_HEIGHT << 8; // Ceiling height is only 8-bit.
|
|
|
|
//
|
|
// A tiny vector pointing to the right of the wall.
|
|
//
|
|
|
|
dmx = SIGN(-dz);
|
|
dmz = SIGN( dx);
|
|
|
|
for (i = 0; i < iw->num_blocks; i++)
|
|
{
|
|
x1 = x;
|
|
z1 = z;
|
|
|
|
x2 = x + dx;
|
|
z2 = z + dz;
|
|
|
|
//
|
|
// Which mapsquare?
|
|
//
|
|
|
|
mx = x1 + x2 >> 1;
|
|
mz = z1 + z2 >> 1;
|
|
|
|
mx += dmx;
|
|
mz += dmz;
|
|
|
|
mx >>= 16;
|
|
mz >>= 16;
|
|
|
|
//
|
|
// Just add one quad per block for now.
|
|
//
|
|
|
|
px[0] = x1 >> 8;
|
|
py[0] = y2 >> 8;
|
|
pz[0] = z1 >> 8;
|
|
|
|
px[1] = x2 >> 8;
|
|
py[1] = y2 >> 8;
|
|
pz[1] = z2 >> 8;
|
|
|
|
px[2] = x1 >> 8;
|
|
py[2] = y1 >> 8;
|
|
pz[2] = z1 >> 8;
|
|
|
|
px[3] = x2 >> 8;
|
|
py[3] = y1 >> 8;
|
|
pz[3] = z2 >> 8;
|
|
|
|
texture = 15;
|
|
quad = TRUE;
|
|
|
|
ID_add_face_to_square(mx, mz, px, py, pz, texture, quad);
|
|
|
|
if (iw->type == ID_WALL_T_INSIDE)
|
|
{
|
|
//
|
|
// Inside walls are double-sided.
|
|
//
|
|
|
|
mx = x1 + x2 >> 1;
|
|
mz = z1 + z2 >> 1;
|
|
|
|
mx -= dmx;
|
|
mz -= dmz;
|
|
|
|
mx >>= 16;
|
|
mz >>= 16;
|
|
|
|
//
|
|
// Just add one quad per block for now.
|
|
//
|
|
|
|
px[0] = x2 >> 8;
|
|
py[0] = y2 >> 8;
|
|
pz[0] = z2 >> 8;
|
|
|
|
px[1] = x1 >> 8;
|
|
py[1] = y2 >> 8;
|
|
pz[1] = z1 >> 8;
|
|
|
|
px[2] = x2 >> 8;
|
|
py[2] = y1 >> 8;
|
|
pz[2] = z2 >> 8;
|
|
|
|
px[3] = x1 >> 8;
|
|
py[3] = y1 >> 8;
|
|
pz[3] = z1 >> 8;
|
|
|
|
texture = 14;
|
|
quad = TRUE;
|
|
|
|
ID_add_face_to_square(mx, mz, px, py, pz, texture, quad);
|
|
}
|
|
|
|
//
|
|
// Next block.
|
|
//
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
//
|
|
// Adds faces for the given room.
|
|
//
|
|
|
|
void ID_add_room_faces(SLONG room)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG x1;
|
|
SLONG z1;
|
|
SLONG x2;
|
|
SLONG z2;
|
|
|
|
SLONG px;
|
|
SLONG pz;
|
|
|
|
SLONG nx;
|
|
SLONG nz;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG vx;
|
|
SLONG vz;
|
|
|
|
SLONG mx;
|
|
SLONG mz;
|
|
|
|
SLONG bw;
|
|
SLONG wall;
|
|
SLONG bwmul;
|
|
SLONG bwadd;
|
|
SLONG length;
|
|
SLONG vlen;
|
|
SLONG result;
|
|
|
|
UWORD cpx[4];
|
|
UWORD cpz[4];
|
|
|
|
SLONG first_cpx;
|
|
SLONG first_cpz;
|
|
|
|
SLONG last_cpx;
|
|
SLONG last_cpz;
|
|
|
|
SLONG block_type;
|
|
SLONG wall_faces_type;
|
|
|
|
ID_Wall *iw;
|
|
|
|
//
|
|
// Work out the perimeter of the room.
|
|
//
|
|
|
|
result = ID_calc_room_perim(room);
|
|
|
|
//
|
|
// This has to work!
|
|
//
|
|
|
|
ASSERT(result);
|
|
|
|
//
|
|
// Go around the perimeter.
|
|
//
|
|
|
|
for (i = 0; i < ID_perim_upto - 1; i++)
|
|
{
|
|
//
|
|
// This line segment.
|
|
//
|
|
|
|
x1 = ID_perim[i + 0].x;
|
|
z1 = ID_perim[i + 0].z;
|
|
x2 = ID_perim[i + 1].x;
|
|
z2 = ID_perim[i + 1].z;
|
|
|
|
//
|
|
// The previous point.
|
|
//
|
|
|
|
if (i == 0)
|
|
{
|
|
px = ID_perim[ID_perim_upto - 2].x;
|
|
pz = ID_perim[ID_perim_upto - 2].z;
|
|
}
|
|
else
|
|
{
|
|
px = ID_perim[i - 1].x;
|
|
pz = ID_perim[i - 1].z;
|
|
}
|
|
|
|
//
|
|
// The next point.
|
|
//
|
|
|
|
if (i + 2 == ID_perim_upto)
|
|
{
|
|
nx = ID_perim[1].x;
|
|
nz = ID_perim[1].z;
|
|
}
|
|
else
|
|
{
|
|
nx = ID_perim[i + 2].x;
|
|
nz = ID_perim[i + 2].z;
|
|
}
|
|
|
|
ID_find_segment_wall(x1, z1, x2, z2,
|
|
&wall,
|
|
&bwmul,
|
|
&bwadd,
|
|
&length);
|
|
|
|
ASSERT(WITHIN(wall, 0, ID_wall_upto - 1));
|
|
|
|
iw = &ID_wall[wall];
|
|
|
|
//
|
|
// Find the position of the first and last controls points that
|
|
// are not along the wall. These will be at funny positions that
|
|
// depend on the angle this segment makes with the previous and
|
|
// the next segments.
|
|
//
|
|
|
|
//
|
|
// We can use one of Mike's functions here.
|
|
//
|
|
|
|
void calc_new_corner_point(
|
|
SLONG x1, SLONG z1,
|
|
SLONG x2, SLONG z2,
|
|
SLONG x3, SLONG z3,
|
|
SLONG width,
|
|
SLONG *res_x,
|
|
SLONG *res_z);
|
|
|
|
//
|
|
// Use fixed-point 8 from now on.
|
|
//
|
|
|
|
ASSERT(ELE_SHIFT == 8);
|
|
|
|
px <<= 8; pz <<= 8;
|
|
x1 <<= 8; z1 <<= 8;
|
|
x2 <<= 8; z2 <<= 8;
|
|
nx <<= 8; nz <<= 8;
|
|
|
|
calc_new_corner_point(x2, z2, x1, z1, px, pz, ID_WALL_WIDTH, &first_cpx, &first_cpz);
|
|
calc_new_corner_point(nx, nz, x2, z2, x1, z1, ID_WALL_WIDTH, &last_cpx, &last_cpz);
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
dx /= length;
|
|
dz /= length;
|
|
|
|
vx = -dz * ID_WALL_WIDTH >> 8;
|
|
vz = dx * ID_WALL_WIDTH >> 8;
|
|
|
|
//
|
|
// Each block in turn.
|
|
//
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
for (j = 0; j < length; j++)
|
|
{
|
|
//
|
|
// Which block along the wall does this segment lie?
|
|
//
|
|
|
|
bw = j * bwmul + bwadd;
|
|
|
|
//
|
|
// What is the type of this block?
|
|
//
|
|
|
|
if (iw->type == ID_WALL_T_OUTSIDE)
|
|
{
|
|
//
|
|
// Use the call-back function.
|
|
//
|
|
|
|
block_type = ID_get_type(iw->id, bw);
|
|
|
|
switch(block_type)
|
|
{
|
|
case ID_BLOCK_TYPE_WALL: wall_faces_type = ID_WALL_FACES_NORMAL; break;
|
|
case ID_BLOCK_TYPE_WINDOW: wall_faces_type = ID_WALL_FACES_OUT_WINDOW; break;
|
|
case ID_BLOCK_TYPE_DOOR: wall_faces_type = ID_WALL_FACES_OUT_FRAME; break;
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Just use the normal...
|
|
//
|
|
|
|
wall_faces_type = ID_WALL_FACES_NORMAL;
|
|
|
|
if (iw->door[0] == bw ||
|
|
iw->door[1] == bw ||
|
|
iw->door[2] == bw)
|
|
{
|
|
wall_faces_type = ID_WALL_FACES_FRAME;
|
|
}
|
|
}
|
|
|
|
cpx[0] = x;
|
|
cpz[0] = z;
|
|
|
|
cpx[1] = x + dx;
|
|
cpz[1] = z + dz;
|
|
|
|
cpx[2] = x + vx;
|
|
cpz[2] = z + vz;
|
|
|
|
cpx[3] = x + dx + vx;
|
|
cpz[3] = z + dz + vz;
|
|
|
|
//
|
|
// The special cases at the beginning and end of the segment.
|
|
//
|
|
|
|
if (j == 0)
|
|
{
|
|
cpx[2] = first_cpx;
|
|
cpz[2] = first_cpz;
|
|
}
|
|
|
|
if (j == length - 1)
|
|
{
|
|
cpx[3] = last_cpx;
|
|
cpz[3] = last_cpz;
|
|
}
|
|
|
|
//
|
|
// Add the faces around the control points to the mapsquare
|
|
// inbetween control points 2 and 3.
|
|
//
|
|
|
|
mx = cpx[2] + cpx[3] >> 1;
|
|
mz = cpz[2] + cpz[3] >> 1;
|
|
|
|
mx >>= 8;
|
|
mz >>= 8;
|
|
|
|
ASSERT(ID_FLOOR(mx,mz)->room == room);
|
|
|
|
|
|
if (ID_storey_type == ID_STOREY_TYPE_WAREHOUSE &&
|
|
iw->type == ID_WALL_T_OUTSIDE)
|
|
{
|
|
ID_create_mapsquare_faces(mx, mz, cpx, cpz, ID_room[room].type, wall_faces_type, FALSE);
|
|
ID_create_mapsquare_faces(mx, mz, cpx, cpz, ID_room[room].type, ID_WALL_FACES_UPPER, TRUE);
|
|
}
|
|
else
|
|
{
|
|
ID_create_mapsquare_faces(mx, mz, cpx, cpz, ID_room[room].type, wall_faces_type, TRUE);
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// Clears all the inside wall info.
|
|
//
|
|
|
|
void ID_clear_inside_walls()
|
|
{
|
|
SLONG i;
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
ID_Square *is;
|
|
|
|
for (x = ID_floor_x1; x <= ID_floor_x2; x++)
|
|
for (z = ID_floor_z1; z <= ID_floor_z2; z++)
|
|
{
|
|
is = ID_FLOOR(x, z);
|
|
|
|
is->flag &= ~(
|
|
ID_FLOOR_FLAG_ON_WALL |
|
|
ID_FLOOR_FLAG_DOORMAT |
|
|
ID_FLOOR_FLAG_INMAT |
|
|
ID_FLOOR_FLAG_FURNISHED |
|
|
ID_FLOOR_FLAG_WALL_XS |
|
|
ID_FLOOR_FLAG_WALL_XL |
|
|
ID_FLOOR_FLAG_WALL_ZS |
|
|
ID_FLOOR_FLAG_WALL_ZL);
|
|
|
|
is->room = 0;
|
|
}
|
|
|
|
ASSERT(WITHIN(ID_wall_upto, 0, ID_MAX_WALLS));
|
|
|
|
while(ID_wall_upto > 0 && ID_wall[ID_wall_upto - 1].type == ID_WALL_T_INSIDE)
|
|
{
|
|
ID_wall_upto -= 1;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Picks a random place to start a wall. It prefers
|
|
// corners. Returns TRUE on sucess or FALSE if it
|
|
// couldn't find a suitable place.
|
|
//
|
|
|
|
SLONG ID_get_wall_start(SLONG *x, SLONG *z)
|
|
{
|
|
SLONG wall;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG length;
|
|
SLONG along;
|
|
SLONG tries;
|
|
|
|
//
|
|
// How many tries do we have to find somewhere suitable?
|
|
//
|
|
|
|
#define ID_WALL_NUM_TRIES 3
|
|
|
|
tries = ID_WALL_NUM_TRIES;
|
|
|
|
while(tries--)
|
|
{
|
|
if ((ID_rand() & 0xff) < 128)
|
|
{
|
|
//
|
|
// Choose the end of a wall.
|
|
//
|
|
|
|
wall = ID_rand() % ID_wall_upto;
|
|
|
|
if (ID_rand() & 0x100)
|
|
{
|
|
*x = ID_wall[wall].x1;
|
|
*z = ID_wall[wall].z1;
|
|
}
|
|
else
|
|
{
|
|
*x = ID_wall[wall].x2;
|
|
*z = ID_wall[wall].z2;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Choose somewhere along the wall.
|
|
//
|
|
|
|
wall = ID_rand() % ID_wall_upto;
|
|
|
|
dx = ID_wall[wall].x2 - ID_wall[wall].x1;
|
|
dz = ID_wall[wall].z2 - ID_wall[wall].z1;
|
|
|
|
if (dx && dz && abs(dx) != abs(dz))
|
|
{
|
|
//
|
|
// Don't start new walls along slanty ones, except
|
|
// diagonals.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
length = MAX(abs(dx), abs(dz));
|
|
along = ID_rand() % length;
|
|
|
|
dx = SIGN(dx);
|
|
dz = SIGN(dz);
|
|
|
|
*x = ID_wall[wall].x1 + along * dx;
|
|
*z = ID_wall[wall].z1 + along * dz;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Puts in the collision bits for the given wall.
|
|
//
|
|
|
|
void ID_generate_collision_bits(SLONG wall)
|
|
{
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG x1, z1;
|
|
SLONG x2, z2;
|
|
|
|
ID_Wall *iw;
|
|
|
|
ASSERT(WITHIN(wall, 0, ID_wall_upto - 1));
|
|
|
|
iw = &ID_wall[wall];
|
|
|
|
x1 = iw->x1;
|
|
z1 = iw->z1;
|
|
|
|
x2 = iw->x2;
|
|
z2 = iw->z2;
|
|
|
|
if (x1 > x2) {SWAP(x1, x2);}
|
|
if (z1 > z2) {SWAP(z1, z2);}
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
dx = SIGN(dx);
|
|
dz = SIGN(dz);
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// Don't insert ollision bits for non-orthogonal walls.
|
|
//
|
|
|
|
TRACE("Cannot insert collision bits for non-orthogonal walls.\n");
|
|
|
|
return;
|
|
}
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
#define ID_IS_VALID(x,z) (WITHIN((x), ID_floor_x1, ID_floor_x2 - 1) && WITHIN((z), ID_floor_z1, ID_floor_z2 - 1))
|
|
|
|
while(!(x == x2 && z == z2))
|
|
{
|
|
if (dx == 0 && dz == 1)
|
|
{
|
|
if (ID_IS_VALID(x, z))
|
|
{
|
|
ID_FLOOR(x, z)->flag |= ID_FLOOR_FLAG_WALL_XS;
|
|
}
|
|
|
|
if (ID_IS_VALID(x - 1, z))
|
|
{
|
|
ID_FLOOR(x - 1, z)->flag |= ID_FLOOR_FLAG_WALL_XL;
|
|
}
|
|
}
|
|
else
|
|
if (dx == 1 && dz == 0)
|
|
{
|
|
if (ID_IS_VALID(x, z))
|
|
{
|
|
ID_FLOOR(x, z)->flag |= ID_FLOOR_FLAG_WALL_ZS;
|
|
}
|
|
|
|
if (ID_IS_VALID(x, z - 1))
|
|
{
|
|
ID_FLOOR(x, z - 1)->flag |= ID_FLOOR_FLAG_WALL_ZL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sets the ON_WALL bits for the given wall.
|
|
//
|
|
|
|
void ID_generate_on_wall_bits(SLONG wall)
|
|
{
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG dxdz;
|
|
|
|
SLONG mx;
|
|
SLONG mz;
|
|
|
|
SLONG x1, z1;
|
|
SLONG x2, z2;
|
|
|
|
ID_Wall *iw;
|
|
|
|
ASSERT(WITHIN(wall, 0, ID_wall_upto - 1));
|
|
|
|
iw = &ID_wall[wall];
|
|
|
|
x1 = iw->x1;
|
|
z1 = iw->z1;
|
|
|
|
x2 = iw->x2;
|
|
z2 = iw->z2;
|
|
|
|
if (z1 == z2)
|
|
{
|
|
//
|
|
// Easy!
|
|
//
|
|
|
|
if (x1 > x2) {SWAP(x1, x2);}
|
|
|
|
for (x = x1; x <= x2; x++)
|
|
{
|
|
ID_FLOOR(x, z1)->flag |= ID_FLOOR_FLAG_ON_WALL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (x1 == x2)
|
|
{
|
|
//
|
|
// Easy!
|
|
//
|
|
|
|
if (z1 > z2) {SWAP(z1, z2);}
|
|
|
|
for (z = z1; z <= z2; z++)
|
|
{
|
|
ID_FLOOR(x1, z)->flag |= ID_FLOOR_FLAG_ON_WALL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Slightly tricky.
|
|
//
|
|
|
|
if (z1 > z2)
|
|
{
|
|
SWAP(x1, x2);
|
|
SWAP(z1, z2);
|
|
}
|
|
|
|
dx = x2 - x1 << 16;
|
|
dz = z2 - z1 << 16;
|
|
|
|
dxdz = DIV64(dx, dz);
|
|
|
|
x = x1 << 16;
|
|
|
|
for (z = z1; z <= z2; z++)
|
|
{
|
|
//
|
|
// is (x, z) on a dot? Or near enough on a dot?
|
|
//
|
|
|
|
if (((x + 0x7f >> 8) & 0xff) == 0)
|
|
{
|
|
//
|
|
// (x, z) is within 1/256'th of a mapsquare dot.
|
|
//
|
|
|
|
mx = x + 0x7f;
|
|
mx >>= 16;
|
|
mz = z;
|
|
|
|
ID_FLOOR(mx, mz)->flag |= ID_FLOOR_FLAG_ON_WALL;
|
|
}
|
|
|
|
x += dxdz;
|
|
}
|
|
}
|
|
|
|
void ID_find_a_camera_for_each_room(void)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG n;
|
|
SLONG choice;
|
|
SLONG off_x;
|
|
SLONG off_z;
|
|
|
|
SLONG threes;
|
|
SLONG ones;
|
|
|
|
#define MAX_POSS 64
|
|
|
|
struct
|
|
{
|
|
UWORD n;
|
|
UWORD x;
|
|
UWORD z;
|
|
|
|
} poss[MAX_POSS];
|
|
|
|
SLONG poss_upto;
|
|
|
|
//
|
|
// Count how many 'three' corners and how many
|
|
// 'one' corners you have. 'Three' corners are
|
|
// better than one corners.
|
|
//
|
|
|
|
#define IN_ROOM(i, x, z) (WITHIN((x), ID_floor_x1, ID_floor_x2 - 1) && WITHIN((z), ID_floor_z1, ID_floor_z2 - 1) && ID_FLOOR((x), (z))->room == (i))
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
threes = 0;
|
|
ones = 0;
|
|
poss_upto = 0;
|
|
|
|
for (x = ID_floor_x1; x <= ID_floor_x2; x++)
|
|
for (z = ID_floor_z1; z <= ID_floor_z2; z++)
|
|
{
|
|
//
|
|
// Count how many squares touching point (x,z)
|
|
// are in the given room.
|
|
//
|
|
|
|
n = 0;
|
|
|
|
off_x = 0;
|
|
off_z = 0;
|
|
|
|
if (IN_ROOM(i, x - 0, z - 0)) {n++; off_x += 1; off_z += 1;}
|
|
if (IN_ROOM(i, x - 1, z - 0)) {n++; off_x -= 1; off_z += 1;}
|
|
if (IN_ROOM(i, x - 0, z - 1)) {n++; off_x += 1; off_z -= 1;}
|
|
if (IN_ROOM(i, x - 1, z - 1)) {n++; off_x -= 1; off_z -= 1;}
|
|
|
|
off_x <<= ELE_SHIFT - 2;
|
|
off_z <<= ELE_SHIFT - 2;
|
|
|
|
if (n == 1)
|
|
{
|
|
ASSERT(WITHIN(poss_upto, 0, MAX_POSS - 1));
|
|
|
|
//
|
|
// This is a 'one' corner of the room.
|
|
//
|
|
|
|
poss[poss_upto].n = 1;
|
|
poss[poss_upto].x = (x << ELE_SHIFT) + off_x;
|
|
poss[poss_upto].z = (z << ELE_SHIFT) + off_z;
|
|
|
|
ones += 1;
|
|
poss_upto += 1;
|
|
}
|
|
|
|
if (n == 3)
|
|
{
|
|
ASSERT(WITHIN(poss_upto, 1, MAX_POSS - 1));
|
|
|
|
//
|
|
// This is a 'three' corner of the room.
|
|
//
|
|
|
|
poss[poss_upto].n = 3;
|
|
poss[poss_upto].x = (x << ELE_SHIFT) + off_x;
|
|
poss[poss_upto].z = (z << ELE_SHIFT) + off_z;
|
|
|
|
threes += 1;
|
|
poss_upto += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is a 'three' corner then choose one of them.
|
|
//
|
|
|
|
if (threes > 0)
|
|
{
|
|
choice = ID_rand() % threes;
|
|
|
|
//
|
|
// Choose the j'th three corner.
|
|
//
|
|
|
|
for (j = 0; j < poss_upto; j++)
|
|
{
|
|
if (poss[j].n == 3)
|
|
{
|
|
if (choice == 0)
|
|
{
|
|
ID_room[i].cam_x = poss[j].x;
|
|
ID_room[i].cam_z = poss[j].z;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
choice -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Pick a random corner.
|
|
//
|
|
|
|
choice = ID_rand() % ones;
|
|
|
|
ID_room[i].cam_x = poss[choice].x;
|
|
ID_room[i].cam_z = poss[choice].z;
|
|
}
|
|
}
|
|
}
|
|
|
|
SLONG ID_intersects_badly(SLONG x1, SLONG z1, SLONG x2, SLONG z2)
|
|
{
|
|
SLONG i;
|
|
SLONG ix;
|
|
SLONG iz;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
ID_Wall *iw;
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
if (iw->type != ID_WALL_T_OUTSIDE)
|
|
{
|
|
//
|
|
// Only outside walls are non-orthogonal, so these
|
|
// are the only ones we have to check.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
dx = iw->x2 - iw->x1;
|
|
dz = iw->z2 - iw->z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// The lines are allowed to share end-points, even though
|
|
// the lines_intersect function counts that as an
|
|
// intersection.
|
|
//
|
|
|
|
if ((x1 == iw->x1 && z1 == iw->z1) ||
|
|
(x1 == iw->x2 && z1 == iw->z2) ||
|
|
(x2 == iw->x1 && z2 == iw->z1) ||
|
|
(x2 == iw->x2 && z2 == iw->z2))
|
|
{
|
|
//
|
|
// Okay to share endpoints.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
if (lines_intersect(
|
|
x1, z1, x2, z2,
|
|
iw->x1,
|
|
iw->z1,
|
|
iw->x2,
|
|
iw->z2,
|
|
&ix,
|
|
&iz) == DO_INTERSECT)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This line does not intersect badly with any outside wall.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Calculate which squares belong in which rooms.
|
|
//
|
|
|
|
void ID_find_rooms(void)
|
|
{
|
|
SLONG i;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG cx;
|
|
SLONG cz;
|
|
SLONG nx;
|
|
SLONG nz;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
ID_Square *is;
|
|
|
|
//
|
|
// Clear old rooms.
|
|
//
|
|
|
|
ID_room_upto = 1;
|
|
|
|
//
|
|
// Make sure we can use UBYTEs
|
|
//
|
|
|
|
ASSERT(ID_FLOOR_SIZE <= 256);
|
|
|
|
//
|
|
// A queue for filling up rooms.
|
|
//
|
|
|
|
#define QUEUE_SIZE 64
|
|
|
|
typedef struct
|
|
{
|
|
UBYTE x;
|
|
UBYTE z;
|
|
|
|
} Queue;
|
|
|
|
Queue queue[QUEUE_SIZE];
|
|
SLONG queue_head;
|
|
SLONG queue_tail;
|
|
|
|
#define QUEUE_EMPTY() (queue_head == queue_tail)
|
|
#define QUEUE_NEXT(q) (((q) + 1) & (QUEUE_SIZE - 1))
|
|
#define QUEUE_FULL() (QUEUE_NEXT(queue_head) == queue_tail)
|
|
#define QUEUE_ADD(a,b) {queue[queue_head].x = (a); queue[queue_head].z = (b); queue_head = QUEUE_NEXT(queue_head);}
|
|
#define QUEUE_TAIL() (queue[queue_tail])
|
|
#define QUEUE_REMOVE() {queue_tail = QUEUE_NEXT(queue_tail);}
|
|
|
|
struct
|
|
{
|
|
SBYTE dx;
|
|
SBYTE dz;
|
|
UBYTE flag;
|
|
UBYTE shit;
|
|
|
|
} udlr[4] =
|
|
{
|
|
{+1, 0, ID_FLOOR_FLAG_WALL_XL, 0},
|
|
{-1, 0, ID_FLOOR_FLAG_WALL_XS, 0},
|
|
{0, +1, ID_FLOOR_FLAG_WALL_ZL, 0},
|
|
{0, -1, ID_FLOOR_FLAG_WALL_ZS, 0}
|
|
};
|
|
|
|
for (x = ID_floor_x1; x < ID_floor_x2; x++)
|
|
for (z = ID_floor_z1; z < ID_floor_z2; z++)
|
|
{
|
|
is = ID_FLOOR(x,z);
|
|
|
|
if (!(is->flag & ID_FLOOR_FLAG_INSIDE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (is->room == 0)
|
|
{
|
|
//
|
|
// Make sure we haven't run out of rooms.
|
|
//
|
|
|
|
ASSERT(WITHIN(ID_room_upto, 1, ID_MAX_ROOMS - 1));
|
|
|
|
//
|
|
// This block has been unassigned.
|
|
//
|
|
|
|
queue_head = 0;
|
|
queue_tail = 0;
|
|
|
|
QUEUE_ADD(x, z);
|
|
|
|
while(!QUEUE_EMPTY())
|
|
{
|
|
cx = QUEUE_TAIL().x;
|
|
cz = QUEUE_TAIL().z;
|
|
|
|
QUEUE_REMOVE();
|
|
|
|
if (ID_FLOOR(cx, cz)->room == 0)
|
|
{
|
|
ID_FLOOR(cx, cz)->room = ID_room_upto;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (ID_FLOOR(cx,cz)->flag & udlr[i].flag)
|
|
{
|
|
//
|
|
// Can't fill in this direction... there
|
|
// is a wall in the way.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
if (QUEUE_FULL())
|
|
{
|
|
TRACE("Queue full!\n");
|
|
}
|
|
else
|
|
{
|
|
dx = udlr[i].dx;
|
|
dz = udlr[i].dz;
|
|
|
|
nx = cx + dx;
|
|
nz = cz + dz;
|
|
|
|
if (!WITHIN(nx, ID_floor_x1, ID_floor_x2 - 1) ||
|
|
!WITHIN(nz, ID_floor_z1, ID_floor_z2 - 1) ||
|
|
!(ID_FLOOR(nx, nz)->flag & ID_FLOOR_FLAG_INSIDE))
|
|
{
|
|
//
|
|
// Invalid square.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
QUEUE_ADD(nx,nz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finished with this room index.
|
|
//
|
|
|
|
ID_room_upto += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Generates room info. Works out the size of each room, whether it is
|
|
// a corridor or not, a lobby, this sort of thing.
|
|
//
|
|
|
|
void ID_generate_room_info(void)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG width;
|
|
SLONG height;
|
|
SLONG room;
|
|
|
|
ID_Room *ir;
|
|
ID_Square *is;
|
|
ID_Wall *iw;
|
|
ID_Stair *it;
|
|
|
|
//
|
|
// Initialise each room.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ID_room[i].type = ID_ROOM_TYPE_NONE;
|
|
ID_room[i].num_doors = 0;
|
|
|
|
ID_room[i].x1 = 255;
|
|
ID_room[i].z1 = 255;
|
|
ID_room[i].x2 = 0;
|
|
ID_room[i].z2 = 0;
|
|
|
|
ID_room[i].flag = 0;
|
|
}
|
|
|
|
//
|
|
// Work out the bounding box of each room.
|
|
//
|
|
|
|
for (x = ID_floor_x1; x < ID_floor_x2; x++)
|
|
for (z = ID_floor_z1; z < ID_floor_z2; z++)
|
|
{
|
|
is = ID_FLOOR(x,z);
|
|
|
|
if (is->room)
|
|
{
|
|
ASSERT(WITHIN(is->room, 1, ID_room_upto - 1));
|
|
|
|
if (x < ID_room[is->room].x1) {ID_room[is->room].x1 = x;}
|
|
if (z < ID_room[is->room].z1) {ID_room[is->room].z1 = z;}
|
|
if (x > ID_room[is->room].x2) {ID_room[is->room].x2 = x;}
|
|
if (z > ID_room[is->room].z2) {ID_room[is->room].z2 = z;}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Work out which rooms are rectangular of not.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
for (x = ID_room[i].x1; x <= ID_room[i].x2; x++)
|
|
for (z = ID_room[i].z1; z <= ID_room[i].z2; z++)
|
|
{
|
|
if (ID_FLOOR(x,z)->room != i)
|
|
{
|
|
goto next_room;
|
|
}
|
|
}
|
|
|
|
ID_room[i].flag |= ID_ROOM_FLAG_RECTANGULAR;
|
|
|
|
next_room:;
|
|
}
|
|
|
|
//
|
|
// Work out which rooms count as corridors.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
if (ir->flag & ID_ROOM_FLAG_RECTANGULAR)
|
|
{
|
|
width = ir->x2 - ir->x1 + 1;
|
|
height = ir->z2 - ir->z1 + 1;
|
|
|
|
if (((width == 1 || width == 2) && height >= ID_CORRIDOR_LENGTH * width ) ||
|
|
((height == 1 || height == 2) && width >= ID_CORRIDOR_LENGTH * height))
|
|
{
|
|
ir->flag |= ID_ROOM_FLAG_CORRIDOR;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Work out which rooms are lobbies. Go through all the outside
|
|
// walls looking for a door. When we find one, work out which
|
|
// square it leads into.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
if (iw->type != ID_WALL_T_OUTSIDE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
for (j = 0; j < iw->num_blocks; j++)
|
|
{
|
|
if (ID_get_type(iw->id, j) == ID_BLOCK_TYPE_DOOR)
|
|
{
|
|
//
|
|
// This is a door in an outside wall. Find the point (x,z)
|
|
// just inside the door.
|
|
//
|
|
|
|
dx = (iw->x2 - iw->x1 << 8) / iw->num_blocks;
|
|
dz = (iw->z2 - iw->z1 << 8) / iw->num_blocks;
|
|
|
|
x = iw->x1 << 8;
|
|
z = iw->z1 << 8;
|
|
|
|
x += dx * j;
|
|
z += dz * j;
|
|
|
|
x += dx >> 1;
|
|
z += dz >> 1;
|
|
|
|
x += (-dz) >> 4;
|
|
z += ( dx) >> 4;
|
|
|
|
x >>= 8;
|
|
z >>= 8;
|
|
|
|
is = ID_FLOOR(x,z);
|
|
|
|
//
|
|
// Mark this square as being a doormat.
|
|
//
|
|
|
|
is->flag |= ID_FLOOR_FLAG_DOORMAT;
|
|
|
|
//
|
|
// The room that this square is in is a lobby.
|
|
//
|
|
|
|
room = ID_FLOOR(x,z)->room;
|
|
|
|
ASSERT(WITHIN(room, 1, ID_room_upto - 1));
|
|
|
|
ID_room[room].flag |= ID_ROOM_FLAG_LOBBY;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Works out which rooms contain stairs.
|
|
//
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
it = &ID_stair[i];
|
|
|
|
room = ID_FLOOR(it->x1, it->z1)->room;
|
|
|
|
ASSERT(WITHIN(room, 1, ID_room_upto - 1));
|
|
|
|
ID_room[room].flag |= ID_ROOM_FLAG_STAIRWAY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Splits up the rooms into flats. Each flat is a set of rooms
|
|
// connected to each other without having to go into a ID_ROOM_TYPE_CORRIDOR.
|
|
//
|
|
|
|
void ID_find_flats(void)
|
|
{
|
|
SLONG i;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG nx;
|
|
SLONG nz;
|
|
SLONG neighbour;
|
|
SLONG changed;
|
|
|
|
ID_Room *ir;
|
|
|
|
//
|
|
// Mark all rooms as not being in a flat.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
ir->flat = 0;
|
|
}
|
|
|
|
//
|
|
// Assign the flats.
|
|
//
|
|
|
|
ID_flat_upto = 1;
|
|
|
|
while(1)
|
|
{
|
|
//
|
|
// Have all rooms apart from corridors been allocated a flat number?
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
if (ir->type != ID_ROOM_TYPE_CORRIDOR && ir->flat == 0)
|
|
{
|
|
ir->flat = ID_flat_upto++;
|
|
|
|
goto more_rooms_to_do;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
more_rooms_to_do:;
|
|
|
|
while(1)
|
|
{
|
|
changed = FALSE;
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
if (ir->flat)
|
|
{
|
|
//
|
|
// Search for connected rooms that aren't corridors and
|
|
// make them part of this flat.
|
|
//
|
|
|
|
for (x = ir->x1; x <= ir->x2; x++)
|
|
for (z = ir->z1; z <= ir->z2; z++)
|
|
{
|
|
if (ID_FLOOR(x,z)->room == i)
|
|
{
|
|
for (dx = -1; dx <= +1; dx += 2)
|
|
for (dz = -1; dz <= +1; dz += 2)
|
|
{
|
|
nx = x + dx;
|
|
nz = z + dz;
|
|
|
|
if (WITHIN(nx, ID_floor_x1, ID_floor_x2) &&
|
|
WITHIN(nz, ID_floor_z1, ID_floor_z2))
|
|
{
|
|
neighbour = ID_FLOOR(nx,nz)->room;
|
|
|
|
if (neighbour)
|
|
{
|
|
ASSERT(WITHIN(neighbour, 1, ID_room_upto - 1));
|
|
|
|
if (neighbour != i &&
|
|
ID_room[neighbour].type != ID_ROOM_TYPE_CORRIDOR &&
|
|
ID_room[neighbour].flat != ir->flat)
|
|
{
|
|
//
|
|
// Mark this room as also being in our flat.
|
|
//
|
|
|
|
changed = TRUE;
|
|
|
|
ID_room[neighbour].flat = ir->flat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!changed)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Assigns the type of each room in the given flat.
|
|
//
|
|
|
|
void ID_assign_flat_room_types(SLONG flat)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG area;
|
|
SLONG changed;
|
|
|
|
SLONG biggest;
|
|
SLONG smallest;
|
|
SLONG middle;
|
|
|
|
SLONG num_rooms;
|
|
SLONG have_loo;
|
|
|
|
UBYTE type;
|
|
|
|
UBYTE size [ID_MAX_ROOMS];
|
|
UBYTE order[ID_MAX_ROOMS];
|
|
|
|
ID_Room *ir;
|
|
|
|
//
|
|
// Find all the rooms in this flat.
|
|
//
|
|
|
|
num_rooms = 0;
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
if (ir->flat == flat)
|
|
{
|
|
ASSERT(WITHIN(num_rooms, 0, ID_MAX_ROOMS - 1));
|
|
|
|
dx = ir->x2 - ir->x1;
|
|
dz = ir->z2 - ir->z1;
|
|
|
|
area = dx * dz;
|
|
|
|
if (area > 255) {area = 255;}
|
|
|
|
order[num_rooms] = i;
|
|
size [num_rooms] = area;
|
|
|
|
num_rooms += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sort the rooms in order of their size... smallest first.
|
|
//
|
|
|
|
do
|
|
{
|
|
changed = FALSE;
|
|
|
|
for (i = 0; i < num_rooms - 1; i++)
|
|
{
|
|
if (size[i] > size[i + 1])
|
|
{
|
|
SWAP(size [i], size [i + 1]);
|
|
SWAP(order[i], order[i + 1]);
|
|
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
while(changed);
|
|
|
|
#ifndef _NDEBUG
|
|
|
|
//
|
|
// Are all the ordered rooms ok?
|
|
//
|
|
|
|
for (i = 0; i < num_rooms; i++)
|
|
{
|
|
ASSERT(WITHIN(order[i], 1, ID_room_upto - 1));
|
|
ASSERT(ID_room[order[i]].flat == flat);
|
|
}
|
|
|
|
#endif
|
|
|
|
biggest = order[num_rooms - 1];
|
|
smallest = order[0];
|
|
|
|
|
|
//
|
|
// Make the biggest room a lounge.
|
|
//
|
|
|
|
switch(num_rooms)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
ID_room[smallest].type = ID_ROOM_TYPE_LOO;
|
|
ID_room[biggest ].type = ID_ROOM_TYPE_LOUNGE;
|
|
break;
|
|
|
|
case 3:
|
|
ID_room[smallest].type = ID_ROOM_TYPE_LOO;
|
|
ID_room[order[1]].type = ID_ROOM_TYPE_BEDROOM;
|
|
ID_room[biggest ].type = ID_ROOM_TYPE_LOUNGE;
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(num_rooms > 3);
|
|
|
|
ID_room[smallest].type = ID_ROOM_TYPE_LOO;
|
|
ID_room[order[1]].type = ID_ROOM_TYPE_KITCHEN;
|
|
ID_room[order[2]].type = ID_ROOM_TYPE_BEDROOM;
|
|
|
|
for (i = 3; i < num_rooms; i++)
|
|
{
|
|
ID_room[order[i]].type = ID_ROOM_TYPE_LOUNGE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Decides which room is which. It assumes the rooms are
|
|
// in a house.
|
|
//
|
|
|
|
void ID_assign_room_types(SLONG storey_type)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG corridor;
|
|
|
|
SLONG area;
|
|
SLONG changed;
|
|
|
|
SLONG biggest;
|
|
SLONG smallest;
|
|
SLONG middle;
|
|
|
|
SLONG num_rooms;
|
|
SLONG have_loo;
|
|
|
|
UBYTE type;
|
|
|
|
UBYTE size [ID_MAX_ROOMS];
|
|
UBYTE order[ID_MAX_ROOMS];
|
|
|
|
ID_Room *ir;
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
ir->type = ID_ROOM_TYPE_NONE;
|
|
|
|
dx = ir->x2 - ir->x1;
|
|
dz = ir->z2 - ir->z1;
|
|
|
|
area = dx * dz;
|
|
|
|
if (area > 255) {area = 255;}
|
|
|
|
order[i] = i;
|
|
size [i] = area;
|
|
}
|
|
|
|
//
|
|
// Sort the rooms in order of their size... smallest first.
|
|
//
|
|
|
|
do
|
|
{
|
|
changed = FALSE;
|
|
|
|
for (i = 1; i < ID_room_upto - 1; i++)
|
|
{
|
|
if (size[i] > size[i + 1])
|
|
{
|
|
SWAP(size [i], size [i + 1]);
|
|
SWAP(order[i], order[i + 1]);
|
|
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
while(changed);
|
|
|
|
//
|
|
// Allocate the rooms.
|
|
//
|
|
|
|
biggest = order[ID_room_upto - 1];
|
|
smallest = order[1];
|
|
num_rooms = ID_room_upto - 1;
|
|
|
|
switch(storey_type)
|
|
{
|
|
case ID_STOREY_TYPE_HOUSE_GROUND:
|
|
case ID_STOREY_TYPE_HOUSE_UPPER:
|
|
case ID_STOREY_TYPE_OFFICE_GROUND:
|
|
case ID_STOREY_TYPE_OFFICE_UPPER:
|
|
|
|
//
|
|
// Make the biggest room a lounge.
|
|
//
|
|
|
|
ASSERT(WITHIN(biggest, 1, ID_room_upto - 1));
|
|
|
|
ID_room[biggest].type = ID_ROOM_TYPE_LOUNGE;
|
|
|
|
num_rooms -= 1;
|
|
|
|
//
|
|
// Look for any room other than the biggeset connected
|
|
// to the outside. Make that room a lobby.
|
|
//
|
|
|
|
for (i = 1; i <= num_rooms; i++)
|
|
{
|
|
ASSERT(WITHIN(order[i], 1, ID_room_upto - 1));
|
|
|
|
if (ID_room[order[i]].flag & ID_ROOM_FLAG_LOBBY)
|
|
{
|
|
ID_room[order[i]].type = ID_ROOM_TYPE_LOBBY;
|
|
|
|
SWAP(size [i], size [num_rooms]);
|
|
SWAP(order[i], order[num_rooms]);
|
|
|
|
num_rooms -= 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Is the smallest room a good size for a loo?
|
|
//
|
|
|
|
#define ID_LOO_AREA 4
|
|
|
|
have_loo = (size[1] <= ID_LOO_AREA);
|
|
|
|
if (num_rooms > 2)
|
|
{
|
|
//
|
|
// Lots of rooms, might as well have a loo.
|
|
//
|
|
|
|
have_loo = TRUE;
|
|
}
|
|
|
|
if (have_loo)
|
|
{
|
|
//
|
|
// Fill in the rooms in the given order.
|
|
//
|
|
|
|
for (i = 1; i <= num_rooms; i++)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 1: type = ID_ROOM_TYPE_LOO; break;
|
|
case 2: type = ID_ROOM_TYPE_KITCHEN; break;
|
|
case 3: type = ID_ROOM_TYPE_DINING; break;
|
|
default: type = ID_ROOM_TYPE_LOUNGE; break;
|
|
}
|
|
|
|
ASSERT(WITHIN(order[i], 1, ID_room_upto - 1));
|
|
|
|
ID_room[order[i]].type = type;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 1; i <= num_rooms; i++)
|
|
{
|
|
ASSERT(WITHIN(order[i], 1, ID_room_upto - 1));
|
|
|
|
if (i == 1)
|
|
{
|
|
ID_room[order[i]].type = ID_ROOM_TYPE_KITCHEN;
|
|
}
|
|
else
|
|
{
|
|
ID_room[order[i]].type = ID_ROOM_TYPE_DINING;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_STOREY_TYPE_WAREHOUSE:
|
|
|
|
//
|
|
// Make the biggest room a warehouse.
|
|
//
|
|
|
|
ASSERT(WITHIN(biggest, 1, ID_room_upto - 1));
|
|
|
|
ID_room[biggest].type = ID_ROOM_TYPE_WAREHOUSE;
|
|
|
|
num_rooms -= 1;
|
|
|
|
//
|
|
// A room that leads onto the outside is a lobby.
|
|
//
|
|
|
|
for (i = 1; i <= num_rooms; i++)
|
|
{
|
|
ASSERT(WITHIN(order[i], 1, ID_room_upto - 1));
|
|
|
|
ir = &ID_room[order[i]];
|
|
|
|
for (x = ir->x1; x <= ir->x2; x++)
|
|
for (z = ir->z1; z <= ir->z2; z++)
|
|
{
|
|
if (ID_FLOOR(x,z)->room == order[i])
|
|
{
|
|
if (ID_FLOOR(x,z)->flag & ID_FLOOR_FLAG_DOORMAT)
|
|
{
|
|
//
|
|
// Make this room a lobby.
|
|
//
|
|
|
|
ir->type = ID_ROOM_TYPE_LOBBY;
|
|
|
|
//
|
|
// Take this room out of the list.
|
|
//
|
|
|
|
for (j = i; j < num_rooms; j++)
|
|
{
|
|
order[j] = order[j + 1];
|
|
size [j] = size [j + 1];
|
|
}
|
|
|
|
i -= 1;
|
|
num_rooms -= 1;
|
|
|
|
goto lobbify_next_room;
|
|
}
|
|
}
|
|
}
|
|
|
|
lobbify_next_room:;
|
|
}
|
|
|
|
//
|
|
// The smallest room is a loo, the middle ones are offices and
|
|
// the largest is a meeting room... but we only have a meeting
|
|
// room if there are at least 3 rooms.
|
|
//
|
|
|
|
for (i = 1; i <= num_rooms; i++)
|
|
{
|
|
type = ID_ROOM_TYPE_OFFICE;
|
|
|
|
if (i == 1) {type = ID_ROOM_TYPE_LOO;}
|
|
if (i != 2 && i == num_rooms) {type = ID_ROOM_TYPE_MEETING;}
|
|
|
|
ASSERT(WITHIN(order[i], 1, ID_room_upto - 1));
|
|
|
|
ID_room[order[i]].type = type;
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_STOREY_TYPE_APARTEMENT_GROUND:
|
|
case ID_STOREY_TYPE_APARTEMENT_UPPER:
|
|
|
|
//
|
|
// The room that contains the stairs is the corridor.
|
|
//
|
|
|
|
if (ID_num_stairs == 0)
|
|
{
|
|
//
|
|
// Argh! I'm confused if I can't make a corridor.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ID_room[i].type = ID_ROOM_TYPE_LOO;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
corridor = ID_FLOOR(ID_stair[0].x1, ID_stair[0].z1)->room;
|
|
|
|
ASSERT(WITHIN(corridor, 1, ID_room_upto - 1));
|
|
|
|
ID_room[corridor].type = ID_ROOM_TYPE_CORRIDOR;
|
|
|
|
//
|
|
// Partition the rooms into flats.
|
|
//
|
|
|
|
ID_find_flats();
|
|
|
|
//
|
|
// Assign the room types for each flat independently.
|
|
//
|
|
|
|
for (i = 1; i < ID_flat_upto; i++)
|
|
{
|
|
ID_assign_flat_room_types(i);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Colour in the floor to match the room types.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
for (x = ir->x1; x <= ir->x2; x++)
|
|
for (z = ir->z1; z <= ir->z2; z++)
|
|
{
|
|
ID_FLOOR(x,z)->texid = ir->type;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Puts in connecting doors between rooms. Returns FALSE
|
|
// on failure.
|
|
//
|
|
|
|
SLONG ID_make_connecting_doors(SLONG storey_type)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG imx;
|
|
SLONG imz;
|
|
SLONG omx;
|
|
SLONG omz;
|
|
SLONG dmx;
|
|
SLONG dmz;
|
|
SLONG wx1, wx2;
|
|
SLONG wz1, wz2;
|
|
SLONG x1, z1;
|
|
SLONG x2, z2;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG score;
|
|
SLONG segment;
|
|
SLONG length;
|
|
SLONG roomtype;
|
|
SLONG best_score;
|
|
SLONG best_choice;
|
|
UBYTE type_in;
|
|
UBYTE type_out;
|
|
UBYTE room_out;
|
|
|
|
SLONG bw;
|
|
SLONG wall;
|
|
SLONG bwmul;
|
|
SLONG bwadd;
|
|
SLONG seglength;
|
|
|
|
#define MAX_DOORCHOICES 64
|
|
|
|
struct
|
|
{
|
|
UBYTE ix, iz; // The inside-the-room block...
|
|
UBYTE ox, oz; // The outside block it leads to...
|
|
SLONG score;
|
|
UBYTE seglength;
|
|
UBYTE wall;
|
|
UBYTE wall_block;
|
|
UBYTE room;
|
|
|
|
} doorchoice[MAX_DOORCHOICES];
|
|
|
|
SLONG doorchoice_upto;
|
|
|
|
//
|
|
// Remember which rooms connect to other rooms?
|
|
// One bit for each (room x room).
|
|
//
|
|
|
|
UBYTE connected[ID_MAX_ROOMS][ID_MAX_ROOMS];
|
|
|
|
//
|
|
// Accessing the connected array.
|
|
//
|
|
|
|
#define DO_CONNECT(r1,r2) {connected[(r1)][(r2)] = TRUE;}
|
|
#define IS_CONNECTED(r1,r2) (connected[(r1)][(r2)])
|
|
#define MARK_CONNECTED(r1,r2) {DO_CONNECT(r1,r2); DO_CONNECT(r2,r1);}
|
|
|
|
ID_Room *ir;
|
|
ID_Wall *iw;
|
|
|
|
//
|
|
// Mark all rooms as unconnected.
|
|
//
|
|
|
|
memset((UBYTE*)connected, 0, sizeof(connected));
|
|
|
|
//
|
|
// Go through all the rooms and decide where are door(s) should be.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
//
|
|
// Loos and kitchens should only have one door each.
|
|
//
|
|
|
|
if (ID_room[i].type == ID_ROOM_TYPE_KITCHEN ||
|
|
ID_room[i].type == ID_ROOM_TYPE_LOO)
|
|
{
|
|
if (ID_room[i].num_doors != 0)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Never make doors from corridors, only _to_ corridors.
|
|
//
|
|
|
|
if (ID_room[i].type == ID_ROOM_TYPE_CORRIDOR)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate the perimeter of the room.
|
|
//
|
|
|
|
if (!ID_calc_room_perim(i))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Go around the perimeter and find all the choices
|
|
// of door positions.
|
|
//
|
|
|
|
doorchoice_upto = 0;
|
|
|
|
for (j = 0; j < ID_perim_upto - 1; j++)
|
|
{
|
|
//
|
|
// This line segment.
|
|
//
|
|
|
|
x1 = ID_perim[j + 0].x << 8;
|
|
z1 = ID_perim[j + 0].z << 8;
|
|
x2 = ID_perim[j + 1].x << 8;
|
|
z2 = ID_perim[j + 1].z << 8;
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// This line segment must be part of an outside wall
|
|
// so we don't put any doors there.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
length = MAX(abs(dx), abs(dz)) >> 8;
|
|
|
|
dx /= length;
|
|
dz /= length;
|
|
|
|
//
|
|
// A tiny vector pointing to the right of the wall.
|
|
//
|
|
|
|
dmx = SIGN(-dz);
|
|
dmz = SIGN( dx);
|
|
|
|
//
|
|
// Go through the wall segment and decide where to put a door.
|
|
//
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
segment = 0;
|
|
|
|
while(x != x2 || z != z2)
|
|
{
|
|
//
|
|
// This bit of the wall segment
|
|
//
|
|
|
|
wx1 = x;
|
|
wz1 = z;
|
|
|
|
wx2 = x + dx;
|
|
wz2 = z + dz;
|
|
|
|
//
|
|
// Find the blocks on either side of
|
|
// this bit of wall segment.
|
|
//
|
|
|
|
imx = wx1 + wx2 >> 1;
|
|
imz = wz1 + wz2 >> 1;
|
|
|
|
omx = wx1 + wx2 >> 1;
|
|
omz = wz1 + wz2 >> 1;
|
|
|
|
imx += dmx;
|
|
imz += dmz;
|
|
|
|
omx -= dmx;
|
|
omz -= dmz;
|
|
|
|
imx >>= 8;
|
|
imz >>= 8;
|
|
|
|
omx >>= 8;
|
|
omz >>= 8;
|
|
|
|
//
|
|
// (imx,imz) is the block in the room and (omx,omz) is
|
|
// the block outside on the other side of the room.
|
|
//
|
|
|
|
if (ID_FLOOR(imx,imz)->room != i) {return FALSE;}
|
|
|
|
ASSERT(WITHIN(doorchoice_upto, 0, MAX_DOORCHOICES - 1));
|
|
|
|
if (WITHIN(omx, ID_floor_x1, ID_floor_x2) &&
|
|
WITHIN(omz, ID_floor_z1, ID_floor_z2))
|
|
{
|
|
room_out = ID_FLOOR(omx,omz)->room;
|
|
|
|
if (room_out && room_out != i)
|
|
{
|
|
//
|
|
// Find which block of wall this block of line segment corresponds to.
|
|
//
|
|
|
|
ID_find_segment_wall(x1 >> 8, z1 >> 8, x2 >> 8, z2 >> 8,
|
|
&wall,
|
|
&bwmul,
|
|
&bwadd,
|
|
&seglength);
|
|
|
|
bw = segment * bwmul + bwadd;
|
|
|
|
doorchoice[doorchoice_upto].ix = imx;
|
|
doorchoice[doorchoice_upto].iz = imz;
|
|
doorchoice[doorchoice_upto].ox = omx;
|
|
doorchoice[doorchoice_upto].oz = omz;
|
|
|
|
doorchoice[doorchoice_upto].wall = wall;
|
|
doorchoice[doorchoice_upto].wall_block = bw;
|
|
doorchoice[doorchoice_upto].seglength = seglength;
|
|
doorchoice[doorchoice_upto].room = room_out;
|
|
|
|
doorchoice_upto += 1;
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
|
|
segment += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Rate each choice of door position.
|
|
//
|
|
|
|
type_in = ID_room[i].type;
|
|
|
|
for (j = 0; j < doorchoice_upto; j++)
|
|
{
|
|
room_out = doorchoice[j].room;
|
|
|
|
ASSERT(WITHIN(room_out, 1, ID_room_upto - 1));
|
|
|
|
type_out = ID_room[room_out].type;
|
|
|
|
//
|
|
// The default score.
|
|
//
|
|
|
|
score = ID_rand() & 0xff;
|
|
score += 2000;
|
|
|
|
//
|
|
// GLOBALLY APPLICABLE SCORING...
|
|
//
|
|
|
|
//
|
|
// Don't have more than one door between the same
|
|
// two rooms.
|
|
//
|
|
|
|
if (IS_CONNECTED(i, room_out))
|
|
{
|
|
score -= 2500;
|
|
}
|
|
|
|
//
|
|
// Kitchens and loos should only have one entrance to them.
|
|
//
|
|
|
|
if (type_out == ID_ROOM_TYPE_KITCHEN ||
|
|
type_out == ID_ROOM_TYPE_LOO)
|
|
{
|
|
if (ID_room[room_out].num_doors)
|
|
{
|
|
score -= 2500;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Dont have doors between kitchens and loos... ugh!
|
|
//
|
|
|
|
if ((type_in == ID_ROOM_TYPE_LOO && type_out == ID_ROOM_TYPE_KITCHEN) ||
|
|
(type_in == ID_ROOM_TYPE_KITCHEN && type_out == ID_ROOM_TYPE_LOO))
|
|
{
|
|
score -= 2500;
|
|
}
|
|
|
|
//
|
|
// We like doors that go to lobbies and lounges, but not loos.
|
|
//
|
|
|
|
if (type_out == ID_ROOM_TYPE_LOBBY) {score += 1500;}
|
|
if (type_out == ID_ROOM_TYPE_LOUNGE) {score += 1000;}
|
|
if (type_out == ID_ROOM_TYPE_LOO) {score -= 500;}
|
|
|
|
//
|
|
// Bedrooms should only connect to lounges and lobbies.
|
|
//
|
|
|
|
if (type_in == ID_ROOM_TYPE_BEDROOM)
|
|
{
|
|
if (type_out == ID_ROOM_TYPE_LOUNGE ||
|
|
type_out == ID_ROOM_TYPE_LOBBY)
|
|
{
|
|
score += 2500;
|
|
}
|
|
else
|
|
{
|
|
score -= 1000;
|
|
}
|
|
}
|
|
|
|
if (type_in == ID_ROOM_TYPE_LOO)
|
|
{
|
|
//
|
|
// Loos should have their door in their shorter wall.
|
|
//
|
|
|
|
score -= doorchoice[j].seglength * 150;
|
|
}
|
|
|
|
if ((type_in == ID_ROOM_TYPE_KITCHEN && type_out == ID_ROOM_TYPE_DINING) ||
|
|
(type_in == ID_ROOM_TYPE_DINING && type_out == ID_ROOM_TYPE_KITCHEN))
|
|
{
|
|
//
|
|
// Go from kitchens to dining rooms.
|
|
//
|
|
|
|
score += 500;
|
|
}
|
|
|
|
//
|
|
// We LOVE! doors going to warehouses.
|
|
//
|
|
|
|
if (type_out == ID_ROOM_TYPE_WAREHOUSE)
|
|
{
|
|
score += 4500;
|
|
}
|
|
|
|
if (type_out == ID_ROOM_TYPE_CORRIDOR)
|
|
{
|
|
//
|
|
// There are only corridors in appartement blocks. Only lounges are allowed
|
|
// to lead onto corridors, and they really enjoy doing it!
|
|
//
|
|
|
|
if (type_in == ID_ROOM_TYPE_LOUNGE)
|
|
{
|
|
score += 10000;
|
|
}
|
|
else
|
|
{
|
|
score = -10000;
|
|
}
|
|
}
|
|
|
|
doorchoice[j].score = score;
|
|
}
|
|
|
|
//
|
|
// Find the best choice.
|
|
//
|
|
|
|
best_choice = 0;
|
|
best_score = 0;
|
|
|
|
for (j = 0; j < doorchoice_upto; j++)
|
|
{
|
|
if (doorchoice[j].score > best_score)
|
|
{
|
|
best_score = doorchoice[j].score;
|
|
best_choice = j;
|
|
}
|
|
}
|
|
|
|
if (best_score > 0)
|
|
{
|
|
ASSERT(WITHIN(best_choice, 0, doorchoice_upto - 1));
|
|
|
|
//
|
|
// Put a door in the wall at the best choice.
|
|
//
|
|
|
|
wall = doorchoice[best_choice].wall;
|
|
bw = doorchoice[best_choice].wall_block;
|
|
|
|
ASSERT(WITHIN(wall, 0, ID_wall_upto - 1));
|
|
|
|
iw = &ID_wall[wall];
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (iw->door[j] == bw)
|
|
{
|
|
//
|
|
// There is already a door here!
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
if (iw->door[j] == 255)
|
|
{
|
|
iw->door[j] = bw;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sort the doors in order of smallest first.
|
|
//
|
|
|
|
if (iw->door[0] > iw->door[1]) {SWAP(iw->door[0], iw->door[1]);}
|
|
if (iw->door[1] > iw->door[2]) {SWAP(iw->door[1], iw->door[2]);}
|
|
if (iw->door[0] > iw->door[1]) {SWAP(iw->door[0], iw->door[1]);}
|
|
|
|
//
|
|
// Increase the number of doors in each room.
|
|
//
|
|
|
|
ASSERT(WITHIN(doorchoice[best_choice].room, 1, ID_room_upto - 1));
|
|
|
|
ID_room[doorchoice[best_choice].room].num_doors += 1;
|
|
ID_room[i ].num_doors += 1;
|
|
|
|
//
|
|
// Mark the floor-squares as connecting to a inside-door.
|
|
//
|
|
|
|
ID_FLOOR(doorchoice[best_choice].ix, doorchoice[best_choice].iz)->flag |= ID_FLOOR_FLAG_INMAT;
|
|
ID_FLOOR(doorchoice[best_choice].ox, doorchoice[best_choice].oz)->flag |= ID_FLOOR_FLAG_INMAT;
|
|
|
|
//
|
|
// Mark the rooms as connected.
|
|
//
|
|
|
|
MARK_CONNECTED(i, doorchoice[best_choice].room);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Adds some furniture to the map.
|
|
//
|
|
// (x,z) in 8-bit fixed point.
|
|
// dir is an angle from 0 to 2047.
|
|
//
|
|
|
|
THING_INDEX ID_add_furniture(MAPCO16 x, MAPCO16 y, MAPCO16 z, SLONG dir, SLONG prim)
|
|
{
|
|
ASSERT(WITHIN(x, ID_floor_x1 << 8, ID_floor_x2 << 8));
|
|
ASSERT(WITHIN(z, ID_floor_z1 << 8, ID_floor_z2 << 8));
|
|
|
|
//
|
|
// Make sure the direction is normalised.
|
|
//
|
|
|
|
dir >>= 3;
|
|
dir &= 0xff;
|
|
|
|
if (ID_furn_upto < ID_MAX_FURN)
|
|
{
|
|
ID_furn[ID_furn_upto].x = x;
|
|
ID_furn[ID_furn_upto].z = z;
|
|
ID_furn[ID_furn_upto].prim = prim;
|
|
ID_furn[ID_furn_upto].yaw = dir;
|
|
|
|
ID_furn_upto += 1;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
/*
|
|
|
|
//
|
|
// Create an ob.
|
|
//
|
|
|
|
OB_create(x, y, z, dir, 0, 0, prim);
|
|
|
|
return NULL;
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
Thing *p_thing;
|
|
|
|
THING_INDEX t_index;
|
|
|
|
//
|
|
// A rotatable prim...
|
|
//
|
|
|
|
t_index = FURN_create(x<<8, y<<8, z<<8, dir, 0, 0, prim);
|
|
|
|
if (t_index)
|
|
{
|
|
//
|
|
// Mark the thing as indoors generated.
|
|
//
|
|
|
|
p_thing = TO_THING(t_index);
|
|
|
|
p_thing->Flags |= FLAGS_IN_BUILDING;
|
|
p_thing->Flags |= FLAGS_INDOORS_GENERATED;
|
|
}
|
|
|
|
return t_index;
|
|
|
|
*/
|
|
}
|
|
|
|
//
|
|
// Puts in the furniture for the doors.
|
|
//
|
|
|
|
void ID_fit_doors(void)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG dwx;
|
|
SLONG dwz;
|
|
|
|
SLONG mid_x;
|
|
SLONG mid_z;
|
|
|
|
SLONG x1, z1;
|
|
SLONG x2, z2;
|
|
|
|
SLONG rx;
|
|
SLONG rz;
|
|
|
|
SLONG room_in;
|
|
SLONG room_out;
|
|
|
|
SLONG door_x;
|
|
SLONG door_y;
|
|
SLONG door_z;
|
|
|
|
THING_INDEX door_thing;
|
|
|
|
#define ID_DOOR_NONE 0
|
|
#define ID_DOOR_INOUT 1
|
|
#define ID_DOOR_OUTIN 2
|
|
|
|
SLONG door;
|
|
SLONG ajar; // 0 => Shut, 512 => open
|
|
SLONG angle;
|
|
|
|
ID_Wall *iw;
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
if (iw->type == ID_WALL_T_OUTSIDE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dwx = SIGN(iw->x2 - iw->x1);
|
|
dwz = SIGN(iw->z2 - iw->z1);
|
|
|
|
//
|
|
// Are there any doors along this wall?
|
|
//
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (iw->door[j] == 255)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
x1 = iw->x1 + dwx * iw->door[j] << 8;
|
|
z1 = iw->z1 + dwz * iw->door[j] << 8;
|
|
|
|
x2 = iw->x1 + dwx << 8;
|
|
z2 = iw->z1 + dwz << 8;
|
|
|
|
//
|
|
// What are the rooms either side of the door?
|
|
//
|
|
|
|
mid_x = x1 + x2 >> 1;
|
|
mid_z = z1 + z2 >> 1;
|
|
|
|
rx = mid_x + dwz >> 8;
|
|
rz = mid_z - dwx >> 8;
|
|
|
|
room_in = ID_FLOOR(rx,rz)->room;
|
|
|
|
rx = mid_x - dwz >> 8;
|
|
rz = mid_z + dwx >> 8;
|
|
|
|
room_out = ID_FLOOR(rx,rz)->room;
|
|
|
|
//
|
|
// We should be going from one room to another.
|
|
//
|
|
|
|
ASSERT(room_in && room_out && room_in != room_out);
|
|
ASSERT(WITHIN(room_in, 1, ID_room_upto - 1));
|
|
ASSERT(WITHIN(room_out, 1, ID_room_upto - 1));
|
|
|
|
//
|
|
// If either of them is a corridor we should have
|
|
// a closed door. Bedrooms and toilets have doors that
|
|
// may be open. All other doorways are door-less.
|
|
//
|
|
|
|
if (ID_room[room_in ].type == ID_ROOM_TYPE_CORRIDOR ||
|
|
ID_room[room_out].type == ID_ROOM_TYPE_CORRIDOR)
|
|
{
|
|
if (ID_room[room_in].type == ID_ROOM_TYPE_CORRIDOR)
|
|
{
|
|
door = ID_DOOR_OUTIN;
|
|
}
|
|
else
|
|
{
|
|
door = ID_DOOR_INOUT;
|
|
}
|
|
|
|
ajar = 0; // Shut.
|
|
}
|
|
else
|
|
if (ID_room[room_in ].type == ID_ROOM_TYPE_BEDROOM ||
|
|
ID_room[room_out].type == ID_ROOM_TYPE_BEDROOM ||
|
|
ID_room[room_in ].type == ID_ROOM_TYPE_LOO ||
|
|
ID_room[room_out].type == ID_ROOM_TYPE_LOO)
|
|
{
|
|
if (ID_room[room_in].type == ID_ROOM_TYPE_BEDROOM ||
|
|
ID_room[room_in].type == ID_ROOM_TYPE_LOO)
|
|
{
|
|
door = ID_DOOR_INOUT;
|
|
}
|
|
else
|
|
{
|
|
door = ID_DOOR_OUTIN;
|
|
}
|
|
|
|
//
|
|
// Maybe open... maybe shut.
|
|
//
|
|
|
|
ajar = ID_rand() & 0x180;
|
|
}
|
|
else
|
|
{
|
|
door = ID_DOOR_NONE;
|
|
}
|
|
|
|
if (dwx == 0 && dwz == 1) {angle += 1536;}
|
|
if (dwx == 1 && dwz == 0) {angle += 0; }
|
|
if (dwx == 0 && dwz == -1) {angle += 512; }
|
|
if (dwx == -1 && dwz == 0) {angle += 1024;}
|
|
|
|
switch(door)
|
|
{
|
|
case ID_DOOR_NONE:
|
|
break;
|
|
|
|
case ID_DOOR_INOUT:
|
|
|
|
door_x = x1 + dwx * (ID_FRAME_END - 1);
|
|
door_z = z1 + dwz * (ID_FRAME_END - 1);
|
|
|
|
door_y = INDOORS_HEIGHT_FLOOR;
|
|
|
|
door_thing = ID_add_furniture(
|
|
door_x,
|
|
door_y,
|
|
door_z,
|
|
(angle + 1024) & 2047,
|
|
PRIM_OBJ_DOOR);
|
|
|
|
if (door_thing)
|
|
{
|
|
FURN_turn_into_door(
|
|
door_thing,
|
|
angle,
|
|
ajar,
|
|
FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_DOOR_OUTIN:
|
|
|
|
door_x = x1 + dwx * (ID_FRAME_START + 1);
|
|
door_z = z1 + dwz * (ID_FRAME_START + 1);
|
|
|
|
door_y = INDOORS_HEIGHT_FLOOR;
|
|
|
|
door_thing = ID_add_furniture(
|
|
door_x,
|
|
door_y,
|
|
door_z,
|
|
angle,
|
|
PRIM_OBJ_DOOR);
|
|
|
|
if (door_thing)
|
|
{
|
|
FURN_turn_into_door(
|
|
door_thing,
|
|
angle,
|
|
ajar,
|
|
FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Puts in the furniture for the stairs.
|
|
//
|
|
|
|
void ID_fit_stairs(void)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG angle;
|
|
|
|
SLONG stairx;
|
|
SLONG stairy;
|
|
SLONG stairz;
|
|
SLONG stairdir;
|
|
SLONG stairprim;
|
|
|
|
ID_Stair *it;
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
it = &ID_stair[i];
|
|
|
|
//
|
|
// Find the angle of the stairs (in 90 degrees)
|
|
//
|
|
|
|
dx = it->x2 - it->x1;
|
|
dz = it->z2 - it->z1;
|
|
|
|
if (dx == 0 && dz == +1) {angle = 2;}
|
|
else if (dx == +1 && dz == 0) {angle = 3;}
|
|
else if (dx == 0 && dz == -1) {angle = 0;}
|
|
else if (dx == -1 && dz == 0) {angle = 1;}
|
|
else ASSERT(0);
|
|
|
|
stairdir = angle * 512;
|
|
|
|
//
|
|
// The position of the stairs.
|
|
//
|
|
|
|
stairx = it->x1 << 8;
|
|
stairz = it->z1 << 8;
|
|
|
|
stairx += 0x80;
|
|
stairz += 0x80;
|
|
|
|
stairx += dx << 7;
|
|
stairz += dz << 7;
|
|
|
|
stairy = INDOORS_HEIGHT_FLOOR;
|
|
|
|
if (it->type != ID_STAIR_TYPE_BOTTOM)
|
|
{
|
|
//
|
|
// There is the bit going down.
|
|
//
|
|
|
|
stairy -= 0x40;
|
|
}
|
|
|
|
//
|
|
// Find the object to use.
|
|
//
|
|
|
|
switch(it->type)
|
|
{
|
|
case ID_STAIR_TYPE_BOTTOM: stairprim = PRIM_OBJ_STAIR_BOTTOM; break;
|
|
case ID_STAIR_TYPE_MIDDLE: stairprim = PRIM_OBJ_STAIR_MIDDLE; break;
|
|
case ID_STAIR_TYPE_TOP: stairprim = PRIM_OBJ_STAIR_TOP; break;
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add it to the map.
|
|
//
|
|
|
|
ID_add_furniture(stairx, stairy, stairz, stairdir, stairprim);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Fits a kitchen out. Returns FALSE on failure.
|
|
//
|
|
|
|
SLONG ID_fit_kitchen(SLONG room)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG x1;
|
|
SLONG z1;
|
|
SLONG x2;
|
|
SLONG z2;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG mx;
|
|
SLONG mz;
|
|
|
|
SLONG dmx;
|
|
SLONG dmz;
|
|
|
|
SLONG length;
|
|
|
|
SLONG best_x1;
|
|
SLONG best_z1;
|
|
SLONG best_x2;
|
|
SLONG best_z2;
|
|
|
|
SLONG best_length;
|
|
|
|
//
|
|
// Find the perimeter of this room.
|
|
//
|
|
|
|
if (!ID_calc_room_perim(room))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Look for the longest unbroken orthogonal wall.
|
|
//
|
|
|
|
best_length = -INFINITY;
|
|
|
|
for (i = 0; i < ID_perim_upto - 1; i++)
|
|
{
|
|
x1 = ID_perim[i + 0].x;
|
|
z1 = ID_perim[i + 0].z;
|
|
x2 = ID_perim[i + 1].x;
|
|
z2 = ID_perim[i + 1].z;
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// Ignore non-orthogonal walls.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
length = MAX(abs(dx), abs(dz));
|
|
|
|
if (length > best_length)
|
|
{
|
|
//
|
|
// Make sure that this is an unbroken wall.
|
|
//
|
|
|
|
dx = SIGN(dx);
|
|
dz = SIGN(dz);
|
|
|
|
dmx = -dz;
|
|
dmz = dx;
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
while(x != x2 || z != z2)
|
|
{
|
|
mx = x + (x + dx) << 7;
|
|
mz = z + (z + dz) << 7;
|
|
|
|
mx += dmx;
|
|
mz += dmz;
|
|
|
|
mx >>= 8;
|
|
mz >>= 8;
|
|
|
|
ASSERT(ID_FLOOR(mx,mz)->room == room);
|
|
|
|
//
|
|
// If this square is a doorway, then it
|
|
// doesn't count.
|
|
//
|
|
|
|
if (ID_FLOOR(mx,mz)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT))
|
|
{
|
|
goto broken_wall;
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
|
|
best_length = length;
|
|
|
|
best_x1 = x1;
|
|
best_z1 = z1;
|
|
best_x2 = x2;
|
|
best_z2 = z2;
|
|
|
|
broken_wall:;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Depending on the length, put in a different
|
|
// fitted kitchen bit from (bx1,bz1) to (bx2, bz2).
|
|
//
|
|
|
|
//
|
|
// If the kitchen is big enough, put a table in the
|
|
// middle.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Returns FALSE on failure.
|
|
//
|
|
|
|
SLONG ID_fit_loo(SLONG room)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG x1;
|
|
SLONG z1;
|
|
SLONG x2;
|
|
SLONG z2;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG mx;
|
|
SLONG mz;
|
|
|
|
SLONG dmx;
|
|
SLONG dmz;
|
|
|
|
SLONG mx1;
|
|
SLONG mz1;
|
|
SLONG mx2;
|
|
SLONG mz2;
|
|
|
|
SLONG score;
|
|
|
|
SLONG bestx;
|
|
SLONG bestz;
|
|
SLONG bestdir;
|
|
SLONG bestscore;
|
|
SLONG bestmx1;
|
|
SLONG bestmz1;
|
|
SLONG bestmx2;
|
|
SLONG bestmz2;
|
|
|
|
SLONG dist;
|
|
|
|
SLONG doorx;
|
|
SLONG doorz;
|
|
|
|
SLONG best_dist;
|
|
SLONG best_loox;
|
|
SLONG best_looz;
|
|
SLONG best_loocorner;
|
|
|
|
SLONG sink_map_x;
|
|
SLONG sink_map_z;
|
|
SLONG sinkx;
|
|
SLONG sinky;
|
|
SLONG sinkz;
|
|
SLONG sink_dir;
|
|
|
|
SLONG loox;
|
|
SLONG looy;
|
|
SLONG looz;
|
|
SLONG loo_dir;
|
|
|
|
SLONG showerx;
|
|
SLONG showery;
|
|
SLONG showerz;
|
|
|
|
SLONG bathx;
|
|
SLONG bathy;
|
|
SLONG bathz;
|
|
SLONG bathdir;
|
|
|
|
SLONG floor_area;
|
|
|
|
ID_Room *ir;
|
|
|
|
ASSERT(WITHIN(room, 1, ID_room_upto - 1));
|
|
ASSERT(ID_room[room].type == ID_ROOM_TYPE_LOO);
|
|
|
|
ir = &ID_room[room];
|
|
|
|
//
|
|
// Where is the entrance to the loo?
|
|
//
|
|
|
|
for (x = ir->x1; x <= ir->x2; x++)
|
|
for (z = ir->z1; z <= ir->z2; z++)
|
|
{
|
|
if (ID_FLOOR(x,z)->room == room)
|
|
{
|
|
if (ID_FLOOR(x,z)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT))
|
|
{
|
|
doorx = x;
|
|
doorz = z;
|
|
|
|
goto found_door;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
TRACE("Couldn't find entrace to the loo\n");
|
|
|
|
return FALSE;
|
|
|
|
found_door:;
|
|
|
|
//
|
|
// Look for a corner furthest from the door to
|
|
// put the loo in.
|
|
//
|
|
|
|
best_dist = -INFINITY;
|
|
best_loox = 0;
|
|
best_looz = 0;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
x = (i & 1) ? ir->x1 : ir->x2;
|
|
z = (i & 2) ? ir->z1 : ir->z2;
|
|
|
|
dx = x - doorx;
|
|
dz = z - doorz;
|
|
|
|
dist = dx*dx + dz*dz;
|
|
|
|
if (dist > best_dist)
|
|
{
|
|
best_dist = dist;
|
|
best_loox = x;
|
|
best_looz = z;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Put in the loo in square (best_loox, best_looz)
|
|
//
|
|
|
|
loox = (best_loox << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
looz = (best_looz << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
|
|
looy = INDOORS_HEIGHT_FLOOR;
|
|
|
|
//
|
|
// Make the loo face into the room and not into a wall.
|
|
//
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
loo_dir = i * 512;
|
|
|
|
mx = (loox - (SIN(loo_dir) >> 8)) >> 8;
|
|
mz = (looz - (COS(loo_dir) >> 8)) >> 8;
|
|
|
|
if (WITHIN(mx, ID_floor_x1, ID_floor_x2 - 1) &&
|
|
WITHIN(mz, ID_floor_z1, ID_floor_z2 - 1))
|
|
{
|
|
if (ID_FLOOR(mx,mz)->room == room)
|
|
{
|
|
//
|
|
// Use this direction for the loo and
|
|
// (mx,mz) for the sink.
|
|
//
|
|
|
|
sink_map_x = mx;
|
|
sink_map_z = mz;
|
|
|
|
goto found_loo_dir;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Couldn't find a direction for the loo
|
|
// to face!
|
|
//
|
|
|
|
loo_dir = 0;
|
|
|
|
TRACE("Couldn't find a decent direction for the loo!\n");
|
|
|
|
found_loo_dir:
|
|
|
|
//
|
|
// Mark the loo square as containing a piece of furniture.
|
|
//
|
|
|
|
ID_FLOOR(best_loox, best_looz)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
|
|
//
|
|
// Create the loo.
|
|
//
|
|
|
|
ID_add_furniture(loox, looy, looz, loo_dir, PRIM_OBJ_LOO);
|
|
|
|
//
|
|
// Put the sink against the wall in the square in
|
|
// front of the loo.
|
|
//
|
|
|
|
sinkx = (sink_map_x << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
sinkz = (sink_map_z << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
sink_dir = i * 512;
|
|
|
|
mx = (sinkx + (SIN(sink_dir) >> 8)) >> 8;
|
|
mz = (sinkz + (COS(sink_dir) >> 8)) >> 8;
|
|
|
|
if (!WITHIN(mx, ID_floor_x1, ID_floor_x2 - 1) ||
|
|
!WITHIN(mz, ID_floor_z1, ID_floor_z2 - 1) ||
|
|
(ID_FLOOR(mx, mz)->room != room && !(ID_FLOOR(mx,mz)->flag & ID_FLOOR_FLAG_INMAT)))
|
|
{
|
|
//
|
|
// We have found the direction of the sink that
|
|
// would put it up against the wall.
|
|
//
|
|
|
|
goto found_sink_dir;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Could not find a direction for the sink...
|
|
// Choose a random one!
|
|
//
|
|
|
|
sink_dir = 512 * (ID_rand() & 0x3);
|
|
|
|
found_sink_dir:
|
|
|
|
//
|
|
// Mark the sink square as containing a piece of furniture.
|
|
//
|
|
|
|
ID_FLOOR(sink_map_x, sink_map_z)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
|
|
//
|
|
// Where do we put the sink?
|
|
//
|
|
|
|
sinkx += SIN(sink_dir) >> 10;
|
|
sinkz += COS(sink_dir) >> 10;
|
|
|
|
sinky = INDOORS_HEIGHT_FLOOR;
|
|
|
|
//
|
|
// Create the sink.
|
|
//
|
|
|
|
ID_add_furniture(sinkx, sinky, sinkz, sink_dir, PRIM_OBJ_SINK);
|
|
|
|
//
|
|
// If this is a big toilet, then try to put in a shower
|
|
// and a bath.
|
|
//
|
|
|
|
floor_area = (ir->x2 - ir->x1 + 1) * (ir->z2 - ir->z1 + 1);
|
|
|
|
if (floor_area < 4)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
x = (i & 1) ? ir->x1 : ir->x2;
|
|
z = (i & 2) ? ir->z1 : ir->z2;
|
|
|
|
if (ID_FLOOR(x,z)->room == room)
|
|
{
|
|
//
|
|
// Look for a spare corner of the room.
|
|
//
|
|
|
|
if (!(ID_FLOOR(x,z)->flag &
|
|
(ID_FLOOR_FLAG_INMAT |
|
|
ID_FLOOR_FLAG_DOORMAT |
|
|
ID_FLOOR_FLAG_FURNISHED |
|
|
ID_FLOOR_FLAG_STAIR)))
|
|
{
|
|
//
|
|
// Where do we put the shower?
|
|
//
|
|
|
|
showerx = (x << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
showerz = (z << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
showery = INDOORS_HEIGHT_FLOOR;
|
|
|
|
//
|
|
// Put a shower here.
|
|
//
|
|
|
|
ID_add_furniture(showerx, showery, showerz, 0, PRIM_OBJ_SHOWER);
|
|
|
|
//
|
|
// Mark this square as furnished.
|
|
//
|
|
|
|
ID_FLOOR(x,z)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (floor_area < 6)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Calculate the perimeter of the room to find where
|
|
// we can put the bath.
|
|
//
|
|
|
|
if (!ID_calc_room_perim(room))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Go through the perimeter looking for somewhere
|
|
// to put the bath.
|
|
//
|
|
|
|
bestscore = -INFINITY;
|
|
|
|
for (i = 0; i < ID_perim_upto - 1; i++)
|
|
{
|
|
x1 = ID_perim[i + 0].x;
|
|
z1 = ID_perim[i + 0].z;
|
|
|
|
x2 = ID_perim[i + 1].x;
|
|
z2 = ID_perim[i + 1].z;
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// Ignore this wall.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The wall has to be long enough.
|
|
//
|
|
|
|
if (abs(dx) >= 2 || abs(dz) >= 2)
|
|
{
|
|
//
|
|
// A tiny vector pointing to the right of the wall.
|
|
//
|
|
|
|
dmx = SIGN(-dz);
|
|
dmz = SIGN( dx);
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
dx = SIGN(dx);
|
|
dz = SIGN(dz);
|
|
|
|
while(x + dx != x2 || z + dz != z2)
|
|
{
|
|
//
|
|
// Check that there is room for the bath.
|
|
//
|
|
|
|
mx1 = (x << ELE_SHIFT) + (dx << (ELE_SHIFT - 1)) + dmx >> ELE_SHIFT;
|
|
mz1 = (z << ELE_SHIFT) + (dz << (ELE_SHIFT - 1)) + dmz >> ELE_SHIFT;
|
|
|
|
mx2 = (x + dx << ELE_SHIFT) + (dx << (ELE_SHIFT - 1)) + dmx >> ELE_SHIFT;
|
|
mz2 = (z + dz << ELE_SHIFT) + (dz << (ELE_SHIFT - 1)) + dmz >> ELE_SHIFT;
|
|
|
|
//
|
|
// (mx1,mz1) and (mx2,mz2) are the squares that the bath would cover.
|
|
//
|
|
|
|
ASSERT(
|
|
ID_FLOOR(mx1,mz1)->room == room &&
|
|
ID_FLOOR(mx2,mz2)->room == room);
|
|
|
|
if (!(ID_FLOOR(mx1,mz1)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR)) &&
|
|
!(ID_FLOOR(mx2,mz2)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR)))
|
|
{
|
|
//
|
|
// We could put the bath here. Is it a good place?
|
|
//
|
|
|
|
score = 256;
|
|
|
|
//
|
|
// Can't think of any heuristics for good placement!
|
|
//
|
|
|
|
score += ID_rand() & 0x7;
|
|
|
|
if (score > bestscore)
|
|
{
|
|
//
|
|
// Remember this place.
|
|
//
|
|
|
|
bestx = x + dx << ELE_SHIFT;
|
|
bestz = z + dz << ELE_SHIFT;
|
|
bestdir = -Arctan(dx,dz);
|
|
bestscore = score;
|
|
bestmx1 = mx1;
|
|
bestmz1 = mz1;
|
|
bestmx2 = mx2;
|
|
bestmz2 = mz2;
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestscore >= 0)
|
|
{
|
|
//
|
|
// Where should we place down the sofa.
|
|
//
|
|
|
|
bathx = bestx;
|
|
bathz = bestz;
|
|
bathy = INDOORS_HEIGHT_FLOOR;
|
|
|
|
bathdir = bestdir;
|
|
|
|
bathx -= SIN((bathdir - 512) & 2047) >> 9;
|
|
bathz -= COS((bathdir - 512) & 2047) >> 9;
|
|
|
|
//
|
|
// Put down the bath.
|
|
//
|
|
|
|
ID_add_furniture(bathx, bathy, bathz, bathdir, PRIM_OBJ_BATH);
|
|
|
|
//
|
|
// Mark the bath squares as furnished.
|
|
//
|
|
|
|
ID_FLOOR(bestmx1, bestmz1)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
ID_FLOOR(bestmx2, bestmz2)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Returns FALSE on failure.
|
|
//
|
|
|
|
SLONG ID_fit_lounge(SLONG room)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG x1;
|
|
SLONG z1;
|
|
SLONG x2;
|
|
SLONG z2;
|
|
|
|
SLONG dir;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG dmx;
|
|
SLONG dmz;
|
|
|
|
SLONG mx;
|
|
SLONG mz;
|
|
|
|
SLONG mx1;
|
|
SLONG mz1;
|
|
SLONG mx2;
|
|
SLONG mz2;
|
|
|
|
SLONG score;
|
|
|
|
SLONG bestx;
|
|
SLONG bestz;
|
|
SLONG bestdir;
|
|
SLONG bestscore;
|
|
SLONG bestmx1;
|
|
SLONG bestmz1;
|
|
SLONG bestmx2;
|
|
SLONG bestmz2;
|
|
SLONG bestdmx;
|
|
SLONG bestdmz;
|
|
|
|
SLONG coffeemx1;
|
|
SLONG coffeemz1;
|
|
SLONG coffeemx2;
|
|
SLONG coffeemz2;
|
|
SLONG coffeex;
|
|
SLONG coffeey;
|
|
SLONG coffeez;
|
|
SLONG coffeedir;
|
|
|
|
SLONG tvx;
|
|
SLONG tvy;
|
|
SLONG tvz;
|
|
SLONG tvdir;
|
|
|
|
SLONG sofax;
|
|
SLONG sofay;
|
|
SLONG sofaz;
|
|
SLONG sofadir;
|
|
|
|
SLONG bookx;
|
|
SLONG booky;
|
|
SLONG bookz;
|
|
SLONG bookdir;
|
|
|
|
SLONG treex;
|
|
SLONG treey;
|
|
SLONG treez;
|
|
SLONG treedir;
|
|
|
|
ID_Room *ir;
|
|
|
|
ASSERT(WITHIN(room, 1, ID_room_upto - 1));
|
|
ASSERT(ID_room[room].type == ID_ROOM_TYPE_LOUNGE);
|
|
|
|
ir = &ID_room[room];
|
|
|
|
//
|
|
// Put a TV in one of the corners.
|
|
//
|
|
|
|
bestscore = -INFINITY;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
x = (i & 1) ? ir->x1 : ir->x2;
|
|
z = (i & 2) ? ir->z1 : ir->z2;
|
|
|
|
switch(i)
|
|
{
|
|
case 0: dir = 256; break;
|
|
case 1: dir = 1792; break;
|
|
case 2: dir = 768; break;
|
|
case 3: dir = 1280; break;
|
|
}
|
|
|
|
if (ID_FLOOR(x,z)->room == room)
|
|
{
|
|
if (!(ID_FLOOR(x,z)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR)))
|
|
{
|
|
score = ID_rand();
|
|
|
|
if (score > bestscore)
|
|
{
|
|
bestscore = score;
|
|
bestdir = dir;
|
|
bestx = x;
|
|
bestz = z;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestscore >= 0)
|
|
{
|
|
//
|
|
// Put a TV at (bestx, bestz).
|
|
//
|
|
|
|
tvx = (bestx << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
tvz = (bestz << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
tvy = INDOORS_HEIGHT_FLOOR;
|
|
|
|
ID_add_furniture(tvx, tvy, tvz, bestdir, PRIM_OBJ_TV);
|
|
|
|
//
|
|
// Mark this square as furnished.
|
|
//
|
|
|
|
ID_FLOOR(bestx, bestz)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
}
|
|
|
|
//
|
|
// Calculate the perimeter of the room to find
|
|
// walls where we can put a sofa.
|
|
//
|
|
|
|
if (!ID_calc_room_perim(room))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Go through the perimeter looking for somewhere
|
|
// to put a sofa.
|
|
//
|
|
|
|
bestscore = -INFINITY;
|
|
|
|
for (i = 0; i < ID_perim_upto - 1; i++)
|
|
{
|
|
x1 = ID_perim[i + 0].x;
|
|
z1 = ID_perim[i + 0].z;
|
|
|
|
x2 = ID_perim[i + 1].x;
|
|
z2 = ID_perim[i + 1].z;
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// Ignore this wall.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The wall has to be long enough.
|
|
//
|
|
|
|
if (abs(dx) >= 2 || abs(dz) >= 2)
|
|
{
|
|
//
|
|
// A tiny vector pointing to the right of the wall.
|
|
//
|
|
|
|
dmx = SIGN(-dz);
|
|
dmz = SIGN( dx);
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
dx = SIGN(dx);
|
|
dz = SIGN(dz);
|
|
|
|
while(x + dx != x2 || z + dz != z2)
|
|
{
|
|
//
|
|
// Check that there is room for the sofa.
|
|
//
|
|
|
|
mx1 = (x << ELE_SHIFT) + (dx << (ELE_SHIFT - 1)) + dmx >> ELE_SHIFT;
|
|
mz1 = (z << ELE_SHIFT) + (dz << (ELE_SHIFT - 1)) + dmz >> ELE_SHIFT;
|
|
|
|
mx2 = (x + dx << ELE_SHIFT) + (dx << (ELE_SHIFT - 1)) + dmx >> ELE_SHIFT;
|
|
mz2 = (z + dz << ELE_SHIFT) + (dz << (ELE_SHIFT - 1)) + dmz >> ELE_SHIFT;
|
|
|
|
//
|
|
// (mx1,mz1) and (mx2,mz2) are the squares that the sofa would cover.
|
|
//
|
|
|
|
ASSERT(
|
|
ID_FLOOR(mx1,mz1)->room == room &&
|
|
ID_FLOOR(mx2,mz2)->room == room);
|
|
|
|
if (!(ID_FLOOR(mx1,mz1)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR)) &&
|
|
!(ID_FLOOR(mx2,mz2)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR)))
|
|
{
|
|
//
|
|
// We could put the sofa here. Is it a good place to put the sofa?
|
|
//
|
|
|
|
score = 256;
|
|
|
|
//
|
|
// Make it face the TV
|
|
//
|
|
|
|
if (mx1 == (tvx >> 8)) {score -= 16;}
|
|
if (mx2 == (tvx >> 8)) {score -= 16;}
|
|
if (mz1 == (tvz >> 8)) {score -= 16;}
|
|
if (mz2 == (tvz >> 8)) {score -= 16;}
|
|
|
|
//
|
|
// Avoid corners.
|
|
//
|
|
|
|
if (mx1 == ir->x1 || mz1 == ir->z1 ||
|
|
mx1 == ir->x2 || mz1 == ir->z2 ||
|
|
mx2 == ir->x1 || mz2 == ir->z1 ||
|
|
mx2 == ir->x2 || mz2 == ir->z2)
|
|
{
|
|
score -= 8;
|
|
}
|
|
|
|
score += ID_rand() & 0x7;
|
|
|
|
if (score > bestscore)
|
|
{
|
|
//
|
|
// Remember this place.
|
|
//
|
|
|
|
bestx = x + dx << ELE_SHIFT;
|
|
bestz = z + dz << ELE_SHIFT;
|
|
bestdir = -Arctan(dx,dz);
|
|
bestscore = score;
|
|
bestmx1 = mx1;
|
|
bestmz1 = mz1;
|
|
bestmx2 = mx2;
|
|
bestmz2 = mz2;
|
|
bestdmx = dmx;
|
|
bestdmz = dmz;
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestscore >= 0)
|
|
{
|
|
//
|
|
// Where should we place down the sofa.
|
|
//
|
|
|
|
sofax = bestx;
|
|
sofaz = bestz;
|
|
sofay = INDOORS_HEIGHT_FLOOR;
|
|
|
|
sofadir = bestdir - 0x1f + (ID_rand() & 0x3f);
|
|
sofadir &= 2047;
|
|
|
|
sofax -= SIN((sofadir - 512) & 2047) >> 9;
|
|
sofaz -= COS((sofadir - 512) & 2047) >> 9;
|
|
|
|
//
|
|
// Put down the sofa.
|
|
//
|
|
|
|
ID_add_furniture(sofax, sofay, sofaz, sofadir, PRIM_OBJ_SOFA);
|
|
|
|
//
|
|
// Mark the sofa squares as furnished.
|
|
//
|
|
|
|
ID_FLOOR(bestmx1, bestmz1)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
ID_FLOOR(bestmx2, bestmz2)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
|
|
//
|
|
// Remember where we could put a coffee table.
|
|
//
|
|
|
|
coffeex = sofax - (SIN((sofadir - 512) & 2047) >> 8);
|
|
coffeez = sofaz - (COS((sofadir - 512) & 2047) >> 8);
|
|
coffeey = INDOORS_HEIGHT_FLOOR;
|
|
|
|
coffeedir = sofadir;
|
|
|
|
coffeemx1 = bestmx1 + bestdmx;
|
|
coffeemz1 = bestmz1 + bestdmz;
|
|
coffeemx2 = bestmx2 + bestdmx;
|
|
coffeemz2 = bestmz2 + bestdmz;
|
|
}
|
|
|
|
//
|
|
// Go through the perimeter looking for somewhere to put the bookcase.
|
|
//
|
|
|
|
bestscore = -INFINITY;
|
|
|
|
for (i = 0; i < ID_perim_upto - 1; i++)
|
|
{
|
|
x1 = ID_perim[i + 0].x;
|
|
z1 = ID_perim[i + 0].z;
|
|
|
|
x2 = ID_perim[i + 1].x;
|
|
z2 = ID_perim[i + 1].z;
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// Ignore this wall.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// A tiny vector pointing to the right of the wall.
|
|
//
|
|
|
|
dmx = SIGN(-dz);
|
|
dmz = SIGN( dx);
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
dx = SIGN(dx);
|
|
dz = SIGN(dz);
|
|
|
|
while(x != x2 || z != z2)
|
|
{
|
|
mx = (x << ELE_SHIFT) + (dx << (ELE_SHIFT - 1)) + dmx >> ELE_SHIFT;
|
|
mz = (z << ELE_SHIFT) + (dz << (ELE_SHIFT - 1)) + dmz >> ELE_SHIFT;
|
|
|
|
ASSERT(ID_FLOOR(mx,mz)->room == room);
|
|
|
|
//
|
|
// (mx,mz) is the square we would put the bookcase in.
|
|
//
|
|
|
|
if (ID_FLOOR(mx,mz)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR))
|
|
{
|
|
//
|
|
// This square is used already.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// How good is this square?
|
|
//
|
|
|
|
score = 256;
|
|
|
|
//
|
|
// Be on the same wall as the tv.
|
|
//
|
|
|
|
if (mx == (tvx >> 8) ||
|
|
mz == (tvz >> 8))
|
|
{
|
|
score += 16;
|
|
}
|
|
|
|
//
|
|
// Don't go in the corners.
|
|
//
|
|
|
|
if (mx == ir->x1) {score -= 8;}
|
|
if (mz == ir->z1) {score -= 8;}
|
|
if (mx == ir->x2) {score -= 8;}
|
|
if (mz == ir->z2) {score -= 8;}
|
|
|
|
score += ID_rand() & 0x7;
|
|
|
|
if (score > bestscore)
|
|
{
|
|
bestx = mx;
|
|
bestz = mz;
|
|
bestdir = -Arctan(dx, dz);
|
|
bestscore = score;
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
}
|
|
|
|
if (bestscore > 0)
|
|
{
|
|
//
|
|
// Where do we put the bookcase?
|
|
//
|
|
|
|
bookx = (bestx << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
bookz = (bestz << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
booky = INDOORS_HEIGHT_FLOOR;
|
|
|
|
bookdir = bestdir - 0xf + (ID_rand() & 0x1f);
|
|
bookdir &= 2047;
|
|
|
|
dx = SIN((bookdir - 512) & 2047);
|
|
dz = COS((bookdir - 512) & 2047);
|
|
|
|
bookx += dx >> 9;
|
|
bookz += dz >> 9;
|
|
bookx -= dx >> 11;
|
|
bookz -= dz >> 11;
|
|
|
|
//
|
|
// Place the bookcase.
|
|
//
|
|
|
|
ID_add_furniture(bookx, booky, bookz, bookdir, PRIM_OBJ_BOOKCASE);
|
|
|
|
//
|
|
// Mark this square as furnished.
|
|
//
|
|
|
|
ID_FLOOR(bestx, bestz)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
}
|
|
|
|
//
|
|
// Put a plant in the corner.
|
|
//
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
x = (i & 1) ? ir->x1 : ir->x2;
|
|
z = (i & 2) ? ir->z1 : ir->z2;
|
|
|
|
if (ID_FLOOR(x,z)->room == room)
|
|
{
|
|
if (ID_FLOOR(x,z)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR))
|
|
{
|
|
//
|
|
// This square is used.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Put a tree here!
|
|
//
|
|
|
|
treex = (x << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
treez = (z << ELE_SHIFT) + (1 << (ELE_SHIFT - 1));
|
|
treey = INDOORS_HEIGHT_FLOOR;
|
|
treedir = ID_rand() & 2047;
|
|
|
|
ID_add_furniture(treex, treey, treez, treedir, PRIM_OBJ_TREE);
|
|
|
|
//
|
|
// Mark the tree square as furnished.
|
|
//
|
|
|
|
ID_FLOOR(x, z)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (WITHIN(coffeemx1, ID_floor_x1, ID_floor_x2) &&
|
|
WITHIN(coffeemz1, ID_floor_z1, ID_floor_z2) &&
|
|
WITHIN(coffeemx2, ID_floor_x1, ID_floor_x2) &&
|
|
WITHIN(coffeemz2, ID_floor_z1, ID_floor_z2))
|
|
{
|
|
//
|
|
// Do we have enough room for a coffee table?
|
|
//
|
|
|
|
if ((ID_FLOOR(coffeemx1, coffeemz1)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR)) ||
|
|
(ID_FLOOR(coffeemx2, coffeemz2)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR)))
|
|
{
|
|
//
|
|
// The squares that the coffee table would have taken are
|
|
// used up.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Put down the coffee table!
|
|
//
|
|
|
|
ID_add_furniture(coffeex, coffeey, coffeez, coffeedir, PRIM_OBJ_COFFEE_TABLE);
|
|
|
|
//
|
|
// Mark the squares as furnished.
|
|
//
|
|
|
|
ID_FLOOR(coffeemx1, coffeemz1)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
ID_FLOOR(coffeemx2, coffeemz2)->flag |= ID_FLOOR_FLAG_FURNISHED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MSG_add("Tried to put the coffee table outside the building!");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Puts down radiators. Returns FALSE on failure.
|
|
//
|
|
|
|
SLONG ID_fit_radiators()
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG size;
|
|
SLONG big;
|
|
SLONG x1;
|
|
SLONG z1;
|
|
SLONG x2;
|
|
SLONG z2;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG mx;
|
|
SLONG mz;
|
|
SLONG dmx;
|
|
SLONG dmz;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG radx;
|
|
SLONG rady;
|
|
SLONG radz;
|
|
SLONG raddir;
|
|
|
|
SLONG wall;
|
|
SLONG bwmul;
|
|
SLONG bwadd;
|
|
SLONG length;
|
|
|
|
ID_Room *ir;
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
if (ir->type == ID_ROOM_TYPE_KITCHEN ||
|
|
ir->type == ID_ROOM_TYPE_WAREHOUSE)
|
|
{
|
|
//
|
|
// Kitchens and warehouses don't have radiators.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// What is the size of the room.
|
|
//
|
|
|
|
size = (ir->x2 - ir->x1) * (ir->z2 - ir->z1);
|
|
|
|
//
|
|
// Depending on the size of the room, we want a small
|
|
// or a big radiator.
|
|
//
|
|
|
|
#define ID_BIG_RADIATOR_SIZE 13
|
|
|
|
big = (size >= ID_BIG_RADIATOR_SIZE);
|
|
|
|
//
|
|
// Find the perimeter of the room.
|
|
//
|
|
|
|
if (!ID_calc_room_perim(i))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Go around the perimeter of the room looking for a
|
|
// place to put a radiator.
|
|
//
|
|
|
|
for (j = 0; j < ID_perim_upto - 1; j++)
|
|
{
|
|
x1 = ID_perim[j + 0].x;
|
|
z1 = ID_perim[j + 0].z;
|
|
|
|
x2 = ID_perim[j + 1].x;
|
|
z2 = ID_perim[j + 1].z;
|
|
|
|
dx = SIGN(x2 - x1);
|
|
dz = SIGN(z2 - z1);
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// Ignore non-orthogonal walls.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// What wall is this segment a part of?
|
|
//
|
|
|
|
ID_find_segment_wall(
|
|
x1, z1,
|
|
x2, z2,
|
|
&wall,
|
|
&bwmul,
|
|
&bwadd,
|
|
&length);
|
|
|
|
//
|
|
// Don't put radiators on outside walls.
|
|
//
|
|
|
|
ASSERT(WITHIN(wall, 0, ID_wall_upto - 1));
|
|
|
|
if (ID_wall[wall].type == ID_WALL_T_OUTSIDE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// A tiny vector pointing to the right of the wall.
|
|
//
|
|
|
|
dmx = SIGN(-dz);
|
|
dmz = SIGN( dx);
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
while(x != x2 || z != z2)
|
|
{
|
|
mx = x + (x + dx) << 7;
|
|
mz = z + (z + dz) << 7;
|
|
|
|
mx += dmx;
|
|
mz += dmz;
|
|
|
|
mx >>= 8;
|
|
mz >>= 8;
|
|
|
|
//
|
|
// (mx,mz) is the block of the room just inside
|
|
// the wall.
|
|
//
|
|
|
|
if (ID_FLOOR(mx,mz)->flag & (ID_FLOOR_FLAG_INMAT | ID_FLOOR_FLAG_DOORMAT | ID_FLOOR_FLAG_FURNISHED | ID_FLOOR_FLAG_STAIR))
|
|
{
|
|
//
|
|
// This floor square is taken.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We could put a radiator here!
|
|
//
|
|
|
|
radx = x + (x + dx) << 7;
|
|
radz = z + (z + dz) << 7;
|
|
|
|
radx += dmx << 4;
|
|
radz += dmz << 4;
|
|
rady = INDOORS_HEIGHT_FLOOR;
|
|
|
|
raddir = -Arctan(dx,dz);
|
|
|
|
//
|
|
// Put a radiator here half the time...
|
|
//
|
|
|
|
if (ID_rand() & 0x100)
|
|
{
|
|
ID_add_furniture(radx, rady, radz, raddir, PRIM_OBJ_RADIATOR);
|
|
}
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Puts down furniture. Returns FALSE on failure.
|
|
//
|
|
|
|
void ID_place_furniture()
|
|
{
|
|
SLONG i;
|
|
|
|
ID_Room *ir;
|
|
|
|
//
|
|
// Put down the stairs.
|
|
//
|
|
|
|
ID_fit_stairs();
|
|
|
|
//
|
|
// Put down the furniture.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
switch(ir->type)
|
|
{
|
|
case ID_ROOM_TYPE_LOO:
|
|
ID_fit_loo(i);
|
|
break;
|
|
case ID_ROOM_TYPE_KITCHEN:
|
|
ID_fit_kitchen(i);
|
|
break;
|
|
case ID_ROOM_TYPE_LOUNGE:
|
|
ID_fit_lounge(i);
|
|
break;
|
|
case ID_ROOM_TYPE_LOBBY:
|
|
break;
|
|
case ID_ROOM_TYPE_DINING:
|
|
break;
|
|
case ID_ROOM_TYPE_WAREHOUSE:
|
|
break;
|
|
case ID_ROOM_TYPE_OFFICE:
|
|
break;
|
|
case ID_ROOM_TYPE_MEETING:
|
|
break;
|
|
case ID_ROOM_TYPE_BEDROOM:
|
|
break;
|
|
case ID_ROOM_TYPE_CORRIDOR:
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Put down radiators, pictures and doors.
|
|
//
|
|
|
|
ID_fit_radiators();
|
|
ID_fit_doors();
|
|
}
|
|
|
|
|
|
//
|
|
// Returns TRUE if the given wall would go through
|
|
// the middle of some stairs.
|
|
//
|
|
|
|
SLONG ID_goes_through_stairs(SLONG x1, SLONG z1, SLONG x2, SLONG z2)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG mid_x;
|
|
SLONG mid_z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
ID_Stair *it;
|
|
|
|
//
|
|
// This function only works if the wall is orthogonal.
|
|
//
|
|
|
|
ASSERT(x2 == x1 || z2 == z1);
|
|
|
|
//
|
|
// Put the wall into 8-bit fixed point.
|
|
//
|
|
|
|
x1 <<= 8;
|
|
z1 <<= 8;
|
|
x2 <<= 8;
|
|
z2 <<= 8;
|
|
|
|
//
|
|
// Order the wall...
|
|
//
|
|
|
|
if (x1 > x2) {SWAP(x1, x2);}
|
|
if (z1 > z2) {SWAP(z1, z2);}
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
it = &ID_stair[i];
|
|
|
|
//
|
|
// The middle point of the stairs in fixed-point 8.
|
|
//
|
|
|
|
mid_x = it->x1 + (it->x1 + 1) + it->x2 + (it->x2 + 1) << 6;
|
|
mid_z = it->z1 + (it->z1 + 1) + it->z2 + (it->z2 + 1) << 6;
|
|
|
|
//
|
|
// The wall goes through the stairs if it passes
|
|
// through (mid_x, mid_z).
|
|
//
|
|
|
|
if (dx == 0 && mid_x == x1)
|
|
{
|
|
if (WITHIN(mid_z, z1, z2))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (dz == 0 && mid_z == z1)
|
|
{
|
|
if (WITHIN(mid_x, x1, x2))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
SLONG ID_generate_inside_walls(SLONG storey_type)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG cx;
|
|
SLONG cz;
|
|
SLONG nx;
|
|
SLONG nz;
|
|
SLONG x1, x2;
|
|
SLONG z1, z2;
|
|
SLONG a;
|
|
SLONG b;
|
|
SLONG num_walls;
|
|
SLONG wall_start_ok;
|
|
SLONG wall_starts = 0;
|
|
SLONG forbid_x = INFINITY;
|
|
SLONG forbid_z = INFINITY;
|
|
|
|
ID_Square *is;
|
|
|
|
struct
|
|
{
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
} dir[4];
|
|
|
|
//
|
|
// The on-wall bits for the outside walls.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
ID_generate_on_wall_bits(i);
|
|
}
|
|
|
|
//
|
|
// Places where we definitely start walls from.
|
|
//
|
|
|
|
#define ID_MAX_WALL_STARTS 4
|
|
|
|
struct
|
|
{
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
} wall_start[ID_MAX_WALL_STARTS];
|
|
SLONG wall_start_upto = 0;
|
|
|
|
//
|
|
// How many walls shall we put in?
|
|
//
|
|
|
|
num_walls = ID_floor_area >> 4;
|
|
num_walls += 1;
|
|
|
|
//
|
|
// For an apartement block we find the two places we start
|
|
// to make the corridor.
|
|
//
|
|
|
|
if (storey_type == ID_STOREY_TYPE_APARTEMENT_GROUND ||
|
|
storey_type == ID_STOREY_TYPE_APARTEMENT_UPPER)
|
|
{
|
|
num_walls = ID_floor_area >> 3;
|
|
num_walls += 2;
|
|
|
|
if (ID_num_stairs != 0)
|
|
{
|
|
struct
|
|
{
|
|
UBYTE onwall;
|
|
UBYTE duplicate;
|
|
UBYTE x;
|
|
UBYTE z;
|
|
|
|
} corner[8];
|
|
|
|
//
|
|
// The corners of all the stair squares.
|
|
//
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
corner[i * 2 + 0].x = ID_stair[0].x1 + (i & 1);
|
|
corner[i * 2 + 0].z = ID_stair[0].z1 + (i >> 1);
|
|
|
|
corner[i * 2 + 1].x = ID_stair[0].x2 + (i & 1);
|
|
corner[i * 2 + 1].z = ID_stair[0].z2 + (i >> 1);
|
|
|
|
corner[i * 2 + 0].duplicate = FALSE;
|
|
corner[i * 2 + 1].duplicate = FALSE;
|
|
|
|
corner[i * 2 + 0].onwall = ID_FLOOR(corner[i * 2 + 0].x, corner[i * 2 + 0].z)->flag & ID_FLOOR_FLAG_ON_WALL;
|
|
corner[i * 2 + 1].onwall = ID_FLOOR(corner[i * 2 + 1].x, corner[i * 2 + 1].z)->flag & ID_FLOOR_FLAG_ON_WALL;
|
|
}
|
|
|
|
//
|
|
// Mark two points at the same place as one another.
|
|
//
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
for (j = i + 1; j < 8; j++)
|
|
{
|
|
if (corner[i].x == corner[j].x &&
|
|
corner[i].z == corner[j].z)
|
|
{
|
|
corner[i].duplicate = TRUE;
|
|
corner[j].duplicate = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Start walls at all non-duplicate corner points that
|
|
// are on an outside wall.
|
|
//
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (corner[i].onwall && !corner[i].duplicate)
|
|
{
|
|
if (WITHIN(wall_start_upto, 0, ID_MAX_WALL_STARTS))
|
|
{
|
|
wall_start[wall_start_upto].x = corner[i].x;
|
|
wall_start[wall_start_upto].z = corner[i].z;
|
|
|
|
wall_start_upto += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wall_start_upto > 2)
|
|
{
|
|
//
|
|
// Take out any wall starts that dont share an x or z
|
|
// and are two squares away from any other start point.
|
|
//
|
|
|
|
start_this_again:;
|
|
|
|
for (i = 0; i < wall_start_upto; i++)
|
|
{
|
|
for (j = 0; j < wall_start_upto; j++)
|
|
{
|
|
if (j == i)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dx = wall_start[j].x - wall_start[i].x;
|
|
dz = wall_start[j].z - wall_start[i].z;
|
|
|
|
if ((dx == 0 && abs(dz) == 2) ||
|
|
(dz == 0 && abs(dx) == 2))
|
|
{
|
|
//
|
|
// Keep this start point.
|
|
//
|
|
|
|
goto keep_start_point;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get rid of this start point.
|
|
//
|
|
|
|
wall_start[i].x = wall_start[wall_start_upto - 1].x;
|
|
wall_start[i].z = wall_start[wall_start_upto - 1].z;
|
|
|
|
wall_start_upto -= 1;
|
|
|
|
//
|
|
// Start again!
|
|
//
|
|
|
|
goto start_this_again;
|
|
|
|
keep_start_point:;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure that no walls split up this corridor.
|
|
//
|
|
|
|
for (i = 0; i < wall_start_upto; i++)
|
|
{
|
|
for (j = i + 1; j < wall_start_upto; j++)
|
|
{
|
|
dx = wall_start[j].x - wall_start[i].x;
|
|
dz = wall_start[j].z - wall_start[i].z;
|
|
|
|
if (abs(dx) == 2)
|
|
{
|
|
forbid_x = wall_start[i].x + wall_start[j].x >> 1;
|
|
}
|
|
|
|
if (abs(dz) == 2)
|
|
{
|
|
forbid_z = wall_start[i].z + wall_start[j].z >> 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (storey_type == ID_STOREY_TYPE_WAREHOUSE)
|
|
{
|
|
//
|
|
// Less walls in a warehouse.
|
|
//
|
|
|
|
num_walls >>= 1;
|
|
}
|
|
|
|
while(num_walls--)
|
|
{
|
|
if (wall_starts < wall_start_upto)
|
|
{
|
|
x = wall_start[wall_starts].x;
|
|
z = wall_start[wall_starts].z;
|
|
wall_start_ok = TRUE;
|
|
}
|
|
else
|
|
{
|
|
wall_start_ok = ID_get_wall_start(&x, &z);
|
|
}
|
|
|
|
wall_starts += 1;
|
|
|
|
if (wall_start_ok)
|
|
{
|
|
//
|
|
// Try all four directions in turn.
|
|
//
|
|
|
|
dir[0].dx = 0;
|
|
dir[0].dz = 1;
|
|
|
|
dir[1].dx = 0;
|
|
dir[1].dz = -1;
|
|
|
|
dir[2].dx = 1;
|
|
dir[2].dz = 0;
|
|
|
|
dir[3].dx = -1;
|
|
dir[3].dz = 0;
|
|
|
|
//
|
|
// Randomize the order in which we
|
|
// try the directions.
|
|
//
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
a = ID_rand() & 0x3;
|
|
b = ID_rand() & 0x3;
|
|
|
|
SWAP(dir[a].dx, dir[b].dx);
|
|
SWAP(dir[a].dz, dir[b].dz);
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
cx = x + dir[i].dx;
|
|
cz = z + dir[i].dz;
|
|
|
|
if (!WITHIN(cx, ID_floor_x1, ID_floor_x2) ||
|
|
!WITHIN(cz, ID_floor_z1, ID_floor_z2))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
is = ID_FLOOR(cx, cz);
|
|
|
|
if ( (is->flag & ID_FLOOR_FLAG_ON_WALL) ||
|
|
!(is->flag & ID_FLOOR_FLAG_P_IN))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Valid direction!
|
|
//
|
|
|
|
dx = dir[i].dx;
|
|
dz = dir[i].dz;
|
|
|
|
goto found_direction;
|
|
}
|
|
|
|
//
|
|
// No direction works :-( We can't do a wall from
|
|
// here.
|
|
//
|
|
|
|
if (ID_rand() & 0x10)
|
|
{
|
|
num_walls += 1;
|
|
}
|
|
|
|
continue;
|
|
|
|
found_direction:
|
|
|
|
//
|
|
// Add the wall until we hit another one.
|
|
//
|
|
|
|
x1 = x;
|
|
z1 = z;
|
|
|
|
while(1)
|
|
{
|
|
if (!WITHIN(x + dx, ID_floor_x1, ID_floor_x2) ||
|
|
!WITHIN(z + dz, ID_floor_z1, ID_floor_z2))
|
|
{
|
|
break;
|
|
}
|
|
|
|
x += dx;
|
|
z += dz;
|
|
|
|
if (ID_FLOOR(x,z)->flag & ID_FLOOR_FLAG_ON_WALL)
|
|
{
|
|
//
|
|
// Hit a wall.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
if (!(ID_FLOOR(x,z)->flag & ID_FLOOR_FLAG_P_IN))
|
|
{
|
|
//
|
|
// We have leaked outside.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The end of the wall.
|
|
//
|
|
|
|
x2 = x;
|
|
z2 = z;
|
|
|
|
//
|
|
// We dont insert this wall if it intersects with the middle
|
|
// of a non-othogonal outside wall, goes through a staircase,
|
|
// or splits up the corridor of an apartement block.
|
|
//
|
|
|
|
#define BETWEEN(x,a,b) (((a) < (b)) ? WITHIN(x,a,b) : WITHIN(x,b,a))
|
|
|
|
if (ID_intersects_badly (x1, z1, x2, z2) ||
|
|
ID_goes_through_stairs(x1, z1, x2, z2) ||
|
|
BETWEEN(forbid_x, x1, x2) ||
|
|
BETWEEN(forbid_z, z1, z2))
|
|
{
|
|
//
|
|
// Invalid wall.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Insert this wall.
|
|
//
|
|
|
|
ASSERT(WITHIN(ID_wall_upto, 0, ID_MAX_WALLS - 1));
|
|
|
|
ID_wall[ID_wall_upto].type = ID_WALL_T_INSIDE;
|
|
ID_wall[ID_wall_upto].door[0] = 255;
|
|
ID_wall[ID_wall_upto].door[1] = 255;
|
|
ID_wall[ID_wall_upto].door[2] = 255;
|
|
ID_wall[ID_wall_upto].x1 = x1;
|
|
ID_wall[ID_wall_upto].z1 = z1;
|
|
ID_wall[ID_wall_upto].x2 = x2;
|
|
ID_wall[ID_wall_upto].z2 = z2;
|
|
ID_wall[ID_wall_upto].num_blocks = MAX(abs(x2- x1), abs(z2 - z1));
|
|
|
|
ID_wall_upto += 1;
|
|
|
|
//
|
|
// Put in the on_wall flags for this new wall.
|
|
//
|
|
|
|
ID_generate_on_wall_bits(ID_wall_upto - 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Could not find a place to start a wall from.
|
|
//
|
|
}
|
|
}
|
|
|
|
//
|
|
// Generate the collision bits for the inside walls.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
if (ID_wall[i].type == ID_WALL_T_INSIDE)
|
|
{
|
|
ID_generate_collision_bits(i);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Work out the rooms and mark them as lobbies, corridors etc...
|
|
//
|
|
|
|
ID_find_rooms();
|
|
ID_generate_room_info();
|
|
ID_assign_room_types(storey_type);
|
|
|
|
//
|
|
// This can fail sometimes.
|
|
//
|
|
|
|
if (!ID_make_connecting_doors(storey_type))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Put a camera in each room.
|
|
//
|
|
|
|
ID_find_a_camera_for_each_room();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Returns true if the layout contains rooms accessible from
|
|
// only one other room.
|
|
//
|
|
|
|
SLONG ID_is_there_a_room_accessible_from_only_one_other_room(void)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG nx;
|
|
SLONG nz;
|
|
|
|
SLONG nextroom;
|
|
|
|
ID_Room *ir;
|
|
|
|
struct
|
|
{
|
|
SBYTE dx;
|
|
SBYTE dz;
|
|
|
|
} dir[4] = {{1,0},{-1,0},{0,1},{0,-1}};
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
nextroom = 0;
|
|
|
|
for (x = ir->x1; x <= ir->x2; x++)
|
|
for (z = ir->z1; z <= ir->z2; z++)
|
|
{
|
|
if (ID_FLOOR(x,z)->room == i)
|
|
{
|
|
//
|
|
// Look for a neighbouring room.
|
|
//
|
|
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
nx = x + dir[j].dx;
|
|
nz = z + dir[j].dz;
|
|
|
|
if (WITHIN(nx, ID_floor_x1, ID_floor_x2 - 1) &&
|
|
WITHIN(nz, ID_floor_z1, ID_floor_z2 - 1))
|
|
{
|
|
if (ID_FLOOR(nx,nz)->room)
|
|
{
|
|
if (nextroom == 0)
|
|
{
|
|
//
|
|
// Found the first neighbouring room.
|
|
//
|
|
|
|
nextroom = ID_FLOOR(nx,nz)->room;
|
|
}
|
|
else
|
|
{
|
|
if (ID_FLOOR(nx,nz)->room != nextroom)
|
|
{
|
|
//
|
|
// Found another neighbouring room!
|
|
//
|
|
|
|
goto this_room_has_at_least_two_neighbouring_rooms;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This room only has one neighbouring room.
|
|
//
|
|
|
|
return TRUE;
|
|
|
|
this_room_has_at_least_two_neighbouring_rooms:;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SLONG ID_score_layout_house_ground()
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG nx;
|
|
SLONG nz;
|
|
|
|
SLONG width;
|
|
SLONG height;
|
|
SLONG ratio;
|
|
SLONG nextroom;
|
|
SLONG room;
|
|
|
|
SLONG found_corridor;
|
|
SLONG score;
|
|
|
|
ID_Room *ir;
|
|
ID_Stair *it;
|
|
|
|
//
|
|
// We favour rectangular rooms whose ratio of width to
|
|
// height is the golden ratio. We favour having on long
|
|
// thin room... but only one.
|
|
//
|
|
|
|
found_corridor = FALSE;
|
|
score = 0;
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
width = ir->x2 - ir->x1 + 1;
|
|
height = ir->z2 - ir->z1 + 1;
|
|
|
|
#if WE_WANT_CORRIDORS
|
|
|
|
if ((width == 1 && height >= 5) ||
|
|
(height == 1 && width >= 5))
|
|
{
|
|
//
|
|
// Found a corridor. One corridor is good, two is bad.
|
|
//
|
|
|
|
if (found_corridor)
|
|
{
|
|
score -= 4000;
|
|
}
|
|
else
|
|
{
|
|
score += 2000;
|
|
|
|
found_corridor = TRUE;
|
|
}
|
|
}
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
if (width > height) {ratio = (width << 8) / height;}
|
|
if (height > width) {ratio = (height << 8) / width;}
|
|
|
|
//
|
|
// We want 414 ... The golden ratio (1.618) * 256
|
|
//
|
|
|
|
if (ratio > 414) {score += (150 + (414 - ratio)) * 10;}
|
|
if (ratio < 414) {score += (150 + (ratio - 414)) * 10;}
|
|
|
|
if (ir->flag & ID_ROOM_FLAG_RECTANGULAR)
|
|
{
|
|
score += 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Penalise layouts with rooms only accessible from one other
|
|
// room.
|
|
//
|
|
|
|
if (ID_is_there_a_room_accessible_from_only_one_other_room())
|
|
{
|
|
score *= 3;
|
|
score >>= 2;
|
|
}
|
|
|
|
//
|
|
// We dont want stairs in the toilets!
|
|
//
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
it = &ID_stair[i];
|
|
|
|
room = ID_FLOOR(it->x1, it->z1)->room;
|
|
|
|
ASSERT(WITHIN(room, 1, ID_room_upto - 1));
|
|
|
|
if (ID_room[room].type == ID_ROOM_TYPE_LOO)
|
|
{
|
|
score -= score >> 1;
|
|
}
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
SLONG ID_score_layout_warehouse()
|
|
{
|
|
SLONG i;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG size;
|
|
SLONG biggest;
|
|
SLONG score;
|
|
|
|
ID_Room *ir;
|
|
|
|
//
|
|
// We want the biggest room to be really big!
|
|
//
|
|
|
|
biggest = -1;
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
dx = ir->x2 - ir->x1 + 1;
|
|
dz = ir->z2 - ir->z1 + 1;
|
|
|
|
size = dx * dz;
|
|
|
|
if (size > biggest) {biggest = size;}
|
|
}
|
|
|
|
score = biggest << 8;
|
|
|
|
//
|
|
// But we like lots of rooms too...
|
|
//
|
|
|
|
score += ID_room_upto << 10;
|
|
|
|
//
|
|
// Room that are too long and thin are bad.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
dx = ir->x2 - ir->x1 + 1;
|
|
dz = ir->z2 - ir->z1 + 1;
|
|
|
|
if (dx > dz * 3 ||
|
|
dz > dx * 3)
|
|
{
|
|
score = -1;
|
|
}
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
SLONG ID_score_layout_apartement()
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG nx;
|
|
SLONG nz;
|
|
SLONG nroom;
|
|
|
|
ID_Room *ir;
|
|
|
|
SLONG score = ID_rand() & 0xff;
|
|
|
|
UBYTE flat_room[ID_MAX_ROOMS];
|
|
|
|
//
|
|
// Count the number of rooms in each flat.
|
|
//
|
|
|
|
for (i = 1; i < ID_flat_upto; i++)
|
|
{
|
|
flat_room[i] = 0;
|
|
}
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ASSERT(WITHIN(ID_room[i].flat, 0, ID_MAX_ROOMS - 1));
|
|
|
|
flat_room[ID_room[i].flat] += 1;
|
|
}
|
|
|
|
//
|
|
// It is good for each flat to have the same number of rooms
|
|
// and more than two rooms.
|
|
//
|
|
|
|
for (i = 1; i < ID_flat_upto; i++)
|
|
{
|
|
for (j = i + 1; j < ID_flat_upto; j++)
|
|
{
|
|
if (flat_room[i] == flat_room[j] && flat_room[i] > 2)
|
|
{
|
|
score += 0x10000;
|
|
score += flat_room[i] * 0x10000;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// It is good to have rooms connected to eachother, and
|
|
// especially connected to the lounge.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
if (ir->type != ID_ROOM_TYPE_LOUNGE &&
|
|
ir->type != ID_ROOM_TYPE_CORRIDOR)
|
|
{
|
|
//
|
|
// Add a bonus for rooms connected to the lounge.
|
|
//
|
|
|
|
for (x = ir->x1; x <= ir->x2; x++)
|
|
for (z = ir->z1; z <= ir->z2; z++)
|
|
{
|
|
if (ID_FLOOR(x,z)->room == i)
|
|
{
|
|
for (dx = -1; dx <= 1; dx += 2)
|
|
for (dz = -1; dz <= 1; dz += 2)
|
|
{
|
|
nx = x + dx;
|
|
nz = z + dz;
|
|
|
|
if (WITHIN(nx, ID_floor_x1, ID_floor_x2) &&
|
|
WITHIN(nz, ID_floor_z1, ID_floor_z2))
|
|
{
|
|
nroom = ID_FLOOR(nx,nz)->room;
|
|
|
|
if (nroom != i && nroom)
|
|
{
|
|
ASSERT(WITHIN(nroom, 1, ID_room_upto - 1));
|
|
|
|
//
|
|
// If we have a lounge as a neighbouring room,
|
|
// then it is good.
|
|
//
|
|
|
|
score += 512;
|
|
|
|
goto look_at_next_room;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
look_at_next_room:;
|
|
}
|
|
|
|
|
|
//
|
|
// We hate long thin rooms.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ir = &ID_room[i];
|
|
|
|
if (ir->type != ID_ROOM_TYPE_CORRIDOR)
|
|
{
|
|
dx = ir->x2 - ir->x1 + 1;
|
|
dz = ir->z2 - ir->z1 + 1;
|
|
|
|
if (dx > dz * 2 ||
|
|
dz > dx * 2)
|
|
{
|
|
score = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
|
|
SLONG ID_score_layout(SLONG storey_type)
|
|
{
|
|
SLONG ans;
|
|
|
|
switch(storey_type)
|
|
{
|
|
case ID_STOREY_TYPE_HOUSE_GROUND:
|
|
case ID_STOREY_TYPE_HOUSE_UPPER:
|
|
case ID_STOREY_TYPE_OFFICE_GROUND:
|
|
case ID_STOREY_TYPE_OFFICE_UPPER:
|
|
ans = ID_score_layout_house_ground();
|
|
break;
|
|
|
|
case ID_STOREY_TYPE_WAREHOUSE:
|
|
ans = ID_score_layout_warehouse();
|
|
break;
|
|
|
|
case ID_STOREY_TYPE_APARTEMENT_GROUND:
|
|
case ID_STOREY_TYPE_APARTEMENT_UPPER:
|
|
ans = ID_score_layout_apartement();
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
return ans;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Works out which squares are inside and outside.
|
|
//
|
|
|
|
void ID_calculate_in_squares(void)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG pos;
|
|
UBYTE wall_type;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG x1, z1;
|
|
SLONG x2, z2;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG dxdz;
|
|
UBYTE next;
|
|
UBYTE next1;
|
|
UBYTE next2;
|
|
UBYTE *prev;
|
|
|
|
ID_Wall *iw;
|
|
|
|
//
|
|
// First work out which squares are outside and which squares are inside. Any squares
|
|
// that are even a little bit inside are counted as being inside.
|
|
//
|
|
|
|
//
|
|
// Clear the linked lists.
|
|
//
|
|
|
|
for (i = 0; i < ID_PLAN_SIZE; i++)
|
|
{
|
|
ID_edge[i] = 0; // The NULL index
|
|
}
|
|
|
|
ID_link_upto = 1; // We don't use index 0 because that is the NULL index.
|
|
|
|
//
|
|
// Go through each outside wall and build up the linked lists for each z-row.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
ASSERT(WITHIN(i, 0, ID_MAX_WALLS - 1));
|
|
|
|
iw = &ID_wall[i];
|
|
|
|
if (iw->z1 == iw->z2)
|
|
{
|
|
//
|
|
// Ignore this wall because it does not cross any x-lines.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The wall's coordinates in 16-bit fixed point.
|
|
//
|
|
|
|
x1 = iw->x1 << 16;
|
|
z1 = iw->z1 << 16;
|
|
x2 = iw->x2 << 16;
|
|
z2 = iw->z2 << 16;
|
|
|
|
if (z1 > z2)
|
|
{
|
|
wall_type = ID_LINK_T_ENTER;
|
|
|
|
//
|
|
// Always go from top to bottom.
|
|
//
|
|
|
|
SWAP(x1, x2);
|
|
SWAP(z1, z2);
|
|
}
|
|
else
|
|
{
|
|
wall_type = ID_LINK_T_LEAVE;
|
|
}
|
|
|
|
//
|
|
// Go through the wall.
|
|
//
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
dxdz = DIV64(dx, dz);
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
while(z < z2)
|
|
{
|
|
switch(wall_type)
|
|
{
|
|
case ID_LINK_T_ENTER: pos = MIN(x, x + dxdz); break;
|
|
case ID_LINK_T_LEAVE: pos = MAX(x, x + dxdz); break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create a new link
|
|
//
|
|
|
|
ASSERT(WITHIN(ID_link_upto, 1, ID_MAX_LINKS - 1));
|
|
|
|
ID_link[ID_link_upto].type = wall_type;
|
|
ID_link[ID_link_upto].pos = pos >> 8; // Only 8-bit fixed point.
|
|
ID_link[ID_link_upto].next = 0;
|
|
|
|
//
|
|
// Insert it in the correct place for this linked list.
|
|
//
|
|
|
|
ASSERT(WITHIN(z >> 16, 0, ID_FLOOR_SIZE - 1));
|
|
|
|
next = ID_EDGE(z >> 16);
|
|
prev = &ID_EDGE(z >> 16);
|
|
|
|
while(1)
|
|
{
|
|
ASSERT(WITHIN(next, 0, ID_MAX_LINKS - 1));
|
|
|
|
if (next == 0 || ID_link[next].pos >= ID_link[ID_link_upto].pos)
|
|
{
|
|
//
|
|
// This is where we should insert the new link.
|
|
//
|
|
|
|
*prev = ID_link_upto;
|
|
ID_link[ID_link_upto].next = next;
|
|
|
|
break;
|
|
}
|
|
|
|
prev = &ID_link[next].next;
|
|
next = ID_link[next].next;
|
|
}
|
|
|
|
//
|
|
// Finished with this link now.
|
|
//
|
|
|
|
ID_link_upto += 1;
|
|
|
|
//
|
|
// Go onto the next line.
|
|
//
|
|
|
|
x += dxdz;
|
|
z += 0x10000;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go through the linked lists and mark squares as inside or outside.
|
|
//
|
|
|
|
for (z = ID_floor_z1; z < ID_floor_z2; z++)
|
|
{
|
|
ASSERT(WITHIN(z, 0, ID_FLOOR_SIZE - 1));
|
|
|
|
next = ID_EDGE(z);
|
|
|
|
//
|
|
// There should always be at least two links because they should
|
|
// come in pairs.
|
|
//
|
|
|
|
ASSERT(next != 0);
|
|
|
|
while(next)
|
|
{
|
|
ASSERT(WITHIN(next, 1, ID_link_upto - 1));
|
|
|
|
//
|
|
// They should come in pairs.
|
|
//
|
|
|
|
next1 = next;
|
|
next2 = ID_link[next1].next;
|
|
|
|
//
|
|
// The pairs should be start and end.
|
|
//
|
|
|
|
ASSERT(WITHIN(next1, 1, ID_link_upto - 1));
|
|
ASSERT(WITHIN(next2, 1, ID_link_upto - 1));
|
|
|
|
//
|
|
// Mark the squares in-between these start and end points as being
|
|
// inside.
|
|
//
|
|
|
|
x1 = ID_link[next1].pos;
|
|
x2 = ID_link[next2].pos;
|
|
|
|
for (x = x1 & 0xff00; x < x2; x += 0x100)
|
|
{
|
|
ASSERT(WITHIN(x >> 8, 0, ID_FLOOR_SIZE - 1));
|
|
|
|
ID_FLOOR(x >> 8, z)->flag |= ID_FLOOR_FLAG_INSIDE;
|
|
|
|
//
|
|
// If the square is completely inside, then it is
|
|
// not outside.
|
|
//
|
|
|
|
if (x >= x1 && x + 0x100 <= x2)
|
|
{
|
|
ID_FLOOR(x >> 8, z)->flag &= ~ID_FLOOR_FLAG_OUTSIDE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go onto the next pair.
|
|
//
|
|
|
|
next = ID_link[next2].next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Works out which points are inside the building.
|
|
// Points on the wall count as being inside.
|
|
//
|
|
// Returns FALSE on failure.
|
|
//
|
|
|
|
SLONG ID_calculate_in_points(void)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG pos;
|
|
UBYTE wall_type;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG x1, z1;
|
|
SLONG x2, z2;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG dxdz;
|
|
UBYTE next;
|
|
UBYTE next1;
|
|
UBYTE next2;
|
|
UBYTE *prev;
|
|
|
|
ID_Wall *iw;
|
|
|
|
//
|
|
// Clear the linked lists.
|
|
//
|
|
|
|
for (i = 0; i < ID_PLAN_SIZE; i++)
|
|
{
|
|
ID_edge[i] = 0; // The NULL index
|
|
}
|
|
|
|
ID_link_upto = 1; // We don't use index 0 because that is the NULL index.
|
|
|
|
//
|
|
// Go through each outside wall and build up the linked lists for each z-row.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
ASSERT(WITHIN(i, 0, ID_MAX_WALLS - 1));
|
|
|
|
iw = &ID_wall[i];
|
|
|
|
if (iw->z1 == iw->z2)
|
|
{
|
|
//
|
|
// Ignore this wall because it does not cross any x-lines.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The wall's coordinates in 16-bit fixed point.
|
|
//
|
|
|
|
x1 = iw->x1 << 16;
|
|
z1 = iw->z1 << 16;
|
|
x2 = iw->x2 << 16;
|
|
z2 = iw->z2 << 16;
|
|
|
|
if (z1 > z2)
|
|
{
|
|
wall_type = ID_LINK_T_ENTER;
|
|
|
|
//
|
|
// Always go from top to bottom.
|
|
//
|
|
|
|
SWAP(x1, x2);
|
|
SWAP(z1, z2);
|
|
}
|
|
else
|
|
{
|
|
wall_type = ID_LINK_T_LEAVE;
|
|
}
|
|
|
|
//
|
|
// Go through the wall.
|
|
//
|
|
|
|
dx = x2 - x1;
|
|
dz = z2 - z1;
|
|
|
|
dxdz = DIV64(dx, dz);
|
|
|
|
x = x1;
|
|
z = z1;
|
|
|
|
while(z <= z2)
|
|
{
|
|
//
|
|
// Create a new link
|
|
//
|
|
|
|
ASSERT(WITHIN(ID_link_upto, 1, ID_MAX_LINKS - 1));
|
|
|
|
ID_link[ID_link_upto].type = wall_type;
|
|
ID_link[ID_link_upto].pos = x >> 8; // Only 8-bit fixed point.
|
|
ID_link[ID_link_upto].next = 0;
|
|
|
|
//
|
|
// Insert it in the correct place for this linked list.
|
|
//
|
|
|
|
ASSERT(WITHIN(z >> 16, 0, ID_FLOOR_SIZE - 1));
|
|
|
|
next = ID_EDGE(z >> 16);
|
|
prev = &ID_EDGE(z >> 16);
|
|
|
|
while(1)
|
|
{
|
|
ASSERT(WITHIN(next, 0, ID_MAX_LINKS - 1));
|
|
|
|
if (next == 0 || ID_link[next].pos >= ID_link[ID_link_upto].pos)
|
|
{
|
|
//
|
|
// Make sure that two links at the same pos appear in the
|
|
// linked list in the order ENTER then LEAVE.
|
|
//
|
|
|
|
if (ID_link[next].pos == ID_link[ID_link_upto].pos &&
|
|
ID_link[next].type == ID_LINK_T_ENTER)
|
|
{
|
|
//
|
|
// Insert it after this link.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is where we should insert the new link.
|
|
//
|
|
|
|
*prev = ID_link_upto;
|
|
ID_link[ID_link_upto].next = next;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
prev = &ID_link[next].next;
|
|
next = ID_link[next].next;
|
|
}
|
|
|
|
//
|
|
// Finished with this link now.
|
|
//
|
|
|
|
ID_link_upto += 1;
|
|
|
|
//
|
|
// Go onto the next line.
|
|
//
|
|
|
|
x += dxdz;
|
|
z += 0x10000;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go through the linked lists and mark points as inside or outside.
|
|
//
|
|
|
|
for (z = ID_floor_z1; z <= ID_floor_z2; z++)
|
|
{
|
|
ASSERT(WITHIN(z, 0, ID_FLOOR_SIZE - 1));
|
|
|
|
prev = &ID_EDGE(z);
|
|
next = ID_EDGE(z);
|
|
|
|
//
|
|
// Take out ENTER/ENTER LEAVE/LEAVE sequences.
|
|
//
|
|
|
|
while(1)
|
|
{
|
|
ASSERT(WITHIN(next, 1, ID_link_upto - 1));
|
|
|
|
next1 = next;
|
|
next2 = ID_link[next].next;
|
|
|
|
if (next1 == NULL || next2 == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
if (ID_link[next1].type == ID_LINK_T_ENTER &&
|
|
ID_link[next2].type == ID_LINK_T_ENTER)
|
|
{
|
|
//
|
|
// Use the first value...
|
|
//
|
|
|
|
ID_link[next1].next = ID_link[next2].next;
|
|
|
|
next = next1;
|
|
}
|
|
else
|
|
if (ID_link[next1].type == ID_LINK_T_LEAVE &&
|
|
ID_link[next2].type == ID_LINK_T_LEAVE)
|
|
{
|
|
//
|
|
// Use the second value.
|
|
//
|
|
|
|
*prev = next2;
|
|
|
|
next = next2;
|
|
}
|
|
else
|
|
{
|
|
prev = &ID_link[next1].next;
|
|
next = next2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark inside points...
|
|
//
|
|
|
|
next = ID_EDGE(z);
|
|
|
|
while(next)
|
|
{
|
|
if (!WITHIN(next, 1, ID_link_upto - 1)) {return FALSE;}
|
|
if (!WITHIN(ID_link[next].next, 1, ID_link_upto - 1)) {return FALSE;}
|
|
|
|
next1 = next;
|
|
next2 = ID_link[next].next;
|
|
|
|
//
|
|
// Mark the points in-between these start and end points as being
|
|
// inside.
|
|
//
|
|
|
|
x1 = ID_link[next1].pos;
|
|
x2 = ID_link[next2].pos;
|
|
|
|
for (x = ID_floor_x1; x <= ID_floor_x2; x++)
|
|
{
|
|
if (WITHIN(x << 8, x1, x2))
|
|
{
|
|
ID_FLOOR(x, z)->flag |= ID_FLOOR_FLAG_P_IN;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go onto the next pair.
|
|
//
|
|
|
|
next = ID_link[next2].next;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
SLONG ID_generate_floorplan(SLONG storey_type, ID_Stair stair[], SLONG num_stairs, UWORD seed, UBYTE find_good_layout, UBYTE furnished)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
SLONG pos;
|
|
UBYTE wall_type;
|
|
SLONG x;
|
|
SLONG z;
|
|
SLONG x1, z1;
|
|
SLONG x2, z2;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG cx;
|
|
SLONG cz;
|
|
SLONG dxdz;
|
|
SLONG best_score;
|
|
UWORD best_seed;
|
|
UWORD seed_used;
|
|
UBYTE next;
|
|
UBYTE next1;
|
|
UBYTE next2;
|
|
UBYTE *prev;
|
|
SLONG num_walls;
|
|
SLONG find_best_start;
|
|
|
|
ID_Wall *iw;
|
|
ID_Stair *is;
|
|
|
|
//
|
|
// Remember where the stairs are.
|
|
//
|
|
|
|
ID_stair = stair;
|
|
ID_num_stairs = num_stairs;
|
|
|
|
//
|
|
// Remember the storey type.
|
|
//
|
|
|
|
ID_storey_type = storey_type;
|
|
|
|
//
|
|
// Make sure that the bounding box does not exceed the maximum size of
|
|
// the floorplan.
|
|
//
|
|
|
|
ASSERT(ID_floor_x2 - ID_floor_x1 <= ID_PLAN_SIZE);
|
|
ASSERT(ID_floor_z2 - ID_floor_z1 <= ID_PLAN_SIZE);
|
|
|
|
//
|
|
// Fail if there are any zero length walls.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
dx = iw->x2 - iw->x1;
|
|
dz = iw->z2 - iw->z1;
|
|
|
|
if (!(dx | dz))
|
|
{
|
|
//
|
|
// A zero-length wall!
|
|
//
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// First work out which squares are outside and which squares are inside. Any squares
|
|
// that are even a little bit inside are counted as being inside.
|
|
//
|
|
|
|
ID_calculate_in_squares();
|
|
|
|
//
|
|
// Work out which points are inside the building. Points on the wall count as
|
|
// being inside.
|
|
//
|
|
|
|
if (!ID_calculate_in_points())
|
|
{
|
|
TRACE("Complete ID failure 1.\n");
|
|
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Mark the staircases.
|
|
//
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
is = &ID_stair[i];
|
|
|
|
ID_FLOOR(is->x1, is->z1)->flag |= ID_FLOOR_FLAG_STAIR;
|
|
ID_FLOOR(is->x2, is->z2)->flag |= ID_FLOOR_FLAG_STAIR;
|
|
}
|
|
|
|
//
|
|
// Work out how many squares are inside the building.
|
|
//
|
|
|
|
ID_floor_area = 0;
|
|
|
|
for (x = ID_floor_x1; x < ID_floor_x2; x++)
|
|
for (z = ID_floor_z1; z < ID_floor_z2; z++)
|
|
{
|
|
if (ID_FLOOR(x, z)->flag & ID_FLOOR_FLAG_INSIDE)
|
|
{
|
|
ID_floor_area += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store the seed.
|
|
//
|
|
|
|
ID_srand(seed);
|
|
|
|
if (find_good_layout)
|
|
{
|
|
//
|
|
// We try putting wall in a few different ways, then
|
|
// pick the best
|
|
//
|
|
|
|
#define ID_MAX_FITS 32
|
|
|
|
struct
|
|
{
|
|
UWORD seed;
|
|
UWORD shit;
|
|
SLONG score;
|
|
|
|
} fit[ID_MAX_FITS];
|
|
|
|
find_best_start = seed;
|
|
|
|
for (i = 0; i < ID_MAX_FITS; i++)
|
|
{
|
|
//
|
|
// Remember the seed that generates these walls... we only use
|
|
// seeds that can fit into a UWORD.
|
|
//
|
|
|
|
fit[i].seed = find_best_start;
|
|
|
|
//
|
|
// Use the seed that has been truncated to a UWORD.
|
|
//
|
|
|
|
ID_srand(fit[i].seed);
|
|
|
|
//
|
|
// Generate the walls.
|
|
//
|
|
|
|
if (!ID_generate_inside_walls(storey_type))
|
|
{
|
|
//
|
|
// This turned out very badly!
|
|
//
|
|
|
|
fit[i].score = -INFINITY;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Score this layout.
|
|
//
|
|
|
|
fit[i].score = ID_score_layout(storey_type);
|
|
}
|
|
|
|
//
|
|
// Clear information.
|
|
//
|
|
|
|
ID_clear_inside_walls();
|
|
|
|
//
|
|
// Try the next seed.
|
|
//
|
|
|
|
find_best_start += 1;
|
|
find_best_start &= 0xffff;
|
|
}
|
|
|
|
//
|
|
// Which seed generates the best walls?
|
|
//
|
|
|
|
best_score = -INFINITY;
|
|
best_seed = 0;
|
|
|
|
for (i = 0; i < ID_MAX_FITS; i++)
|
|
{
|
|
if (fit[i].score > best_score)
|
|
{
|
|
best_score = fit[i].score;
|
|
best_seed = fit[i].seed;
|
|
}
|
|
}
|
|
|
|
if (best_score >= 0)
|
|
{
|
|
//
|
|
// Use the best seed to generate the walls.
|
|
//
|
|
|
|
ID_srand(best_seed);
|
|
ID_generate_inside_walls(storey_type);
|
|
seed_used = best_seed;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// What do we do now?
|
|
//
|
|
|
|
TRACE("Complete ID failure 2.\n");
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Use the seed we were given.
|
|
//
|
|
|
|
ID_generate_inside_walls(storey_type);
|
|
|
|
seed_used = seed;
|
|
}
|
|
|
|
#if YOU_WANT_THIN_WALLS
|
|
|
|
//
|
|
// Add the wall faces into the mapwho.
|
|
//
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
ID_add_wall_faces(i);
|
|
}
|
|
|
|
#else
|
|
|
|
//
|
|
// Add the faces for each room.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ID_add_room_faces(i);
|
|
}
|
|
|
|
#endif
|
|
|
|
ID_furn_upto = 0;
|
|
|
|
if (furnished)
|
|
{
|
|
//
|
|
// Add the furniture.
|
|
//
|
|
|
|
ID_place_furniture();
|
|
}
|
|
|
|
ASSERT(WITHIN(seed_used, 0, 65535));
|
|
ASSERT(find_good_layout || seed == seed_used);
|
|
|
|
return seed_used;
|
|
}
|
|
|
|
|
|
//
|
|
// Returns the position of (x,z) in stair space...
|
|
//
|
|
|
|
void ID_get_stair_space_position(
|
|
SLONG x,
|
|
SLONG z,
|
|
SLONG sx1, // The first stair square.
|
|
SLONG sz1,
|
|
SLONG sx2, // The second stair square.
|
|
SLONG sz2,
|
|
SLONG *stair_x,
|
|
SLONG *stair_z)
|
|
{
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG tx;
|
|
SLONG tz;
|
|
|
|
SLONG angle;
|
|
SLONG origin_x;
|
|
SLONG origin_z;
|
|
|
|
SLONG matrix[4];
|
|
|
|
dx = sx2 - sx1;
|
|
dz = sz2 - sz1;
|
|
|
|
//
|
|
// Find the origin and angle of the stairs.
|
|
//
|
|
|
|
if (dx == 0 && dz == 1)
|
|
{
|
|
origin_x = sx1 + 0 << 8;
|
|
origin_z = sz1 + 0 << 8;
|
|
|
|
angle = 0; // * 90 degrees...
|
|
}
|
|
else
|
|
if (dx == 1 && dz == 0)
|
|
{
|
|
origin_x = sx1 + 0 << 8;
|
|
origin_z = sz1 + 1 << 8;
|
|
|
|
angle = 1; // * 90 degrees...
|
|
}
|
|
else
|
|
if (dx == 0 && dz == -1)
|
|
{
|
|
origin_x = sx1 + 1 << 8;
|
|
origin_z = sz1 + 1 << 8;
|
|
|
|
angle = 2; // * 90 degrees...
|
|
}
|
|
else
|
|
if (dx == -1 && dz == 0)
|
|
{
|
|
origin_x = sx1 + 1 << 8;
|
|
origin_z = sz1 + 0 << 8;
|
|
|
|
angle = 3; // * 90 degrees...
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
|
|
//
|
|
// Find the 2d rotation matrix from angle.
|
|
//
|
|
// | cos a sin a |
|
|
// matrix = | |
|
|
// | -sin a cos a |
|
|
//
|
|
|
|
angle *= 512;
|
|
|
|
matrix[0] = COS(angle);
|
|
matrix[1] = -SIN(angle);
|
|
matrix[2] = SIN(angle);
|
|
matrix[3] = COS(angle);
|
|
|
|
//
|
|
// Find (sx,sz)... the position of (x,z) in stair-space.
|
|
//
|
|
|
|
tx = x - origin_x;
|
|
tz = z - origin_z;
|
|
|
|
*stair_x = MUL64(tx, matrix[0]) + MUL64(tz, matrix[1]);
|
|
*stair_z = MUL64(tx, matrix[2]) + MUL64(tz, matrix[3]);
|
|
|
|
//
|
|
// This is WIERD!!! I don't know why.
|
|
//
|
|
|
|
*stair_x = 0x100 - *stair_x;
|
|
}
|
|
|
|
//
|
|
// Returns the position you teleport up to.
|
|
//
|
|
|
|
void ID_teleport_up_pos(ID_Stair *it, SLONG *tx, SLONG *tz)
|
|
{
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
ASSERT(WITHIN(it, &ID_stair[0], &ID_stair[ID_num_stairs - 1]));
|
|
|
|
dx = it->x2 - it->x1 << 8;
|
|
dz = it->z2 - it->z1 << 8;
|
|
|
|
*tx = (it->x2 << 8) + (dx >> 1) - (dz >> 2) + (dx >> 5);
|
|
*tz = (it->z2 << 8) + (dz >> 1) + (dx >> 2) + (dz >> 5);
|
|
|
|
*tx = (it->x2 << 8) + 0x80;
|
|
*tz = (it->z2 << 8) + 0x80;
|
|
}
|
|
|
|
//
|
|
// Returns the position you teleport down to.
|
|
//
|
|
|
|
void ID_teleport_down_pos(ID_Stair *it, SLONG *tx, SLONG *tz)
|
|
{
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
ASSERT(WITHIN(it, &ID_stair[0], &ID_stair[ID_num_stairs - 1]));
|
|
|
|
dx = it->x2 - it->x1 << 8;
|
|
dz = it->z2 - it->z1 << 8;
|
|
|
|
*tx = (it->x2 << 8) - (dz >> 2) - (dx >> 5);
|
|
*tz = (it->z2 << 8) + (dx >> 2) - (dz >> 5);
|
|
|
|
*tx = (it->x1 << 8) + 0x80;
|
|
*tz = (it->z1 << 8) + 0x80;
|
|
}
|
|
|
|
SLONG ID_change_floor(
|
|
SLONG x,
|
|
SLONG z,
|
|
SLONG *new_x,
|
|
SLONG *new_z,
|
|
SLONG *handle)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG map_x = x >> 8;
|
|
SLONG map_z = z >> 8;
|
|
|
|
SLONG sx;
|
|
SLONG sz;
|
|
|
|
SLONG quadrant;
|
|
SLONG ans;
|
|
|
|
ID_Stair *it;
|
|
|
|
if (ID_FLOOR(map_x, map_z)->flag & ID_FLOOR_FLAG_STAIR)
|
|
{
|
|
//
|
|
// We are on some stairs. Which stair-case is it?
|
|
//
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
it = &ID_stair[i];
|
|
|
|
if ((it->x1 == map_x && it->z1 == map_z) ||
|
|
(it->x2 == map_x && it->z2 == map_z))
|
|
{
|
|
//
|
|
// This is the set of stairs we are on. Find our
|
|
// position in stair space.
|
|
//
|
|
|
|
ID_get_stair_space_position(
|
|
x, z,
|
|
it->x1, it->z1,
|
|
it->x2, it->z2,
|
|
&sx,
|
|
&sz);
|
|
|
|
// The stairs are all the same size.
|
|
//
|
|
|
|
ASSERT(
|
|
WITHIN(sx, 0, 0x100) &&
|
|
WITHIN(sz, 0, 0x200));
|
|
|
|
//
|
|
// The right hand side is always a corridor.
|
|
//
|
|
|
|
if (sx >= 0x80)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Which z-quadrant are we in?
|
|
//
|
|
|
|
quadrant = sz >> 7;
|
|
|
|
//
|
|
// Are we in the active quadrant?
|
|
//
|
|
|
|
if (quadrant == 2)
|
|
{
|
|
switch(it->type)
|
|
{
|
|
case ID_STAIR_TYPE_BOTTOM:
|
|
ans = +1;
|
|
break;
|
|
case ID_STAIR_TYPE_MIDDLE:
|
|
ans = (sz > 0x140) ? -1 : +1;
|
|
break;
|
|
case ID_STAIR_TYPE_TOP:
|
|
ans = -1;
|
|
break;
|
|
default:
|
|
ans = 0;
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Where we are on the next storey.
|
|
//
|
|
|
|
if (ans == +1)
|
|
{
|
|
ID_teleport_up_pos(it, new_x, new_z);
|
|
*handle = it->handle_up;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(ans == -1);
|
|
|
|
ID_teleport_down_pos(it, new_x, new_z);
|
|
*handle = it->handle_down;
|
|
}
|
|
|
|
return ans;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are on floor-square marked as being on some
|
|
// stairs, but we can't find those stairs!
|
|
//
|
|
|
|
ASSERT(0);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// You can only change floors if you are on some stairs!
|
|
//
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
void ID_get_floorplan_bounding_box(
|
|
SLONG *x1,
|
|
SLONG *z1,
|
|
SLONG *x2,
|
|
SLONG *z2)
|
|
{
|
|
*x1 = ID_floor_x1;
|
|
*z1 = ID_floor_z1;
|
|
*x2 = ID_floor_x2;
|
|
*z2 = ID_floor_z2;
|
|
}
|
|
|
|
SLONG ID_am_i_completely_outside(SLONG x, SLONG z)
|
|
{
|
|
ASSERT(WITHIN(x, 0, ID_FLOOR_SIZE - 1));
|
|
ASSERT(WITHIN(z, 0, ID_FLOOR_SIZE - 1));
|
|
|
|
//
|
|
// Outside here means completely outside
|
|
//
|
|
|
|
return !(ID_FLOOR(x,z)->flag & ID_FLOOR_FLAG_INSIDE);
|
|
}
|
|
|
|
SLONG ID_get_mapsquare_texture(SLONG x, SLONG z,
|
|
float *u0, float *v0,
|
|
float *u1, float *v1,
|
|
float *u2, float *v2,
|
|
float *u3, float *v3)
|
|
{
|
|
UWORD texture;
|
|
SLONG page;
|
|
|
|
ASSERT(WITHIN(ID_FLOOR(x,z)->texid, 0, ID_MAX_FLOOR_TEXTURES - 1));
|
|
|
|
texture = ID_floor_texture[ID_FLOOR(x,z)->texid];
|
|
|
|
SLONG i;
|
|
ID_Stair *it;
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
it = &ID_stair[i];
|
|
|
|
if (it->x1 == x && it->z1 == z) {texture = 3;}
|
|
if (it->x2 == x && it->z2 == z) {texture = rand() % ID_MAX_FLOOR_TEXTURES;}
|
|
}
|
|
|
|
page = ID_get_texture_uvs(texture, u0,v0, u1,v1, u2,v2, u3,v3);
|
|
|
|
return page;
|
|
}
|
|
|
|
SLONG ID_should_i_draw_mapsquare(SLONG x, SLONG z)
|
|
{
|
|
return !(ID_FLOOR(x,z)->flag & ID_FLOOR_FLAG_STAIR) || Keys[KB_8];
|
|
}
|
|
|
|
|
|
void ID_this_is_where_i_am(SLONG x, SLONG z)
|
|
{
|
|
SLONG i;
|
|
SLONG mx;
|
|
SLONG mz;
|
|
SLONG dx;
|
|
SLONG dz;
|
|
SLONG room;
|
|
|
|
//
|
|
// Mark all rooms an unseeable.
|
|
//
|
|
|
|
for (i = 1; i < ID_room_upto; i++)
|
|
{
|
|
ID_room[i].flag &= ~ID_ROOM_FLAG_SEEABLE;
|
|
}
|
|
|
|
if (WITHIN(x, ID_floor_x1, ID_floor_x2 - 1) &&
|
|
WITHIN(z, ID_floor_z1, ID_floor_z2 - 1))
|
|
{
|
|
room = ID_FLOOR(x,z)->room;
|
|
|
|
if (room == 0)
|
|
{
|
|
//
|
|
// The player isn't inside any room!
|
|
//
|
|
}
|
|
else
|
|
{
|
|
ASSERT(WITHIN(room, 1, ID_room_upto - 1));
|
|
|
|
//
|
|
// The room that the player is inside is seeable.
|
|
//
|
|
|
|
ID_room[room].flag |= ID_ROOM_FLAG_SEEABLE;
|
|
|
|
//
|
|
// If you are standing on a doorway, draw
|
|
// the rooms connected via this door.
|
|
//
|
|
|
|
if (ID_FLOOR(x,z)->flag & ID_FLOOR_FLAG_INMAT)
|
|
{
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
dx = 0;
|
|
dz = 0;
|
|
|
|
switch(i)
|
|
{
|
|
case 0: dx = -1; break;
|
|
case 1: dx = +1; break;
|
|
case 2: dz = -1; break;
|
|
case 3: dz = +1; break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
mx = x + dx;
|
|
mz = z + dz;
|
|
|
|
if (WITHIN(mx, ID_floor_x1, ID_floor_x2 - 1) &&
|
|
WITHIN(mz, ID_floor_z1, ID_floor_z2 - 1))
|
|
{
|
|
room = ID_FLOOR(mx,mz)->room;
|
|
|
|
if (room)
|
|
{
|
|
ASSERT(WITHIN(room, 0, ID_room_upto - 1));
|
|
|
|
//
|
|
// Mark this room as seeable too.
|
|
//
|
|
|
|
ID_room[room].flag |= ID_ROOM_FLAG_SEEABLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SLONG ID_should_i_draw(SLONG x, SLONG z)
|
|
{
|
|
SLONG room;
|
|
|
|
ASSERT(WITHIN(x, ID_floor_x1, ID_floor_x2 - 1));
|
|
ASSERT(WITHIN(z, ID_floor_z1, ID_floor_z2 - 1));
|
|
|
|
room = ID_FLOOR(x,z)->room;
|
|
|
|
ASSERT(WITHIN(room, 0, ID_room_upto - 1));
|
|
|
|
return (!Keys[KB_A] || (ID_room[room].flag & ID_ROOM_FLAG_SEEABLE));
|
|
}
|
|
|
|
|
|
//
|
|
// Accessing the faces.
|
|
//
|
|
|
|
SLONG ID_get_first_face(SLONG x, SLONG z)
|
|
{
|
|
SLONG ans;
|
|
|
|
ASSERT(WITHIN(x, ID_floor_x1, ID_floor_x2 - 1));
|
|
ASSERT(WITHIN(z, ID_floor_z1, ID_floor_z2 - 1));
|
|
|
|
ans = ID_FLOOR(x, z)->next;
|
|
|
|
return ans;
|
|
}
|
|
|
|
SLONG ID_is_face_a_quad(SLONG face)
|
|
{
|
|
SLONG ans;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
|
|
ans = ID_face[face].flag & ID_FACE_FLAG_QUAD;
|
|
|
|
return ans;
|
|
}
|
|
|
|
UWORD ID_get_face_texture(SLONG face)
|
|
{
|
|
UWORD ans;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
|
|
ans = ID_face[face].texture;
|
|
|
|
return ans;
|
|
}
|
|
|
|
SLONG ID_get_next_face(SLONG face)
|
|
{
|
|
SLONG ans;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
|
|
ans = ID_face[face].next;
|
|
|
|
return ans;
|
|
}
|
|
|
|
|
|
//
|
|
// Accessing the points of the faces.
|
|
//
|
|
|
|
void ID_clear_indices()
|
|
{
|
|
SLONG i;
|
|
|
|
for (i = ID_point_upto - 1; i >= 0; i--)
|
|
{
|
|
ASSERT(WITHIN(i, 0, ID_MAX_POINTS - 1));
|
|
|
|
ID_point[i].index = 0;
|
|
}
|
|
}
|
|
|
|
SLONG ID_is_point_a_mapsquare(SLONG face, SLONG point)
|
|
{
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
ASSERT(WITHIN(point, 0, 3 + ((ID_face[face].flag & ID_FACE_FLAG_QUAD) ? 1 : 0)));
|
|
|
|
return (ID_face[face].flag & (ID_FACE_FLAG_ONFLOOR0 << point));
|
|
}
|
|
|
|
void ID_get_point_mapsquare(SLONG face, SLONG point, SLONG *x, SLONG *z)
|
|
{
|
|
SLONG p_index;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
ASSERT(WITHIN(point, 0, 3 + ((ID_face[face].flag & ID_FACE_FLAG_QUAD) ? 1 : 0)));
|
|
|
|
ASSERT(ID_is_point_a_mapsquare(face, point));
|
|
|
|
p_index = ID_face[face].point[point];
|
|
|
|
*x = p_index & 0xff;
|
|
*z = p_index >> 8;
|
|
}
|
|
|
|
void ID_get_point_position(SLONG face, SLONG point, SLONG *x, SLONG *y, SLONG *z)
|
|
{
|
|
SLONG p_index;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
ASSERT(WITHIN(point, 0, 3 + ((ID_face[face].flag & ID_FACE_FLAG_QUAD) ? 1 : 0)));
|
|
|
|
ASSERT(!ID_is_point_a_mapsquare(face, point));
|
|
|
|
p_index = ID_face[face].point[point];
|
|
|
|
ASSERT(WITHIN(p_index, 0, ID_point_upto - 1));
|
|
|
|
*x = ID_point[p_index].x;
|
|
*y = ID_point[p_index].y;
|
|
*z = ID_point[p_index].z;
|
|
|
|
//
|
|
// Incase ELE_SHIFT stops being 8 and we need to
|
|
// fix the shifts...
|
|
//
|
|
|
|
ASSERT(ELE_SHIFT == 8);
|
|
}
|
|
|
|
UWORD ID_get_point_index(SLONG face, SLONG point)
|
|
{
|
|
SLONG p_index;
|
|
UWORD ans;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
ASSERT(WITHIN(point, 0, 3 + ((ID_face[face].flag & ID_FACE_FLAG_QUAD) ? 1 : 0)));
|
|
|
|
ASSERT(!ID_is_point_a_mapsquare(face, point));
|
|
|
|
p_index = ID_face[face].point[point];
|
|
|
|
ASSERT(WITHIN(p_index, 0, ID_point_upto - 1));
|
|
|
|
ans = ID_point[p_index].index;
|
|
|
|
return ans;
|
|
}
|
|
|
|
void ID_set_point_index(SLONG face, SLONG point, UWORD index)
|
|
{
|
|
SLONG p_index;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
ASSERT(WITHIN(point, 0, 3 + ((ID_face[face].flag & ID_FACE_FLAG_QUAD) ? 1 : 0)));
|
|
|
|
ASSERT(!ID_is_point_a_mapsquare(face, point));
|
|
|
|
p_index = ID_face[face].point[point];
|
|
|
|
ASSERT(WITHIN(p_index, 0, ID_point_upto - 1));
|
|
|
|
ID_point[p_index].index = index;
|
|
}
|
|
|
|
|
|
UBYTE ID_get_mapsquare_room(SLONG x, SLONG z)
|
|
{
|
|
UBYTE ans;
|
|
|
|
if (!WITHIN(x, ID_floor_x1, ID_floor_x2 - 1) ||
|
|
!WITHIN(z, ID_floor_z1, ID_floor_z2 - 1))
|
|
{
|
|
//
|
|
// Outside the bounding box...
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (!(ID_FLOOR(x, z)->flag & ID_FLOOR_FLAG_INSIDE))
|
|
{
|
|
//
|
|
// Not inside the building.
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
ans = ID_FLOOR(x,z)->room;
|
|
|
|
return ans;
|
|
}
|
|
|
|
void ID_get_room_camera(UBYTE room, SLONG *x, SLONG *y, SLONG *z)
|
|
{
|
|
ASSERT(WITHIN(room, 1, ID_room_upto - 1));
|
|
|
|
*x = ID_room[room].cam_x;
|
|
*z = ID_room[room].cam_z;
|
|
|
|
*y = INDOORS_HEIGHT_CEILING - 0x10;
|
|
}
|
|
|
|
|
|
void ID_remove_inside_things(void)
|
|
{
|
|
SLONG i;
|
|
|
|
Thing *p_thing;
|
|
|
|
#define ID_MAX_INSIDE_THINGS 64
|
|
|
|
THING_INDEX inside_thing[ID_MAX_INSIDE_THINGS];
|
|
SLONG num_inside_things;
|
|
|
|
num_inside_things = THING_find_box(
|
|
ID_floor_x1,
|
|
ID_floor_z1,
|
|
ID_floor_x2,
|
|
ID_floor_z2,
|
|
inside_thing,
|
|
ID_MAX_INSIDE_THINGS,
|
|
THING_FIND_EVERYTHING);
|
|
|
|
for (i = 0; i < num_inside_things; i++)
|
|
{
|
|
p_thing = TO_THING(inside_thing[i]);
|
|
|
|
if (p_thing->Flags & FLAGS_INDOORS_GENERATED)
|
|
{
|
|
//
|
|
// Remove this thing.
|
|
//
|
|
|
|
THING_kill(p_thing);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SLONG ID_get_face_texture(SLONG face,
|
|
float *u0, float *v0,
|
|
float *u1, float *v1,
|
|
float *u2, float *v2,
|
|
float *u3, float *v3)
|
|
{
|
|
UWORD texture;
|
|
SLONG page;
|
|
|
|
ASSERT(WITHIN(face, 1, ID_face_upto - 1));
|
|
|
|
texture = ID_face[face].texture;
|
|
|
|
page = ID_get_texture_uvs(texture, u0,v0, u1,v1, u2,v2, u3,v3);
|
|
|
|
return page;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ########################################################
|
|
// ========================================================
|
|
//
|
|
// COLLISION CODE...
|
|
//
|
|
// ========================================================
|
|
// ########################################################
|
|
|
|
|
|
SLONG ID_collide_3d(
|
|
SLONG x1, SLONG y1, SLONG z1,
|
|
SLONG x2, SLONG y2, SLONG z2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
SLONG ID_calc_height_at(SLONG x, SLONG z)
|
|
{
|
|
SLONG i;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG origin_x;
|
|
SLONG origin_z;
|
|
SLONG angle;
|
|
|
|
SLONG tx;
|
|
SLONG tz;
|
|
|
|
SLONG sx;
|
|
SLONG sz;
|
|
|
|
SLONG ans;
|
|
|
|
SLONG matrix[4];
|
|
|
|
//
|
|
// I can't be arsed to write ELE_SHIFT all the time, lets hope
|
|
// it stays the same... I bet it will!
|
|
//
|
|
|
|
ASSERT(ELE_SHIFT == 8);
|
|
|
|
SLONG x_map = x >> 8;
|
|
SLONG z_map = z >> 8;
|
|
|
|
ID_Square *is;
|
|
ID_Stair *it;
|
|
|
|
is = ID_FLOOR(x_map, z_map);
|
|
|
|
//
|
|
// Make sure this square is inside the building.
|
|
//
|
|
|
|
ASSERT(is->flag & ID_FLOOR_FLAG_INSIDE);
|
|
|
|
//
|
|
// If we are not on some stairs, then it is easy!
|
|
//
|
|
|
|
if (!(is->flag & ID_FLOOR_FLAG_STAIR))
|
|
{
|
|
return INDOORS_HEIGHT_FLOOR;
|
|
}
|
|
|
|
//
|
|
// We are on a square with some stairs. Which set
|
|
// of stairs is it?
|
|
//
|
|
|
|
for (i = 0; i < ID_num_stairs; i++)
|
|
{
|
|
it = &ID_stair[i];
|
|
|
|
if ((x_map == it->x1 && z_map == it->z1) ||
|
|
(x_map == it->x2 && z_map == it->z2))
|
|
{
|
|
//
|
|
// This is the set of stairs we want.
|
|
//
|
|
|
|
goto found_stairs;
|
|
}
|
|
}
|
|
|
|
//
|
|
// A very strange occurence! The floor flags says that there
|
|
// are some stairs here, but we can't find a set of stairs
|
|
// in the array here.
|
|
//
|
|
|
|
ASSERT(0);
|
|
|
|
return INDOORS_HEIGHT_FLOOR;
|
|
|
|
found_stairs:
|
|
|
|
//
|
|
// Find our position in stair-space.
|
|
//
|
|
|
|
ID_get_stair_space_position(
|
|
x, z,
|
|
it->x1, it->z1,
|
|
it->x2, it->z2,
|
|
&sx,
|
|
&sz);
|
|
|
|
//
|
|
// The stairs are all the same size.
|
|
//
|
|
|
|
ASSERT(
|
|
WITHIN(sx, 0, 0x100) &&
|
|
WITHIN(sz, 0, 0x200));
|
|
|
|
//
|
|
// The right hand side is always a corridor.
|
|
//
|
|
|
|
if (sx >= 0x80)
|
|
{
|
|
return INDOORS_HEIGHT_FLOOR;
|
|
}
|
|
|
|
//
|
|
// From now on it depends on the type of stair...
|
|
//
|
|
|
|
switch(it->type)
|
|
{
|
|
case ID_STAIR_TYPE_BOTTOM:
|
|
|
|
// _
|
|
// /
|
|
// _/ Ground
|
|
|
|
ans = sz - 0x80 + INDOORS_HEIGHT_FLOOR;
|
|
|
|
SATURATE(ans, INDOORS_HEIGHT_FLOOR, INDOORS_HEIGHT_CEILING);
|
|
|
|
break;
|
|
|
|
case ID_STAIR_TYPE_MIDDLE:
|
|
|
|
//
|
|
// _/ _ Ground
|
|
// /
|
|
|
|
ans = sz - 0x80 + INDOORS_HEIGHT_FLOOR;
|
|
|
|
SATURATE(ans, INDOORS_HEIGHT_FLOOR, INDOORS_HEIGHT_CEILING);
|
|
|
|
if (sz >= 0x100)
|
|
{
|
|
ans -= INDOORS_HEIGHT_CEILING - INDOORS_HEIGHT_FLOOR;
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_STAIR_TYPE_TOP:
|
|
|
|
// _ _ Ground
|
|
// /
|
|
// /
|
|
|
|
if (WITHIN(sz, 0x80, 0x180))
|
|
{
|
|
ans = INDOORS_HEIGHT_FLOOR -0x100 + sz;
|
|
}
|
|
else
|
|
{
|
|
ans = INDOORS_HEIGHT_FLOOR;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
return ans;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SLONG ID_collide_2d(
|
|
SLONG x1, SLONG z1,
|
|
SLONG x2, SLONG z2,
|
|
SLONG radius,
|
|
SLONG *slide_x,
|
|
SLONG *slide_z)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG wx1;
|
|
SLONG wz1;
|
|
SLONG wx2;
|
|
SLONG wz2;
|
|
|
|
SLONG min;
|
|
SLONG max;
|
|
|
|
SLONG start;
|
|
SLONG end;
|
|
SLONG doorway_start;
|
|
SLONG doorway_end;
|
|
|
|
SLONG collided;
|
|
|
|
ID_Wall *iw;
|
|
|
|
//
|
|
// Collision with the walls treated as sausages
|
|
//
|
|
|
|
collided = FALSE;
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
dx = iw->x2 - iw->x1;
|
|
dz = iw->z2 - iw->z1;
|
|
|
|
wx1 = iw->x1 << ELE_SHIFT;
|
|
wz1 = iw->z1 << ELE_SHIFT;
|
|
wx2 = iw->x2 << ELE_SHIFT;
|
|
wz2 = iw->z2 << ELE_SHIFT;
|
|
|
|
//
|
|
// No diagonal walls nowadays.
|
|
//
|
|
|
|
ASSERT(dx == 0 || dz == 0);
|
|
|
|
if (dx == 0)
|
|
{
|
|
if (WITHIN(x2, wx1 - radius + 1, wx1 + radius - 1))
|
|
{
|
|
min = wz1;
|
|
max = wz2;
|
|
|
|
if (max < min) {SWAP(min, max);}
|
|
|
|
min -= radius - 1;
|
|
max += radius - 1;
|
|
|
|
if (WITHIN(z2, min, max))
|
|
{
|
|
//
|
|
// Make sure the doors are sorted.
|
|
//
|
|
|
|
ASSERT(iw->door[0] <= iw->door[1]);
|
|
ASSERT(iw->door[1] <= iw->door[2]);
|
|
|
|
//
|
|
// Find each of the sausages along this wall.
|
|
//
|
|
|
|
start = wz1;
|
|
end = wz2;
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (iw->door[j] == 255)
|
|
{
|
|
//
|
|
// No more doors.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The z-range of the doorway.
|
|
//
|
|
|
|
if (dz > 0)
|
|
{
|
|
doorway_start = wz1 + (iw->door[j] << ELE_SHIFT) + 0x30;
|
|
doorway_end = wz1 + (iw->door[j] << ELE_SHIFT) + 0xd0;
|
|
}
|
|
else
|
|
{
|
|
doorway_start = wz1 - (iw->door[j] << ELE_SHIFT) - 0x30;
|
|
doorway_end = wz1 - (iw->door[j] << ELE_SHIFT) - 0xd0;
|
|
}
|
|
|
|
//
|
|
// Collide against a sausage.
|
|
//
|
|
|
|
if (collide_against_sausage(
|
|
wx1, start,
|
|
wx1, doorway_start,
|
|
radius,
|
|
x1, z1,
|
|
x2, z2,
|
|
slide_x,
|
|
slide_z))
|
|
{
|
|
collided = TRUE;
|
|
|
|
x2 = *slide_x;
|
|
z2 = *slide_z;
|
|
|
|
//
|
|
// You can only ever collide with one sausage from each wall.
|
|
//
|
|
|
|
goto try_the_next_wall;
|
|
}
|
|
|
|
start = doorway_end;
|
|
}
|
|
|
|
if (collide_against_sausage(
|
|
wx1, start,
|
|
wx1, end,
|
|
radius,
|
|
x1, z1,
|
|
x2, z2,
|
|
slide_x,
|
|
slide_z))
|
|
{
|
|
collided = TRUE;
|
|
|
|
x2 = *slide_x;
|
|
z2 = *slide_z;
|
|
|
|
//
|
|
// You can only ever collide with one sausage from each wall.
|
|
//
|
|
|
|
goto try_the_next_wall;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(dz == 0);
|
|
|
|
if (WITHIN(z2, wz1 - radius + 1, wz1 + radius - 1))
|
|
{
|
|
min = wx1;
|
|
max = wx2;
|
|
|
|
if (max < min) {SWAP(min, max);}
|
|
|
|
min -= radius - 1;
|
|
max += radius - 1;
|
|
|
|
if (WITHIN(x2, min, max))
|
|
{
|
|
//
|
|
// Make sure the doors are sorted.
|
|
//
|
|
|
|
ASSERT(iw->door[0] <= iw->door[1]);
|
|
ASSERT(iw->door[1] <= iw->door[2]);
|
|
|
|
//
|
|
// Find each of the sausages along this wall.
|
|
//
|
|
|
|
start = wx1;
|
|
end = wx2;
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (iw->door[j] == 255)
|
|
{
|
|
//
|
|
// No more doors.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The z-range of the doorway.
|
|
//
|
|
|
|
if (dx > 0)
|
|
{
|
|
doorway_start = wx1 + (iw->door[j] << ELE_SHIFT) + 0x30;
|
|
doorway_end = wx1 + (iw->door[j] << ELE_SHIFT) + 0xd0;
|
|
}
|
|
else
|
|
{
|
|
doorway_start = wx1 - (iw->door[j] << ELE_SHIFT) - 0x30;
|
|
doorway_end = wx1 - (iw->door[j] << ELE_SHIFT) - 0xd0;
|
|
}
|
|
|
|
//
|
|
// Collide against a sausage.
|
|
//
|
|
|
|
if (collide_against_sausage(
|
|
start, wz1,
|
|
doorway_start, wz1,
|
|
radius,
|
|
x1, z1,
|
|
x2, z2,
|
|
slide_x,
|
|
slide_z))
|
|
{
|
|
collided = TRUE;
|
|
|
|
x2 = *slide_x;
|
|
z2 = *slide_z;
|
|
|
|
//
|
|
// You can only ever collide with one sausage from each wall.
|
|
//
|
|
|
|
goto try_the_next_wall;
|
|
}
|
|
|
|
start = doorway_end;
|
|
}
|
|
|
|
if (collide_against_sausage(
|
|
start, wz1,
|
|
end, wz1,
|
|
radius,
|
|
x1, z1,
|
|
x2, z2,
|
|
slide_x,
|
|
slide_z))
|
|
{
|
|
collided = TRUE;
|
|
|
|
x2 = *slide_x;
|
|
z2 = *slide_z;
|
|
|
|
//
|
|
// You can only ever collide with one sausage from each wall.
|
|
//
|
|
|
|
goto try_the_next_wall;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try_the_next_wall:;
|
|
}
|
|
|
|
return collided;
|
|
}
|
|
|
|
|
|
|
|
SLONG ID_collide_2d_old(
|
|
SLONG x1, SLONG z1,
|
|
SLONG x2, SLONG z2,
|
|
SLONG *slide_x,
|
|
SLONG *slide_z)
|
|
{
|
|
SLONG i;
|
|
SLONG j;
|
|
|
|
SLONG dx;
|
|
SLONG dz;
|
|
|
|
SLONG wx1;
|
|
SLONG wz1;
|
|
SLONG wx2;
|
|
SLONG wz2;
|
|
|
|
SLONG dx1;
|
|
SLONG dz1;
|
|
SLONG dx2;
|
|
SLONG dz2;
|
|
|
|
SLONG sx;
|
|
SLONG sz;
|
|
|
|
SLONG pushx;
|
|
SLONG pushz;
|
|
|
|
SLONG dprod;
|
|
SLONG length;
|
|
SLONG collided;
|
|
SLONG through;
|
|
|
|
SLONG bot;
|
|
SLONG top;
|
|
|
|
ID_Wall *iw;
|
|
|
|
//
|
|
// Collide with all the walls.
|
|
//
|
|
|
|
collided = FALSE;
|
|
|
|
for (i = 0; i < ID_wall_upto; i++)
|
|
{
|
|
iw = &ID_wall[i];
|
|
|
|
dx = iw->x2 - iw->x1;
|
|
dz = iw->z2 - iw->z1;
|
|
|
|
wx1 = iw->x1 << ELE_SHIFT;
|
|
wz1 = iw->z1 << ELE_SHIFT;
|
|
wx2 = iw->x2 << ELE_SHIFT;
|
|
wz2 = iw->z2 << ELE_SHIFT;
|
|
|
|
if (dx && dz)
|
|
{
|
|
//
|
|
// This must be a wonky outside wall. Do the two line segments
|
|
// intersect?
|
|
//
|
|
|
|
if (MATHS_seg_intersect(wx1, wz1, wx2, wz2, x1, z1, x2, z2))
|
|
{
|
|
//
|
|
// The lines intersect. Slide along.
|
|
//
|
|
|
|
dx1 = wx2 - wx1;
|
|
dz1 = wz2 - wz1;
|
|
|
|
dx2 = x2 - x1;
|
|
dz2 = z2 - z1;
|
|
|
|
dprod = dx1*dx2 + dz1*dz2;
|
|
length = QDIST2(abs(dx1), abs(dz1));
|
|
|
|
sx = dx1 * dprod / length;
|
|
sz = dz1 * dprod / length;
|
|
|
|
sx >>= 8;
|
|
sz >>= 8;
|
|
|
|
*slide_x = x1 + sx;
|
|
*slide_z = z1 + sz;
|
|
|
|
x2 = *slide_x;
|
|
z2 = *slide_z;
|
|
|
|
collided = TRUE;
|
|
|
|
//
|
|
// Go through all the walls again!
|
|
//
|
|
|
|
i = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is easier because the wall is orthogonal.
|
|
//
|
|
|
|
if (dx == 0)
|
|
{
|
|
through = FALSE;
|
|
|
|
//
|
|
// Collide with the wall if we start and end on
|
|
// either side of it or we end on it.
|
|
//
|
|
|
|
if (x2 == wx1)
|
|
{
|
|
if (x1 != wx1)
|
|
{
|
|
//
|
|
// Starting and ending on the wall doesn't count.
|
|
//
|
|
|
|
through = TRUE;
|
|
}
|
|
}
|
|
else
|
|
if ((x1 < wx1 && x2 > wx1) ||
|
|
(x1 > wx1 && x2 < wx1))
|
|
{
|
|
through = TRUE;
|
|
}
|
|
|
|
if (through)
|
|
{
|
|
//
|
|
// The line crosses the line defined the wall. Does it cross
|
|
// at the wall? We can use this approximation if we assume that
|
|
// the line segments we deal with are smallish.
|
|
//
|
|
|
|
//
|
|
// The top and bottom of the wall.
|
|
//
|
|
|
|
top = wz1;
|
|
bot = wz2;
|
|
|
|
if (top < bot) {SWAP(top, bot);}
|
|
|
|
//
|
|
// Don't bother working out the z position of the line segment
|
|
// at wx1.
|
|
//
|
|
|
|
if (WITHIN(z1, bot, top) || WITHIN(z2, bot, top))
|
|
{
|
|
//
|
|
// Check for doors.
|
|
//
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (iw->door[j] != 255)
|
|
{
|
|
bot = iw->z1;
|
|
bot += iw->door[j] * SIGN(dz);
|
|
bot <<= ELE_SHIFT;
|
|
top = bot;
|
|
|
|
bot += 0x30 * SIGN(dz);
|
|
top += 0xd0 * SIGN(dz);
|
|
|
|
if (top < bot) {SWAP(top, bot);}
|
|
|
|
if (WITHIN(z1, bot, top) && WITHIN(z2, bot, top))
|
|
{
|
|
//
|
|
// We have walked through a doorframe.
|
|
//
|
|
|
|
goto passed_through_door;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is an intersection.. take out the x component from
|
|
// the movement vector.
|
|
//
|
|
|
|
*slide_x = x1;
|
|
*slide_z = z2;
|
|
|
|
*slide_x += (x2 < x1) ? 0x1f : -0x1f;
|
|
|
|
x2 = *slide_x;
|
|
z2 = *slide_z;
|
|
|
|
collided = TRUE;
|
|
|
|
//
|
|
// Go through all the walls again!
|
|
//
|
|
|
|
i = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(dz == 0);
|
|
|
|
through = FALSE;
|
|
|
|
//
|
|
// Collide with the wall if we start and end on
|
|
// either side of it or we end on it.
|
|
//
|
|
|
|
if (z2 == wz1)
|
|
{
|
|
if (z1 != wz1)
|
|
{
|
|
//
|
|
// Starting and ending on the wall doesn't count.
|
|
//
|
|
|
|
through = TRUE;
|
|
}
|
|
}
|
|
else
|
|
if ((z1 < wz1 && z2 > wz1) ||
|
|
(z1 > wz1 && z2 < wz1))
|
|
{
|
|
through = TRUE;
|
|
}
|
|
|
|
if (through)
|
|
{
|
|
//
|
|
// The line crosses the line defined the wall. Does it cross
|
|
// at the wall? We can use this approximation if we assume that
|
|
// the line segments we deal with are smallish.
|
|
//
|
|
|
|
//
|
|
// The top and bottom of the wall.
|
|
//
|
|
|
|
top = wx1;
|
|
bot = wx2;
|
|
|
|
if (top < bot) {SWAP(top, bot);}
|
|
|
|
//
|
|
// Don't bother working out the z position of the line segment
|
|
// at wx1.
|
|
//
|
|
|
|
if (WITHIN(x1, bot, top) || WITHIN(x2, bot, top))
|
|
{
|
|
//
|
|
// Check for doors.
|
|
//
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (iw->door[j] != 255)
|
|
{
|
|
bot = iw->x1;
|
|
bot += iw->door[j] * SIGN(dx);
|
|
bot <<= ELE_SHIFT;
|
|
top = bot;
|
|
|
|
bot += 0x30 * SIGN(dx);
|
|
top += 0xd0 * SIGN(dx);
|
|
|
|
if (top < bot) {SWAP(top, bot);}
|
|
|
|
if (WITHIN(x1, bot, top) && WITHIN(x2, bot, top))
|
|
{
|
|
//
|
|
// We have walked through a doorframe.
|
|
//
|
|
|
|
goto passed_through_door;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is an intersection.. take out the x component from
|
|
// the movement vector.
|
|
//
|
|
|
|
*slide_x = x2;
|
|
*slide_z = z1;
|
|
|
|
//*slide_z += (z2 < z1) ? 0xf : -0xf;
|
|
|
|
x2 = *slide_x;
|
|
z2 = *slide_z;
|
|
|
|
collided = TRUE;
|
|
|
|
//
|
|
// Go through all the walls again!
|
|
//
|
|
|
|
i = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
passed_through_door:;
|
|
}
|
|
|
|
return collided;
|
|
}
|
|
|
|
|
|
|
|
SLONG ID_editor_get_room_upto;
|
|
SLONG ID_editor_get_wall_upto;
|
|
SLONG ID_editor_get_stair_upto;
|
|
|
|
void ID_editor_start_get_rooms () {ID_editor_get_room_upto = 1;}
|
|
void ID_editor_start_get_walls () {ID_editor_get_wall_upto = 0;}
|
|
void ID_editor_start_get_stairs() {ID_editor_get_stair_upto = 0;}
|
|
|
|
//
|
|
// These functions return FALSE if there are no more rooms, walls
|
|
// or stairs, otherwise they fill out the given structure with
|
|
// info describing the next room, wall or staircase.
|
|
//
|
|
|
|
#define ID_MAX_ROOM_NAME 32
|
|
|
|
CBYTE ID_room_name[ID_MAX_ROOM_NAME];
|
|
|
|
SLONG ID_editor_get_room(ID_Roominfo *ans)
|
|
{
|
|
SLONG room;
|
|
|
|
if (ID_editor_get_room_upto >= ID_room_upto)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(WITHIN(ID_editor_get_room_upto, 1, ID_room_upto - 1));
|
|
|
|
SLONG x;
|
|
SLONG z;
|
|
|
|
ID_Room *ir = &ID_room[ID_editor_get_room_upto];
|
|
|
|
for (x = ir->x1; x <= ir->x2; x++)
|
|
for (z = ir->z1; z <= ir->z2; z++)
|
|
{
|
|
room = ID_FLOOR(x, z)->room;
|
|
|
|
if (room == ID_editor_get_room_upto)
|
|
{
|
|
ans->x = x;
|
|
ans->z = z;
|
|
|
|
switch(ir->type)
|
|
{
|
|
case ID_ROOM_TYPE_LOO: ans->what = "Loo"; break;
|
|
case ID_ROOM_TYPE_KITCHEN: ans->what = "Kitchen"; break;
|
|
case ID_ROOM_TYPE_LOUNGE: ans->what = "Lounge"; break;
|
|
case ID_ROOM_TYPE_LOBBY: ans->what = "Lobby"; break;
|
|
case ID_ROOM_TYPE_DINING: ans->what = "Dining"; break;
|
|
case ID_ROOM_TYPE_WAREHOUSE: ans->what = "Warehouse"; break;
|
|
case ID_ROOM_TYPE_OFFICE: ans->what = "Office"; break;
|
|
case ID_ROOM_TYPE_MEETING: ans->what = "Meeting room"; break;
|
|
case ID_ROOM_TYPE_BEDROOM: ans->what = "Bedroom"; break;
|
|
case ID_ROOM_TYPE_CORRIDOR: ans->what = "Corridor"; break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the next room next time.
|
|
//
|
|
|
|
ID_editor_get_room_upto += 1;
|
|
|
|
if (ID_room[room].flat)
|
|
{
|
|
sprintf(ID_room_name, "%d:%s", ID_room[room].flat, ans->what);
|
|
|
|
ans->what = ID_room_name;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// A bit of error recovery...
|
|
//
|
|
|
|
ans->x = ir->x1;
|
|
ans->z = ir->z1;
|
|
ans->what = "ERROR";
|
|
|
|
ID_editor_get_room_upto += 1;
|
|
|
|
//
|
|
// No room square in the bounding box of the room!
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
SLONG ID_editor_get_wall(ID_Wallinfo *ans)
|
|
{
|
|
ID_Wall *iw;
|
|
|
|
while(1)
|
|
{
|
|
if (ID_editor_get_wall_upto >= ID_wall_upto)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(WITHIN(ID_editor_get_wall_upto, 0, ID_wall_upto - 1));
|
|
|
|
iw = &ID_wall[ID_editor_get_wall_upto++];
|
|
|
|
if (iw->type == ID_WALL_T_OUTSIDE)
|
|
{
|
|
//
|
|
// Skip outside walls.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
ans->x1 = iw->x1;
|
|
ans->z1 = iw->z1;
|
|
ans->x2 = iw->x2;
|
|
ans->z2 = iw->z2;
|
|
|
|
ans->door[0] = iw->door[0];
|
|
ans->door[1] = iw->door[1];
|
|
ans->door[2] = iw->door[2];
|
|
ans->door[3] = 255; // Never a fourth door...
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SLONG ID_editor_get_stair(ID_Stairinfo *ans)
|
|
{
|
|
if (ID_editor_get_stair_upto >= ID_num_stairs)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(WITHIN(ID_editor_get_stair_upto, 0, ID_num_stairs - 1));
|
|
|
|
ID_Stair *it = &ID_stair[ID_editor_get_stair_upto];
|
|
|
|
ans->x1 = it->x1;
|
|
ans->z1 = it->z1;
|
|
ans->x2 = it->x2;
|
|
ans->z2 = it->z2;
|
|
|
|
//
|
|
// Get the next staircase next time.
|
|
//
|
|
|
|
ID_editor_get_stair_upto += 1;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ID_Finfo ID_get_finfo;
|
|
|
|
SLONG ID_get_num_furn()
|
|
{
|
|
return ID_furn_upto;
|
|
}
|
|
|
|
ID_Finfo *ID_get_furn(SLONG number)
|
|
{
|
|
ASSERT(WITHIN(number, 0, ID_furn_upto - 1));
|
|
|
|
ID_get_finfo.x = ID_furn[number].x;
|
|
ID_get_finfo.y = INDOORS_HEIGHT_FLOOR;
|
|
ID_get_finfo.z = ID_furn[number].z;
|
|
ID_get_finfo.prim = ID_furn[number].prim;
|
|
ID_get_finfo.yaw = ID_furn[number].yaw << 3;
|
|
|
|
return &ID_get_finfo;
|
|
}
|
|
|
|
#else
|
|
//
|
|
// no need to do psx version of this unused stuff, but lets get it compiling
|
|
//
|
|
|
|
SLONG ID_calc_height_at(SLONG x, SLONG z)
|
|
{
|
|
}
|
|
|
|
SLONG ID_get_num_furn()
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
ID_Finfo *ID_get_furn(SLONG number)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
SLONG ID_collide_2d(
|
|
SLONG x1, SLONG z1,
|
|
SLONG x2, SLONG z2,
|
|
SLONG radius,
|
|
SLONG *slide_x,
|
|
SLONG *slide_z)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
void ID_this_is_where_i_am(SLONG x, SLONG z)
|
|
{
|
|
}
|
|
|
|
void ID_get_floorplan_bounding_box(
|
|
SLONG *x1,
|
|
SLONG *z1,
|
|
SLONG *x2,
|
|
SLONG *z2)
|
|
{
|
|
*x1=0;
|
|
*z1=0;
|
|
*x2=0;
|
|
*z2=0;
|
|
}
|
|
|
|
#endif
|