2021-08-30 17:50:32 +03:00
|
|
|
#include "framework.h"
|
|
|
|
#include "items.h"
|
|
|
|
#include "control.h"
|
|
|
|
#include "floordata.h"
|
|
|
|
#include "objectslist.h"
|
|
|
|
#include "box.h"
|
|
|
|
#include "camera.h"
|
|
|
|
#include "puzzles_keys.h"
|
2021-09-08 18:19:06 +03:00
|
|
|
#include "pickup\pickup.h"
|
2021-08-30 17:50:32 +03:00
|
|
|
#include "lot.h"
|
|
|
|
#include "spotcam.h"
|
|
|
|
#include "traps.h"
|
|
|
|
#include "lara.h"
|
|
|
|
#include "Lara\lara_climb.h"
|
|
|
|
#include "Objects\Generic\Switches\generic_switch.h"
|
|
|
|
|
|
|
|
using namespace TEN::Entities::Switches;
|
|
|
|
|
2021-08-30 17:52:38 +03:00
|
|
|
int TriggerActive(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
int flag;
|
|
|
|
|
2021-08-30 15:13:18 -05:00
|
|
|
flag = (~item->flags & IFLAG_REVERSE) >> 14;
|
2021-08-30 17:52:38 +03:00
|
|
|
if ((item->flags & IFLAG_ACTIVATION_MASK) != IFLAG_ACTIVATION_MASK)
|
|
|
|
{
|
|
|
|
flag = !flag;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (item->timer)
|
|
|
|
{
|
|
|
|
if (item->timer > 0)
|
|
|
|
{
|
|
|
|
--item->timer;
|
|
|
|
if (!item->timer)
|
|
|
|
item->timer = -1;
|
|
|
|
}
|
|
|
|
else if (item->timer < -1)
|
|
|
|
{
|
|
|
|
++item->timer;
|
|
|
|
if (item->timer == -1)
|
|
|
|
item->timer = 0;
|
|
|
|
}
|
|
|
|
if (item->timer <= -1)
|
|
|
|
flag = !flag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return flag;
|
|
|
|
}
|
|
|
|
|
2021-08-30 17:50:32 +03:00
|
|
|
int GetKeyTrigger(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
auto triggerIndex = GetTriggerIndex(item);
|
|
|
|
|
|
|
|
if (triggerIndex)
|
|
|
|
{
|
|
|
|
short* trigger = triggerIndex;
|
|
|
|
for (short i = *triggerIndex; (i & 0x1F) != 4; trigger++)
|
|
|
|
{
|
|
|
|
if (i < 0)
|
|
|
|
break;
|
|
|
|
i = trigger[1];
|
|
|
|
}
|
|
|
|
if (*trigger & 4)
|
|
|
|
{
|
2021-08-30 15:13:18 -05:00
|
|
|
for (short* j = &trigger[2]; (*j >> 8) & 0x3C || item != &g_Level.Items[*j & 0x3FF]; j++)
|
2021-08-30 17:50:32 +03:00
|
|
|
{
|
|
|
|
if (*j & 0x8000)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetSwitchTrigger(ITEM_INFO* item, short* itemNos, int AttatchedToSwitch)
|
|
|
|
{
|
|
|
|
auto triggerIndex = GetTriggerIndex(item);
|
|
|
|
|
|
|
|
if (triggerIndex)
|
|
|
|
{
|
|
|
|
short* trigger;
|
|
|
|
for (trigger = triggerIndex; (*trigger & DATA_TYPE) != TRIGGER_TYPE; trigger++)
|
|
|
|
{
|
|
|
|
if (*trigger & END_BIT)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*trigger & 4)
|
|
|
|
{
|
|
|
|
trigger += 2;
|
|
|
|
short* current = itemNos;
|
|
|
|
int k = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (TRIG_BITS(*trigger) == TO_OBJECT && item != &g_Level.Items[*trigger & VALUE_BITS])
|
|
|
|
{
|
|
|
|
current[k] = *trigger & VALUE_BITS;
|
|
|
|
++k;
|
|
|
|
}
|
|
|
|
if (*trigger & END_BIT)
|
|
|
|
break;
|
|
|
|
++trigger;
|
|
|
|
} while (true);
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SwitchTrigger(short itemNum, short timer)
|
|
|
|
{
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNum];
|
|
|
|
if (item->status == ITEM_DEACTIVATED)
|
|
|
|
{
|
|
|
|
if ((!item->currentAnimState && item->objectNumber != ID_JUMP_SWITCH || item->currentAnimState == 1 && item->objectNumber == ID_JUMP_SWITCH) && timer > 0)
|
|
|
|
{
|
|
|
|
item->timer = timer;
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
if (timer != 1)
|
|
|
|
item->timer = 30 * timer;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (item->triggerFlags != 6 || item->currentAnimState)
|
|
|
|
{
|
|
|
|
RemoveActiveItem(itemNum);
|
|
|
|
|
|
|
|
item->status = ITEM_NOT_ACTIVE;
|
|
|
|
if (!item->itemFlags[0] == 0)
|
|
|
|
item->flags |= 0x100;
|
|
|
|
if (item->currentAnimState != 1)
|
|
|
|
return 1;
|
|
|
|
if (item->triggerFlags != 5 && item->triggerFlags != 6)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (item->status)
|
|
|
|
{
|
2021-08-30 15:13:18 -05:00
|
|
|
return (item->flags & 0x100u) >> 8;
|
2021-08-30 17:50:32 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
short* GetTriggerIndex(FLOOR_INFO* floor, int x, int y, int z)
|
|
|
|
{
|
|
|
|
ROOM_INFO* r;
|
|
|
|
while (floor->pitRoom != NO_ROOM)
|
|
|
|
{
|
|
|
|
if (CheckNoColFloorTriangle(floor, x, z) == 1)
|
|
|
|
break;
|
|
|
|
r = &g_Level.Rooms[floor->pitRoom];
|
2021-09-03 09:37:42 +02:00
|
|
|
floor = XZ_GET_SECTOR(r, x - r->x, z - r->z);
|
2021-08-30 17:50:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((floor->floor * 256) == NO_HEIGHT || floor->index == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
short* triggerIndex = NULL;
|
|
|
|
|
|
|
|
short* data = &g_Level.FloorData[floor->index];
|
|
|
|
short type;
|
|
|
|
int trigger;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
type = *(data++);
|
|
|
|
|
|
|
|
switch (type & DATA_TYPE)
|
|
|
|
{
|
|
|
|
case DOOR_TYPE:
|
|
|
|
case TILT_TYPE:
|
|
|
|
case ROOF_TYPE:
|
|
|
|
case SPLIT1:
|
|
|
|
case SPLIT2:
|
|
|
|
case SPLIT3:
|
|
|
|
case SPLIT4:
|
|
|
|
case NOCOLF1T:
|
|
|
|
case NOCOLF1B:
|
|
|
|
case NOCOLF2T:
|
|
|
|
case NOCOLF2B:
|
|
|
|
case NOCOLC1T:
|
|
|
|
case NOCOLC1B:
|
|
|
|
case NOCOLC2T:
|
|
|
|
case NOCOLC2B:
|
|
|
|
data++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LAVA_TYPE:
|
|
|
|
case CLIMB_TYPE:
|
|
|
|
case MONKEY_TYPE:
|
|
|
|
case TRIGTRIGGER_TYPE:
|
|
|
|
case MINER_TYPE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRIGGER_TYPE:
|
|
|
|
triggerIndex = data - 1;
|
|
|
|
data++;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
trigger = *(data++);
|
|
|
|
|
|
|
|
if (TRIG_BITS(trigger) != TO_OBJECT)
|
|
|
|
{
|
|
|
|
if (TRIG_BITS(trigger) == TO_CAMERA ||
|
|
|
|
TRIG_BITS(trigger) == TO_FLYBY)
|
|
|
|
{
|
|
|
|
trigger = *(data++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (!(trigger & END_BIT));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (!(type & END_BIT));
|
|
|
|
|
|
|
|
return triggerIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
short* GetTriggerIndex(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
auto roomNumber = item->roomNumber;
|
|
|
|
auto floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
|
|
|
|
return GetTriggerIndex(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TestTriggers(short* data, bool heavy, int heavyFlags)
|
|
|
|
{
|
|
|
|
int flip = -1;
|
|
|
|
int flipAvailable = 0;
|
|
|
|
int newEffect = -1;
|
|
|
|
int switchOff = 0;
|
|
|
|
int switchFlag = 0;
|
|
|
|
short objectNumber = 0;
|
|
|
|
int keyResult = 0;
|
|
|
|
short cameraFlags = 0;
|
|
|
|
short cameraTimer = 0;
|
|
|
|
int spotCamIndex = 0;
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
short triggerType = (*(data++) >> 8) & 0x3F;
|
|
|
|
short flags = *(data++);
|
|
|
|
short timer = flags & 0xFF;
|
|
|
|
|
|
|
|
if (Camera.type != HEAVY_CAMERA)
|
|
|
|
RefreshCamera(triggerType, data);
|
|
|
|
|
|
|
|
short value = 0;
|
|
|
|
|
|
|
|
if (heavy)
|
|
|
|
{
|
|
|
|
switch (triggerType)
|
|
|
|
{
|
|
|
|
case TRIGGER_TYPES::HEAVY:
|
|
|
|
case TRIGGER_TYPES::HEAVYANTITRIGGER:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::HEAVYSWITCH:
|
|
|
|
if (!heavyFlags)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (heavyFlags >= 0)
|
|
|
|
{
|
|
|
|
flags &= CODE_BITS;
|
|
|
|
if (flags != heavyFlags)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
flags |= CODE_BITS;
|
|
|
|
flags += heavyFlags;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Enemies can only activate heavy triggers
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (triggerType)
|
|
|
|
{
|
|
|
|
case TRIGGER_TYPES::SWITCH:
|
|
|
|
value = *(data++) & 0x3FF;
|
|
|
|
|
|
|
|
if (flags & ONESHOT)
|
|
|
|
g_Level.Items[value].itemFlags[0] = 1;
|
|
|
|
|
|
|
|
if (!SwitchTrigger(value, timer))
|
|
|
|
return;
|
|
|
|
|
|
|
|
objectNumber = g_Level.Items[value].objectNumber;
|
|
|
|
if (objectNumber >= ID_SWITCH_TYPE1 && objectNumber <= ID_SWITCH_TYPE6 && g_Level.Items[value].triggerFlags == 5)
|
|
|
|
switchFlag = 1;
|
|
|
|
|
|
|
|
switchOff = (g_Level.Items[value].currentAnimState == 1);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::MONKEY:
|
|
|
|
if (LaraItem->currentAnimState >= LS_MONKEYSWING_IDLE &&
|
|
|
|
(LaraItem->currentAnimState <= LS_MONKEYSWING_TURN_180 ||
|
|
|
|
LaraItem->currentAnimState == LS_MONKEYSWING_TURN_LEFT ||
|
|
|
|
LaraItem->currentAnimState == LS_MONKEYSWING_TURN_RIGHT))
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::TIGHTROPE_T:
|
|
|
|
if (LaraItem->currentAnimState >= LS_TIGHTROPE_IDLE &&
|
|
|
|
LaraItem->currentAnimState <= LS_TIGHTROPE_RECOVER_BALANCE &&
|
|
|
|
LaraItem->currentAnimState != LS_DOVESWITCH)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::CRAWLDUCK_T:
|
|
|
|
if (LaraItem->currentAnimState == LS_DOVESWITCH ||
|
|
|
|
LaraItem->currentAnimState == LS_CRAWL_IDLE ||
|
|
|
|
LaraItem->currentAnimState == LS_CRAWL_TURN_LEFT ||
|
|
|
|
LaraItem->currentAnimState == LS_CRAWL_TURN_RIGHT ||
|
|
|
|
LaraItem->currentAnimState == LS_CRAWL_BACK ||
|
|
|
|
LaraItem->currentAnimState == LS_CROUCH_IDLE ||
|
|
|
|
LaraItem->currentAnimState == LS_CROUCH_ROLL ||
|
|
|
|
LaraItem->currentAnimState == LS_CROUCH_TURN_LEFT ||
|
|
|
|
LaraItem->currentAnimState == LS_CROUCH_TURN_RIGHT)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::CLIMB_T:
|
|
|
|
if (LaraItem->currentAnimState == LS_HANG ||
|
|
|
|
LaraItem->currentAnimState == LS_LADDER_IDLE ||
|
|
|
|
LaraItem->currentAnimState == LS_LADDER_UP ||
|
|
|
|
LaraItem->currentAnimState == LS_LADDER_LEFT ||
|
|
|
|
LaraItem->currentAnimState == LS_LADDER_STOP ||
|
|
|
|
LaraItem->currentAnimState == LS_LADDER_RIGHT ||
|
|
|
|
LaraItem->currentAnimState == LS_LADDER_DOWN ||
|
|
|
|
LaraItem->currentAnimState == LS_MONKEYSWING_IDLE)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::PAD:
|
|
|
|
case TRIGGER_TYPES::ANTIPAD:
|
|
|
|
if (LaraItem->pos.yPos == LaraItem->floor)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::KEY:
|
|
|
|
value = *(data++) & 0x3FF;
|
|
|
|
keyResult = KeyTrigger(value);
|
|
|
|
if (keyResult != -1)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::PICKUP:
|
|
|
|
value = *(data++) & 0x3FF;
|
|
|
|
if (!PickupTrigger(value))
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::COMBAT:
|
|
|
|
if (Lara.gunStatus == LG_READY)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
// case TRIGGER_TYPES::SKELETON_T: //NO.
|
|
|
|
// Lara.skelebob = 2;
|
|
|
|
// break;
|
|
|
|
|
|
|
|
case TRIGGER_TYPES::HEAVY:
|
|
|
|
case TRIGGER_TYPES::DUMMY:
|
|
|
|
case TRIGGER_TYPES::HEAVYSWITCH:
|
|
|
|
case TRIGGER_TYPES::HEAVYANTITRIGGER:
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
short targetType = 0;
|
|
|
|
short trigger = 0;
|
|
|
|
|
|
|
|
ITEM_INFO* item = NULL;
|
|
|
|
ITEM_INFO* cameraItem = NULL;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
trigger = *(data++);
|
|
|
|
value = trigger & VALUE_BITS;
|
|
|
|
targetType = (trigger >> 10) & 0xF;
|
|
|
|
|
|
|
|
switch (targetType)
|
|
|
|
{
|
|
|
|
case TO_OBJECT:
|
|
|
|
item = &g_Level.Items[value];
|
|
|
|
|
|
|
|
if (keyResult >= 2 ||
|
|
|
|
(triggerType == TRIGGER_TYPES::ANTIPAD ||
|
|
|
|
triggerType == TRIGGER_TYPES::ANTITRIGGER ||
|
|
|
|
triggerType == TRIGGER_TYPES::HEAVYANTITRIGGER) &&
|
|
|
|
item->flags & ATONESHOT)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (triggerType == TRIGGER_TYPES::SWITCH)
|
|
|
|
{
|
|
|
|
if (item->flags & SWONESHOT)
|
|
|
|
break;
|
|
|
|
if (item->objectNumber == ID_DART_EMITTER && item->active)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
item->timer = timer;
|
|
|
|
if (timer != 1)
|
|
|
|
item->timer = 30 * timer;
|
|
|
|
|
|
|
|
if (triggerType == TRIGGER_TYPES::SWITCH ||
|
|
|
|
triggerType == TRIGGER_TYPES::HEAVYSWITCH)
|
|
|
|
{
|
|
|
|
if (heavyFlags >= 0)
|
|
|
|
{
|
|
|
|
if (switchFlag)
|
|
|
|
item->flags |= (flags & CODE_BITS);
|
|
|
|
else
|
|
|
|
item->flags ^= (flags & CODE_BITS);
|
|
|
|
|
|
|
|
if (flags & ONESHOT)
|
|
|
|
item->flags |= SWONESHOT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (((flags ^ item->flags) & CODE_BITS) == CODE_BITS)
|
|
|
|
{
|
|
|
|
item->flags ^= (flags & CODE_BITS);
|
|
|
|
if (flags & ONESHOT)
|
|
|
|
item->flags |= SWONESHOT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (triggerType == TRIGGER_TYPES::ANTIPAD ||
|
|
|
|
triggerType == TRIGGER_TYPES::ANTITRIGGER ||
|
|
|
|
triggerType == TRIGGER_TYPES::HEAVYANTITRIGGER)
|
|
|
|
{
|
|
|
|
if (item->objectNumber == ID_EARTHQUAKE)
|
|
|
|
{
|
|
|
|
item->itemFlags[0] = 0;
|
|
|
|
item->itemFlags[1] = 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
item->flags &= ~(CODE_BITS | REVERSE);
|
|
|
|
|
|
|
|
if (flags & ONESHOT)
|
|
|
|
item->flags |= ATONESHOT;
|
|
|
|
|
|
|
|
if (item->active && Objects[item->objectNumber].intelligent)
|
|
|
|
{
|
|
|
|
item->hitPoints = NOT_TARGETABLE;
|
|
|
|
DisableBaddieAI(value);
|
|
|
|
KillItem(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (flags & CODE_BITS)
|
|
|
|
{
|
|
|
|
item->flags |= flags & CODE_BITS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((item->flags & CODE_BITS) == CODE_BITS)
|
|
|
|
{
|
|
|
|
item->flags |= 0x20;
|
|
|
|
|
|
|
|
if (flags & ONESHOT)
|
|
|
|
item->flags |= ONESHOT;
|
|
|
|
|
|
|
|
if (!(item->active) && !(item->flags & IFLAG_KILLED))
|
|
|
|
{
|
|
|
|
if (Objects[item->objectNumber].intelligent)
|
|
|
|
{
|
|
|
|
if (item->status != ITEM_NOT_ACTIVE)
|
|
|
|
{
|
|
|
|
if (item->status == ITEM_INVISIBLE)
|
|
|
|
{
|
|
|
|
item->touchBits = 0;
|
|
|
|
if (EnableBaddieAI(value, 0))
|
|
|
|
{
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
AddActiveItem(value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->status == ITEM_INVISIBLE;
|
|
|
|
AddActiveItem(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->touchBits = 0;
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
AddActiveItem(value);
|
|
|
|
EnableBaddieAI(value, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->touchBits = 0;
|
|
|
|
AddActiveItem(value);
|
|
|
|
item->status = ITEM_ACTIVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_CAMERA:
|
|
|
|
trigger = *(data++);
|
|
|
|
|
|
|
|
if (keyResult == 1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (g_Level.Cameras[value].flags & ONESHOT)
|
|
|
|
break;
|
|
|
|
|
|
|
|
Camera.number = value;
|
|
|
|
|
|
|
|
if (Camera.type == LOOK_CAMERA || Camera.type == COMBAT_CAMERA && !(g_Level.Cameras[value].flags & 3))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (triggerType == TRIGGER_TYPES::COMBAT)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (triggerType == TRIGGER_TYPES::SWITCH && timer && switchOff)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (Camera.number != Camera.last || triggerType == TRIGGER_TYPES::SWITCH)
|
|
|
|
{
|
|
|
|
Camera.timer = (trigger & 0xFF) * 30;
|
|
|
|
|
|
|
|
if (trigger & ONESHOT)
|
|
|
|
g_Level.Cameras[Camera.number].flags |= ONESHOT;
|
|
|
|
|
|
|
|
Camera.speed = ((trigger & CODE_BITS) >> 6) + 1;
|
|
|
|
Camera.type = heavy ? HEAVY_CAMERA : FIXED_CAMERA;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_FLYBY:
|
|
|
|
trigger = *(data++);
|
|
|
|
|
|
|
|
if (keyResult == 1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (triggerType == TRIGGER_TYPES::ANTIPAD ||
|
|
|
|
triggerType == TRIGGER_TYPES::ANTITRIGGER ||
|
|
|
|
triggerType == TRIGGER_TYPES::HEAVYANTITRIGGER)
|
|
|
|
UseSpotCam = false;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spotCamIndex = 0;
|
|
|
|
if (SpotCamRemap[value] != 0)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < SpotCamRemap[value]; i++)
|
|
|
|
{
|
|
|
|
spotCamIndex += CameraCnt[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(SpotCam[spotCamIndex].flags & SCF_CAMERA_ONE_SHOT))
|
|
|
|
{
|
|
|
|
if (trigger & ONESHOT)
|
|
|
|
SpotCam[spotCamIndex].flags |= SCF_CAMERA_ONE_SHOT;
|
|
|
|
|
|
|
|
if (!UseSpotCam || CurrentLevel == 0)
|
|
|
|
{
|
|
|
|
UseSpotCam = true;
|
|
|
|
if (LastSpotCam != value)
|
|
|
|
TrackCameraInit = false;
|
|
|
|
InitialiseSpotCam(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_TARGET:
|
|
|
|
cameraItem = &g_Level.Items[value];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_SINK:
|
|
|
|
Lara.currentActive = value + 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_FLIPMAP:
|
|
|
|
flipAvailable = true;
|
|
|
|
|
|
|
|
if (FlipMap[value] & 0x100)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (triggerType == TRIGGER_TYPES::SWITCH)
|
|
|
|
FlipMap[value] ^= (flags & CODE_BITS);
|
|
|
|
else if (flags & CODE_BITS)
|
|
|
|
FlipMap[value] |= (flags & CODE_BITS);
|
|
|
|
|
|
|
|
if ((FlipMap[value] & CODE_BITS) == CODE_BITS)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (flags & 0x100)
|
|
|
|
FlipMap[value] |= 0x100;
|
|
|
|
if (!FlipStats[value])
|
|
|
|
flip = value;
|
|
|
|
}
|
|
|
|
else if (FlipStats[value])
|
|
|
|
flip = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_FLIPON:
|
|
|
|
flipAvailable = true;
|
|
|
|
FlipMap[value] |= CODE_BITS;
|
|
|
|
if (!FlipStats[value])
|
|
|
|
flip = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_FLIPOFF:
|
|
|
|
flipAvailable = true;
|
|
|
|
FlipMap[value] &= ~CODE_BITS;
|
|
|
|
if (FlipStats[value])
|
|
|
|
flip = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_FLIPEFFECT:
|
|
|
|
TriggerTimer = timer;
|
|
|
|
newEffect = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_FINISH:
|
|
|
|
RequiredStartPos = false;
|
|
|
|
LevelComplete = CurrentLevel + 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_CD:
|
|
|
|
PlaySoundTrack(value, flags);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TO_CUTSCENE:
|
|
|
|
// TODO: not used for now
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (!(trigger & END_BIT));
|
|
|
|
|
|
|
|
if (cameraItem && (Camera.type == FIXED_CAMERA || Camera.type == HEAVY_CAMERA))
|
|
|
|
Camera.item = cameraItem;
|
|
|
|
|
|
|
|
if (flip != -1)
|
|
|
|
DoFlipMap(flip);
|
|
|
|
|
|
|
|
if (newEffect != -1 && (flip || !flipAvailable))
|
|
|
|
{
|
|
|
|
FlipEffect = newEffect;
|
|
|
|
FlipTimer = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestTriggers(ITEM_INFO* item, bool heavy, int heavyFlags)
|
|
|
|
{
|
|
|
|
TestTriggers(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber, heavy, heavyFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags)
|
|
|
|
{
|
|
|
|
auto roomNum = roomNumber;
|
|
|
|
auto floor = GetFloor(x, y, z, &roomNum);
|
|
|
|
|
|
|
|
// Don't process legacy triggers if trigger triggerer wasn't used
|
|
|
|
if (floor->Flags.MarkTriggerer && !floor->Flags.MarkTriggererActive)
|
|
|
|
return;
|
|
|
|
|
|
|
|
TestTriggers(GetTriggerIndex(floor, x, y, z), heavy, heavyFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessSectorFlags(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
ProcessSectorFlags(GetCollisionResult(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber).BottomBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessSectorFlags(int x, int y, int z, short roomNumber)
|
|
|
|
{
|
|
|
|
ProcessSectorFlags(GetCollisionResult(x, y, z, roomNumber).BottomBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessSectorFlags(FLOOR_INFO* floor)
|
|
|
|
{
|
|
|
|
// Monkeyswing
|
|
|
|
Lara.canMonkeySwing = floor->Flags.Monkeyswing;
|
|
|
|
|
|
|
|
// Burn Lara
|
|
|
|
if (floor->Flags.Death && (LaraItem->pos.yPos == LaraItem->floor || Lara.waterStatus))
|
|
|
|
LavaBurn(LaraItem);
|
|
|
|
|
|
|
|
// Set climb status
|
|
|
|
if ((1 << (GetQuadrant(LaraItem->pos.yRot) + 8)) & GetClimbFlags(floor))
|
|
|
|
Lara.climbStatus = true;
|
2021-09-04 15:43:37 +03:00
|
|
|
else
|
|
|
|
Lara.climbStatus = false;
|
2021-08-30 17:50:32 +03:00
|
|
|
}
|