room: refactor tilt and portal interpretation (#1433)

This introduces a floor data master parser to allow setting all
relevant properties for sectors once. This is applied initially to
floor and ceiling tilts, and horizontal portals. Related refactors
have been undertaken, such as combining all portal values into a struct
in the sector, and simplifying sector traversal.
This commit is contained in:
lahm86 2024-08-08 19:51:47 +01:00 committed by GitHub
parent 330a57d2b9
commit 3405e8ca80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 239 additions and 195 deletions

View file

@ -248,7 +248,7 @@ static void Carrier_AnimateDrop(CARRIED_ITEM *item)
Room_GetHeight(sector, pickup->pos.x, pickup->pos.y, pickup->pos.z);
const bool in_water = g_RoomInfo[pickup->room_number].flags & RF_UNDERWATER;
if (sector->pit_room == NO_ROOM && pickup->pos.y >= height) {
if (sector->portal_room.pit == NO_ROOM && pickup->pos.y >= height) {
item->status = DS_DROPPED;
pickup->pos.y = height;
pickup->fall_speed = 0;
@ -260,9 +260,9 @@ static void Carrier_AnimateDrop(CARRIED_ITEM *item)
pickup->pos.y += pickup->fall_speed;
pickup->rot.y += in_water ? DROP_SLOW_TURN : DROP_FAST_TURN;
if (sector->pit_room != NO_ROOM
if (sector->portal_room.pit != NO_ROOM
&& pickup->pos.y > sector->floor.height) {
room_num = sector->pit_room;
room_num = sector->portal_room.pit;
}
}

View file

@ -212,9 +212,9 @@ static bool Level_LoadRooms(MYFILE *fp)
SECTOR_INFO *const sector = &current_room_info->sectors[j];
sector->index = File_ReadU16(fp);
sector->box = File_ReadS16(fp);
sector->pit_room = File_ReadU8(fp);
sector->portal_room.pit = File_ReadU8(fp);
const int8_t floor_clicks = File_ReadS8(fp);
sector->sky_room = File_ReadU8(fp);
sector->portal_room.sky = File_ReadU8(fp);
const int8_t ceiling_clicks = File_ReadS8(fp);
sector->floor.height = floor_clicks * STEP_L;
@ -853,6 +853,11 @@ static void Level_CompleteSetup(int32_t level_num)
{
Inject_AllInjections(&m_LevelInfo);
// Expand raw floor data into sectors
Room_ParseFloorData(g_FloorData);
// TODO: store raw FD temporarily in m_LevelInfo, release here and eliminate
// g_FloorData
// Must be called post-injection to allow for floor data changes.
Stats_ObserveRoomsLoad();

View file

@ -113,7 +113,7 @@ static void Bridge_FixEmbeddedPosition(int16_t item_num)
// Only move the bridge up if it's at floor level and there
// isn't a room portal below.
if (item->floor != floor_height || sector->pit_room != NO_ROOM) {
if (item->floor != floor_height || sector->portal_room.pit != NO_ROOM) {
return;
}

View file

@ -55,8 +55,8 @@ static void Door_Shut(DOORPOS_DATA *const d)
sector->box = NO_BOX;
sector->floor.height = NO_HEIGHT;
sector->ceiling.height = NO_HEIGHT;
sector->sky_room = NO_ROOM;
sector->pit_room = NO_ROOM;
sector->portal_room.sky = NO_ROOM;
sector->portal_room.pit = NO_ROOM;
const int16_t box_num = d->block;
if (box_num != NO_BOX) {
@ -119,7 +119,7 @@ void Door_Initialise(int16_t item_num)
z_sector = ((item->pos.z - r->z) >> WALL_SHIFT) + dx;
x_sector = ((item->pos.x - r->x) >> WALL_SHIFT) + dy;
door->d1.sector = &r->sectors[z_sector + x_sector * r->z_size];
room_num = Room_GetDoor(door->d1.sector);
room_num = door->d1.sector->portal_room.wall;
if (room_num == NO_ROOM) {
box_num = door->d1.sector->box;
} else {
@ -139,7 +139,7 @@ void Door_Initialise(int16_t item_num)
z_sector = ((item->pos.z - r->z) >> WALL_SHIFT) + dx;
x_sector = ((item->pos.x - r->x) >> WALL_SHIFT) + dy;
door->d1flip.sector = &r->sectors[z_sector + x_sector * r->z_size];
room_num = Room_GetDoor(door->d1flip.sector);
room_num = door->d1flip.sector->portal_room.wall;
if (room_num == NO_ROOM) {
box_num = door->d1flip.sector->box;
} else {
@ -157,7 +157,7 @@ void Door_Initialise(int16_t item_num)
door->d1flip.sector = NULL;
}
room_num = Room_GetDoor(door->d1.sector);
room_num = door->d1.sector->portal_room.wall;
Door_Shut(&door->d1);
Door_Shut(&door->d1flip);
@ -171,7 +171,7 @@ void Door_Initialise(int16_t item_num)
z_sector = (item->pos.z - r->z) >> WALL_SHIFT;
x_sector = (item->pos.x - r->x) >> WALL_SHIFT;
door->d2.sector = &r->sectors[z_sector + x_sector * r->z_size];
room_num = Room_GetDoor(door->d2.sector);
room_num = door->d2.sector->portal_room.wall;
if (room_num == NO_ROOM) {
box_num = door->d2.sector->box;
} else {
@ -191,7 +191,7 @@ void Door_Initialise(int16_t item_num)
z_sector = (item->pos.z - r->z) >> WALL_SHIFT;
x_sector = (item->pos.x - r->x) >> WALL_SHIFT;
door->d2flip.sector = &r->sectors[z_sector + x_sector * r->z_size];
room_num = Room_GetDoor(door->d2flip.sector);
room_num = door->d2flip.sector->portal_room.wall;
if (room_num == NO_ROOM) {
box_num = door->d2flip.sector->box;
} else {

View file

@ -19,6 +19,9 @@
#include <stddef.h>
#define NEG_TILT(T, H) ((T * (H & (WALL_L - 1))) >> 2)
#define POS_TILT(T, H) ((T * ((WALL_L - 1 - H) & (WALL_L - 1))) >> 2)
int16_t *g_TriggerIndex = NULL;
int32_t g_FlipTimer = 0;
int32_t g_FlipEffect = -1;
@ -29,6 +32,18 @@ static void Room_TriggerMusicTrack(int16_t track, int16_t flags, int16_t type);
static void Room_AddFlipItems(ROOM_INFO *r);
static void Room_RemoveFlipItems(ROOM_INFO *r);
static int16_t Room_GetFloorTiltHeight(
const SECTOR_INFO *sector, const int32_t x, const int32_t z);
static int16_t Room_GetCeilingTiltHeight(
const SECTOR_INFO *sector, const int32_t x, const int32_t z);
static SECTOR_INFO *Room_GetPitSector(
const SECTOR_INFO *sector, int32_t x, int32_t z);
static SECTOR_INFO *Room_GetSkySector(
const SECTOR_INFO *sector, int32_t x, int32_t z);
static void Room_PopulateSectorData(
SECTOR_INFO *sector, const int16_t *floor_data);
static void Room_TriggerMusicTrack(int16_t track, int16_t flags, int16_t type)
{
if (track == MX_UNUSED_0 && type == TT_ANTIPAD) {
@ -162,27 +177,13 @@ static void Room_RemoveFlipItems(ROOM_INFO *r)
int16_t Room_GetTiltType(
const SECTOR_INFO *sector, int32_t x, int32_t y, int32_t z)
{
ROOM_INFO *r;
sector = Room_GetPitSector(sector, x, z);
while (sector->pit_room != NO_ROOM) {
r = &g_RoomInfo[sector->pit_room];
sector = &r->sectors
[((z - r->z) >> WALL_SHIFT)
+ ((x - r->x) >> WALL_SHIFT) * r->z_size];
}
if ((y + 512) < sector->floor.height) {
if ((y + STEP_L * 2) < sector->floor.height) {
return 0;
}
if (sector->index) {
int16_t *data = &g_FloorData[sector->index];
if ((data[0] & DATA_TYPE) == FT_TILT) {
return data[1];
}
}
return 0;
return sector->floor.tilt;
}
int32_t Room_FindGridShift(int32_t src, int32_t dst)
@ -234,9 +235,35 @@ void Room_GetNewRoom(int32_t x, int32_t y, int32_t z, int16_t room_num)
}
}
static SECTOR_INFO *Room_GetPitSector(
const SECTOR_INFO *sector, const int32_t x, const int32_t z)
{
while (sector->portal_room.pit != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sector->portal_room.pit];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
}
return (SECTOR_INFO *)sector;
}
static SECTOR_INFO *Room_GetSkySector(
const SECTOR_INFO *sector, const int32_t x, const int32_t z)
{
while (sector->portal_room.sky != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sector->portal_room.sky];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
}
return (SECTOR_INFO *)sector;
}
SECTOR_INFO *Room_GetSector(int32_t x, int32_t y, int32_t z, int16_t *room_num)
{
int16_t data;
int16_t portal_room;
SECTOR_INFO *sector;
const ROOM_INFO *r = &g_RoomInfo[*room_num];
do {
@ -268,35 +295,35 @@ SECTOR_INFO *Room_GetSector(int32_t x, int32_t y, int32_t z, int16_t *room_num)
break;
}
data = Room_GetDoor(sector);
if (data != NO_ROOM) {
*room_num = data;
r = &g_RoomInfo[data];
portal_room = sector->portal_room.wall;
if (portal_room != NO_ROOM) {
*room_num = portal_room;
r = &g_RoomInfo[portal_room];
}
} while (data != NO_ROOM);
} while (portal_room != NO_ROOM);
if (y >= sector->floor.height) {
do {
if (sector->pit_room == NO_ROOM) {
if (sector->portal_room.pit == NO_ROOM) {
break;
}
*room_num = sector->pit_room;
*room_num = sector->portal_room.pit;
r = &g_RoomInfo[sector->pit_room];
r = &g_RoomInfo[sector->portal_room.pit];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
} while (y >= sector->floor.height);
} else if (y < sector->ceiling.height) {
do {
if (sector->sky_room == NO_ROOM) {
if (sector->portal_room.sky == NO_ROOM) {
break;
}
*room_num = sector->sky_room;
*room_num = sector->portal_room.sky;
r = &g_RoomInfo[sector->sky_room];
r = &g_RoomInfo[sector->portal_room.sky];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
@ -311,61 +338,12 @@ int16_t Room_GetCeiling(
{
int16_t *data;
int16_t type;
int16_t ended;
int16_t trigger;
const SECTOR_INFO *sky_sector = sector;
while (sky_sector->sky_room != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sky_sector->sky_room];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sky_sector = &r->sectors[z_sector + x_sector * r->z_size];
}
int16_t height = sky_sector->ceiling.height;
if (sky_sector->index) {
data = &g_FloorData[sky_sector->index];
type = *data & DATA_TYPE;
ended = *data++ & END_BIT;
if (!ended && type == FT_TILT) {
data++;
type = *data++ & DATA_TYPE;
}
if (type == FT_ROOF) {
int32_t xoff = data[0] >> 8;
int32_t yoff = (int8_t)data[0];
if (!g_ChunkyFlag
|| (xoff >= -2 && xoff <= 2 && yoff >= -2 && yoff <= 2)) {
if (xoff < 0) {
height += (int16_t)((xoff * (z & (WALL_L - 1))) >> 2);
} else {
height -=
(int16_t)((xoff * ((WALL_L - 1 - z) & (WALL_L - 1)))
>> 2);
}
if (yoff < 0) {
height +=
(int16_t)((yoff * ((WALL_L - 1 - x) & (WALL_L - 1)))
>> 2);
} else {
height -= (int16_t)((yoff * (x & (WALL_L - 1))) >> 2);
}
}
}
}
while (sector->pit_room != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sector->pit_room];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
}
const SECTOR_INFO *const sky_sector = Room_GetSkySector(sector, x, z);
int16_t height = Room_GetCeilingTiltHeight(sky_sector, x, z);
sector = Room_GetPitSector(sector, x, z);
if (!sector->index) {
return height;
}
@ -412,43 +390,13 @@ int16_t Room_GetCeiling(
return height;
}
int16_t Room_GetDoor(const SECTOR_INFO *sector)
{
if (!sector->index) {
return NO_ROOM;
}
int16_t *data = &g_FloorData[sector->index];
int16_t type = *data++;
if (type == FT_TILT) {
data++;
type = *data++;
}
if (type == FT_ROOF) {
data++;
type = *data++;
}
if ((type & DATA_TYPE) == FT_DOOR) {
return *data;
}
return NO_ROOM;
}
int16_t Room_GetHeight(
const SECTOR_INFO *sector, int32_t x, int32_t y, int32_t z)
{
g_HeightType = HT_WALL;
while (sector->pit_room != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sector->pit_room];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
}
sector = Room_GetPitSector(sector, x, z);
int16_t height = sector->floor.height;
int16_t height = Room_GetFloorTiltHeight(sector, x, z);
g_TriggerIndex = NULL;
@ -463,38 +411,7 @@ int16_t Room_GetHeight(
type = *data++;
switch (type & DATA_TYPE) {
case FT_TILT: {
int32_t xoff = data[0] >> 8;
int32_t yoff = (int8_t)data[0];
if (!g_ChunkyFlag || (ABS(xoff) <= 2 && ABS(yoff) <= 2)) {
if (ABS(xoff) > 2 || ABS(yoff) > 2) {
g_HeightType = HT_BIG_SLOPE;
} else {
g_HeightType = HT_SMALL_SLOPE;
}
if (xoff < 0) {
height -= (int16_t)((xoff * (z & (WALL_L - 1))) >> 2);
} else {
height +=
(int16_t)((xoff * ((WALL_L - 1 - z) & (WALL_L - 1)))
>> 2);
}
if (yoff < 0) {
height -= (int16_t)((yoff * (x & (WALL_L - 1))) >> 2);
} else {
height +=
(int16_t)((yoff * ((WALL_L - 1 - x) & (WALL_L - 1)))
>> 2);
}
}
data++;
break;
}
case FT_TILT:
case FT_ROOF:
case FT_DOOR:
data++;
@ -536,11 +453,75 @@ int16_t Room_GetHeight(
return height;
}
static int16_t Room_GetFloorTiltHeight(
const SECTOR_INFO *sector, const int32_t x, const int32_t z)
{
int16_t height = sector->floor.height;
if (sector->floor.tilt == 0) {
return height;
}
const int32_t z_off = sector->floor.tilt >> 8;
const int32_t x_off = (int8_t)sector->floor.tilt;
const HEIGHT_TYPE slope_type =
(ABS(z_off) > 2 || ABS(x_off) > 2) ? HT_BIG_SLOPE : HT_SMALL_SLOPE;
if (g_ChunkyFlag && slope_type == HT_BIG_SLOPE) {
return height;
}
g_HeightType = slope_type;
if (z_off < 0) {
height -= (int16_t)NEG_TILT(z_off, z);
} else {
height += (int16_t)POS_TILT(z_off, z);
}
if (x_off < 0) {
height -= (int16_t)NEG_TILT(x_off, x);
} else {
height += (int16_t)POS_TILT(x_off, x);
}
return height;
}
static int16_t Room_GetCeilingTiltHeight(
const SECTOR_INFO *sector, const int32_t x, const int32_t z)
{
int16_t height = sector->ceiling.height;
if (sector->ceiling.tilt == 0) {
return height;
}
const int32_t z_off = sector->ceiling.tilt >> 8;
const int32_t x_off = (int8_t)sector->ceiling.tilt;
if (g_ChunkyFlag && (ABS(z_off) > 2 || ABS(x_off) > 2)) {
return height;
}
if (z_off < 0) {
height += (int16_t)NEG_TILT(z_off, z);
} else {
height -= (int16_t)POS_TILT(z_off, z);
}
if (x_off < 0) {
height += (int16_t)POS_TILT(x_off, x);
} else {
height -= (int16_t)NEG_TILT(x_off, x);
}
return height;
}
int16_t Room_GetWaterHeight(int32_t x, int32_t y, int32_t z, int16_t room_num)
{
const ROOM_INFO *r = &g_RoomInfo[room_num];
int16_t data;
int16_t portal_room;
const SECTOR_INFO *sector;
int32_t z_sector, x_sector;
@ -569,15 +550,15 @@ int16_t Room_GetWaterHeight(int32_t x, int32_t y, int32_t z, int16_t room_num)
}
sector = &r->sectors[z_sector + x_sector * r->z_size];
data = Room_GetDoor(sector);
if (data != NO_ROOM) {
r = &g_RoomInfo[data];
portal_room = sector->portal_room.wall;
if (portal_room != NO_ROOM) {
r = &g_RoomInfo[portal_room];
}
} while (data != NO_ROOM);
} while (portal_room != NO_ROOM);
if (r->flags & RF_UNDERWATER) {
while (sector->sky_room != NO_ROOM) {
r = &g_RoomInfo[sector->sky_room];
while (sector->portal_room.sky != NO_ROOM) {
r = &g_RoomInfo[sector->portal_room.sky];
if (!(r->flags & RF_UNDERWATER)) {
break;
}
@ -587,8 +568,8 @@ int16_t Room_GetWaterHeight(int32_t x, int32_t y, int32_t z, int16_t room_num)
}
return sector->ceiling.height;
} else {
while (sector->pit_room != NO_ROOM) {
r = &g_RoomInfo[sector->pit_room];
while (sector->portal_room.pit != NO_ROOM) {
r = &g_RoomInfo[sector->portal_room.pit];
if (r->flags & RF_UNDERWATER) {
return sector->floor.height;
}
@ -623,7 +604,7 @@ void Room_AlterFloorHeight(ITEM_INFO *item, int32_t height)
return;
}
int16_t data;
int16_t portal_room;
SECTOR_INFO *sector;
const ROOM_INFO *r = &g_RoomInfo[item->room_number];
@ -642,26 +623,15 @@ void Room_AlterFloorHeight(ITEM_INFO *item, int32_t height)
}
sector = &r->sectors[z_sector + x_sector * r->z_size];
data = Room_GetDoor(sector);
if (data != NO_ROOM) {
r = &g_RoomInfo[data];
portal_room = sector->portal_room.wall;
if (portal_room != NO_ROOM) {
r = &g_RoomInfo[portal_room];
}
} while (data != NO_ROOM);
} while (portal_room != NO_ROOM);
SECTOR_INFO *sky_sector = sector;
while (sky_sector->sky_room != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sky_sector->sky_room];
const int32_t z_sector = (item->pos.z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (item->pos.x - r->x) >> WALL_SHIFT;
sky_sector = &r->sectors[z_sector + x_sector * r->z_size];
}
while (sector->pit_room != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sector->pit_room];
const int32_t z_sector = (item->pos.z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (item->pos.x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
}
const SECTOR_INFO *const sky_sector =
Room_GetSkySector(sector, item->pos.x, item->pos.z);
sector = Room_GetPitSector(sector, item->pos.x, item->pos.z);
if (sector->floor.height != NO_HEIGHT) {
sector->floor.height += ROUND_TO_CLICK(height);
@ -712,6 +682,76 @@ void Room_FlipMap(void)
g_FlipStatus = !g_FlipStatus;
}
void Room_ParseFloorData(const int16_t *floor_data)
{
for (int32_t i = 0; i < g_RoomCount; i++) {
const ROOM_INFO *const room = &g_RoomInfo[i];
for (int32_t j = 0; j < room->x_size * room->z_size; j++) {
Room_PopulateSectorData(&room->sectors[j], floor_data);
}
}
}
static void Room_PopulateSectorData(
SECTOR_INFO *const sector, const int16_t *floor_data)
{
sector->floor.tilt = 0;
sector->ceiling.tilt = 0;
sector->portal_room.wall = NO_ROOM;
if (sector->index == 0) {
return;
}
const int16_t *data = &floor_data[sector->index];
int16_t fd_entry;
do {
fd_entry = *data++;
switch (fd_entry & DATA_TYPE) {
case FT_TILT:
sector->floor.tilt = *data++;
break;
case FT_ROOF:
sector->ceiling.tilt = *data++;
break;
case FT_DOOR:
sector->portal_room.wall = *data++;
break;
case FT_LAVA:
break; // TODO: (bool)is_death_sector
case FT_TRIGGER: {
// TODO: (TRIGGER *)trigger
const int16_t trig_setup = *data++;
const TRIGGER_TYPE trig_type = TRIG_TYPE(fd_entry);
if (trig_type == TT_SWITCH || trig_type == TT_KEY
|| trig_type == TT_PICKUP) {
data++; // TODO: (int16_t)item_index
}
while (true) {
int16_t command = *data++;
if (TRIG_BITS(command) == TO_CAMERA) {
command = *data++;
}
if (command & END_BIT) {
break;
}
}
break;
}
default:
break;
}
} while (!(fd_entry & END_BIT));
}
void Room_TestTriggers(int16_t *data, bool heavy)
{
if (!data) {
@ -989,12 +1029,7 @@ bool Room_IsOnWalkable(
const SECTOR_INFO *sector, const int32_t x, const int32_t y,
const int32_t z, const int32_t room_height)
{
while (sector->pit_room != NO_ROOM) {
const ROOM_INFO *const r = &g_RoomInfo[sector->pit_room];
const int32_t z_sector = (z - r->z) >> WALL_SHIFT;
const int32_t x_sector = (x - r->x) >> WALL_SHIFT;
sector = &r->sectors[z_sector + x_sector * r->z_size];
}
sector = Room_GetPitSector(sector, x, z);
int16_t height = sector->floor.height;
bool object_found = false;

View file

@ -21,7 +21,6 @@ void Room_GetNearByRooms(
SECTOR_INFO *Room_GetSector(int32_t x, int32_t y, int32_t z, int16_t *room_num);
int16_t Room_GetCeiling(
const SECTOR_INFO *sector, int32_t x, int32_t y, int32_t z);
int16_t Room_GetDoor(const SECTOR_INFO *sector);
int16_t Room_GetHeight(
const SECTOR_INFO *sector, int32_t x, int32_t y, int32_t z);
int16_t Room_GetWaterHeight(int32_t x, int32_t y, int32_t z, int16_t room_num);
@ -29,6 +28,7 @@ int16_t Room_GetIndexFromPos(int32_t x, int32_t y, int32_t z);
void Room_AlterFloorHeight(ITEM_INFO *item, int32_t height);
void Room_ParseFloorData(const int16_t *floor_data);
void Room_TestTriggers(int16_t *data, bool heavy);
void Room_FlipMap(void);
bool Room_IsOnWalkable(

View file

@ -1163,10 +1163,14 @@ typedef struct DOOR_INFOS {
typedef struct SECTOR_INFO {
uint16_t index;
int16_t box;
uint8_t pit_room;
uint8_t sky_room;
struct {
uint8_t pit;
uint8_t sky;
int16_t wall;
} portal_room;
struct {
int16_t height;
int16_t tilt;
} floor, ceiling;
} SECTOR_INFO;