mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00

* restore CF_FOLLOW_CENTER camera flag functionality * remove useless comments, use IsLara; * Fix formatting Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
1141 lines
33 KiB
C++
1141 lines
33 KiB
C++
#include "framework.h"
|
|
#include "Game/Lara/lara_overhang.h"
|
|
|
|
#include "Game/camera.h"
|
|
#include "Game/collision/floordata.h"
|
|
#include "Game/collision/collide_room.h"
|
|
#include "Game/control/control.h"
|
|
#include "Game/Lara/lara.h"
|
|
#include "Game/Lara/lara_climb.h"
|
|
#include "Game/Lara/lara_helpers.h"
|
|
#include "Game/Lara/lara_tests.h"
|
|
#include "Game/items.h"
|
|
#include "Specific/input.h"
|
|
#include "Specific/setup.h"
|
|
#include "Flow/ScriptInterfaceFlowHandler.h"
|
|
#include "Specific/level.h"
|
|
|
|
using namespace TEN::Input;
|
|
|
|
constexpr auto HORIZONTAL_ALIGN_NORTHEAST = 155;
|
|
constexpr auto HORIZONTAL_ALIGN_SOUTHWEST = 101;
|
|
constexpr auto FORWARD_ALIGNMENT = 868;
|
|
constexpr auto BACKWARD_ALIGNMENT = 100;
|
|
constexpr auto SLOPE_ALIGNMENT = 154;
|
|
|
|
constexpr short FACING_NORTH = 0;
|
|
constexpr short FACING_EAST = 16384;
|
|
constexpr short FACING_SOUTH = -32768;
|
|
constexpr short FACING_WEST = -16384;
|
|
|
|
constexpr auto HEIGHT_ADJUST = CLICK(2) + 20;
|
|
|
|
struct SlopeData
|
|
{
|
|
Vector3Int Offset;
|
|
Vector2 Goal;
|
|
short ClimbOrient;
|
|
short GoalOrient;
|
|
};
|
|
|
|
// -----------------
|
|
// UTILITY FUNCTIONS
|
|
// -----------------
|
|
|
|
inline bool SlopeCheck(Vector2 slope, Vector2 goal)
|
|
{
|
|
return (slope.x == goal.x && slope.y == goal.y);
|
|
}
|
|
|
|
inline bool SlopeInvCheck(Vector2 slope, Vector2 goal)
|
|
{
|
|
return (slope.x == -goal.x && slope.y == -goal.y);
|
|
}
|
|
|
|
short FindBridge(int tiltGrade, short orient, Vector3Int& pos, int* returnHeight, int ceilingMinY = 0, int ceilingMaxY = 0)
|
|
{
|
|
short bridgeSlot;
|
|
|
|
switch (tiltGrade)
|
|
{
|
|
case 0:
|
|
bridgeSlot = ID_BRIDGE_FLAT;
|
|
break;
|
|
case 1:
|
|
bridgeSlot = ID_BRIDGE_TILT1;
|
|
break;
|
|
case 2:
|
|
bridgeSlot = ID_BRIDGE_TILT2;
|
|
break;
|
|
case 3:
|
|
bridgeSlot = ID_BRIDGE_TILT3;
|
|
break;
|
|
case 4:
|
|
bridgeSlot = ID_BRIDGE_TILT4;
|
|
break;
|
|
default:
|
|
bridgeSlot = ID_BRIDGE_CUSTOM;
|
|
break;
|
|
}
|
|
|
|
int xMin = pos.x & ~WALL_MASK;
|
|
int xMax = xMin + WALL_MASK;
|
|
int zMin = pos.z & ~WALL_MASK;
|
|
int zMax = zMin + WALL_MASK;
|
|
|
|
for (int i = 0; i < g_Level.Items.size(); i++)
|
|
{
|
|
auto* bridgeItem = &g_Level.Items[i];
|
|
if (bridgeItem->ObjectNumber != bridgeSlot)
|
|
continue;
|
|
|
|
short orientDelta = (short)(bridgeItem->Pose.Orientation.y - orient);
|
|
|
|
bool orientCheck = false;
|
|
if (orientDelta == ANGLE(90.0f))
|
|
orientCheck = true;
|
|
else if (bridgeItem->ObjectNumber == ID_BRIDGE_FLAT)
|
|
orientCheck = true;
|
|
else if ((bridgeItem->ObjectNumber == ID_BRIDGE_TILT1 || bridgeItem->ObjectNumber == ID_BRIDGE_TILT2) && abs(orientDelta) == ANGLE(90.0f))
|
|
orientCheck = true;
|
|
|
|
if (!orientCheck)
|
|
continue;
|
|
|
|
if (bridgeItem->Pose.Position.x >= xMin &&
|
|
bridgeItem->Pose.Position.x <= xMax &&
|
|
bridgeItem->Pose.Position.z >= zMin &&
|
|
bridgeItem->Pose.Position.z <= zMax)
|
|
{
|
|
if (ceilingMinY || ceilingMaxY)
|
|
{
|
|
if (Objects[bridgeItem->ObjectNumber].ceiling == nullptr)
|
|
continue;
|
|
|
|
*returnHeight = Objects[bridgeItem->ObjectNumber].ceiling(i, pos.x, pos.y, pos.z).value_or(NO_HEIGHT);
|
|
|
|
int ceilingDistance = *returnHeight - pos.y;
|
|
if (ceilingDistance >= ceilingMinY && ceilingDistance <= ceilingMaxY)
|
|
return i;
|
|
}
|
|
else
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return NO_ITEM;
|
|
}
|
|
|
|
// Get the signed difference between two orientations.
|
|
short OrientDelta(short orient1, short orient2)
|
|
{
|
|
int deltaOrient = orient1 - orient2;
|
|
|
|
if (deltaOrient > ANGLE(180.0f))
|
|
deltaOrient -= ANGLE(360.0f);
|
|
|
|
if (deltaOrient < -ANGLE(180.0f))
|
|
deltaOrient += ANGLE(360.0f);
|
|
|
|
return short(deltaOrient);
|
|
}
|
|
|
|
// Test if inside sector strip (0-1023) in currently faced quadrant, between min and max.
|
|
bool InStrip(int x, int z, short facing, int min, int max)
|
|
{
|
|
if (min > WALL_MASK)
|
|
min = WALL_MASK;
|
|
if (max > WALL_MASK)
|
|
max = WALL_MASK;
|
|
|
|
int quadrant = GetQuadrant(facing);
|
|
int dx = x & WALL_MASK;
|
|
int dz = z & WALL_MASK;
|
|
|
|
switch (quadrant)
|
|
{
|
|
case NORTH:
|
|
if (dz >= (WALL_MASK - max) && dz <= (WALL_MASK - min))
|
|
return true;
|
|
|
|
break;
|
|
|
|
case SOUTH:
|
|
if (dz >= min && dz <= max)
|
|
return true;
|
|
|
|
break;
|
|
|
|
case EAST:
|
|
if (dx >= (WALL_MASK - max) && dx <= (WALL_MASK - min))
|
|
return true;
|
|
|
|
break;
|
|
|
|
case WEST:
|
|
if (dx >= min && dx <= max)
|
|
return true;
|
|
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Align facing and X/Z pos to sector edge.
|
|
void AlignToEdge(ItemInfo* item, short edgeDist)
|
|
{
|
|
if (edgeDist > WALL_MASK)
|
|
edgeDist = WALL_MASK;
|
|
|
|
// Align to closest cardinal facing.
|
|
item->Pose.Orientation.y += ANGLE(45.0f);
|
|
item->Pose.Orientation.y &= ANGLE(270.0f);
|
|
|
|
// Align to faced edge.
|
|
switch (item->Pose.Orientation.y)
|
|
{
|
|
case FACING_NORTH:
|
|
item->Pose.Position.z &= ~WALL_MASK;
|
|
item->Pose.Position.z += (WALL_MASK - edgeDist);
|
|
break;
|
|
|
|
case FACING_SOUTH:
|
|
item->Pose.Position.z &= ~WALL_MASK;
|
|
item->Pose.Position.z += edgeDist + 1;
|
|
break;
|
|
|
|
case FACING_EAST:
|
|
item->Pose.Position.x &= ~WALL_MASK;
|
|
item->Pose.Position.x += (WALL_MASK - edgeDist);
|
|
break;
|
|
|
|
case FACING_WEST:
|
|
item->Pose.Position.x &= ~WALL_MASK;
|
|
item->Pose.Position.x += edgeDist + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Correct position after grabbing slope.
|
|
bool AlignToGrab(ItemInfo* item)
|
|
{
|
|
bool legLeft = false;
|
|
|
|
item->Pose.Orientation.y += ANGLE(45.0f);
|
|
item->Pose.Orientation.y &= ANGLE(270.0f);
|
|
|
|
switch (item->Pose.Orientation.y)
|
|
{
|
|
case FACING_NORTH:
|
|
if (((item->Pose.Position.z + (int)CLICK(0.5f)) & ~WALL_MASK) == (item->Pose.Position.z & ~WALL_MASK))
|
|
item->Pose.Position.z += CLICK(0.5f);
|
|
|
|
item->Pose.Position.z = (item->Pose.Position.z & ~CLICK(1)) + HORIZONTAL_ALIGN_NORTHEAST;
|
|
legLeft = (item->Pose.Position.z & CLICK(1)) ? false : true;
|
|
break;
|
|
|
|
case FACING_SOUTH:
|
|
if (((item->Pose.Position.z - (int)CLICK(0.5f)) & ~WALL_MASK) == (item->Pose.Position.z & ~WALL_MASK))
|
|
item->Pose.Position.z -= CLICK(0.5f);
|
|
|
|
item->Pose.Position.z = (item->Pose.Position.z & ~CLICK(1)) + HORIZONTAL_ALIGN_SOUTHWEST;
|
|
legLeft = (item->Pose.Position.z & CLICK(1)) ? true : false;
|
|
break;
|
|
|
|
case FACING_WEST:
|
|
if (((item->Pose.Position.x - (int)CLICK(0.5f)) & ~WALL_MASK) == (item->Pose.Position.x & ~WALL_MASK))
|
|
item->Pose.Position.x -= CLICK(0.5f);
|
|
|
|
item->Pose.Position.x = (item->Pose.Position.x & ~CLICK(1)) + HORIZONTAL_ALIGN_SOUTHWEST;
|
|
legLeft = (item->Pose.Position.x & CLICK(1)) ? true : false;
|
|
break;
|
|
|
|
case FACING_EAST:
|
|
if (((item->Pose.Position.x + (int)CLICK(0.5f)) & ~WALL_MASK) == (item->Pose.Position.x & ~WALL_MASK))
|
|
item->Pose.Position.x += CLICK(0.5f);
|
|
|
|
item->Pose.Position.x = (item->Pose.Position.x & ~CLICK(1)) + HORIZONTAL_ALIGN_NORTHEAST;
|
|
legLeft = (item->Pose.Position.x & CLICK(1)) ? false : true;
|
|
break;
|
|
}
|
|
|
|
return legLeft;
|
|
}
|
|
|
|
SlopeData GetSlopeData(ItemInfo* item)
|
|
{
|
|
SlopeData slopeData;
|
|
switch (GetQuadrant(item->Pose.Orientation.y))
|
|
{
|
|
case NORTH:
|
|
slopeData.Offset.z = CLICK(1);
|
|
slopeData.Goal.y = -4;
|
|
slopeData.ClimbOrient = (short)CLIMB_DIRECTION::North;
|
|
slopeData.GoalOrient = 0;
|
|
break;
|
|
|
|
case EAST:
|
|
slopeData.Offset.x = CLICK(1);
|
|
slopeData.Goal.x = -4;
|
|
slopeData.ClimbOrient = (short)CLIMB_DIRECTION::East;
|
|
slopeData.GoalOrient = ANGLE(90.0f);
|
|
break;
|
|
|
|
case SOUTH:
|
|
slopeData.Offset.z = -CLICK(1);
|
|
slopeData.Goal.y = 4;
|
|
slopeData.ClimbOrient = (short)CLIMB_DIRECTION::South;
|
|
slopeData.GoalOrient = ANGLE(180.0f);
|
|
break;
|
|
|
|
case WEST:
|
|
slopeData.Offset.x = -CLICK(1);
|
|
slopeData.Goal.x = 4;
|
|
slopeData.ClimbOrient = (short)CLIMB_DIRECTION::West;
|
|
slopeData.GoalOrient = ANGLE(270.0f);
|
|
break;
|
|
}
|
|
|
|
return slopeData;
|
|
}
|
|
|
|
// -----------------------------
|
|
// OVERHANG CLIMB
|
|
// Control & Collision Functions
|
|
// -----------------------------
|
|
|
|
void lara_col_slopeclimb(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
auto* lara = GetLaraInfo(item);
|
|
|
|
GetCollisionInfo(coll, item);
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto now = item->Pose.Position;
|
|
auto up = Vector3Int(item->Pose.Position.x - slopeData.Offset.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z - slopeData.Offset.z);
|
|
auto down = Vector3Int(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
|
|
|
|
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber);
|
|
auto probeUp = GetCollision(up.x, up.y, up.z, item->RoomNumber);
|
|
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber);
|
|
|
|
if (item->Animation.AnimNumber == LA_OVERHANG_LADDER_SLOPE_CONCAVE)
|
|
return;
|
|
|
|
item->Pose.Position.y = probeNow.Position.Ceiling + HEIGHT_ADJUST;
|
|
|
|
// Drop down if action not pressed.
|
|
if (!(TrInput & IN_ACTION))
|
|
{
|
|
SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_DROP_LEFT : LA_OVERHANG_DROP_RIGHT);
|
|
return;
|
|
}
|
|
|
|
// Engage shimmy mode if LEFT/LSTEP or RIGHT/RSTEP are pressed.
|
|
if (TrInput & IN_LEFT || TrInput & IN_RIGHT)
|
|
{
|
|
lara->NextCornerPos.Orientation.z = (item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT) ? true : false; // HACK.
|
|
SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_IDLE_2_HANG_LEFT : LA_OVERHANG_IDLE_2_HANG_RIGHT);
|
|
return;
|
|
}
|
|
|
|
if (TrInput & IN_FORWARD)
|
|
{
|
|
// Test for ledge over slope.
|
|
short tempRoom = probeUp.Block->RoomAbove(up.x, up.z).value_or(NO_ROOM);
|
|
if (tempRoom != NO_ROOM)
|
|
{
|
|
auto probeLedge = GetCollision(now.x, now.y - CLICK(3), now.z, tempRoom);
|
|
|
|
if ((probeLedge.Position.Floor - probeLedge.Position.Ceiling) >= CLICK(3) &&
|
|
abs((item->Pose.Position.y - (CLICK(2.5f) + 48)) - probeLedge.Position.Floor) < 64)
|
|
{
|
|
AlignToEdge(item, FORWARD_ALIGNMENT);
|
|
SetAnimation(item, LA_OVERHANG_LEDGE_VAULT_START); // Ledge climb-up from slope.
|
|
}
|
|
}
|
|
|
|
// Test for slope to overhead ladder transition (convex).
|
|
if (GetClimbFlags(probeUp.BottomBlock) & slopeData.ClimbOrient &&
|
|
InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, CLICK(3), CLICK(4)))
|
|
{
|
|
if (GetCollision(probeUp.Block, up.x, up.y, up.z).Position.Ceiling - item->Pose.Position.y <= (SECTOR(1.5f) - 80)) // Check if a wall is actually there.
|
|
{
|
|
AlignToEdge(item, FORWARD_ALIGNMENT);
|
|
SetAnimation(item, LA_OVERHANG_SLOPE_LADDER_CONVEX_START);
|
|
}
|
|
}
|
|
|
|
// Test for monkey at next position.
|
|
if (probeUp.BottomBlock->Flags.Monkeyswing)
|
|
{
|
|
int yDelta = probeUp.Position.Ceiling - probeNow.Position.Ceiling;
|
|
|
|
int height; // Height variable for bridge ceiling functions.
|
|
|
|
// Test for upwards slope to climb.
|
|
short bridge = FindBridge(4, item->Pose.Orientation.y, up, &height, -CLICK(2.5f), -CLICK(1.5f));
|
|
if (yDelta >= -CLICK(1.25f) && yDelta <= -CLICK(0.75f) && (SlopeCheck(probeUp.CeilingTilt, slopeData.Goal) || bridge >= 0))
|
|
{
|
|
// Do one more check for wall/ceiling step 2 * offX / Z further to avoid lara sinking her head in wall/step.
|
|
auto probeWall = GetCollision((up.x - slopeData.Offset.x), (up.y - CLICK(1)), (up.z - slopeData.Offset.z), item->RoomNumber);
|
|
|
|
if (!probeWall.Block->IsWall((up.x - slopeData.Offset.x), (up.z - slopeData.Offset.z)) &&
|
|
(probeNow.Position.Ceiling - probeWall.Position.Ceiling) > CLICK(0.5f)) // No wall or downward ceiling step.
|
|
{
|
|
TranslateItem(item, 0, -CLICK(1), -CLICK(1));
|
|
SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_CLIMB_UP_LEFT : LA_OVERHANG_CLIMB_UP_RIGHT);
|
|
//item->TargetState = 62;
|
|
}
|
|
}
|
|
|
|
// Test for flat monkey (abs(slope) < 2).
|
|
bridge = FindBridge(0, slopeData.GoalOrient, up, &height, -CLICK(2.25f), -CLICK(1.25f));
|
|
if (bridge < 0)
|
|
bridge = FindBridge(1, slopeData.GoalOrient, up, &height, -CLICK(2.25f), -CLICK(1.25f));
|
|
|
|
// HACK: because of the different calculations of bridge height in TR4 and TEN, we need to lower yDiff tolerance to 0.9f.
|
|
if (yDelta > -CLICK(0.9f) && yDelta <= -CLICK(0.5f) &&
|
|
((abs(probeUp.CeilingTilt.x) <= 2 && abs(probeUp.CeilingTilt.y) <= 2) || bridge >= 0))
|
|
{
|
|
SetAnimation(item, LA_OVERHANG_SLOPE_MONKEY_CONCAVE); // Slope to overhead monkey transition (concave).
|
|
}
|
|
}
|
|
}
|
|
else if (TrInput & IN_BACK)
|
|
{
|
|
if ((GetClimbFlags(GetCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).BottomBlock) & slopeData.ClimbOrient) &&
|
|
InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(1)))
|
|
{
|
|
AlignToEdge(item, BACKWARD_ALIGNMENT);
|
|
SetAnimation(item, LA_OVERHANG_SLOPE_LADDER_CONCAVE); // Slope to underlying ladder transition (concave).
|
|
return;
|
|
}
|
|
|
|
if (probeDown.BottomBlock->Flags.Monkeyswing)
|
|
{
|
|
int height;
|
|
int yDiff = probeDown.Position.Ceiling - probeNow.Position.Ceiling;
|
|
|
|
// Test for flat monkey (abs(slope) < 2).
|
|
short bridge = FindBridge(0, slopeData.GoalOrient, down, &height, -CLICK(3), -CLICK(2));
|
|
if (bridge < 0)
|
|
bridge = FindBridge(1, slopeData.GoalOrient, down, &height, -CLICK(3), -CLICK(2));
|
|
|
|
if ((abs(yDiff) < CLICK(1) && abs(probeDown.CeilingTilt.x) <= 2 && abs(probeDown.CeilingTilt.y) <= 2) || bridge >= 0)
|
|
SetAnimation(item, LA_OVERHANG_SLOPE_MONKEY_CONVEX); // Force slope to underlying monkey transition (convex)
|
|
|
|
// Test for downward slope to climb.
|
|
bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(2.5f), -CLICK(1.5f));
|
|
if (yDiff >= CLICK(0.75f) && yDiff <= CLICK(1.25f) && (SlopeCheck(probeDown.CeilingTilt, slopeData.Goal) || bridge >= 0))
|
|
{
|
|
SetAnimation(item, item->Animation.AnimNumber == LA_OVERHANG_IDLE_LEFT ? LA_OVERHANG_CLIMB_DOWN_LEFT : LA_OVERHANG_CLIMB_DOWN_RIGHT);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void lara_as_slopeclimb(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x--;
|
|
else
|
|
item->Pose.Orientation.x++;
|
|
|
|
Camera.flags = CF_FOLLOW_CENTER;
|
|
|
|
if (Camera.type != CameraType::Chase)
|
|
return;
|
|
|
|
Camera.targetElevation = -ANGLE(16.75f);
|
|
Camera.targetDistance = SECTOR(1.75f);
|
|
Camera.speed = 15;
|
|
}
|
|
|
|
void lara_as_slopefall(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
item->Animation.IsAirborne = true;
|
|
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x--;
|
|
else
|
|
item->Pose.Orientation.x++;
|
|
|
|
Camera.flags = CF_FOLLOW_CENTER;
|
|
|
|
if (Camera.type != CameraType::Chase)
|
|
return;
|
|
|
|
Camera.targetElevation = -ANGLE(16.75f);
|
|
Camera.targetDistance = SECTOR(1.75f);
|
|
Camera.speed = 15;
|
|
}
|
|
|
|
void lara_col_slopehang(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
auto* lara = GetLaraInfo(item);
|
|
|
|
GetCollisionInfo(coll, item);
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto now = item->Pose.Position;
|
|
|
|
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber);
|
|
|
|
item->Pose.Position.y = probeNow.Position.Ceiling + HEIGHT_ADJUST;
|
|
|
|
// Drop down if action not pressed.
|
|
if (!(TrInput & IN_ACTION))
|
|
{
|
|
SetAnimation(item, LA_OVERHANG_HANG_DROP);
|
|
item->Animation.IsAirborne = true;
|
|
return;
|
|
}
|
|
|
|
if (item->Animation.AnimNumber != LA_OVERHANG_HANG_SWING)
|
|
{
|
|
// Return to climbing mode.
|
|
if (TrInput & IN_FORWARD || TrInput & IN_BACK)
|
|
SetAnimation(item, lara->NextCornerPos.Orientation.z ? LA_OVERHANG_HANG_2_IDLE_LEFT : LA_OVERHANG_HANG_2_IDLE_RIGHT); // HACK.
|
|
|
|
// Shimmy control.
|
|
if (TrInput & IN_LEFT || TrInput & IN_RIGHT)
|
|
{
|
|
auto shimmy = now;
|
|
short direction = 0;
|
|
|
|
if (TrInput & IN_LEFT)
|
|
{
|
|
shimmy.x -= slopeData.Offset.z / 2;
|
|
shimmy.z += slopeData.Offset.x / 2;
|
|
direction = -ANGLE(90.0f);
|
|
}
|
|
else if (TrInput & IN_RIGHT)
|
|
{
|
|
shimmy.x += slopeData.Offset.z / 2;
|
|
shimmy.z -= slopeData.Offset.x / 2;
|
|
direction = ANGLE(90.0f);
|
|
}
|
|
|
|
auto probeShimmy = GetCollision(shimmy.x, shimmy.y, shimmy.z, item->RoomNumber);
|
|
|
|
if (probeShimmy.BottomBlock->Flags.Monkeyswing)
|
|
{
|
|
int yDiff = probeShimmy.Position.Ceiling - probeNow.Position.Ceiling;
|
|
|
|
int height;
|
|
short bridge = FindBridge(4, slopeData.GoalOrient, shimmy, &height, -CLICK(2.5f), -CLICK(1.5f));
|
|
|
|
if ((SlopeCheck(probeShimmy.CeilingTilt, slopeData.Goal) && abs(yDiff) < 64) || bridge >= 0)
|
|
SetAnimation(item, direction < 0 ? LA_OVERHANG_SHIMMY_LEFT : LA_OVERHANG_SHIMMY_RIGHT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void lara_as_slopehang(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x--;
|
|
else
|
|
item->Pose.Orientation.x++;
|
|
|
|
if (Camera.type != CameraType::Chase)
|
|
return;
|
|
|
|
Camera.targetElevation = -SECTOR(1);
|
|
Camera.targetDistance = CLICK(6.5f);
|
|
Camera.speed = 15;
|
|
}
|
|
|
|
void lara_col_slopeshimmy(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
GetCollisionInfo(coll, item);
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto now = item->Pose.Position;
|
|
|
|
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber);
|
|
|
|
item->Pose.Position.y = probeNow.Position.Ceiling + HEIGHT_ADJUST;
|
|
|
|
auto shimmy = item->Pose.Position;
|
|
if (item->Animation.AnimNumber == LA_OVERHANG_SHIMMY_LEFT)
|
|
{
|
|
shimmy.x -= slopeData.Offset.z / 2;
|
|
shimmy.z += slopeData.Offset.x / 2;
|
|
}
|
|
else
|
|
{
|
|
shimmy.x += slopeData.Offset.z / 2;
|
|
shimmy.z -= slopeData.Offset.x / 2;
|
|
}
|
|
|
|
auto probeShimmy = GetCollision(shimmy.x, shimmy.y, shimmy.z, item->RoomNumber);
|
|
|
|
bool cancelShimmy = true;
|
|
if (probeShimmy.BottomBlock->Flags.Monkeyswing)
|
|
{
|
|
int yDiff = probeShimmy.Position.Ceiling - probeNow.Position.Ceiling;
|
|
|
|
int height;
|
|
short bridge = FindBridge(4, slopeData.GoalOrient, shimmy, &height, -CLICK(2.5f), -CLICK(1.5f));
|
|
|
|
if ((SlopeCheck(probeShimmy.CeilingTilt, slopeData.Goal) && abs(yDiff) < 64) || bridge >= 0)
|
|
cancelShimmy = false;
|
|
}
|
|
|
|
if (cancelShimmy)
|
|
SetAnimation(item, (item->Animation.AnimNumber == LA_OVERHANG_SHIMMY_LEFT) ? LA_OVERHANG_SHIMMY_LEFT_STOP : LA_OVERHANG_SHIMMY_RIGHT_STOP);
|
|
}
|
|
|
|
void lara_as_slopeshimmy(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x--;
|
|
else
|
|
item->Pose.Orientation.x++;
|
|
|
|
if (Camera.type != CameraType::Chase)
|
|
return;
|
|
|
|
Camera.targetElevation = -SECTOR(1);
|
|
Camera.targetDistance = CLICK(6.5f);
|
|
Camera.speed = 15;
|
|
|
|
auto* lara = GetLaraInfo(item);
|
|
|
|
if (item->Animation.AnimNumber == LA_OVERHANG_SHIMMY_LEFT)
|
|
{
|
|
lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f);
|
|
Camera.targetAngle = -ANGLE(22.5f);
|
|
}
|
|
else
|
|
{
|
|
lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90.0f);
|
|
Camera.targetAngle = ANGLE(22.5f);
|
|
}
|
|
}
|
|
|
|
void lara_as_slopeclimbup(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x--;
|
|
else
|
|
item->Pose.Orientation.x++;
|
|
|
|
Camera.flags = CF_FOLLOW_CENTER;
|
|
|
|
if (Camera.type != CameraType::Chase)
|
|
return; // If camera mode isn't chase (0) then don't change camera angles.
|
|
|
|
Camera.targetElevation = SECTOR(2);
|
|
Camera.targetDistance = CLICK(7);
|
|
Camera.speed = 15;
|
|
|
|
|
|
if (!(TrInput & IN_ACTION))
|
|
{
|
|
int frame = GetCurrentRelativeFrameNumber(item);
|
|
int length = GetFrameCount(item->Animation.AnimNumber);
|
|
int dPos = CLICK(1) - (frame * CLICK(1) / length);
|
|
|
|
TranslateItem(item, 0, dPos, dPos);
|
|
if (item->Animation.AnimNumber == LA_OVERHANG_CLIMB_UP_LEFT)
|
|
SetAnimation(item, frame <= 2 * length / 3 ? LA_OVERHANG_DROP_LEFT : LA_OVERHANG_DROP_RIGHT);
|
|
else
|
|
SetAnimation(item, frame <= 2 * length / 3 ? LA_OVERHANG_DROP_RIGHT : LA_OVERHANG_DROP_LEFT);
|
|
}
|
|
}
|
|
|
|
void lara_as_slopeclimbdown(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x--;
|
|
else
|
|
item->Pose.Orientation.x++;
|
|
|
|
Camera.flags = CF_FOLLOW_CENTER;
|
|
|
|
if (Camera.type != CameraType::Chase)
|
|
return;
|
|
|
|
Camera.targetElevation = -3072;
|
|
Camera.targetDistance = 1664;
|
|
Camera.speed = 15;
|
|
|
|
if (!(TrInput & IN_ACTION))
|
|
{
|
|
int frame = GetCurrentRelativeFrameNumber(item);
|
|
int length = GetFrameCount(item->Animation.AnimNumber);
|
|
int dPos = frame * CLICK(1) / length;
|
|
|
|
TranslateItem(item, 0, dPos, dPos);
|
|
if (item->Animation.AnimNumber == LA_OVERHANG_CLIMB_DOWN_LEFT)
|
|
SetAnimation(item, frame <= length / 2 ? LA_OVERHANG_DROP_LEFT : LA_OVERHANG_DROP_RIGHT);
|
|
else
|
|
SetAnimation(item, frame <= length / 2 ? LA_OVERHANG_DROP_RIGHT : LA_OVERHANG_DROP_LEFT);
|
|
}
|
|
}
|
|
|
|
void lara_as_sclimbstart(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
// Rotating camera effect during monkey to overhead slope transition.
|
|
if (item->Animation.AnimNumber == LA_OVERHANG_MONKEY_SLOPE_CONVEX)
|
|
{
|
|
int frame = GetCurrentRelativeFrameNumber(item);
|
|
int numFrames = GetFrameCount(item->Animation.AnimNumber);
|
|
|
|
float frac = (frame * 1.5f) / (float)(numFrames);
|
|
if (frac > 1.0f)
|
|
frac = 1.0f;
|
|
|
|
Camera.flags = CF_FOLLOW_CENTER;
|
|
|
|
int distance = TestLaraWall(item, 0, SECTOR(1.5f), 0) ? SECTOR(1) : CLICK(6.5f);
|
|
|
|
if (item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameEnd)
|
|
{
|
|
Camera.targetDistance = distance;
|
|
Camera.targetElevation = int(SECTOR(3) * frac);
|
|
Camera.targetAngle = int(-ANGLE(180.0f) * frac);
|
|
Camera.targetspeed = 15;
|
|
}
|
|
else
|
|
{
|
|
|
|
Camera.targetDistance = distance;
|
|
Camera.targetElevation = SECTOR(3);
|
|
Camera.targetAngle = 0;
|
|
Camera.targetspeed = 15;
|
|
}
|
|
}
|
|
else// if (item->animNumber == LA_OVERHANG_MONKEY_SLOPE_CONCAVE)
|
|
{
|
|
//Camera.flags = CF_FOLLOW_CENTER;
|
|
Camera.targetElevation = -ANGLE(11.25f);
|
|
Camera.targetDistance = CLICK(6.5f);
|
|
Camera.speed = 15;
|
|
}
|
|
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x++;
|
|
else
|
|
item->Pose.Orientation.x--;
|
|
}
|
|
|
|
void lara_as_sclimbstop(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
// Rotating camera effect during monkey to overhead slope transition.
|
|
|
|
// Following camera effect during the slope to underlying monkey transition.
|
|
if (item->Animation.AnimNumber == LA_OVERHANG_SLOPE_MONKEY_CONVEX)
|
|
{
|
|
Camera.flags = CF_FOLLOW_CENTER;
|
|
Camera.targetDistance = CLICK(6.5f);
|
|
Camera.targetElevation = ANGLE(11.25f);
|
|
Camera.targetspeed = 15;
|
|
}
|
|
// Rotating camera effect during concave slope to monkey transition.
|
|
else if (item->Animation.AnimNumber == LA_OVERHANG_SLOPE_MONKEY_CONCAVE)
|
|
{
|
|
int frame = GetCurrentRelativeFrameNumber(item);
|
|
int numFrames = GetFrameCount(item->Animation.AnimNumber);
|
|
|
|
float frac = (frame * 1.25f) / (float)(numFrames);
|
|
if (frac > 1.0f)
|
|
frac = 1.0f;
|
|
|
|
Camera.flags = CF_FOLLOW_CENTER;
|
|
|
|
if (item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameEnd)
|
|
{
|
|
|
|
Camera.targetAngle = (short)(-ANGLE(90.0f) * frac);
|
|
Camera.targetDistance = SECTOR(1.75f) - int(CLICK(2) * frac);
|
|
Camera.targetspeed = 15;
|
|
}
|
|
else
|
|
{
|
|
Camera.targetAngle = ANGLE(90.0f);
|
|
Camera.targetDistance = SECTOR(1.25f);
|
|
Camera.targetspeed = 15;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Camera.targetDistance = CLICK(6.5f);
|
|
Camera.targetElevation = -ANGLE(11.25f);
|
|
Camera.targetspeed = 15;
|
|
}
|
|
|
|
|
|
if (GlobalCounter % 2)
|
|
item->Pose.Orientation.x++;
|
|
else
|
|
item->Pose.Orientation.x--;
|
|
}
|
|
|
|
void lara_as_sclimbend(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
switch (item->Animation.AnimNumber)
|
|
{
|
|
case LA_OVERHANG_EXIT_MONKEY_FORWARD:
|
|
SetAnimation(item, LA_MONKEY_FORWARD);
|
|
break;
|
|
|
|
case LA_OVERHANG_EXIT_MONKEY_IDLE:
|
|
SetAnimation(item, LA_MONKEY_IDLE);
|
|
break;
|
|
|
|
case LA_OVERHANG_EXIT_LADDER:
|
|
SetAnimation(item, LA_LADDER_IDLE);
|
|
break;
|
|
|
|
case LA_OVERHANG_EXIT_VAULT:
|
|
SetAnimation(item, LA_HANG_TO_STAND_END);
|
|
break;
|
|
|
|
case LA_OVERHANG_EXIT_DROP:
|
|
SetAnimation(item, LA_FALL);
|
|
break;
|
|
|
|
case LA_OVERHANG_EXIT_HANG:
|
|
SetAnimation(item, LA_JUMP_UP);
|
|
break;
|
|
}
|
|
|
|
GetLaraInfo(item)->NextCornerPos.Orientation.z = 0;
|
|
item->Pose.Orientation.x = 0;
|
|
}
|
|
|
|
// ----------------------------------------
|
|
// EXTENSION FUNCTIONS
|
|
// For Existing State Control and Collision
|
|
// ----------------------------------------
|
|
|
|
// Extends LS_HANG (10)
|
|
void SlopeHangExtra(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (!g_GameFlow->HasOverhangClimb())
|
|
return;
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto down = Vector3Int(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
|
|
|
|
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber);
|
|
|
|
int ceilDist = item->Pose.Position.y - probeDown.Position.Ceiling;
|
|
|
|
if (item->Animation.TargetState == LS_LADDER_IDLE) // Prevent going from hang to climb mode if slope is under ladder.
|
|
{
|
|
if (ceilDist >= CLICK(1) && ceilDist < CLICK(2))
|
|
{
|
|
if ((probeDown.CeilingTilt.x / 3) == (slopeData.Goal.x / 3) ||
|
|
(probeDown.CeilingTilt.y / 3) == (slopeData.Goal.y / 3))
|
|
{
|
|
item->Animation.TargetState = LS_HANG;
|
|
if (TrInput & IN_FORWARD)
|
|
SetAnimation(item, LA_LADDER_SHIMMY_UP);
|
|
/*else if (TrInput & IN_BACK)
|
|
SetAnimation(item, LA_LADDER_SHIMMY_DOWN);*/
|
|
}
|
|
}
|
|
}
|
|
/*else if (item->TargetState == AS_HANG)
|
|
{
|
|
if (item->animNumber == LA_LADDER_SHIMMY_DOWN)
|
|
{
|
|
if (ceilDist < CLICK(1))
|
|
{
|
|
if ((probeDown.CeilingTilt.x / 3) == (goal.x / 3) ||
|
|
(probeDown.CeilingTilt.z / 3) == (goal.y / 3))
|
|
{
|
|
SetAnimation(item, LA_REACH_TO_HANG, 21);
|
|
}
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
// Extends LS_REACH (11)
|
|
void SlopeReachExtra(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (!g_GameFlow->HasOverhangClimb())
|
|
return;
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto now = item->Pose.Position;
|
|
|
|
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber);
|
|
|
|
int ceilDist = item->Pose.Position.y - probeNow.Position.Ceiling;
|
|
|
|
if (probeNow.BottomBlock->Flags.Monkeyswing && ceilDist <= CLICK(3.5f))
|
|
{
|
|
int height;
|
|
short bridge = FindBridge(4, slopeData.GoalOrient, now, &height, -CLICK(4), -CLICK(2.5f));
|
|
|
|
if (abs(probeNow.CeilingTilt.x) > 2 || abs(probeNow.CeilingTilt.y) > 2 || bridge >= 0)
|
|
{
|
|
bool disableGrab = true;
|
|
if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0)
|
|
{
|
|
if (abs(OrientDelta(item->Pose.Orientation.y, slopeData.GoalOrient)) < ANGLE(33.75f))
|
|
disableGrab = false;
|
|
}
|
|
|
|
if (disableGrab)
|
|
TrInput &= ~IN_ACTION; // HACK.
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extends LS_CLIMB_IDLE (56)
|
|
void SlopeClimbExtra(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (!g_GameFlow->HasOverhangClimb())
|
|
return;
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto now = item->Pose.Position;
|
|
auto down = Vector3Int(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
|
|
|
|
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber);
|
|
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber);
|
|
|
|
// Block for ladder to overhead slope transition.
|
|
if (item->Animation.AnimNumber == LA_LADDER_IDLE)
|
|
{
|
|
if (TrInput & IN_FORWARD)
|
|
{
|
|
int ceilDist = probeNow.Position.Ceiling - item->Pose.Position.y;
|
|
|
|
if (probeNow.BottomBlock->Flags.Monkeyswing && ceilDist >= -CLICK(4) && ceilDist <= -CLICK(3))
|
|
{
|
|
short facing = item->Pose.Orientation.y + ANGLE(45.0f);
|
|
facing &= ANGLE(270.0f);
|
|
|
|
int height;
|
|
short bridge = FindBridge(4, facing, now, &height, -CLICK(4), -CLICK(3));
|
|
|
|
if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0)
|
|
{
|
|
item->Pose.Position.y = probeNow.Position.Ceiling + 900;
|
|
SetAnimation(item, LA_OVERHANG_LADDER_SLOPE_CONCAVE); // Ladder to overhead slope transition (concave).
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TrInput & IN_BACK)
|
|
{
|
|
int ceilDist = probeDown.Position.Ceiling - item->Pose.Position.y;
|
|
|
|
if (probeDown.BottomBlock->Flags.Monkeyswing && ceilDist >= 0 && ceilDist <= CLICK(1))
|
|
{
|
|
short facing = item->Pose.Orientation.y + ANGLE(45.0f);
|
|
facing &= ANGLE(270.0f);
|
|
|
|
int height;
|
|
short bridge = FindBridge(4, facing, down, &height, -CLICK(0.5f), -CLICK(0.25f));
|
|
|
|
if (SlopeCheck(probeDown.CeilingTilt, slopeData.Goal) || bridge >= 0)
|
|
{
|
|
item->Pose.Position.y = probeDown.Position.Ceiling - 156;
|
|
SetAnimation(item, LA_OVERHANG_LADDER_SLOPE_CONVEX); // Ladder to underlying slope transition (convex).
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extends LS_LADDER_IDLE (56)
|
|
bool LadderMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
auto probe = GetCollision(item);
|
|
|
|
if (probe.Position.CeilingSlope)
|
|
return false;
|
|
|
|
if (probe.BottomBlock->Flags.Monkeyswing && (item->Pose.Position.y - coll->Setup.Height - CLICK(0.5f) <= probe.Position.Ceiling))
|
|
{
|
|
item->Animation.TargetState = LS_MONKEY_IDLE;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Extends LS_LADDER_DOWN (61)
|
|
void SlopeClimbDownExtra(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
if (!g_GameFlow->HasOverhangClimb())
|
|
return;
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto down = Vector3Int(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
|
|
|
|
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber);
|
|
|
|
if (item->Animation.AnimNumber == LA_LADDER_DOWN) // Make Lara stop before underlying slope ceiling at correct height.
|
|
{
|
|
if (TrInput & IN_BACK)
|
|
{
|
|
int ceilDist = probeDown.Position.Ceiling - item->Pose.Position.y;
|
|
|
|
if (probeDown.BottomBlock->Flags.Monkeyswing && ceilDist >= 0 && ceilDist <= CLICK(1))
|
|
{
|
|
short facing = item->Pose.Orientation.y + ANGLE(45.0f);
|
|
facing &= ANGLE(270.0f);
|
|
|
|
int height;
|
|
short bridge = FindBridge(4, facing, down, &height, -CLICK(0.5f), -CLICK(0.25f));
|
|
|
|
if (SlopeCheck(probeDown.CeilingTilt, slopeData.Goal) || bridge >= 0)
|
|
{
|
|
item->Pose.Position.y = probeDown.Position.Ceiling - 156;
|
|
item->Animation.TargetState = LS_LADDER_IDLE;
|
|
}
|
|
}
|
|
|
|
// Old block.
|
|
/*if (probeDown.BottomBlock->Flags.Monkeyswing)
|
|
{
|
|
int midpoint = 29; // HACK: lara_col_climb_down func, case for frame 29, dehardcode later.
|
|
|
|
//down.y += 256;
|
|
int height;
|
|
if (!GetFrameNumber(item, 0))
|
|
{
|
|
short bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(3), CLICK(4));
|
|
if (ceilDist < CLICK(1) && (bridge >= 0 || SlopeCheck(probeDown.CeilingTilt, slopeData.Goal)))
|
|
item->Animation.TargetState = LS_LADDER_IDLE;
|
|
}
|
|
else if (GetFrameNumber(item, 0) == midpoint)
|
|
{
|
|
short bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(2), CLICK(5));
|
|
if (ceilDist < CLICK(1) * 2 && (bridge >= 0 || SlopeCheck(probeDown.CeilingTilt, slopeData.Goal)))
|
|
{
|
|
item->Pose.Position.y += CLICK(1); // Do midpoint Y translation.
|
|
item->Animation.TargetState = LS_LADDER_IDLE;
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extends LS_MONKEY_IDLE (75)
|
|
void SlopeMonkeyExtra(ItemInfo* item, CollisionInfo* coll)
|
|
{
|
|
auto* lara = GetLaraInfo(item);
|
|
|
|
if (!g_GameFlow->HasOverhangClimb())
|
|
return;
|
|
|
|
auto slopeData = GetSlopeData(item);
|
|
|
|
auto now = item->Pose.Position;
|
|
auto down = Vector3Int(item->Pose.Position.x + slopeData.Offset.x, item->Pose.Position.y + CLICK(1), item->Pose.Position.z + slopeData.Offset.z);
|
|
|
|
auto probeNow = GetCollision(now.x, now.y, now.z, item->RoomNumber);
|
|
auto probeDown = GetCollision(down.x, down.y, down.z, item->RoomNumber);
|
|
|
|
if (item->Animation.AnimNumber == LA_REACH_TO_MONKEY && !GetFrameNumber(item, 0)) // Manage proper grabbing of monkey slope on forward jump.
|
|
{
|
|
int ceilDist = item->Pose.Position.y - probeNow.Position.Ceiling;
|
|
|
|
if (probeNow.BottomBlock->Flags.Monkeyswing && ceilDist <= CLICK(3.5f))
|
|
{
|
|
short facing = item->Pose.Orientation.y + ANGLE(45.0f);
|
|
facing &= 0xC000;
|
|
|
|
int height;
|
|
short bridge = FindBridge(4, facing, now, &height, -CLICK(3.5f), -CLICK(2.5f));
|
|
|
|
if (SlopeCheck(probeNow.CeilingTilt, slopeData.Goal) || bridge >= 0)
|
|
{
|
|
lara->NextCornerPos.Orientation.z = AlignToGrab(item);
|
|
|
|
int ceiling = GetCollision(probeNow.Block, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z).Position.Ceiling;
|
|
item->Pose.Position.y = ceiling + HEIGHT_ADJUST;
|
|
|
|
SetAnimation(item, LA_OVERHANG_HANG_SWING);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TrInput & IN_FORWARD) // Monkey to slope transitions.
|
|
{
|
|
if (probeNow.BottomBlock->Flags.Monkeyswing &&
|
|
((item->Animation.AnimNumber == LA_REACH_TO_MONKEY && GetFrameNumber(item, 0) >= 54) || item->Animation.AnimNumber == LA_MONKEY_IDLE))
|
|
{
|
|
if (abs(OrientDelta(slopeData.GoalOrient, item->Pose.Orientation.y)) <= ANGLE(30.0f) &&
|
|
InStrip(item->Pose.Position.x, item->Pose.Position.z, item->Pose.Orientation.y, 0, CLICK(0.5f)))
|
|
{
|
|
if (probeDown.BottomBlock->Flags.Monkeyswing)
|
|
{
|
|
int ceiling = GetCollision(probeDown.Block, down.x, now.y, down.z).Position.Ceiling;
|
|
int yDiff = ceiling - probeNow.Position.Ceiling;
|
|
|
|
int height;
|
|
short bridge = FindBridge(4, slopeData.GoalOrient, down, &height, -CLICK(7) >> 1, -CLICK(5) >> 1);
|
|
if ((SlopeCheck(probeDown.CeilingTilt, slopeData.Goal) && yDiff > 0 && yDiff < CLICK(1)) || bridge >= 0)
|
|
{
|
|
AlignToEdge(item, SLOPE_ALIGNMENT);
|
|
SetAnimation(item, LA_OVERHANG_MONKEY_SLOPE_CONCAVE); // Transition from monkey to underlying slope (concave).
|
|
return;
|
|
//item->Pose.Position.y = ceiling + 496;
|
|
//PerformFlipeffect(NULL, 51, 1, 2); // Disable the UP key command for 2 sec // HACK!!!
|
|
}
|
|
|
|
bridge = FindBridge(4, slopeData.GoalOrient + ANGLE(180.0f), down, &height, -CLICK(5), -CLICK(4));
|
|
if ((SlopeInvCheck(probeDown.CeilingTilt, slopeData.Goal) && yDiff > -CLICK(1) && yDiff < 0) || bridge >= 0)
|
|
{
|
|
AlignToEdge(item, SLOPE_ALIGNMENT);
|
|
SetAnimation(item, LA_OVERHANG_MONKEY_SLOPE_CONVEX); // Transition from monkey to overhanging slope (convex).
|
|
return;
|
|
//item->Pose.Position.y = ceiling + 914;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lara->Control.CanMonkeySwing)
|
|
{
|
|
// Additional overhang ladder tests.
|
|
|
|
int y = item->Pose.Position.y - coll->Setup.Height;
|
|
auto probe = GetCollision(down.x, item->Pose.Position.y - coll->Setup.Height, down.z, item->RoomNumber);
|
|
|
|
if (probe.BottomBlock->Flags.ClimbPossible(GetClimbDirection(item->Pose.Orientation.y + ANGLE(180.0f))) &&
|
|
probe.Position.Floor >= (item->Pose.Position.y - CLICK(1)) &&
|
|
probe.Position.Ceiling <= (y - CLICK(1)))
|
|
{
|
|
// Primary checks succeeded, now do C-shaped secondary probing.
|
|
probe = GetCollision(down.x, y, down.z, probe.RoomNumber);
|
|
probe = GetCollision(down.x, y - CLICK(2), down.z, probe.RoomNumber);
|
|
probe = GetCollision(now.x, y - CLICK(2), now.z, probe.RoomNumber);
|
|
|
|
if (probe.Position.Floor <= (y - CLICK(1)) ||
|
|
probe.Position.Ceiling >= (y - CLICK(1)))
|
|
{
|
|
if (item->Animation.TargetState != LS_LADDER_IDLE)
|
|
{
|
|
SnapItemToLedge(item, coll);
|
|
item->Animation.TargetState = LS_LADDER_IDLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|