room: refactor lava and trigger interpretation

This creates an is_death_sector property on sectors and updates the
checks for when this is applicable in terms of collision and trigger
checks.
Triggers are also now directly in sectors, and their list of commands.
Most duplicated FD iterations are now removed; injection logic to
follow suit and use the new approach.
This commit is contained in:
lahm86 2024-08-08 22:08:01 +01:00
parent 3405e8ca80
commit da2353e05f
18 changed files with 282 additions and 396 deletions

View file

@ -782,20 +782,14 @@ void Camera_UpdateCutscene(void)
Viewport_SetFOV(ref->fov);
}
void Camera_RefreshFromTrigger(int16_t type, int16_t *data)
void Camera_RefreshFromTrigger(const TRIGGER *const trigger)
{
int16_t trigger;
int16_t target_ok = 2;
do {
trigger = *data++;
int16_t value = trigger & VALUE_BITS;
switch (TRIG_BITS(trigger)) {
case TO_CAMERA:
trigger = *data++;
if (value == g_Camera.last) {
g_Camera.number = value;
for (int32_t i = 0; i < trigger->command_count; i++) {
const TRIGGER_CMD *const cmd = &trigger->commands[i];
if (cmd->type == TO_CAMERA) {
if (cmd->parameter == g_Camera.last) {
g_Camera.number = cmd->parameter;
if (g_Camera.timer < 0 || g_Camera.type == CAM_LOOK
|| g_Camera.type == CAM_COMBAT) {
@ -808,15 +802,12 @@ void Camera_RefreshFromTrigger(int16_t type, int16_t *data)
} else {
target_ok = 0;
}
break;
case TO_TARGET:
} else if (cmd->type == TO_TARGET) {
if (g_Camera.type != CAM_LOOK && g_Camera.type != CAM_COMBAT) {
g_Camera.item = &g_Items[value];
g_Camera.item = &g_Items[cmd->parameter];
}
}
break;
}
} while (!(trigger & END_BIT));
if (g_Camera.item != NULL) {
if (!target_ok

View file

@ -14,6 +14,6 @@ void Camera_Fixed(void);
void Camera_Update(void);
void Camera_UpdateCutscene(void);
void Camera_OffsetReset(void);
void Camera_RefreshFromTrigger(int16_t type, int16_t *data);
void Camera_RefreshFromTrigger(const TRIGGER *trigger);
void Camera_MoveManual(void);
void Camera_Apply(void);

View file

@ -135,8 +135,8 @@ void Collide_GetCollisionInfo(
&& coll->front_floor > 0) {
coll->front_floor = 512;
} else if (
coll->lava_is_pit && coll->front_floor > 0 && g_TriggerIndex
&& (g_TriggerIndex[0] & DATA_TYPE) == FT_LAVA) {
coll->lava_is_pit && coll->front_floor > 0
&& Room_GetPitSector(sector, x, z)->is_death_sector) {
coll->front_floor = 512;
}
}
@ -170,8 +170,8 @@ void Collide_GetCollisionInfo(
&& coll->left_floor > 0) {
coll->left_floor = 512;
} else if (
coll->lava_is_pit && coll->left_floor > 0 && g_TriggerIndex
&& (g_TriggerIndex[0] & DATA_TYPE) == FT_LAVA) {
coll->lava_is_pit && coll->left_floor > 0
&& Room_GetPitSector(sector, x, z)->is_death_sector) {
coll->left_floor = 512;
}
}
@ -205,8 +205,8 @@ void Collide_GetCollisionInfo(
&& coll->right_floor > 0) {
coll->right_floor = 512;
} else if (
coll->lava_is_pit && coll->right_floor > 0 && g_TriggerIndex
&& (g_TriggerIndex[0] & DATA_TYPE) == FT_LAVA) {
coll->lava_is_pit && coll->right_floor > 0
&& Room_GetPitSector(sector, x, z)->is_death_sector) {
coll->right_floor = 512;
}
}

View file

@ -240,7 +240,7 @@ void Lara_HandleAboveWater(ITEM_INFO *item, COLL_INFO *coll)
g_LaraCollisionRoutines[item->current_anim_state](item, coll);
Item_UpdateRoom(item, -LARA_HEIGHT / 2);
Gun_Control();
Room_TestTriggers(coll->trigger, false);
Room_TestTriggers(item);
}
void Lara_HandleSurface(ITEM_INFO *item, COLL_INFO *coll)
@ -307,7 +307,7 @@ void Lara_HandleSurface(ITEM_INFO *item, COLL_INFO *coll)
g_LaraCollisionRoutines[item->current_anim_state](item, coll);
Item_UpdateRoom(item, 100);
Gun_Control();
Room_TestTriggers(coll->trigger, false);
Room_TestTriggers(item);
}
void Lara_HandleUnderwater(ITEM_INFO *item, COLL_INFO *coll)
@ -397,5 +397,5 @@ void Lara_HandleUnderwater(ITEM_INFO *item, COLL_INFO *coll)
g_LaraCollisionRoutines[item->current_anim_state](item, coll);
Item_UpdateRoom(item, 0);
Gun_Control();
Room_TestTriggers(coll->trigger, false);
Room_TestTriggers(item);
}

View file

@ -556,7 +556,7 @@ bool Lara_LandedBad(ITEM_INFO *item, COLL_INFO *coll)
item->floor = height;
item->pos.y = height;
Room_TestTriggers(g_TriggerIndex, false);
Room_TestTriggers(item);
item->pos.y = old_y;
int landspeed = item->fall_speed - DAMAGE_START;

View file

@ -116,13 +116,11 @@ void BaconLara_Control(int16_t item_num)
const int32_t h = Room_GetHeight(sector, x, y, z);
item->floor = h;
Room_TestTriggers(g_TriggerIndex, true);
Room_TestTriggers(item);
if (item->pos.y >= h) {
item->floor = h;
item->pos.y = h;
sector = Room_GetSector(x, h, z, &room_num);
Room_GetHeight(sector, x, h, z);
Room_TestTriggers(g_TriggerIndex, true);
Room_TestTriggers(item);
item->gravity_status = 0;
item->fall_speed = 0;
item->goal_anim_state = LS_DEATH;

View file

@ -255,10 +255,7 @@ void Torso_Control(int16_t item_num)
if (item->status == IS_DEACTIVATED) {
Sound_Effect(SFX_ATLANTEAN_DEATH, &item->pos, SPM_NORMAL);
Effect_ExplodingDeath(item_num, -1, TORSO_PART_DAMAGE);
const SECTOR_INFO *const sector = Room_GetSector(
item->pos.x, item->pos.y, item->pos.z, &item->room_number);
Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);
Room_TestTriggers(g_TriggerIndex, true);
Room_TestTriggers(item);
Item_Kill(item_num);
item->status = IS_DEACTIVATED;

View file

@ -116,11 +116,7 @@ void Scion_Control3(int16_t item_num)
if (counter == 0) {
item->status = IS_INVISIBLE;
item->hit_points = DONT_TARGET;
int16_t room_num = item->room_number;
const SECTOR_INFO *const sector =
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);
Room_TestTriggers(g_TriggerIndex, true);
Room_TestTriggers(item);
Item_RemoveDrawn(item_num);
}

View file

@ -14,7 +14,7 @@
#define LAVA_EMBER_DAMAGE 10
#define LAVA_WEDGE_SPEED 25
bool Lava_TestFloor(ITEM_INFO *item)
bool Lava_TestFloor(const ITEM_INFO *const item)
{
if (item->hit_points < 0 || g_Lara.water_status == LWS_CHEAT
|| (g_Lara.water_status == LWS_ABOVE_WATER
@ -25,40 +25,11 @@ bool Lava_TestFloor(ITEM_INFO *item)
// OG fix: check if floor index has lava
int16_t room_num = item->room_number;
const SECTOR_INFO *const sector =
Room_GetSector(item->pos.x, 32000, item->pos.z, &room_num);
int16_t *data = &g_FloorData[sector->index];
int16_t type;
do {
type = *data++;
switch (type & DATA_TYPE) {
case FT_TILT: {
data++;
break;
}
case FT_ROOF:
case FT_DOOR:
data++;
break;
case FT_LAVA:
return true;
case FT_TRIGGER:
data++;
break;
default:
break;
}
} while (!(type & END_BIT));
return false;
Room_GetSector(item->pos.x, MAX_HEIGHT, item->pos.z, &room_num);
return sector->is_death_sector;
}
void Lava_Burn(ITEM_INFO *item)
void Lava_Burn(ITEM_INFO *const item)
{
if (g_Lara.water_status == LWS_CHEAT) {
return;
@ -70,8 +41,9 @@ void Lava_Burn(ITEM_INFO *item)
int16_t room_num = item->room_number;
const SECTOR_INFO *const sector =
Room_GetSector(item->pos.x, 32000, item->pos.z, &room_num);
int16_t height = Room_GetHeight(sector, item->pos.x, 32000, item->pos.z);
Room_GetSector(item->pos.x, MAX_HEIGHT, item->pos.z, &room_num);
const int16_t height =
Room_GetHeight(sector, item->pos.x, MAX_HEIGHT, item->pos.z);
if (item->floor != height) {
return;
@ -84,7 +56,7 @@ void Lava_Burn(ITEM_INFO *item)
}
for (int i = 0; i < 10; i++) {
int16_t fx_num = Effect_Create(item->room_number);
const int16_t fx_num = Effect_Create(item->room_number);
if (fx_num != NO_ITEM) {
FX_INFO *fx = &g_Effects[fx_num];
fx->object_number = O_FLAME;

View file

@ -5,7 +5,7 @@
#include <stdbool.h>
#include <stdint.h>
bool Lava_TestFloor(ITEM_INFO *item);
bool Lava_TestFloor(const ITEM_INFO *item);
void Lava_Burn(ITEM_INFO *item);
void Lava_Setup(OBJECT_INFO *obj);

View file

@ -276,12 +276,7 @@ void MovableBlock_Control(int16_t item_num)
item->status = IS_NOT_ACTIVE;
Item_RemoveActive(item_num);
Room_AlterFloorHeight(item, -WALL_L);
room_num = item->room_number;
sector =
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);
Room_TestTriggers(g_TriggerIndex, true);
Room_TestTriggers(item);
}
}

View file

@ -66,7 +66,7 @@ void RollingBall_Control(int16_t item_num)
item->floor =
Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);
Room_TestTriggers(g_TriggerIndex, true);
Room_TestTriggers(item);
if (item->pos.y >= item->floor - STEP_L) {
item->gravity_status = 0;

View file

@ -113,11 +113,7 @@ void ThorsHandle_Control(int16_t item_num)
int32_t old_x = x;
int32_t old_z = z;
int16_t room_num = item->room_number;
const SECTOR_INFO *const sector =
Room_GetSector(x, item->pos.y, z, &room_num);
Room_GetHeight(sector, x, item->pos.y, z);
Room_TestTriggers(g_TriggerIndex, true);
Room_TestTriggers(item);
switch (item->rot.y) {
case 0:

View file

@ -1,6 +1,7 @@
#include "game/room.h"
#include "game/camera.h"
#include "game/gamebuf.h"
#include "game/items.h"
#include "game/lot.h"
#include "game/music.h"
@ -17,6 +18,7 @@
#include <libtrx/utils.h>
#include <assert.h>
#include <stddef.h>
#define NEG_TILT(T, H) ((T * (H & (WALL_L - 1))) >> 2)
@ -28,7 +30,7 @@ int32_t g_FlipEffect = -1;
int32_t g_FlipStatus = 0;
int32_t g_FlipMapTable[MAX_FLIP_MAPS] = { 0 };
static void Room_TriggerMusicTrack(int16_t track, int16_t flags, int16_t type);
static void Room_TriggerMusicTrack(int16_t track, const TRIGGER *const trigger);
static void Room_AddFlipItems(ROOM_INFO *r);
static void Room_RemoveFlipItems(ROOM_INFO *r);
@ -36,17 +38,15 @@ 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)
static void Room_TriggerMusicTrack(int16_t track, const TRIGGER *const trigger)
{
if (track == MX_UNUSED_0 && type == TT_ANTIPAD) {
if (track == MX_UNUSED_0 && trigger->type == TT_ANTIPAD) {
Music_Stop();
return;
}
@ -108,16 +108,16 @@ static void Room_TriggerMusicTrack(int16_t track, int16_t flags, int16_t type)
return;
}
if (type == TT_SWITCH) {
g_MusicTrackFlags[track] ^= flags & IF_CODE_BITS;
} else if (type == TT_ANTIPAD) {
g_MusicTrackFlags[track] &= -1 - (flags & IF_CODE_BITS);
} else if (flags & IF_CODE_BITS) {
g_MusicTrackFlags[track] |= flags & IF_CODE_BITS;
if (trigger->type == TT_SWITCH) {
g_MusicTrackFlags[track] ^= trigger->mask;
} else if (trigger->type == TT_ANTIPAD) {
g_MusicTrackFlags[track] &= -1 - trigger->mask;
} else if (trigger->mask) {
g_MusicTrackFlags[track] |= trigger->mask;
}
if ((g_MusicTrackFlags[track] & IF_CODE_BITS) == IF_CODE_BITS) {
if (flags & IF_ONESHOT) {
if (trigger->one_shot) {
g_MusicTrackFlags[track] |= IF_ONESHOT;
}
Music_Play(track);
@ -235,7 +235,7 @@ void Room_GetNewRoom(int32_t x, int32_t y, int32_t z, int16_t room_num)
}
}
static SECTOR_INFO *Room_GetPitSector(
SECTOR_INFO *Room_GetPitSector(
const SECTOR_INFO *sector, const int32_t x, const int32_t z)
{
while (sector->portal_room.pit != NO_ROOM) {
@ -344,48 +344,22 @@ int16_t Room_GetCeiling(
int16_t height = Room_GetCeilingTiltHeight(sky_sector, x, z);
sector = Room_GetPitSector(sector, x, z);
if (!sector->index) {
if (sector->trigger == NULL) {
return height;
}
data = &g_FloorData[sector->index];
do {
type = *data++;
switch (type & DATA_TYPE) {
case FT_DOOR:
case FT_TILT:
case FT_ROOF:
data++;
break;
case FT_LAVA:
break;
case FT_TRIGGER:
data++;
do {
trigger = *data++;
if (TRIG_BITS(trigger) != TO_OBJECT) {
if (TRIG_BITS(trigger) == TO_CAMERA) {
trigger = *data++;
for (int32_t i = 0; i < sector->trigger->command_count; i++) {
const TRIGGER_CMD *const cmd = &sector->trigger->commands[i];
if (cmd->type != TO_OBJECT) {
continue;
}
} else {
ITEM_INFO *item = &g_Items[trigger & VALUE_BITS];
OBJECT_INFO *object = &g_Objects[item->object_number];
const ITEM_INFO *const item = &g_Items[cmd->parameter];
const OBJECT_INFO *const object = &g_Objects[item->object_number];
if (object->ceiling_height_func) {
height =
object->ceiling_height_func(item, x, y, z, height);
height = object->ceiling_height_func(item, x, y, z, height);
}
}
} while (!(trigger & END_BIT));
break;
default:
Shell_ExitSystem("GetCeiling(): Unknown type");
break;
}
} while (!(type & END_BIT));
return height;
}
@ -398,57 +372,22 @@ int16_t Room_GetHeight(
int16_t height = Room_GetFloorTiltHeight(sector, x, z);
g_TriggerIndex = NULL;
if (!sector->index) {
if (sector->trigger == NULL) {
return height;
}
int16_t *data = &g_FloorData[sector->index];
int16_t type;
int16_t trigger;
do {
type = *data++;
switch (type & DATA_TYPE) {
case FT_TILT:
case FT_ROOF:
case FT_DOOR:
data++;
break;
case FT_LAVA:
g_TriggerIndex = data - 1;
break;
case FT_TRIGGER:
if (!g_TriggerIndex) {
g_TriggerIndex = data - 1;
for (int32_t i = 0; i < sector->trigger->command_count; i++) {
const TRIGGER_CMD *const cmd = &sector->trigger->commands[i];
if (cmd->type != TO_OBJECT) {
continue;
}
data++;
do {
trigger = *data++;
if (TRIG_BITS(trigger) != TO_OBJECT) {
if (TRIG_BITS(trigger) == TO_CAMERA) {
trigger = *data++;
}
} else {
ITEM_INFO *item = &g_Items[trigger & VALUE_BITS];
OBJECT_INFO *object = &g_Objects[item->object_number];
const ITEM_INFO *const item = &g_Items[cmd->parameter];
const OBJECT_INFO *const object = &g_Objects[item->object_number];
if (object->floor_height_func) {
height =
object->floor_height_func(item, x, y, z, height);
height = object->floor_height_func(item, x, y, z, height);
}
}
} while (!(trigger & END_BIT));
break;
default:
Shell_ExitSystem("GetHeight(): Unknown type");
break;
}
} while (!(type & END_BIT));
return height;
}
@ -698,6 +637,8 @@ static void Room_PopulateSectorData(
sector->floor.tilt = 0;
sector->ceiling.tilt = 0;
sector->portal_room.wall = NO_ROOM;
sector->is_death_sector = false;
sector->trigger = NULL;
if (sector->index == 0) {
return;
@ -722,18 +663,39 @@ static void Room_PopulateSectorData(
break;
case FT_LAVA:
break; // TODO: (bool)is_death_sector
sector->is_death_sector = true;
break;
case FT_TRIGGER: {
// TODO: (TRIGGER *)trigger
assert(sector->trigger == NULL);
TRIGGER *const trigger =
GameBuf_Alloc(sizeof(TRIGGER), GBUF_FLOOR_DATA);
sector->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
trigger->type = TRIG_TYPE(fd_entry);
trigger->timer = trig_setup & 0xFF;
trigger->one_shot = trig_setup & IF_ONESHOT;
trigger->mask = trig_setup & IF_CODE_BITS;
trigger->item_index = NO_ITEM;
trigger->command_count = 0;
if (trigger->type == TT_SWITCH || trigger->type == TT_KEY
|| trigger->type == TT_PICKUP) {
const int16_t item_data = *data++;
trigger->item_index = item_data & VALUE_BITS;
if (item_data & END_BIT) {
// See City of Khamoon room 49 - two dangling key triggers
// with no commands. Exit early to avoid populating garbage
// command data.
break;
}
}
const int16_t *command_data = data;
while (true) {
trigger->command_count++;
int16_t command = *data++;
if (TRIG_BITS(command) == TO_CAMERA) {
command = *data++;
@ -743,6 +705,22 @@ static void Room_PopulateSectorData(
}
}
trigger->commands = GameBuf_Alloc(
sizeof(TRIGGER_CMD) * trigger->command_count, GBUF_FLOOR_DATA);
for (int32_t i = 0; i < trigger->command_count; i++) {
int16_t command = *command_data++;
TRIGGER_CMD *const cmd = &trigger->commands[i];
cmd->type = TRIG_BITS(command);
cmd->parameter = command & VALUE_BITS;
if (cmd->type == TO_CAMERA) {
command = *command_data++;
cmd->camera.timer = command & 0xFF;
cmd->camera.glide = (command & IF_CODE_BITS) >> 6;
cmd->camera.one_shot = command & IF_ONESHOT;
}
}
break;
}
@ -752,68 +730,65 @@ static void Room_PopulateSectorData(
} while (!(fd_entry & END_BIT));
}
void Room_TestTriggers(int16_t *data, bool heavy)
void Room_TestTriggers(const ITEM_INFO *const item)
{
if (!data) {
return;
}
const bool is_heavy = item->object_number != O_LARA;
int16_t room_num = item->room_number;
const SECTOR_INFO *const sector =
Room_GetSector(item->pos.x, MAX_HEIGHT, item->pos.z, &room_num);
if ((*data & DATA_TYPE) == FT_LAVA) {
if (!heavy && Lava_TestFloor(g_LaraItem)) {
if (!is_heavy && sector->is_death_sector && Lava_TestFloor(item)) {
Lava_Burn(g_LaraItem);
}
if (*data & END_BIT) {
const TRIGGER *const trigger = sector->trigger;
if (trigger == NULL) {
return;
}
data++;
}
int16_t type = (*data++ >> 8) & 0x3F;
int32_t switch_off = 0;
int32_t flip = 0;
int32_t new_effect = -1;
int16_t flags = *data++;
int16_t timer = flags & 0xFF;
if (g_Camera.type != CAM_HEAVY) {
Camera_RefreshFromTrigger(type, data);
Camera_RefreshFromTrigger(trigger);
}
if (heavy) {
if (type != TT_HEAVY) {
bool switch_off = false;
bool flip_map = false;
int32_t new_effect = -1;
ITEM_INFO *camera_item = NULL;
if (is_heavy) {
if (trigger->type != TT_HEAVY) {
return;
}
} else {
switch (type) {
switch (trigger->type) {
case TT_TRIGGER:
break;
case TT_SWITCH: {
int16_t value = *data++ & VALUE_BITS;
if (!Switch_Trigger(value, timer)) {
if (!Switch_Trigger(trigger->item_index, trigger->timer)) {
return;
}
switch_off = g_Items[value].current_anim_state == LS_RUN;
switch_off =
g_Items[trigger->item_index].current_anim_state == LS_RUN;
break;
}
case TT_PAD:
case TT_ANTIPAD:
if (g_LaraItem->pos.y != g_LaraItem->floor) {
if (item->pos.y != item->floor) {
return;
}
break;
case TT_KEY: {
int16_t value = *data++ & VALUE_BITS;
if (!KeyHole_Trigger(value)) {
if (!KeyHole_Trigger(trigger->item_index)) {
return;
}
break;
}
case TT_PICKUP: {
int16_t value = *data++ & VALUE_BITS;
if (!Pickup_Trigger(value)) {
if (!Pickup_Trigger(trigger->item_index)) {
return;
}
break;
@ -831,38 +806,35 @@ void Room_TestTriggers(int16_t *data, bool heavy)
}
}
ITEM_INFO *camera_item = NULL;
int16_t trigger;
do {
trigger = *data++;
int16_t value = trigger & VALUE_BITS;
for (int32_t i = 0; i < trigger->command_count; i++) {
const TRIGGER_CMD *const cmd = &trigger->commands[i];
switch (TRIG_BITS(trigger)) {
switch (cmd->type) {
case TO_OBJECT: {
ITEM_INFO *item = &g_Items[value];
const int16_t item_num = cmd->parameter;
ITEM_INFO *const item = &g_Items[item_num];
if (item->flags & IF_ONESHOT) {
break;
}
item->timer = timer;
if (timer != 1) {
item->timer = trigger->timer;
if (item->timer != 1) {
item->timer *= LOGIC_FPS;
}
if (type == TT_SWITCH) {
item->flags ^= flags & IF_CODE_BITS;
} else if (type == TT_ANTIPAD) {
item->flags &= -1 - (flags & IF_CODE_BITS);
} else if (flags & IF_CODE_BITS) {
item->flags |= flags & IF_CODE_BITS;
if (trigger->type == TT_SWITCH) {
item->flags ^= trigger->mask;
} else if (trigger->type == TT_ANTIPAD) {
item->flags &= -1 - trigger->mask;
} else if (trigger->mask) {
item->flags |= trigger->mask;
}
if ((item->flags & IF_CODE_BITS) != IF_CODE_BITS) {
break;
}
if (flags & IF_ONESHOT) {
if (trigger->one_shot) {
item->flags |= IF_ONESHOT;
}
@ -871,73 +843,71 @@ void Room_TestTriggers(int16_t *data, bool heavy)
if (item->status == IS_NOT_ACTIVE) {
item->touch_bits = 0;
item->status = IS_ACTIVE;
Item_AddActive(value);
LOT_EnableBaddieAI(value, 1);
Item_AddActive(item_num);
LOT_EnableBaddieAI(item_num, 1);
} else if (item->status == IS_INVISIBLE) {
item->touch_bits = 0;
if (LOT_EnableBaddieAI(value, 0)) {
if (LOT_EnableBaddieAI(item_num, 0)) {
item->status = IS_ACTIVE;
} else {
item->status = IS_INVISIBLE;
}
Item_AddActive(value);
Item_AddActive(item_num);
}
} else {
item->touch_bits = 0;
item->status = IS_ACTIVE;
Item_AddActive(value);
Item_AddActive(item_num);
}
}
break;
}
case TO_CAMERA: {
trigger = *data++;
int16_t camera_flags = trigger;
int16_t camera_timer = trigger & 0xFF;
if (g_Camera.fixed[value].flags & IF_ONESHOT) {
const int16_t camera_num = cmd->parameter;
if (g_Camera.fixed[camera_num].flags & IF_ONESHOT) {
break;
}
g_Camera.number = value;
g_Camera.number = camera_num;
if (g_Camera.type == CAM_LOOK || g_Camera.type == CAM_COMBAT) {
break;
}
if (type == TT_COMBAT) {
if (trigger->type == TT_COMBAT) {
break;
}
if (type == TT_SWITCH && timer && switch_off) {
if (trigger->type == TT_SWITCH && trigger->timer && switch_off) {
break;
}
if (g_Camera.number == g_Camera.last && type != TT_SWITCH) {
if (g_Camera.number == g_Camera.last
&& trigger->type != TT_SWITCH) {
break;
}
g_Camera.timer = camera_timer;
g_Camera.timer = cmd->camera.timer;
if (g_Camera.timer != 1) {
g_Camera.timer *= LOGIC_FPS;
}
if (camera_flags & IF_ONESHOT) {
if (cmd->camera.one_shot) {
g_Camera.fixed[g_Camera.number].flags |= IF_ONESHOT;
}
g_Camera.speed = ((camera_flags & IF_CODE_BITS) >> 6) + 1;
g_Camera.type = heavy ? CAM_HEAVY : CAM_FIXED;
g_Camera.speed = cmd->camera.glide + 1;
g_Camera.type = is_heavy ? CAM_HEAVY : CAM_FIXED;
break;
}
case TO_TARGET:
camera_item = &g_Items[value];
camera_item = &g_Items[cmd->parameter];
break;
case TO_SINK: {
OBJECT_VECTOR *obvector = &g_Camera.fixed[value];
OBJECT_VECTOR *obvector = &g_Camera.fixed[cmd->parameter];
if (g_Lara.LOT.required_box != obvector->flags) {
g_Lara.LOT.target.x = obvector->x;
@ -950,46 +920,48 @@ void Room_TestTriggers(int16_t *data, bool heavy)
break;
}
case TO_FLIPMAP:
if (g_FlipMapTable[value] & IF_ONESHOT) {
case TO_FLIPMAP: {
const int16_t flip_slot = cmd->parameter;
if (g_FlipMapTable[flip_slot] & IF_ONESHOT) {
break;
}
if (type == TT_SWITCH) {
g_FlipMapTable[value] ^= flags & IF_CODE_BITS;
} else if (flags & IF_CODE_BITS) {
g_FlipMapTable[value] |= flags & IF_CODE_BITS;
if (trigger->type == TT_SWITCH) {
g_FlipMapTable[flip_slot] ^= trigger->mask;
} else if (trigger->mask) {
g_FlipMapTable[flip_slot] |= trigger->mask;
}
if ((g_FlipMapTable[value] & IF_CODE_BITS) == IF_CODE_BITS) {
if (flags & IF_ONESHOT) {
g_FlipMapTable[value] |= IF_ONESHOT;
if ((g_FlipMapTable[flip_slot] & IF_CODE_BITS) == IF_CODE_BITS) {
if (trigger->one_shot) {
g_FlipMapTable[flip_slot] |= IF_ONESHOT;
}
if (!g_FlipStatus) {
flip = 1;
flip_map = true;
}
} else if (g_FlipStatus) {
flip = 1;
flip_map = true;
}
break;
}
case TO_FLIPON:
if ((g_FlipMapTable[value] & IF_CODE_BITS) == IF_CODE_BITS
if ((g_FlipMapTable[cmd->parameter] & IF_CODE_BITS) == IF_CODE_BITS
&& !g_FlipStatus) {
flip = 1;
flip_map = true;
}
break;
case TO_FLIPOFF:
if ((g_FlipMapTable[value] & IF_CODE_BITS) == IF_CODE_BITS
if ((g_FlipMapTable[cmd->parameter] & IF_CODE_BITS) == IF_CODE_BITS
&& g_FlipStatus) {
flip = 1;
flip_map = true;
}
break;
case TO_FLIPEFFECT:
new_effect = value;
new_effect = cmd->parameter;
break;
case TO_FINISH:
@ -997,26 +969,28 @@ void Room_TestTriggers(int16_t *data, bool heavy)
break;
case TO_CD:
Room_TriggerMusicTrack(value, flags, type);
Room_TriggerMusicTrack(cmd->parameter, trigger);
break;
case TO_SECRET:
if ((g_GameInfo.current[g_CurrentLevel].stats.secret_flags
& (1 << value))) {
case TO_SECRET: {
const int16_t secret_num = 1 << cmd->parameter;
if (g_GameInfo.current[g_CurrentLevel].stats.secret_flags
& secret_num) {
break;
}
g_GameInfo.current[g_CurrentLevel].stats.secret_flags |= 1 << value;
g_GameInfo.current[g_CurrentLevel].stats.secret_flags |= secret_num;
Music_Play(MX_SECRET);
break;
}
} while (!(trigger & END_BIT));
}
}
if (camera_item
&& (g_Camera.type == CAM_FIXED || g_Camera.type == CAM_HEAVY)) {
g_Camera.item = camera_item;
}
if (flip) {
if (flip_map) {
Room_FlipMap();
if (new_effect != -1) {
g_FlipEffect = new_effect;
@ -1032,50 +1006,24 @@ bool Room_IsOnWalkable(
sector = Room_GetPitSector(sector, x, z);
int16_t height = sector->floor.height;
if (sector->trigger == NULL) {
return room_height == height;
}
bool object_found = false;
for (int32_t i = 0; i < sector->trigger->command_count; i++) {
const TRIGGER_CMD *const cmd = &sector->trigger->commands[i];
if (cmd->type != TO_OBJECT) {
continue;
}
int16_t *floor_data = &g_FloorData[sector->index];
int16_t type;
int16_t trigger;
int16_t trig_flags;
int16_t trig_type;
do {
type = *floor_data++;
switch (type & DATA_TYPE) {
case FT_TILT:
case FT_ROOF:
case FT_DOOR:
floor_data++;
break;
case FT_LAVA:
break;
case FT_TRIGGER:
trig_flags = *floor_data;
floor_data++;
trig_type = (type >> 8) & 0x3F;
do {
trigger = *floor_data++;
if (TRIG_BITS(trigger) == TO_OBJECT) {
const int16_t item_num = trigger & VALUE_BITS;
ITEM_INFO *item = &g_Items[item_num];
OBJECT_INFO *object = &g_Objects[item->object_number];
const ITEM_INFO *const item = &g_Items[cmd->parameter];
const OBJECT_INFO *const object = &g_Objects[item->object_number];
if (object->floor_height_func) {
height =
object->floor_height_func(item, x, y, z, height);
height = object->floor_height_func(item, x, y, z, height);
object_found = true;
}
} else if (TRIG_BITS(trigger) == TO_CAMERA) {
trigger = *floor_data++;
}
} while (!(trigger & END_BIT));
break;
}
} while (!(type & END_BIT));
return object_found && room_height == height;
}

View file

@ -19,6 +19,7 @@ void Room_GetNewRoom(int32_t x, int32_t y, int32_t z, int16_t room_num);
void Room_GetNearByRooms(
int32_t x, int32_t y, int32_t z, int32_t r, int32_t h, int16_t room_num);
SECTOR_INFO *Room_GetSector(int32_t x, int32_t y, int32_t z, int16_t *room_num);
SECTOR_INFO *Room_GetPitSector(const SECTOR_INFO *sector, int32_t x, int32_t z);
int16_t Room_GetCeiling(
const SECTOR_INFO *sector, int32_t x, int32_t y, int32_t z);
int16_t Room_GetHeight(
@ -29,7 +30,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_TestTriggers(const ITEM_INFO *item);
void Room_FlipMap(void);
bool Room_IsOnWalkable(
const SECTOR_INFO *sector, int32_t x, int32_t y, int32_t z,

View file

@ -83,80 +83,49 @@ static void Stats_CheckTriggers(
const SECTOR_INFO *const sector =
&m_CachedSectorArray[room_num][z_sector + x_sector * r->z_size];
if (!sector->index) {
if (sector->trigger == NULL) {
return;
}
int16_t *data = &g_FloorData[sector->index];
int16_t type;
int16_t trigger;
int16_t trig_flags;
int16_t trig_type;
do {
type = *data++;
for (int32_t i = 0; i < sector->trigger->command_count; i++) {
const TRIGGER_CMD *const cmd = &sector->trigger->commands[i];
switch (type & DATA_TYPE) {
case FT_TILT:
case FT_ROOF:
case FT_DOOR:
data++;
break;
case FT_LAVA:
break;
case FT_TRIGGER:
trig_flags = *data;
data++;
trig_type = (type >> 8) & 0x3F;
do {
trigger = *data++;
if (TRIG_BITS(trigger) == TO_SECRET) {
int16_t number = trigger & VALUE_BITS;
if (!(m_SecretRoom & (1 << number))) {
m_SecretRoom |= (1 << number);
if (cmd->type == TO_SECRET) {
const int16_t secret_num = 1 << cmd->parameter;
if (!(m_SecretRoom & secret_num)) {
m_SecretRoom |= secret_num;
m_LevelSecrets++;
}
}
if (TRIG_BITS(trigger) != TO_OBJECT) {
if (TRIG_BITS(trigger) == TO_CAMERA) {
trigger = *data++;
}
} else {
int16_t idx = trigger & VALUE_BITS;
if (m_KillableItems[idx]) {
} else if (cmd->type == TO_OBJECT) {
const int16_t item_num = cmd->parameter;
if (m_KillableItems[item_num]) {
continue;
}
ITEM_INFO *item = &g_Items[idx];
const ITEM_INFO *const item = &g_Items[item_num];
// Add Pierre pickup and kills if oneshot
if (item->object_number == O_PIERRE
&& trig_flags & IF_ONESHOT) {
Stats_IncludeKillableItem(idx);
if (item->object_number == O_PIERRE && sector->trigger->one_shot) {
Stats_IncludeKillableItem(item_num);
}
// Check for only valid pods
if ((item->object_number == O_PODS
|| item->object_number == O_BIG_POD)
&& item->data != NULL) {
int16_t bug_item_num = *(int16_t *)item->data;
const ITEM_INFO *bug_item = &g_Items[bug_item_num];
const int16_t bug_item_num = *(int16_t *)item->data;
const ITEM_INFO *const bug_item = &g_Items[bug_item_num];
if (g_Objects[bug_item->object_number].loaded) {
Stats_IncludeKillableItem(idx);
Stats_IncludeKillableItem(item_num);
}
}
// Add killable if object triggered
if (Stats_IsObjectKillable(item->object_number)) {
Stats_IncludeKillableItem(idx);
Stats_IncludeKillableItem(item_num);
}
}
} while (!(trigger & END_BIT));
break;
}
} while (!(type & END_BIT));
}
static bool Stats_IsObjectKillable(int32_t obj_num)

View file

@ -123,6 +123,7 @@
#define DONT_TARGET (-16384)
#define UNIT_SHADOW 256
#define NO_HEIGHT (-32512)
#define MAX_HEIGHT 32000
#define NO_BAD_POS (-NO_HEIGHT)
#define NO_BAD_NEG NO_HEIGHT
#define BAD_JUMP_CEILING ((STEP_L * 3) / 4) // = 192

View file

@ -1160,9 +1160,31 @@ typedef struct DOOR_INFOS {
DOOR_INFO door[];
} DOOR_INFOS;
typedef struct TRIGGER_CMD {
TRIGGER_OBJECT type;
int16_t parameter;
struct {
int8_t timer;
int8_t glide;
bool one_shot;
} camera;
} TRIGGER_CMD;
typedef struct TRIGGER {
TRIGGER_TYPE type;
int8_t timer;
int16_t mask;
bool one_shot;
int16_t item_index;
int32_t command_count;
TRIGGER_CMD *commands;
} TRIGGER;
typedef struct SECTOR_INFO {
uint16_t index;
int16_t box;
bool is_death_sector;
TRIGGER *trigger;
struct {
uint8_t pit;
uint8_t sky;