TombEngine/TR5Main/Game/Lara/lara_tests.cpp

1493 lines
38 KiB
C++
Raw Normal View History

#include "framework.h"
#include "lara.h"
#include "lara_tests.h"
#include "input.h"
2021-09-19 18:29:25 +03:00
#include "level.h"
#include "animation.h"
#include "lara_climb.h"
2021-10-27 10:13:47 +03:00
#include "lara_monkey.h"
#include "lara_collide.h"
2021-09-19 23:41:26 +03:00
#include "control/control.h"
2021-10-07 19:39:41 +03:00
#include "control/los.h"
2021-09-25 16:00:30 +03:00
#include "items.h"
2021-10-08 00:54:00 +03:00
#include "Renderer11.h"
2021-09-25 11:27:47 +02:00
2021-10-08 00:54:00 +03:00
using namespace TEN::Renderer;
2021-08-30 18:03:21 +03:00
using namespace TEN::Floordata;
2021-01-17 15:56:48 -03:00
/*this file has all the generic test functions called in lara's state code*/
2021-10-07 16:45:26 +03:00
// Test if a ledge in front of item is valid to climb.
bool TestValidLedge(ITEM_INFO* item, COLL_INFO* coll, bool ignoreHeadroom, bool heightLimit)
2021-10-07 16:45:26 +03:00
{
2021-10-07 19:49:59 +03:00
// Determine probe base point.
2021-10-23 14:34:02 +03:00
// We use 1/3 radius extents here for two purposes. First - we can't guarantee that
2021-10-07 19:49:59 +03:00
// shifts weren't already applied and misfire may occur. Second - it guarantees
// that Lara won't land on a very thin edge of diagonal geometry.
2021-10-23 14:34:02 +03:00
int xf = phd_sin(coll->NearestLedgeAngle) * (coll->Setup.Radius * 1.3f);
int zf = phd_cos(coll->NearestLedgeAngle) * (coll->Setup.Radius * 1.3f);
2021-10-07 19:49:59 +03:00
// Determine probe left/right points
2021-10-07 19:39:41 +03:00
int xl = xf + phd_sin(coll->NearestLedgeAngle - ANGLE(90)) * coll->Setup.Radius;
int zl = zf + phd_cos(coll->NearestLedgeAngle - ANGLE(90)) * coll->Setup.Radius;
int xr = xf + phd_sin(coll->NearestLedgeAngle + ANGLE(90)) * coll->Setup.Radius;
int zr = zf + phd_cos(coll->NearestLedgeAngle + ANGLE(90)) * coll->Setup.Radius;
2021-10-07 16:45:26 +03:00
2021-10-08 00:54:00 +03:00
// Determine probe top point
int y = item->pos.yPos - coll->Setup.Height;
2021-10-07 19:49:59 +03:00
// Get floor heights at both points
2021-10-08 00:54:00 +03:00
auto left = GetCollisionResult(item->pos.xPos + xl, y, item->pos.zPos + zl, GetRoom(item->location, item->pos.xPos, y, item->pos.zPos).roomNumber).Position.Floor;
auto right = GetCollisionResult(item->pos.xPos + xr, y, item->pos.zPos + zr, GetRoom(item->location, item->pos.xPos, y, item->pos.zPos).roomNumber).Position.Floor;
//g_Renderer.addDebugSphere(Vector3(item->pos.xPos + xl, left, item->pos.zPos + zl), 64, Vector4::One, RENDERER_DEBUG_PAGE::LOGIC_STATS);
//g_Renderer.addDebugSphere(Vector3(item->pos.xPos + xr, right, item->pos.zPos + zr), 64, Vector4::One, RENDERER_DEBUG_PAGE::LOGIC_STATS);
2021-10-07 16:45:26 +03:00
2021-10-07 19:49:59 +03:00
// Determine allowed slope difference for a given collision radius
auto slopeDelta = ((float)STEPUP_HEIGHT / (float)WALL_SIZE) * (coll->Setup.Radius * 2);
2021-10-07 16:45:26 +03:00
2021-10-29 05:13:32 +03:00
// If specified, limit vertical search zone only to nearest height
if (heightLimit && (abs(left - y) > (STEP_SIZE / 2) || abs(right - y) > (STEP_SIZE / 2)))
return false;
2021-10-07 19:49:59 +03:00
// Discard if there is a slope beyond tolerance delta
if (abs(left - right) >= slopeDelta)
2021-10-07 16:45:26 +03:00
return false;
2021-11-01 05:26:15 +03:00
// Discard if ledge is not within distance threshold
if (abs(coll->NearestLedgeDistance) > coll->Setup.Radius)
return false;
// Discard if ledge is not within angle threshold
if (!TestValidLedgeAngle(item, coll))
return false;
2021-10-23 12:53:55 +03:00
if (!ignoreHeadroom)
{
auto headroom = (coll->Front.Floor + coll->Setup.Height) - coll->Middle.Ceiling;
if (headroom < STEP_SIZE)
return false;
}
2021-10-07 19:49:59 +03:00
2021-10-07 16:45:26 +03:00
return (coll->CollisionType == CT_FRONT);
}
bool TestValidLedgeAngle(ITEM_INFO* item, COLL_INFO* coll)
2021-10-27 01:23:59 +03:00
{
2021-11-10 02:01:43 +11:00
return (abs((short) (coll->NearestLedgeAngle - item->pos.yRot)) <= LARA_GRAB_THRESHOLD);
2021-10-27 01:23:59 +03:00
}
2021-09-13 23:44:52 +03:00
bool TestLaraVault(ITEM_INFO* item, COLL_INFO* coll)
{
if (!(TrInput & IN_ACTION) || Lara.gunStatus != LG_NO_ARMS)
2021-09-13 23:44:52 +03:00
return false;
if (TestLaraSwamp(item) && Lara.waterSurfaceDist < -(STOP_SIZE + STEP_SIZE))
2021-11-07 04:54:48 +03:00
return false;
2021-10-08 01:46:29 +03:00
// TODO: Enable with lua!
Lara.NewAnims.CrawlVault1click = 1;
Lara.NewAnims.CrawlVault2click = 1;
Lara.NewAnims.CrawlVault3click = 1;
Lara.NewAnims.MonkeyVault = 1;
2021-10-07 16:45:26 +03:00
if (TestValidLedge(item, coll))
{
2021-10-27 01:23:59 +03:00
bool success = false;
2021-09-10 00:18:47 +03:00
if (coll->Front.Floor < 0 && coll->Front.Floor >= -256)
{
2021-10-07 16:45:26 +03:00
if (Lara.NewAnims.CrawlVault1click && (abs(coll->Front.Ceiling - coll->Front.Floor) < 256))
{
item->animNumber = LA_VAULT_TO_CROUCH_1CLICK;
item->currentAnimState = LS_GRABBING;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = LS_CROUCH_IDLE;
2021-09-10 00:18:47 +03:00
item->pos.yPos += coll->Front.Floor + 256;
Lara.gunStatus = LG_HANDS_BUSY;
2021-10-27 01:23:59 +03:00
success = true;
}
}
2021-09-10 00:18:47 +03:00
else if (coll->Front.Floor >= -640 && coll->Front.Floor <= -384)
{
2021-10-07 16:45:26 +03:00
if (coll->Front.Floor - coll->Front.Ceiling >= 0 &&
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor - coll->FrontLeft.Ceiling >= 0 &&
coll->FrontRight.Floor - coll->FrontRight.Ceiling >= 0)
{
item->animNumber = LA_VAULT_TO_STAND_2CLICK_START;
item->currentAnimState = LS_GRABBING;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = LS_STOP;
2021-09-10 00:18:47 +03:00
item->pos.yPos += coll->Front.Floor + 512;
Lara.gunStatus = LG_HANDS_BUSY;
2021-10-27 01:23:59 +03:00
success = true;
}
2021-10-07 16:45:26 +03:00
else if (Lara.NewAnims.CrawlVault2click && (abs(coll->Front.Ceiling - coll->Front.Floor) < 256))
{
item->animNumber = LA_VAULT_TO_CROUCH_2CLICK;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->currentAnimState = LS_GRABBING;
item->goalAnimState = LS_CROUCH_IDLE;
2021-09-10 00:18:47 +03:00
item->pos.yPos += coll->Front.Floor + 512;
Lara.gunStatus = LG_HANDS_BUSY;
2021-10-27 01:23:59 +03:00
success = true;
}
}
2021-09-10 00:18:47 +03:00
else if (coll->Front.Floor >= -896 && coll->Front.Floor <= -640)
{
2021-10-07 16:45:26 +03:00
if (coll->Front.Floor - coll->Front.Ceiling >= 0 &&
2021-09-10 00:18:47 +03:00
coll->FrontLeft.Floor - coll->FrontLeft.Ceiling >= 0 &&
coll->FrontRight.Floor - coll->FrontRight.Ceiling >= 0)
{
item->animNumber = LA_VAULT_TO_STAND_3CLICK;
item->currentAnimState = LS_GRABBING;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = LS_STOP;
2021-09-10 00:18:47 +03:00
item->pos.yPos += coll->Front.Floor + 768;
Lara.gunStatus = LG_HANDS_BUSY;
2021-10-27 01:23:59 +03:00
success = true;
}
2021-10-07 16:45:26 +03:00
else if (Lara.NewAnims.CrawlVault3click && (abs(coll->Front.Ceiling - coll->Front.Floor) < 256))
{
item->animNumber = LA_VAULT_TO_CROUCH_3CLICK;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->currentAnimState = LS_GRABBING;
item->goalAnimState = LS_CROUCH_IDLE;
2021-09-10 00:18:47 +03:00
item->pos.yPos += coll->Front.Floor + 768;
Lara.gunStatus = LG_HANDS_BUSY;
2021-10-27 01:23:59 +03:00
success = true;
}
}
2021-10-07 16:45:26 +03:00
else if (coll->Front.Floor >= -1920 && coll->Front.Floor <= -896)
{
item->animNumber = LA_STAND_SOLID;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = LS_JUMP_UP;
item->currentAnimState = LS_STOP;
2021-09-10 00:18:47 +03:00
Lara.calcFallSpeed = -3 - sqrt(-9600 - 12 * coll->Front.Floor);
AnimateLara(item);
2021-10-27 01:23:59 +03:00
success = true;
}
2021-10-27 01:23:59 +03:00
if (success)
2021-10-28 17:36:12 +03:00
{
2021-10-29 05:35:27 +03:00
SnapItemToLedge(item, coll, 0.2f);
2021-10-28 17:36:12 +03:00
return true;
}
2021-10-09 01:55:22 +03:00
}
2021-10-27 01:23:59 +03:00
if (TestValidLedgeAngle(item, coll) && Lara.climbStatus)
2021-10-09 01:55:22 +03:00
{
if (coll->Front.Floor > -1920 || Lara.waterStatus == LW_WADE || coll->FrontLeft.Floor > -1920 || coll->FrontRight.Floor > -2048 || coll->Middle.Ceiling > -1158)
{
if ((coll->Front.Floor < -1024 || coll->Front.Ceiling >= 506) && coll->Middle.Ceiling <= -518)
{
2021-10-09 01:55:22 +03:00
if (TestLaraClimbStance(item, coll))
{
2021-10-09 01:55:22 +03:00
item->animNumber = LA_STAND_SOLID;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = LS_LADDER_IDLE;
item->currentAnimState = LS_STOP;
Lara.gunStatus = LG_HANDS_BUSY;
Lara.turnRate = 0;
2021-10-27 01:23:59 +03:00
ShiftItem(item, coll);
SnapItemToGrid(item, coll); // HACK: until fragile ladder code is refactored, we must exactly snap to grid.
2021-10-27 10:31:46 +03:00
AnimateLara(item);
2021-10-27 01:23:59 +03:00
2021-10-09 01:55:22 +03:00
return true;
}
}
2021-10-09 01:55:22 +03:00
return false;
}
2021-10-09 01:55:22 +03:00
item->animNumber = LA_STAND_SOLID;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->goalAnimState = LS_JUMP_UP;
item->currentAnimState = LS_STOP;
Lara.calcFallSpeed = -116;
Lara.turnRate = 0;
2021-10-27 11:31:31 +03:00
ShiftItem(item, coll);
SnapItemToGrid(item, coll); // HACK: until fragile ladder code is refactored, we must exactly snap to grid.
2021-10-27 11:31:31 +03:00
AnimateLara(item);
2021-09-13 23:44:52 +03:00
return true;
}
2021-10-27 01:23:59 +03:00
if (Lara.canMonkeySwing && Lara.NewAnims.MonkeyVault)
{
2021-10-09 01:55:22 +03:00
short roomNum = item->roomNumber;
int ceiling = (GetCeiling(GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNum),
item->pos.xPos, item->pos.yPos, item->pos.zPos))-(item->pos.yPos);
2021-07-08 14:15:14 -05:00
2021-10-09 01:55:22 +03:00
if (ceiling > 1792 || ceiling < -1792 || abs(ceiling) == 768)
return false;
2021-07-08 14:15:14 -05:00
2021-10-09 01:55:22 +03:00
item->animNumber = LA_STAND_IDLE;
item->frameNumber = g_Level.Anims[LA_STAND_IDLE].frameBase;
item->goalAnimState = LS_JUMP_UP;
item->currentAnimState = LS_TEST_1;
2021-10-27 01:23:59 +03:00
2021-10-09 01:55:22 +03:00
return true;
}
2021-09-13 23:44:52 +03:00
return false;
}
2021-09-09 20:21:05 +03:00
bool TestLaraStandUp(COLL_INFO* coll)
{
return (coll->Middle.Ceiling >= -362);
2021-09-09 20:21:05 +03:00
}
2021-09-13 09:12:46 +03:00
bool TestLaraSlide(ITEM_INFO* item, COLL_INFO* coll)
{
static short oldAngle = 1;
if (abs(coll->TiltX) <= 2 && abs(coll->TiltZ) <= 2)
return false;
short angle = ANGLE(0.0f);
if (coll->TiltX > 2)
angle = -ANGLE(90.0f);
else if (coll->TiltX < -2)
angle = ANGLE(90.0f);
if (coll->TiltZ > 2 && coll->TiltZ > abs(coll->TiltX))
angle = ANGLE(180.0f);
else if (coll->TiltZ < -2 && -coll->TiltZ > abs(coll->TiltX))
angle = ANGLE(0.0f);
short delta = angle - item->pos.yRot;
ShiftItem(item, coll);
if (delta < -ANGLE(90.0f) || delta > ANGLE(90.0f))
{
if (item->currentAnimState == LS_SLIDE_BACK && oldAngle == angle)
return true;
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_SLIDE_BACK_START);
2021-09-13 09:12:46 +03:00
item->pos.yRot = angle + ANGLE(180.0f);
}
else
{
if (item->currentAnimState == LS_SLIDE_FORWARD && oldAngle == angle)
return true;
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_SLIDE_FORWARD);
2021-09-13 09:12:46 +03:00
item->pos.yRot = angle;
}
Lara.moveAngle = angle;
oldAngle = angle;
return true;
}
2021-09-14 00:41:11 +03:00
SPLAT_COLL TestLaraWall(ITEM_INFO* item, int front, int right, int down)
{
int x = item->pos.xPos;
int y = item->pos.yPos + down;
int z = item->pos.zPos;
short angle = GetQuadrant(item->pos.yRot);
short roomNum = item->roomNumber;
FLOOR_INFO* floor;
int h, c;
switch (angle)
{
case NORTH:
x -= right;
break;
case EAST:
z -= right;
break;
case SOUTH:
x += right;
break;
case WEST:
z += right;
break;
default:
break;
}
GetFloor(x, y, z, &roomNum);
switch (angle)
{
case NORTH:
z += front;
break;
case EAST:
x += front;
break;
case SOUTH:
z -= front;
break;
case WEST:
x -= front;
break;
default:
break;
}
floor = GetFloor(x, y, z, &roomNum);
h = GetFloorHeight(floor, x, y, z);
c = GetCeiling(floor, x, y, z);
if (h == NO_HEIGHT)
2021-10-27 09:48:31 +03:00
return SPLAT_COLL::WALL;
if (y >= h || y <= c)
2021-10-27 09:48:31 +03:00
return SPLAT_COLL::STEP;
2021-10-27 09:48:31 +03:00
return SPLAT_COLL::NONE;
}
2021-10-27 10:13:47 +03:00
bool TestLaraHangJumpUp(ITEM_INFO* item, COLL_INFO* coll)
{
if (!(TrInput & IN_ACTION) || (Lara.gunStatus != LG_NO_ARMS) || (coll->HitStatic))
return false;
if (Lara.canMonkeySwing && coll->CollisionType == CT_TOP)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_JUMP_UP_TO_MONKEYSWING);
2021-10-27 10:13:47 +03:00
item->gravityStatus = false;
item->speed = 0;
item->fallspeed = 0;
Lara.gunStatus = LG_HANDS_BUSY;
MonkeySwingSnap(item, coll);
return true;
}
2021-10-28 03:04:20 +03:00
if ((coll->CollisionType != CT_FRONT) || (coll->Middle.Ceiling > -STEPUP_HEIGHT))
2021-10-27 10:13:47 +03:00
return false;
int edge;
auto edgeCatch = TestLaraEdgeCatch(item, coll, &edge);
if (!edgeCatch)
return false;
bool ladder = TestLaraHangOnClimbWall(item, coll);
if (!(ladder && edgeCatch) &&
!(TestValidLedge(item, coll, true, true) && edgeCatch > 0))
2021-10-27 10:13:47 +03:00
return false;
auto angle = item->pos.yRot;
if (TestHangSwingIn(item, angle))
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_JUMP_UP_TO_MONKEYSWING);
2021-10-27 10:13:47 +03:00
}
else
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_HANG, 12);
2021-10-27 10:13:47 +03:00
if (TestHangFeet(item, angle))
item->goalAnimState = LS_HANG_FEET;
}
auto bounds = GetBoundsAccurate(item);
if (edgeCatch <= 0)
item->pos.yPos = edge - bounds->Y1 + 4;
else
item->pos.yPos += coll->Front.Floor - bounds->Y1;
if (ladder)
SnapItemToGrid(item, coll); // HACK: until fragile ladder code is refactored, we must exactly snap to grid.
else
SnapItemToLedge(item, coll);
2021-10-27 10:13:47 +03:00
item->gravityStatus = false;
item->speed = 0;
item->fallspeed = 0;
Lara.gunStatus = LG_HANDS_BUSY;
Lara.torsoYrot = 0;
Lara.torsoXrot = 0;
return true;
}
2021-10-27 09:48:48 +03:00
bool TestLaraHangJump(ITEM_INFO* item, COLL_INFO* coll)
{
if (!(TrInput & IN_ACTION) || (Lara.gunStatus != LG_NO_ARMS) || (coll->HitStatic))
return false;
if (Lara.canMonkeySwing && coll->CollisionType == CT_TOP)
{
Lara.headYrot = 0;
Lara.headXrot = 0;
Lara.torsoYrot = 0;
Lara.torsoXrot = 0;
Lara.gunStatus = LG_HANDS_BUSY;
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_MONKEYSWING);
2021-10-27 09:48:48 +03:00
item->gravityStatus = false;
item->speed = 0;
item->fallspeed = 0;
return true;
}
if ((coll->Middle.Ceiling > -STEPUP_HEIGHT) ||
(coll->Middle.Floor < 200) ||
(coll->CollisionType != CT_FRONT))
return false;
2021-10-27 10:13:47 +03:00
int edge;
2021-10-27 09:48:48 +03:00
auto edgeCatch = TestLaraEdgeCatch(item, coll, &edge);
if (!edgeCatch)
return false;
bool ladder = TestLaraHangOnClimbWall(item, coll);
if (!(ladder && edgeCatch) &&
!(TestValidLedge(item, coll, true, true) && edgeCatch > 0))
2021-10-27 09:48:48 +03:00
return false;
auto angle = item->pos.yRot;
2021-11-10 02:01:43 +11:00
Lara.NewAnims.OscillateHanging = true;
2021-10-27 09:48:48 +03:00
if (TestHangSwingIn(item, angle))
{
if (Lara.NewAnims.OscillateHanging)
{
Lara.headYrot = 0;
Lara.headXrot = 0;
Lara.torsoYrot = 0;
Lara.torsoXrot = 0;
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_HANG_OSCILLATE);
2021-10-27 09:48:48 +03:00
}
else
{
Lara.headYrot = 0;
Lara.headXrot = 0;
Lara.torsoYrot = 0;
Lara.torsoXrot = 0;
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_MONKEYSWING);
2021-10-27 09:48:48 +03:00
}
}
else
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_HANG);
2021-10-27 09:48:48 +03:00
if (TestHangFeet(item, angle))
item->goalAnimState = LS_HANG_FEET;
}
auto bounds = GetBoundsAccurate(item);
if (edgeCatch <= 0)
{
item->pos.yPos = edge - bounds->Y1 - 20;
item->pos.yRot = coll->NearestLedgeAngle;
}
else
item->pos.yPos += coll->Front.Floor - bounds->Y1 - 20;
if (ladder)
SnapItemToGrid(item, coll); // HACK: until fragile ladder code is refactored, we must exactly snap to grid.
else
SnapItemToLedge(item, coll, 0.2f);
2021-10-27 09:48:48 +03:00
item->gravityStatus = true;
item->speed = 2;
item->fallspeed = 1;
Lara.gunStatus = LG_HANDS_BUSY;
return true;
}
2021-10-24 05:33:32 +03:00
bool TestLaraHang(ITEM_INFO* item, COLL_INFO* coll)
{
2021-09-14 00:03:24 +03:00
auto delta = 0;
2021-11-15 02:32:07 +03:00
auto flag = false;
2021-09-14 00:03:24 +03:00
auto angle = Lara.moveAngle;
if (Lara.moveAngle == (short) (item->pos.yRot - ANGLE(90.0f)))
2021-10-24 05:33:32 +03:00
delta = -coll->Setup.Radius;
else if (Lara.moveAngle == (short) (item->pos.yRot + ANGLE(90.0f)))
2021-10-24 05:33:32 +03:00
delta = coll->Setup.Radius;
2021-09-14 00:03:24 +03:00
2021-10-23 15:35:59 +03:00
auto s = phd_sin(Lara.moveAngle);
auto c = phd_cos(Lara.moveAngle);
auto testShift = Vector2(s * delta, c * delta);
2021-09-14 00:03:24 +03:00
2021-11-29 20:45:22 +03:00
auto oldPos = item->pos;
item->pos.xPos += phd_sin(item->pos.yRot) * coll->Setup.Radius * 0.5f;
item->pos.zPos += phd_cos(item->pos.yRot) * coll->Setup.Radius * 0.5f;
auto hdif = LaraFloorFront(item, Lara.moveAngle, coll->Setup.Radius * 1.5f);
if (hdif < 200)
2021-11-15 02:32:07 +03:00
flag = true;
2021-11-29 20:45:22 +03:00
auto cdif = LaraCeilingFront(item, Lara.moveAngle, coll->Setup.Radius * 1.5f, 0);
item->pos = oldPos;
2021-09-14 00:03:24 +03:00
auto dir = GetQuadrant(item->pos.yRot);
2021-10-24 05:33:32 +03:00
// When Lara is about to move, use larger embed offset for stabilizing diagonal shimmying)
auto embedOffset = 4;
2021-11-10 02:01:43 +11:00
if (TrInput & (IN_LEFT | IN_RIGHT))
2021-10-24 05:33:32 +03:00
embedOffset = 16;
item->pos.xPos += phd_sin(item->pos.yRot) * embedOffset;
item->pos.zPos += phd_cos(item->pos.yRot) * embedOffset;
2021-09-14 00:03:24 +03:00
Lara.moveAngle = item->pos.yRot;
2021-09-19 17:48:32 +03:00
coll->Setup.BadHeightDown = NO_BAD_POS;
coll->Setup.BadHeightUp = -STEPUP_HEIGHT;
2021-09-10 00:20:59 +03:00
coll->Setup.BadCeilingHeight = 0;
coll->Setup.ForwardAngle = Lara.moveAngle;
2021-09-14 00:03:24 +03:00
GetCollisionInfo(coll, item);
2021-09-14 00:03:24 +03:00
2021-09-16 01:56:02 +03:00
bool result = false;
if (Lara.climbStatus)
{
2021-11-10 02:01:43 +11:00
if (TrInput & IN_ACTION &&
item->hitPoints > 0)
{
Lara.moveAngle = angle;
2021-09-14 00:03:24 +03:00
2021-09-14 00:30:44 +03:00
if (!TestLaraHangOnClimbWall(item, coll))
{
2021-11-10 02:01:43 +11:00
if (item->animNumber != LA_LADDER_TO_HANG_RIGHT &&
item->animNumber != LA_LADDER_TO_HANG_LEFT)
{
2021-09-25 13:00:14 +03:00
LaraSnapToEdgeOfBlock(item, coll, dir);
2021-09-10 00:20:59 +03:00
item->pos.yPos = coll->Setup.OldPosition.y;
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_HANG, 21);
}
2021-11-10 02:01:43 +11:00
2021-09-14 00:03:24 +03:00
result = true;
}
else
{
2021-11-10 02:01:43 +11:00
if (item->animNumber == LA_REACH_TO_HANG &&
2021-11-10 02:41:59 +03:00
item->frameNumber == GetFrameNumber(item, 21) &&
2021-11-10 02:01:43 +11:00
TestLaraClimbStance(item, coll))
{
item->goalAnimState = LS_LADDER_IDLE;
2021-11-10 02:01:43 +11:00
}
}
}
else
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_FALL_START);
item->pos.yPos += 256;
item->gravityStatus = true;
item->speed = 2;
item->fallspeed = 1;
Lara.gunStatus = LG_NO_ARMS;
}
}
else
{
2021-11-10 02:01:43 +11:00
if (TrInput & IN_ACTION &&
item->hitPoints > 0 &&
coll->Front.Floor <= 0)
{
2021-09-10 00:18:47 +03:00
if (flag && hdif > 0 && delta > 0 == coll->MiddleLeft.Floor > coll->MiddleRight.Floor)
2021-11-15 02:32:07 +03:00
flag = false;
2021-09-14 00:03:24 +03:00
auto front = coll->Front.Floor;
auto dfront = coll->Front.Floor - GetBoundsAccurate(item)->Y1;
2021-11-15 02:32:07 +03:00
auto flag2 = false;
2021-09-14 00:03:24 +03:00
auto x = item->pos.xPos;
auto z = item->pos.zPos;
2021-10-23 15:35:59 +03:00
if (delta != 0)
{
2021-10-23 15:35:59 +03:00
x += testShift.x;
z += testShift.y;
}
2021-09-14 00:03:24 +03:00
Lara.moveAngle = angle;
2021-09-14 00:03:24 +03:00
2021-08-20 02:01:50 +03:00
if (256 << dir & GetClimbFlags(x, item->pos.yPos, z, item->roomNumber))
{
2021-09-14 00:30:44 +03:00
if (!TestLaraHangOnClimbWall(item, coll))
dfront = 0;
}
2021-10-23 15:35:59 +03:00
else if (!TestValidLedge(item, coll, true))
{
2021-09-10 00:18:47 +03:00
if (delta < 0 && coll->FrontLeft.Floor != coll->Front.Floor || delta > 0 && coll->FrontRight.Floor != coll->Front.Floor)
2021-11-15 02:32:07 +03:00
flag2 = true;
}
2021-09-14 00:03:24 +03:00
2021-09-10 00:18:47 +03:00
coll->Front.Floor = front;
2021-09-14 00:03:24 +03:00
if (!flag2 &&
coll->Middle.Ceiling < 0 &&
coll->CollisionType == CT_FRONT &&
!flag &&
!coll->HitStatic &&
cdif <= -950 &&
abs(dfront) < SLOPE_DIFFERENCE &&
TestValidLedgeAngle(item, coll))
{
2021-10-23 15:35:59 +03:00
if (item->speed != 0)
SnapItemToLedge(item, coll);
2021-09-14 00:03:24 +03:00
item->pos.yPos += dfront;
}
else
{
2021-09-10 00:20:59 +03:00
item->pos.xPos = coll->Setup.OldPosition.x;
item->pos.yPos = coll->Setup.OldPosition.y;
item->pos.zPos = coll->Setup.OldPosition.z;
2021-09-14 00:03:24 +03:00
2021-11-10 02:01:43 +11:00
if (item->currentAnimState == LS_SHIMMY_LEFT ||
item->currentAnimState == LS_SHIMMY_RIGHT)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_HANG, 21);
}
2021-11-10 02:01:43 +11:00
else if (item->currentAnimState == LS_SHIMMY_FEET_LEFT ||
2021-11-29 20:45:22 +03:00
item->currentAnimState == LS_SHIMMY_FEET_RIGHT)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_HANG_FEET_IDLE);
2021-07-09 19:41:56 -05:00
}
2021-09-14 00:03:24 +03:00
result = true;
}
}
else
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_JUMP_UP, 9);
2021-09-10 00:18:47 +03:00
item->pos.xPos += coll->Shift.x;
item->pos.yPos += GetBoundsAccurate(item)->Y2;
2021-09-10 00:18:47 +03:00
item->pos.zPos += coll->Shift.z;
item->gravityStatus = true;
item->speed = 2;
item->fallspeed = 1;
Lara.gunStatus = LG_NO_ARMS;
}
}
2021-09-14 00:03:24 +03:00
return result;
}
2021-10-26 15:44:36 +03:00
CORNER_RESULT TestLaraHangCorner(ITEM_INFO* item, COLL_INFO* coll, float testAngle)
{
2021-10-26 12:50:26 +03:00
// Lara isn't in stop state yet, bypass test
2021-07-09 19:41:56 -05:00
if (item->animNumber != LA_REACH_TO_HANG && item->animNumber != LA_HANG_FEET_IDLE)
2021-10-26 15:44:36 +03:00
return CORNER_RESULT::NONE;
2021-10-26 12:50:26 +03:00
// Static is in the way, bypass test
2021-09-10 00:18:47 +03:00
if (coll->HitStatic)
2021-10-26 15:44:36 +03:00
return CORNER_RESULT::NONE;
2021-10-26 12:50:26 +03:00
// INNER CORNER TESTS
2021-10-26 12:50:26 +03:00
// Backup old Lara position and frontal collision
auto oldPos = item->pos;
2021-11-29 18:09:34 +03:00
auto oldMoveAngle = Lara.moveAngle;
2021-09-10 00:18:47 +03:00
int oldFrontFloor = coll->Front.Floor;
// Do further testing only if test angle is equal to resulting edge angle
2021-11-29 18:09:34 +03:00
if (SnapAndTestItemAtNextCornerPosition(item, coll, testAngle, false))
{
2021-11-29 18:09:34 +03:00
// Get bounding box height for further ledge height calculations
auto bounds = GetBoundsAccurate(item);
// Store next position
2021-11-29 18:09:34 +03:00
Lara.nextCornerPos.xPos = item->pos.xPos;
Lara.nextCornerPos.yPos = LaraCollisionAboveFront(item, item->pos.yRot, coll->Setup.Radius * 2, abs(bounds->Y1)).Position.Floor + abs(bounds->Y1);
Lara.nextCornerPos.zPos = item->pos.zPos;
Lara.nextCornerPos.yRot = item->pos.yRot;
Lara.moveAngle = item->pos.yRot;
auto result = TestLaraValidHangPos(item, coll);
2021-10-26 12:50:26 +03:00
2021-11-29 18:21:50 +03:00
// Restore original item positions
item->pos = oldPos;
Lara.moveAngle = oldMoveAngle;
2021-10-27 02:24:55 +03:00
2021-11-29 18:21:50 +03:00
if (result && (abs(oldFrontFloor - coll->Front.Floor) <= SLOPE_DIFFERENCE))
return CORNER_RESULT::INNER;
2021-10-27 02:24:55 +03:00
if (Lara.climbStatus)
{
auto angleSet = testAngle > 0 ? LeftExtRightIntTab : LeftIntRightExtTab;
2021-11-29 18:09:34 +03:00
if (GetClimbFlags(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber) & (short)angleSet[GetQuadrant(item->pos.yRot)])
return CORNER_RESULT::INNER;
}
}
2021-10-27 02:24:55 +03:00
// Restore original item positions
item->pos = oldPos;
2021-11-29 18:09:34 +03:00
Lara.moveAngle = oldMoveAngle;
2021-10-27 02:24:55 +03:00
2021-10-26 12:50:26 +03:00
// OUTER CORNER TESTS
2021-10-26 13:39:42 +03:00
// Test if there's a material obstacles blocking outer corner pathway
2021-11-29 18:09:34 +03:00
if ((LaraFloorFront(item, item->pos.yRot + ANGLE(testAngle), coll->Setup.Radius + STEP_SIZE) < 0) ||
(LaraCeilingFront(item, item->pos.yRot + ANGLE(testAngle), coll->Setup.Radius + STEP_SIZE, coll->Setup.Height) > 0))
2021-10-26 15:44:36 +03:00
return CORNER_RESULT::NONE;
2021-10-28 01:13:28 +03:00
// Last chance for possible diagonal vs. non-diagonal cases: ray test
if (!LaraPositionOnLOS(item, item->pos.yRot + ANGLE(testAngle), coll->Setup.Radius + STEP_SIZE))
return CORNER_RESULT::NONE;
2021-11-29 18:09:34 +03:00
bool snappable = SnapAndTestItemAtNextCornerPosition(item, coll, testAngle, true);
2021-11-29 18:09:34 +03:00
// Additional test if there's a material obstacles blocking outer corner pathway
2021-11-29 18:21:50 +03:00
if ((LaraFloorFront(item, item->pos.yRot, 0) < 0) ||
(LaraCeilingFront(item, item->pos.yRot, 0, coll->Setup.Height) > 0))
return CORNER_RESULT::NONE;
// Do further testing only if test angle is equal to resulting edge angle
2021-11-29 18:09:34 +03:00
if (snappable)
2021-10-27 02:24:55 +03:00
{
2021-11-29 18:09:34 +03:00
// Get bounding box height for further ledge height calculations
auto bounds = GetBoundsAccurate(item);
// Store next position
2021-11-29 18:09:34 +03:00
Lara.nextCornerPos.xPos = item->pos.xPos;
Lara.nextCornerPos.yPos = LaraCollisionAboveFront(item, item->pos.yRot, coll->Setup.Radius * 2, abs(bounds->Y1)).Position.Floor + abs(bounds->Y1);
Lara.nextCornerPos.zPos = item->pos.zPos;
Lara.nextCornerPos.yRot = item->pos.yRot;
Lara.moveAngle = item->pos.yRot;
2021-11-29 18:21:50 +03:00
auto result = TestLaraValidHangPos(item, coll);
2021-11-29 18:21:50 +03:00
// Restore original item positions
item->pos = oldPos;
Lara.moveAngle = oldMoveAngle;
2021-11-29 18:21:50 +03:00
if (result && (abs(oldFrontFloor - coll->Front.Floor) <= SLOPE_DIFFERENCE))
return CORNER_RESULT::OUTER;
if (Lara.climbStatus)
2021-10-27 02:24:55 +03:00
{
auto angleSet = testAngle > 0 ? LeftIntRightExtTab : LeftExtRightIntTab;
2021-11-29 18:09:34 +03:00
if (GetClimbFlags(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber) & (short)angleSet[GetQuadrant(item->pos.yRot)])
return CORNER_RESULT::OUTER;
2021-10-27 02:24:55 +03:00
}
}
2021-10-27 02:24:55 +03:00
// Restore original item positions
item->pos = oldPos;
2021-11-29 18:09:34 +03:00
Lara.moveAngle = oldMoveAngle;
2021-10-27 02:24:55 +03:00
return CORNER_RESULT::NONE;
}
2021-10-26 15:44:36 +03:00
bool TestLaraValidHangPos(ITEM_INFO* item, COLL_INFO* coll)
{
2021-10-26 12:50:26 +03:00
// Get incoming ledge height and own Lara's upper bound.
// First one will be negative while first one is positive.
// Difference between two indicates difference in height between ledges.
2021-11-15 02:32:07 +03:00
auto frontFloor = LaraCollisionAboveFront(item, Lara.moveAngle, coll->Setup.Radius + STEP_SIZE / 2, LARA_HEIGHT).Position.Floor;
2021-10-26 12:50:26 +03:00
auto laraUpperBound = item->pos.yPos - coll->Setup.Height;
// If difference is above 1/2 click, return false (ledge is out of reach).
2021-10-26 13:39:42 +03:00
if (abs(frontFloor - laraUpperBound) > STEP_SIZE / 2)
2021-10-26 15:44:36 +03:00
return false;
2021-10-26 12:50:26 +03:00
// Embed Lara into wall to make collision test succeed
item->pos.xPos += phd_sin(item->pos.yRot) * 8;
item->pos.zPos += phd_cos(item->pos.yRot) * 8;
2021-10-26 12:50:26 +03:00
// Setup new GCI call
Lara.moveAngle = item->pos.yRot;
2021-09-19 17:48:32 +03:00
coll->Setup.BadHeightDown = NO_BAD_POS;
coll->Setup.BadHeightUp = -512;
2021-09-10 00:20:59 +03:00
coll->Setup.BadCeilingHeight = 0;
2021-10-28 12:58:46 +03:00
coll->Setup.Mode = COLL_PROBE_MODE::FREE_FLAT;
2021-09-10 00:20:59 +03:00
coll->Setup.ForwardAngle = Lara.moveAngle;
2021-10-26 12:50:26 +03:00
GetCollisionInfo(coll, item);
2021-10-26 12:50:26 +03:00
// Filter out narrow ceiling spaces, no collision cases and statics in front.
2021-09-10 00:18:47 +03:00
if (coll->Middle.Ceiling >= 0 || coll->CollisionType != CT_FRONT || coll->HitStatic)
2021-10-26 15:44:36 +03:00
return false;
2021-10-26 12:50:26 +03:00
// Finally, do ordinary ledge checks (slope difference etc.)
return TestValidLedge(item, coll);
}
2021-09-14 00:30:44 +03:00
bool TestLaraClimbStance(ITEM_INFO* item, COLL_INFO* coll)
{
int shift_r, shift_l;
2021-11-10 02:01:43 +11:00
if (LaraTestClimbPos(item, coll->Setup.Radius, coll->Setup.Radius + 120, -700, STOP_SIZE, &shift_r) != 1)
return false;
2021-11-10 02:01:43 +11:00
if (LaraTestClimbPos(item, coll->Setup.Radius, -(coll->Setup.Radius + 120), -700, STOP_SIZE, &shift_l) != 1)
return false;
if (shift_r)
{
if (shift_l)
{
if (shift_r < 0 != shift_l < 0)
return false;
if ((shift_r < 0 && shift_l < shift_r) ||
(shift_r > 0 && shift_l > shift_r))
{
item->pos.yPos += shift_l;
return true;
}
}
item->pos.yPos += shift_r;
}
else if (shift_l)
{
item->pos.yPos += shift_l;
}
return true;
}
2021-09-14 00:30:44 +03:00
bool TestLaraHangOnClimbWall(ITEM_INFO* item, COLL_INFO* coll)
{
int shift, result;
if (Lara.climbStatus == 0)
2021-09-13 09:12:46 +03:00
return false;
if (item->fallspeed < 0)
2021-09-13 09:12:46 +03:00
return false;
// HACK: Climb wall tests are highly fragile and depend on quadrant shifts.
// Until climb wall tests are fully refactored, we need to recalculate COLL_INFO.
auto coll2 = *coll;
coll2.Setup.Mode = COLL_PROBE_MODE::QUADRANTS;
GetCollisionInfo(&coll2, item);
switch (GetQuadrant(item->pos.yRot))
{
case NORTH:
case SOUTH:
item->pos.zPos += coll2.Shift.z;
break;
case EAST:
case WEST:
item->pos.xPos += coll2.Shift.x;
break;
default:
break;
}
auto bounds = GetBoundsAccurate(item);
if (Lara.moveAngle != item->pos.yRot)
{
short l = LaraCeilingFront(item, item->pos.yRot, 0, 0);
2021-11-10 02:01:43 +11:00
short r = LaraCeilingFront(item, Lara.moveAngle, STEP_SIZE / 2, 0);
if (abs(l - r) > SLOPE_DIFFERENCE)
2021-09-13 09:12:46 +03:00
return false;
}
if (LaraTestClimbPos(item, LARA_RAD, LARA_RAD, bounds->Y1, bounds->Y2 - bounds->Y1, &shift) &&
LaraTestClimbPos(item, LARA_RAD, -LARA_RAD, bounds->Y1, bounds->Y2 - bounds->Y1, &shift))
{
result = LaraTestClimbPos(item, LARA_RAD, 0, bounds->Y1, bounds->Y2 - bounds->Y1, &shift);
if (result)
{
if (result != 1)
item->pos.yPos += shift;
2021-11-10 02:01:43 +11:00
2021-09-13 09:12:46 +03:00
return true;
}
}
2021-09-13 09:12:46 +03:00
return false;
}
2021-09-14 00:30:44 +03:00
int TestLaraEdgeCatch(ITEM_INFO* item, COLL_INFO* coll, int* edge)
{
BOUNDING_BOX* bounds = GetBoundsAccurate(item);
2021-09-10 00:18:47 +03:00
int hdif = coll->Front.Floor - bounds->Y1;
if (hdif < 0 == hdif + item->fallspeed < 0)
{
hdif = item->pos.yPos + bounds->Y1;
if ((hdif + item->fallspeed & 0xFFFFFF00) != (hdif & 0xFFFFFF00))
{
if (item->fallspeed > 0)
*edge = (hdif + item->fallspeed) & 0xFFFFFF00;
else
*edge = hdif & 0xFFFFFF00;
return -1;
}
return 0;
}
2021-10-23 12:53:55 +03:00
if (!TestValidLedge(item, coll, true))
return 0;
return 1;
}
2021-09-13 09:12:46 +03:00
bool TestHangSwingIn(ITEM_INFO* item, short angle)
{
int x = item->pos.xPos;
int y = item->pos.yPos;
int z = item->pos.zPos;
short roomNum = item->roomNumber;
FLOOR_INFO* floor;
2021-11-10 02:01:43 +11:00
int floorHeight, ceilingHeight;
2021-11-10 02:01:43 +11:00
Lara.NewAnims.OscillateHanging = true;
2021-10-24 04:08:54 +03:00
z += phd_cos(angle) * (STEP_SIZE / 2);
x += phd_sin(angle) * (STEP_SIZE / 2);
floor = GetFloor(x, y, z, &roomNum);
2021-11-10 02:01:43 +11:00
floorHeight = GetFloorHeight(floor, x, y, z);
ceilingHeight = GetCeiling(floor, x, y, z);
2021-11-10 02:01:43 +11:00
if (floorHeight != NO_HEIGHT)
{
2021-07-09 19:41:56 -05:00
if (Lara.NewAnims.OscillateHanging)
{
2021-11-10 02:01:43 +11:00
if (floorHeight - y > 0 && ceilingHeight - y < -400)
2021-09-13 09:12:46 +03:00
return true;
}
else
2021-07-09 19:41:56 -05:00
{
2021-11-10 02:01:43 +11:00
if (floorHeight - y > 0 && ceilingHeight - y < -400 && (y - 819 - ceilingHeight > -72))
2021-09-13 09:12:46 +03:00
return true;
2021-07-09 19:41:56 -05:00
}
}
2021-09-13 09:12:46 +03:00
return false;
2021-07-09 19:41:56 -05:00
}
2021-07-09 19:41:56 -05:00
bool TestHangFeet(ITEM_INFO* item, short angle)
{
//##LUA debug etc.
2021-11-10 02:01:43 +11:00
Lara.NewAnims.FeetHanging = false;
2021-07-09 19:41:56 -05:00
if (Lara.climbStatus || !Lara.NewAnims.FeetHanging)
2021-09-13 23:44:52 +03:00
return false;
int x = item->pos.xPos;
int y = item->pos.yPos;
int z = item->pos.zPos;
short roomNum = item->roomNumber;
2021-10-24 04:08:54 +03:00
z += phd_cos(angle) * (STEP_SIZE / 2);
x += phd_sin(angle) * (STEP_SIZE / 2);
auto floor = GetFloor(x, y, z, &roomNum);
int floorHeight = GetFloorHeight(floor, x, y, z);
int ceilingHeight = GetCeiling(floor, x, y, z);
int m = ceilingHeight - y;
int j = y - (STEP_SIZE / 2) - ceilingHeight;
2021-11-10 02:01:43 +11:00
if (floorHeight != NO_HEIGHT)
{
if (floorHeight < y && m < -(STEP_SIZE / 2) && j > -(STEP_SIZE / 4 + STEP_SIZE / 32))
2021-09-13 23:44:52 +03:00
return true;
}
2021-09-13 23:44:52 +03:00
return false;
2021-07-09 19:41:56 -05:00
}
2021-09-14 00:30:44 +03:00
bool TestLaraHangSideways(ITEM_INFO* item, COLL_INFO* coll, short angle)
{
int oldx = item->pos.xPos;
int oldz = item->pos.zPos;
int x = item->pos.xPos;
int z = item->pos.zPos;
Lara.moveAngle = item->pos.yRot + angle;
2021-10-24 04:45:31 +03:00
2021-10-24 10:20:02 +03:00
z += phd_cos(Lara.moveAngle) * 16;
x += phd_sin(Lara.moveAngle) * 16;
item->pos.xPos = x;
item->pos.zPos = z;
2021-09-10 00:20:59 +03:00
coll->Setup.OldPosition.y = item->pos.yPos;
2021-09-14 00:30:44 +03:00
auto res = TestLaraHang(item, coll);
item->pos.xPos = oldx;
item->pos.zPos = oldz;
Lara.moveAngle = item->pos.yRot + angle;
return !res;
}
2021-11-29 18:09:34 +03:00
void SetCornerAnim(ITEM_INFO* item, COLL_INFO* coll, bool flip)
{
if (item->hitPoints <= 0)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_FALL_START);
item->gravityStatus = true;
item->speed = 2;
2021-11-10 02:01:43 +11:00
item->pos.yPos += STEP_SIZE;
item->fallspeed = 1;
Lara.gunStatus = LG_NO_ARMS;
2021-11-29 18:09:34 +03:00
item->pos.yRot += Lara.nextCornerPos.yRot / 2;
return;
}
2021-11-29 18:09:34 +03:00
if (flip)
{
if (Lara.isClimbing)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_LADDER_IDLE);
}
else
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_REACH_TO_HANG, 21);
}
2021-11-29 18:09:34 +03:00
coll->Setup.OldPosition.x = item->pos.xPos = Lara.nextCornerPos.xPos;
coll->Setup.OldPosition.y = item->pos.yPos = Lara.nextCornerPos.yPos;
coll->Setup.OldPosition.z = item->pos.zPos = Lara.nextCornerPos.zPos;
2021-11-29 21:50:07 +03:00
item->pos.yRot = Lara.nextCornerPos.yRot;
}
}
2021-11-29 18:09:34 +03:00
void SetCornerAnimFeet(ITEM_INFO* item, COLL_INFO* coll, bool flip)
{
if (item->hitPoints <= 0)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_FALL_START);
item->gravityStatus = true;
item->speed = 2;
2021-11-10 02:01:43 +11:00
item->pos.yPos += STEP_SIZE;
item->fallspeed = 1;
Lara.gunStatus = LG_NO_ARMS;
2021-11-29 18:09:34 +03:00
item->pos.yRot += Lara.nextCornerPos.yRot / 2;
return;
}
2021-11-29 18:09:34 +03:00
if (flip)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_HANG_FEET_IDLE);
2021-11-29 18:09:34 +03:00
coll->Setup.OldPosition.x = item->pos.xPos = Lara.nextCornerPos.xPos;
coll->Setup.OldPosition.y = item->pos.yPos = Lara.nextCornerPos.yPos;
coll->Setup.OldPosition.z = item->pos.zPos = Lara.nextCornerPos.zPos;
}
2021-11-29 18:09:34 +03:00
item->pos.yRot = Lara.nextCornerPos.yRot;
2021-07-09 19:41:56 -05:00
}
bool LaraFacingCorner(ITEM_INFO* item, short ang, int dist)
{
auto angle1 = ang + ANGLE(15);
auto angle2 = ang - ANGLE(15);
auto vec1 = GAME_VECTOR(item->pos.xPos + dist * phd_sin(angle1),
item->pos.yPos - (LARA_HEIGHT / 2),
item->pos.zPos + dist * phd_cos(angle1),
item->roomNumber);
auto vec2 = GAME_VECTOR(item->pos.xPos + dist * phd_sin(angle2),
item->pos.yPos - (LARA_HEIGHT / 2),
item->pos.zPos + dist * phd_cos(angle2),
item->roomNumber);
auto pos = GAME_VECTOR(item->pos.xPos,
item->pos.yPos - (LARA_HEIGHT / 2),
item->pos.zPos,
item->roomNumber);
auto result1 = LOS(&pos, &vec1);
auto result2 = LOS(&pos, &vec2);
return ((result1 == 0) && (result2 == 0));
}
bool LaraPositionOnLOS(ITEM_INFO* item, short ang, int dist)
{
auto pos1 = GAME_VECTOR(item->pos.xPos,
item->pos.yPos - LARA_HEADROOM,
item->pos.zPos,
item->roomNumber);
auto pos2 = GAME_VECTOR(item->pos.xPos,
item->pos.yPos - LARA_HEIGHT + LARA_HEADROOM,
item->pos.zPos,
item->roomNumber);
auto vec1 = GAME_VECTOR(item->pos.xPos + dist * phd_sin(ang),
item->pos.yPos - LARA_HEADROOM,
item->pos.zPos + dist * phd_cos(ang),
item->roomNumber);
auto vec2 = GAME_VECTOR(item->pos.xPos + dist * phd_sin(ang),
item->pos.yPos - LARA_HEIGHT + LARA_HEADROOM,
item->pos.zPos + dist * phd_cos(ang),
item->roomNumber);
auto result1 = LOS(&pos1, &vec1);
auto result2 = LOS(&pos2, &vec2);
return (result1 != 0 && result2 != 0);
}
2021-02-06 11:50:08 -03:00
int LaraFloorFront(ITEM_INFO* item, short ang, int dist)
{
2021-09-10 00:43:26 +03:00
return LaraCollisionFront(item, ang, dist).Position.Floor;
}
COLL_RESULT LaraCollisionFront(ITEM_INFO* item, short ang, int dist)
{
auto collResult = GetCollisionResult(item, ang, dist, -LARA_HEIGHT);
2021-09-10 00:43:26 +03:00
if (collResult.Position.Floor != NO_HEIGHT)
collResult.Position.Floor -= item->pos.yPos;
return collResult;
}
COLL_RESULT LaraCollisionAboveFront(ITEM_INFO* item, short ang, int dist, int h)
{
int x = item->pos.xPos + dist * phd_sin(ang);
int y = item->pos.yPos - h;
int z = item->pos.zPos + dist * phd_cos(ang);
return GetCollisionResult(x, y, z, GetCollisionResult(item->pos.xPos, y, item->pos.zPos, item->roomNumber).RoomNumber);
}
2021-02-06 11:50:08 -03:00
int LaraCeilingFront(ITEM_INFO* item, short ang, int dist, int h)
{
2021-09-10 00:43:26 +03:00
return LaraCeilingCollisionFront(item, ang, dist, h).Position.Ceiling;
}
COLL_RESULT LaraCeilingCollisionFront(ITEM_INFO* item, short ang, int dist, int h)
{
auto collResult = GetCollisionResult(item, ang, dist, -h);
2021-09-10 00:43:26 +03:00
if (collResult.Position.Ceiling != NO_HEIGHT)
collResult.Position.Ceiling += h - item->pos.yPos;
return collResult;
}
2021-09-13 09:12:46 +03:00
bool LaraFallen(ITEM_INFO* item, COLL_INFO* coll)
{
2021-09-10 00:18:47 +03:00
if (Lara.waterStatus == LW_WADE || coll->Middle.Floor <= STEPUP_HEIGHT)
{
2021-09-13 09:12:46 +03:00
return false;
}
item->animNumber = LA_FALL_START;
item->currentAnimState = LS_JUMP_FORWARD;
item->goalAnimState = LS_JUMP_FORWARD;
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
item->fallspeed = 0;
item->gravityStatus = true;
2021-09-13 09:12:46 +03:00
return true;
}
2021-09-13 09:12:46 +03:00
bool LaraLandedBad(ITEM_INFO* item, COLL_INFO* coll)
{
int landspeed = item->fallspeed - 140;
if (landspeed > 0)
{
if (landspeed <= 14)
{
item->hitPoints -= 1000 * SQUARE(landspeed) / 196;
return item->hitPoints <= 0;
}
else
{
item->hitPoints = -1;
2021-09-13 09:12:46 +03:00
return true;
}
}
2021-09-13 09:12:46 +03:00
return false;
}
2021-09-13 09:12:46 +03:00
bool TestLaraWaterStepOut(ITEM_INFO* item, COLL_INFO* coll)
{
if (coll->CollisionType == CT_FRONT ||
coll->Middle.Slope ||
coll->Middle.Floor >= 0)
{
return false;
}
if (coll->Middle.Floor >= -(STEP_SIZE / 2))
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_STAND_IDLE);
}
else
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_ONWATER_TO_WADE_1CLICK);
item->goalAnimState = LS_STOP;
}
item->pos.yPos += coll->Middle.Floor + (STOP_SIZE + STEP_SIZE / 2 + STEP_SIZE / 4 - 9);
UpdateItemRoom(item, -(STEPUP_HEIGHT - 3));
item->pos.zRot = 0;
item->pos.xRot = 0;
item->gravityStatus = false;
item->speed = 0;
item->fallspeed = 0;
Lara.waterStatus = LW_WADE;
return true;
}
bool TestLaraWaterClimbOut(ITEM_INFO* item, COLL_INFO* coll)
{
if (coll->CollisionType != CT_FRONT || !(TrInput & IN_ACTION))
return false;
// TODO: Enable with lua!
Lara.NewAnims.CrawlFlexWaterPullUp = true;
Lara.NewAnims.CrawlFlexSubmerged = true;
if (Lara.gunStatus &&
(Lara.gunStatus != LG_READY || Lara.gunType != WEAPON_FLARE))
{
return false;
}
if (coll->Middle.Ceiling > -STEPUP_HEIGHT)
return false;
int frontFloor = coll->Front.Floor + LARA_HEIGHT_SURFSWIM;
if (frontFloor <= -STOP_SIZE ||
frontFloor > (STEP_SIZE + STEP_SIZE / 4 - 4))
{
return false;
}
if (!TestValidLedge(item, coll))
return false;
auto surface = LaraCollisionAboveFront(item, coll->Setup.ForwardAngle, (STEP_SIZE * 2), STEP_SIZE);
auto headroom = surface.Position.Floor - surface.Position.Ceiling;
if (frontFloor <= -STEP_SIZE)
{
if (headroom < LARA_HEIGHT)
{
if (Lara.NewAnims.CrawlFlexWaterPullUp)
SetAnimation(item, LA_ONWATER_TO_CROUCH_1CLICK);
else
return false;
}
else
SetAnimation(item, LA_ONWATER_TO_STAND_1CLICK);
}
else if (frontFloor > (STEP_SIZE / 2))
{
if (headroom < LARA_HEIGHT)
{
if (Lara.NewAnims.CrawlFlexSubmerged)
SetAnimation(item, LA_ONWATER_TO_CROUCH_M1CLICK);
else
return false;
}
else
SetAnimation(item, LA_ONWATER_TO_STAND_M1CLICK);
}
else
{
if (headroom < LARA_HEIGHT)
{
if (Lara.NewAnims.CrawlFlexWaterPullUp)
SetAnimation(item, LA_ONWATER_TO_CROUCH_0CLICK);
else
return false;
}
else
SetAnimation(item, LA_ONWATER_TO_STAND_0CLICK);
}
UpdateItemRoom(item, -LARA_HEIGHT / 2);
SnapItemToLedge(item, coll, 1.7f);
item->pos.yPos += frontFloor - 5;
item->currentAnimState = LS_ONWATER_EXIT;
item->gravityStatus = false;
item->speed = 0;
item->fallspeed = 0;
Lara.gunStatus = LG_HANDS_BUSY;
Lara.waterStatus = LW_ABOVE_WATER;
return true;
}
bool TestLaraLadderClimbOut(ITEM_INFO* item, COLL_INFO* coll) // NEW function for water to ladder move
{
if (!(TrInput & IN_ACTION) ||
!Lara.climbStatus ||
coll->CollisionType != CT_FRONT)
{
return false;
}
if (Lara.gunStatus &&
(Lara.gunStatus != LG_READY || Lara.gunType != WEAPON_FLARE))
{
return false;
}
if (!TestLaraClimbStance(item, coll))
return false;
short rot = item->pos.yRot;
if (rot >= -ANGLE(35.0f) && rot <= ANGLE(35.0f))
rot = 0;
else if (rot >= ANGLE(55.0f) && rot <= ANGLE(125.0f))
rot = ANGLE(90.0f);
else if (rot >= ANGLE(145.0f) || rot <= -ANGLE(145.0f))
rot = ANGLE(180.0f);
else if (rot >= -ANGLE(125.0f) && rot <= -ANGLE(55.0f))
rot = -ANGLE(90.0f);
if (rot & 0x3FFF)
return false;
switch ((unsigned short)rot / ANGLE(90.0f))
{
case NORTH:
item->pos.zPos = (item->pos.zPos | (WALL_SIZE - 1)) - LARA_RAD - 1;
break;
case EAST:
item->pos.xPos = (item->pos.xPos | (WALL_SIZE - 1)) - LARA_RAD - 1;
break;
case SOUTH:
item->pos.zPos = (item->pos.zPos & -WALL_SIZE) + LARA_RAD + 1;
break;
case WEST:
item->pos.xPos = (item->pos.xPos & -WALL_SIZE) + LARA_RAD + 1;
break;
}
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_ONWATER_IDLE);
item->goalAnimState = LS_LADDER_IDLE;
AnimateLara(item);
item->pos.yRot = rot;
item->pos.yPos -= 10;//otherwise she falls back into the water
item->pos.zRot = 0;
item->pos.xRot = 0;
item->gravityStatus = false;
item->speed = 0;
item->fallspeed = 0;
Lara.gunStatus = LG_HANDS_BUSY;
Lara.waterStatus = LW_ABOVE_WATER;
return true;
}
void TestLaraWaterDepth(ITEM_INFO* item, COLL_INFO* coll)
{
short roomNumber = item->roomNumber;
FLOOR_INFO* floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber);
int waterDepth = GetWaterDepth(item->pos.xPos, item->pos.yPos, item->pos.zPos, roomNumber);
if (waterDepth == NO_HEIGHT)
{
item->fallspeed = 0;
item->pos.xPos = coll->Setup.OldPosition.x;
item->pos.yPos = coll->Setup.OldPosition.y;
item->pos.zPos = coll->Setup.OldPosition.z;
}
// Height check was at STEP_SIZE * 2 before but changed to this
// because now Lara surfaces on a head level, not mid-body level.
if (waterDepth <= LARA_HEIGHT - LARA_HEADROOM / 2)
{
2021-11-10 02:41:59 +03:00
SetAnimation(item, LA_UNDERWATER_TO_STAND);
item->goalAnimState = LS_STOP;
item->pos.zRot = 0;
item->pos.xRot = 0;
item->speed = 0;
item->fallspeed = 0;
item->gravityStatus = false;
Lara.waterStatus = LW_WADE;
item->pos.yPos = GetFloorHeight(floor, item->pos.xPos, item->pos.yPos, item->pos.zPos);
}
}
#ifndef NEW_TIGHTROPE
void GetTighRopeFallOff(int regularity) {
if(LaraItem->hitPoints <= 0 || LaraItem->hitStatus)
{
2021-11-10 02:41:59 +03:00
SetAnimation(LaraItem, LA_TIGHTROPE_FALL_LEFT);
}
if(!Lara.tightRopeFall && !(GetRandomControl() & regularity))
Lara.tightRopeFall = 2 - ((GetRandomControl() & 0xF) != 0);
}
#endif // !NEW_TIGHTROPE
2020-12-21 13:16:29 -03:00
bool TestLaraLean(ITEM_INFO* item, COLL_INFO* coll)
{
2021-07-09 19:41:56 -05:00
#if 0
2020-12-21 13:16:29 -03:00
// TODO: make it more fine-tuned when new collision is done.
2021-09-10 00:18:47 +03:00
switch (coll->CollisionType)
2020-12-21 13:16:29 -03:00
{
case CT_RIGHT:
if (TrInput & IN_RIGHT)
return false;
case CT_LEFT:
if (TrInput & IN_LEFT)
return false;
}
return true;
2021-07-09 19:41:56 -05:00
#else
2021-09-10 00:18:47 +03:00
if (coll->CollisionType == CT_RIGHT || coll->CollisionType == CT_LEFT)
2021-09-13 09:12:46 +03:00
return false;
2021-07-09 19:41:56 -05:00
2021-09-13 09:12:46 +03:00
return true;
2021-07-09 19:41:56 -05:00
#endif
2020-12-21 13:16:29 -03:00
}
2021-11-07 04:54:48 +03:00
bool TestLaraSwamp(ITEM_INFO* item)
{
return (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_SWAMP) != 0;
}
bool TestLaraWater(ITEM_INFO* item)
{
return (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER) != 0;
}