#include "framework.h" #include "Specific/level.h" #include "Game/control/control.h" #include "Game/control/box.h" #include "Game/items.h" #include "Game/control/lot.h" #include "Specific/input.h" #include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_struct.h" #include "Game/Lara/lara.h" #include "Specific/trmath.h" #include "Game/collision/collide_room.h" #include "Game/collision/sphere.h" #include "Objects/Generic/Object/rope.h" #include "Sound/sound.h" #include "Game/camera.h" namespace TEN::Entities::Generic { PENDULUM CurrentPendulum; PENDULUM AlternatePendulum; std::vector Ropes; int RopeSwing = 0; void InitialiseRope(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; short roomNumber = item->RoomNumber; PHD_VECTOR itemPos; itemPos.x = item->Position.xPos; itemPos.y = item->Position.yPos; itemPos.z = item->Position.zPos; FLOOR_INFO* floor = GetFloor(itemPos.x, itemPos.y, itemPos.z, &roomNumber); itemPos.y = GetCeiling(floor, itemPos.x, itemPos.y, itemPos.z); PHD_VECTOR pos = { 0, 16384, 0 }; ROPE_STRUCT rope; PrepareRope(&rope, &itemPos, &pos, CLICK(0.5f), item); item->TriggerFlags = Ropes.size(); Ropes.push_back(rope); } void PrepareRope(ROPE_STRUCT* rope, PHD_VECTOR* pos1, PHD_VECTOR* pos2, int length, ITEM_INFO* item) { rope->position = *pos1; rope->segmentLength = length << 16; pos2->x <<= 16; pos2->y <<= 16; pos2->z <<= 16; NormaliseRopeVector(pos2); if (item->TriggerFlags == -1) rope->coiled = 30; else rope->coiled = 0; int l = 0; int sum = 0; int il = 3145728; for (int i = 0; i < ROPE_SEGMENTS; ++i) { rope->segment[i].x = (int64_t)sum * pos2->x >> FP_SHIFT; rope->segment[i].y = (int64_t)sum * pos2->x >> FP_SHIFT; rope->segment[i].z = (int64_t)sum * pos2->z >> FP_SHIFT; rope->velocity[i].x = 0; rope->velocity[i].y = 0; rope->velocity[i].z = 0; if (item->TriggerFlags == -1) { rope->segment[i].x = l; rope->segment[i].y >>= 4; rope->velocity[i].x = 16384; rope->velocity[i].y = il; rope->velocity[i].z = 16384; } l += 1024; sum += rope->segmentLength; il -= 131072; } rope->active = 0; } PHD_VECTOR* NormaliseRopeVector(PHD_VECTOR* vec) { int x = vec->x >> FP_SHIFT; int y = vec->y >> FP_SHIFT; int z = vec->z >> FP_SHIFT; if (!x && !y && !z) return vec; int length = SQUARE(x) + SQUARE(y) + SQUARE(z); if (length < 0) length = -length; length = 65536 / sqrt(length); vec->x = ((int64_t)length * vec->x) >> FP_SHIFT; vec->y = ((int64_t)length * vec->y) >> FP_SHIFT; vec->z = ((int64_t)length * vec->z) >> FP_SHIFT; return vec; } void GetRopePos(ROPE_STRUCT* rope, int segmentFrame, int* x, int* y, int* z) { int segment; short frame; segment = segmentFrame / 128; frame = segmentFrame & 0x7F; *x = (rope->normalisedSegment[segment].x * frame >> FP_SHIFT) + (rope->meshSegment[segment].x >> FP_SHIFT) + rope->position.x; *y = (rope->normalisedSegment[segment].y * frame >> FP_SHIFT) + (rope->meshSegment[segment].y >> FP_SHIFT) + rope->position.y; *z = (rope->normalisedSegment[segment].z * frame >> FP_SHIFT) + (rope->meshSegment[segment].z >> FP_SHIFT) + rope->position.z; } int DotProduct(PHD_VECTOR* u, PHD_VECTOR* v) { return (u->x * v->x + u->y * v->y + u->z * v->z) >> W2V_SHIFT; } void ScaleVector(PHD_VECTOR* u, int c, PHD_VECTOR* destination) { destination->x = c * u->x >> W2V_SHIFT; destination->y = c * u->y >> W2V_SHIFT; destination->z = c * u->z >> W2V_SHIFT; } void CrossProduct(PHD_VECTOR* u, PHD_VECTOR* v, PHD_VECTOR* destination) { destination->x = (u->y * v->z - u->z * v->y) >> W2V_SHIFT; destination->y = (u->z * v->x - u->x * v->z) >> W2V_SHIFT; destination->z = (u->x * v->y - u->y * v->x) >> W2V_SHIFT; } void phd_GetMatrixAngles(int* matrix, short* angle) { angle[0] = phd_atan(sqrt(SQUARE(matrix[M22]) + SQUARE(matrix[M02])), matrix[M12]); if (matrix[M12] >= 0 && angle[0] > 0 || matrix[M12] < 0 && angle[0] < 0) angle[0] = -angle[0]; angle[1] = phd_atan(matrix[M22], matrix[M02]); angle[2] = phd_atan(matrix[M00] * phd_cos(angle[1]) - matrix[M20] * phd_sin(angle[1]), matrix[M21] * phd_sin(angle[1]) - matrix[M01] * phd_cos(angle[1])); } void RopeControl(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; auto* rope = &Ropes[item->TriggerFlags]; if (TriggerActive(item)) { rope->active = 1; RopeDynamics(rope); } else rope->active = 0; } void RopeCollision(short itemNumber, ITEM_INFO* laraItem, COLL_INFO* coll) { auto* laraInfo = GetLaraInfo(laraItem); auto* ropeItem = &g_Level.Items[itemNumber]; auto* rope = &Ropes[ropeItem->TriggerFlags]; if (TrInput & IN_ACTION && laraInfo->Control.HandStatus == HandStatus::Free && (laraItem->ActiveState == LS_REACH || laraItem->ActiveState == LS_JUMP_UP) && laraItem->Airborne && laraItem->VerticalVelocity > 0&& rope->active) { auto* frame = GetBoundsAccurate(laraItem); int segment = RopeNodeCollision( rope, laraItem->Position.xPos, laraItem->Position.yPos + frame->Y1 + 512, laraItem->Position.zPos + frame->Z2 * phd_cos(laraItem->Position.yRot), laraItem->ActiveState == LS_REACH ? 128 : 320); if (segment >= 0) { if (laraItem->ActiveState == LS_REACH) { laraItem->AnimNumber = LA_REACH_TO_ROPE_SWING; laraItem->ActiveState = LS_ROPE_SWING; laraInfo->Control.Rope.Frame = g_Level.Anims[LA_ROPE_SWING].frameBase + 32 << 8; laraInfo->Control.Rope.DFrame = g_Level.Anims[LA_ROPE_SWING].frameBase + 60 << 8; } else { laraItem->AnimNumber = LA_JUMP_UP_TO_ROPE_START; laraItem->ActiveState = LS_ROPE_IDLE; } laraItem->FrameNumber = g_Level.Anims[laraItem->AnimNumber].frameBase; laraItem->VerticalVelocity = 0; laraItem->Airborne = false; laraInfo->Control.HandStatus = HandStatus::Busy; laraInfo->Control.Rope.Ptr = ropeItem->TriggerFlags; laraInfo->Control.Rope.Segment = segment; laraInfo->Control.Rope.Y = laraItem->Position.yRot; DelAlignLaraToRope(laraItem); CurrentPendulum.velocity.x = 0; CurrentPendulum.velocity.y = 0; CurrentPendulum.velocity.z = 0; ApplyVelocityToRope(segment, laraItem->Position.yRot, 16 * laraItem->Velocity); } } } void RopeDynamics(ROPE_STRUCT* rope) { PENDULUM* pendulumPointer; PHD_VECTOR vec, vec2; int flag = 0; if (rope->coiled) { --rope->coiled; if (!rope->coiled) { for (int i = 0; i < ROPE_SEGMENTS; ++i) rope->velocity[i].y = 0; } } if (Lara.Control.Rope.Ptr != -1 && rope == &Ropes[Lara.Control.Rope.Ptr]) { pendulumPointer = &CurrentPendulum; if (CurrentPendulum.node != Lara.Control.Rope.Segment + 1) { SetPendulumPoint(rope, Lara.Control.Rope.Segment + 1); flag = 1; } } else { pendulumPointer = &AlternatePendulum; if (Lara.Control.Rope.Ptr == -1 && CurrentPendulum.rope) { for (int i = 0; i < CurrentPendulum.node; i++) { CurrentPendulum.rope->velocity[i].x = CurrentPendulum.rope->velocity[CurrentPendulum.node].x; CurrentPendulum.rope->velocity[i].y = CurrentPendulum.rope->velocity[CurrentPendulum.node].y; CurrentPendulum.rope->velocity[i].z = CurrentPendulum.rope->velocity[CurrentPendulum.node].z; } CurrentPendulum.position.x = 0; CurrentPendulum.position.y = 0; CurrentPendulum.position.z = 0; CurrentPendulum.velocity.x = 0; CurrentPendulum.velocity.y = 0; CurrentPendulum.velocity.z = 0; CurrentPendulum.node = -1; CurrentPendulum.rope = NULL; } } if (Lara.Control.Rope.Ptr != -1) { vec.x = pendulumPointer->position.x - rope->segment[0].x; vec.y = pendulumPointer->position.y - rope->segment[0].y; vec.z = pendulumPointer->position.z - rope->segment[0].z; NormaliseRopeVector(&vec); for (int i = pendulumPointer->node; i >= 0; --i) { rope->segment[i].x = rope->meshSegment[i - 1].x + ((int64_t)rope->segmentLength * vec.x >> FP_SHIFT); rope->segment[i].y = rope->meshSegment[i - 1].y + ((int64_t)rope->segmentLength * vec.y >> FP_SHIFT); rope->segment[i].z = rope->meshSegment[i - 1].z + ((int64_t)rope->segmentLength * vec.z >> FP_SHIFT); rope->velocity[i].x = 0; rope->velocity[i].y = 0; rope->velocity[i].z = 0; } if (flag) { vec2.x = pendulumPointer->position.x - rope->segment[pendulumPointer->node].x; vec2.y = pendulumPointer->position.y - rope->segment[pendulumPointer->node].y; vec2.z = pendulumPointer->position.z - rope->segment[pendulumPointer->node].z; rope->segment[pendulumPointer->node].x = pendulumPointer->position.x; rope->segment[pendulumPointer->node].y = pendulumPointer->position.y; rope->segment[pendulumPointer->node].z = pendulumPointer->position.z; for (int i = pendulumPointer->node; i < ROPE_SEGMENTS; ++i) { rope->segment[i].x -= vec2.x; rope->segment[i].y -= vec2.y; rope->segment[i].z -= vec2.z; rope->velocity[i].x = 0; rope->velocity[i].y = 0; rope->velocity[i].z = 0; } } ModelRigidRope( rope, pendulumPointer, &rope->velocity[0], &pendulumPointer->velocity, rope->segmentLength * pendulumPointer->node); pendulumPointer->velocity.y += 6 << FP_SHIFT; pendulumPointer->position.x += pendulumPointer->velocity.x; pendulumPointer->position.y += pendulumPointer->velocity.y; pendulumPointer->position.z += pendulumPointer->velocity.z; pendulumPointer->velocity.x -= pendulumPointer->velocity.x >> 8; pendulumPointer->velocity.z -= pendulumPointer->velocity.z >> 8; } for (int i = pendulumPointer->node; i < ROPE_SEGMENTS - 1; ++i) ModelRigid( &rope->segment[i], &rope->segment[i + 1], &rope->velocity[i], &rope->velocity[i + 1], rope->segmentLength); for (int i = 0; i < ROPE_SEGMENTS; ++i) { rope->segment[i].x += rope->velocity[i].x; rope->segment[i].y += rope->velocity[i].y; rope->segment[i].z += rope->velocity[i].z; } for (int i = pendulumPointer->node; i < ROPE_SEGMENTS; ++i) { rope->velocity[i].y += 3 << FP_SHIFT; if (pendulumPointer->rope) { rope->velocity[i].x -= rope->velocity[i].x >> 4; rope->velocity[i].z -= rope->velocity[i].z >> 4; } else { rope->velocity[i].x -= rope->velocity[i].x >> 4; rope->velocity[i].z -= rope->velocity[i].z >> 4; } } rope->segment[0].x = 0; rope->segment[0].y = 0; rope->segment[0].z = 0; rope->velocity[0].x = 0; rope->velocity[0].y = 0; rope->velocity[0].z = 0; for (INT i = 0; i < ROPE_SEGMENTS - 1; ++i) { rope->normalisedSegment[i].x = rope->segment[i + 1].x - rope->segment[i].x; rope->normalisedSegment[i].y = rope->segment[i + 1].y - rope->segment[i].y; rope->normalisedSegment[i].z = rope->segment[i + 1].z - rope->segment[i].z; NormaliseRopeVector(&rope->normalisedSegment[i]); } if (Lara.Control.Rope.Ptr != -1 && rope != &Ropes[Lara.Control.Rope.Ptr]) { rope->meshSegment[0].x = rope->segment[0].x; rope->meshSegment[0].y = rope->segment[0].y; rope->meshSegment[0].z = rope->segment[0].z; rope->meshSegment[1].x = rope->segment[0].x + ((int64_t)rope->segmentLength * rope->normalisedSegment[0].x >> FP_SHIFT); rope->meshSegment[1].y = rope->segment[0].y + ((int64_t)rope->segmentLength * rope->normalisedSegment[0].y >> FP_SHIFT); rope->meshSegment[1].z = rope->segment[0].z + ((int64_t)rope->segmentLength * rope->normalisedSegment[0].z >> FP_SHIFT); for (int i = 2; i < ROPE_SEGMENTS; i++) { rope->meshSegment[i].x = rope->meshSegment[i - 1].x + ((int64_t)rope->segmentLength * rope->normalisedSegment[i - 1].x >> FP_SHIFT); rope->meshSegment[i].y = rope->meshSegment[i - 1].y + ((int64_t)rope->segmentLength * rope->normalisedSegment[i - 1].y >> FP_SHIFT); rope->meshSegment[i].z = rope->meshSegment[i - 1].z + ((int64_t)rope->segmentLength * rope->normalisedSegment[i - 1].z >> FP_SHIFT); } } else { rope->meshSegment[pendulumPointer->node].x = rope->segment[pendulumPointer->node].x; rope->meshSegment[pendulumPointer->node].y = rope->segment[pendulumPointer->node].y; rope->meshSegment[pendulumPointer->node].z = rope->segment[pendulumPointer->node].z; rope->meshSegment[pendulumPointer->node + 1].x = rope->segment[pendulumPointer->node].x + ((int64_t)rope->segmentLength * rope->normalisedSegment[pendulumPointer->node].x >> FP_SHIFT); rope->meshSegment[pendulumPointer->node + 1].y = rope->segment[pendulumPointer->node].y + ((int64_t)rope->segmentLength * rope->normalisedSegment[pendulumPointer->node].y >> FP_SHIFT); rope->meshSegment[pendulumPointer->node + 1].z = rope->segment[pendulumPointer->node].z + ((int64_t)rope->segmentLength * rope->normalisedSegment[pendulumPointer->node].z >> FP_SHIFT); for (int i = pendulumPointer->node + 1; i < ROPE_SEGMENTS - 1; ++i) { rope->meshSegment[i + 1].x = rope->meshSegment[i].x + ((int64_t)rope->segmentLength * rope->normalisedSegment[i].x >> FP_SHIFT); rope->meshSegment[i + 1].y = rope->meshSegment[i].y + ((int64_t)rope->segmentLength * rope->normalisedSegment[i].y >> FP_SHIFT); rope->meshSegment[i + 1].z = rope->meshSegment[i].z + ((int64_t)rope->segmentLength * rope->normalisedSegment[i].z >> FP_SHIFT); } for (int i = 0; i < pendulumPointer->node; i++) { rope->meshSegment[i].x = rope->segment[i].x; rope->meshSegment[i].y = rope->segment[i].y; rope->meshSegment[i].z = rope->segment[i].z; } } } int RopeNodeCollision(ROPE_STRUCT* rope, int x, int y, int z, int radius) { for (int i = 0; i < ROPE_SEGMENTS - 2; ++i) { if (y > rope->position.y + (rope->meshSegment[i].y >> FP_SHIFT) && y < rope->position.y + (rope->meshSegment[i + 1].y >> FP_SHIFT)) { int dx = x - ((rope->meshSegment[i + 1].x + rope->meshSegment[i].x) >> (FP_SHIFT + 1)) - rope->position.x; int dy = y - ((rope->meshSegment[i + 1].y + rope->meshSegment[i].y) >> (FP_SHIFT + 1)) - rope->position.y; int dz = z - ((rope->meshSegment[i + 1].z + rope->meshSegment[i].z) >> (FP_SHIFT + 1)) - rope->position.z; if (pow(dx, 2) + pow(dy, 2) + pow(dz, 2) < pow(radius + 64, 2)) return i; } } return -1; } void ApplyVelocityToRope(int node, short angle, short n) { SetPendulumVelocity(n * phd_sin(angle) * 4096, 0, n * phd_cos(angle) * 4096); } void SetPendulumVelocity(int x, int y, int z) { int node = 2 * (CurrentPendulum.node >> 1); if (node < ROPE_SEGMENTS) { int val = 4096 / (ROPE_SEGMENTS - node) * 256; x = (int64_t)val * x >> FP_SHIFT; y = (int64_t)val * y >> FP_SHIFT; z = (int64_t)val * z >> FP_SHIFT; } CurrentPendulum.velocity.x += x; CurrentPendulum.velocity.y += y; CurrentPendulum.velocity.z += z; } void SetPendulumPoint(ROPE_STRUCT* rope, int node) { CurrentPendulum.position.x = rope->segment[node].x; CurrentPendulum.position.y = rope->segment[node].y; CurrentPendulum.position.z = rope->segment[node].z; if (CurrentPendulum.node == -1) { CurrentPendulum.velocity.x += rope->velocity[node].x; CurrentPendulum.velocity.y += rope->velocity[node].y; CurrentPendulum.velocity.z += rope->velocity[node].z; } CurrentPendulum.node = node; CurrentPendulum.rope = rope; } void ModelRigidRope(ROPE_STRUCT* rope, PENDULUM* pendulumPointer, PHD_VECTOR* ropeVelocity, PHD_VECTOR* pendulumVelocity, int value) { PHD_VECTOR vec; vec.x = pendulumPointer->position.x + pendulumVelocity->x - rope->segment[0].x; vec.y = pendulumPointer->position.y + pendulumVelocity->y - rope->segment[0].y; vec.z = pendulumPointer->position.z + pendulumVelocity->z - rope->segment[0].z; int result = 65536 * sqrt(abs(pow(vec.x >> FP_SHIFT, 2) + pow(vec.y >> FP_SHIFT, 2) + pow(vec.z >> FP_SHIFT, 2))) - value; NormaliseRopeVector(&vec); pendulumVelocity->x -= (int64_t)result * vec.x >> FP_SHIFT; pendulumVelocity->y -= (int64_t)result * vec.y >> FP_SHIFT; pendulumVelocity->z -= (int64_t)result * vec.z >> FP_SHIFT; } void ModelRigid(PHD_VECTOR* segment, PHD_VECTOR* nextSegment, PHD_VECTOR* velocity, PHD_VECTOR* nextVelocity, int length) { PHD_VECTOR vec; vec.x = nextSegment->x + nextVelocity->x - segment->x - velocity->x; vec.y = nextSegment->y + nextVelocity->y - segment->y - velocity->y; vec.z = nextSegment->z + nextVelocity->z - segment->z - velocity->z; int result = (65536 * sqrt(abs(pow(vec.x >> FP_SHIFT, 2) + pow(vec.y >> FP_SHIFT, 2) + pow(vec.z >> FP_SHIFT, 2))) - length) / 2; NormaliseRopeVector(&vec); vec.x = (int64_t)result * vec.x >> FP_SHIFT; vec.y = (int64_t)result * vec.y >> FP_SHIFT; vec.z = (int64_t)result * vec.z >> FP_SHIFT; velocity->x += vec.x; velocity->y += vec.y; velocity->z += vec.z; nextVelocity->x -= vec.x; nextVelocity->y -= vec.y; nextVelocity->z -= vec.z; } void UpdateRopeSwing(ITEM_INFO* item) { if (Lara.Control.Rope.MaxXForward > 9000) Lara.Control.Rope.MaxXForward = 9000; if (Lara.Control.Rope.MaxXBackward > 9000) Lara.Control.Rope.MaxXBackward = 9000; if (Lara.Control.Rope.Direction) { if (item->Position.xRot > 0 && item->Position.xRot - Lara.Control.Rope.LastX < -100) { Lara.Control.Rope.ArcFront = Lara.Control.Rope.LastX; Lara.Control.Rope.Direction = 0; Lara.Control.Rope.MaxXBackward = 0; int frame = 15 * Lara.Control.Rope.MaxXForward / 18000 + g_Level.Anims[LA_ROPE_SWING].frameBase + 47 << 8; if (frame > Lara.Control.Rope.DFrame) { Lara.Control.Rope.DFrame = frame; RopeSwing = 1; } else RopeSwing = 0; SoundEffect(SFX_TR4_LARA_ROPE_CREAK, &item->Position, 0); } else if (Lara.Control.Rope.LastX < 0 && Lara.Control.Rope.Frame == Lara.Control.Rope.DFrame) { RopeSwing = 0; Lara.Control.Rope.DFrame = 15 * Lara.Control.Rope.MaxXBackward / 18000 + g_Level.Anims[LA_ROPE_SWING].frameBase + 47 << 8; Lara.Control.Rope.FrameRate = 15 * Lara.Control.Rope.MaxXBackward / 9000 + 1; } else if (Lara.Control.Rope.FrameRate < 512) { int num = RopeSwing ? 31 : 7; Lara.Control.Rope.FrameRate += num * Lara.Control.Rope.MaxXBackward / 9000 + 1; } } else { if (item->Position.xRot < 0 && item->Position.xRot - Lara.Control.Rope.LastX > 100) { Lara.Control.Rope.ArcBack = Lara.Control.Rope.LastX; Lara.Control.Rope.Direction = 1; Lara.Control.Rope.MaxXForward = 0; int frame = g_Level.Anims[LA_ROPE_SWING].frameBase - 15 * Lara.Control.Rope.MaxXBackward / 18000 + 17 << 8; if (frame < Lara.Control.Rope.DFrame) { Lara.Control.Rope.DFrame = frame; RopeSwing = 1; } else RopeSwing = 0; SoundEffect(SFX_TR4_LARA_ROPE_CREAK, &item->Position, 0); } else if (Lara.Control.Rope.LastX > 0 && Lara.Control.Rope.Frame == Lara.Control.Rope.DFrame) { RopeSwing = 0; Lara.Control.Rope.DFrame = g_Level.Anims[LA_ROPE_SWING].frameBase - 15 * Lara.Control.Rope.MaxXForward / 18000 + 17 << 8; Lara.Control.Rope.FrameRate = 15 * Lara.Control.Rope.MaxXForward / 9000 + 1; } else if (Lara.Control.Rope.FrameRate < 512) { int num = RopeSwing ? 31 : 7; Lara.Control.Rope.FrameRate += num * Lara.Control.Rope.MaxXForward / 9000 + 1; } } Lara.Control.Rope.LastX = item->Position.xRot; if (Lara.Control.Rope.Direction) { if (item->Position.xRot > Lara.Control.Rope.MaxXForward) Lara.Control.Rope.MaxXForward = item->Position.xRot; } else { if (item->Position.xRot < -Lara.Control.Rope.MaxXBackward) Lara.Control.Rope.MaxXBackward = abs(item->Position.xRot); } } void JumpOffRope(ITEM_INFO* item) { if (Lara.Control.Rope.Ptr != -1) { if (item->Position.xRot >= 0) { item->VerticalVelocity = -112; item->Velocity = item->Position.xRot / 128; } else { item->Velocity = 0; item->VerticalVelocity = -20; } item->Position.xRot = 0; item->Airborne = true; Lara.Control.HandStatus = HandStatus::Free; if (item->FrameNumber - g_Level.Anims[LA_ROPE_SWING].frameBase > 42) item->AnimNumber = LA_ROPE_SWING_TO_REACH_1; else if (item->FrameNumber - g_Level.Anims[LA_ROPE_SWING].frameBase > 21) item->AnimNumber = LA_ROPE_SWING_TO_REACH_2; else item->AnimNumber = LA_ROPE_SWING_TO_REACH_3; item->FrameNumber = g_Level.Anims[item->AnimNumber].frameBase; item->ActiveState = LS_REACH; item->TargetState = LS_REACH; Lara.Control.Rope.Ptr = -1; } } void FallFromRope(ITEM_INFO* item) { item->Velocity = abs(CurrentPendulum.velocity.x >> FP_SHIFT) + abs(CurrentPendulum.velocity.z >> FP_SHIFT) >> 1; item->Position.xRot = 0; item->Position.yPos += 320; item->AnimNumber = LA_FALL_START; item->FrameNumber = g_Level.Anims[LA_FALL_START].frameBase; item->ActiveState = LS_JUMP_FORWARD; item->TargetState = LS_JUMP_FORWARD; item->VerticalVelocity = 0; item->Airborne = true; Lara.Control.HandStatus = HandStatus::Free; Lara.Control.Rope.Ptr = -1; } void LaraClimbRope(ITEM_INFO* item, COLL_INFO* coll) { if (!(TrInput & IN_ACTION)) FallFromRope(item); else { Camera.targetAngle = ANGLE(30.0f); if (Lara.Control.Rope.Count) { if (!Lara.Control.Rope.Flag) { --Lara.Control.Rope.Count; Lara.Control.Rope.Offset += Lara.Control.Rope.DownVel; if (!Lara.Control.Rope.Count) Lara.Control.Rope.Flag = 1; return; } } else { if (!Lara.Control.Rope.Flag) { ROPE_STRUCT* rope = &Ropes[Lara.Control.Rope.Ptr]; Lara.Control.Rope.Offset = 0; Lara.Control.Rope.DownVel = (unsigned int)(rope->meshSegment[Lara.Control.Rope.Segment + 1].y - rope->meshSegment[Lara.Control.Rope.Segment].y) >> 17; Lara.Control.Rope.Count = 0; Lara.Control.Rope.Offset += Lara.Control.Rope.DownVel; Lara.Control.Rope.Flag = 1; return; } } if (item->AnimNumber == LA_ROPE_DOWN && item->FrameNumber == g_Level.Anims[item->AnimNumber].frameEnd) { SoundEffect(SFX_TR4_LARA_POLE_LOOP, &LaraItem->Position, 0); item->FrameNumber = g_Level.Anims[item->AnimNumber].frameBase; Lara.Control.Rope.Flag = 0; ++Lara.Control.Rope.Segment; Lara.Control.Rope.Offset = 0; } if (!(TrInput & IN_BACK) || Lara.Control.Rope.Segment >= 21) item->TargetState = LS_ROPE_IDLE; } } void DelAlignLaraToRope(ITEM_INFO* item) { ROPE_STRUCT* rope; short ropeY; PHD_VECTOR vec, vec2, vec3, vec4, vec5, pos, pos2, diff, diff2; int matrix[12]; short angle[3]; ANIM_FRAME* frame; vec.x = 4096; vec.y = 0; vec.z = 0; frame = (ANIM_FRAME*)GetBestFrame(item); ropeY = Lara.Control.Rope.Y - ANGLE(90); rope = &Ropes[Lara.Control.Rope.Ptr]; GetRopePos(rope, (Lara.Control.Rope.Segment - 1 << 7) + frame->offsetY, &pos.x, &pos.y, &pos.z); GetRopePos(rope, (Lara.Control.Rope.Segment - 1 << 7) + frame->offsetY - 192, &pos2.x, &pos2.y, &pos2.z); diff.x = pos.x - pos2.x << 16; diff.y = pos.y - pos2.y << 16; diff.z = pos.z - pos2.z << 16; NormaliseRopeVector(&diff); diff.x >>= 2; diff.y >>= 2; diff.z >>= 2; ScaleVector(&diff, DotProduct(&vec, &diff), &vec2); vec2.x = vec.x - vec2.x; vec2.y = vec.y - vec2.y; vec2.z = vec.z - vec2.z; vec3.x = vec2.x; vec3.y = vec2.y; vec3.z = vec2.z; vec4.x = vec2.x; vec4.y = vec2.y; vec4.z = vec2.z; diff2.x = diff.x; diff2.y = diff.y; diff2.z = diff.z; ScaleVector(&vec3, 16384 * phd_cos(ropeY), &vec3); ScaleVector(&diff2, DotProduct(&diff2, &vec2), &diff2); ScaleVector(&diff2, 4096 - 16384 * phd_cos(ropeY), &diff2); CrossProduct(&diff, &vec2, &vec4); ScaleVector(&vec4, 16384 * phd_sin(ropeY), &vec4); diff2.x += vec3.x; diff2.y += vec3.y; diff2.z += vec3.z; vec2.x = diff2.x + vec4.x << 16; vec2.y = diff2.y + vec4.y << 16; vec2.z = diff2.z + vec4.z << 16; NormaliseRopeVector(&vec2); vec2.x >>= 2; vec2.y >>= 2; vec2.z >>= 2; CrossProduct(&diff, &vec2, &vec5); vec5.x <<= 16; vec5.y <<= 16; vec5.z <<= 16; NormaliseRopeVector(&vec5); vec5.x >>= 2; vec5.y >>= 2; vec5.z >>= 2; matrix[M00] = vec5.x; matrix[M01] = diff.x; matrix[M02] = vec2.x; matrix[M10] = vec5.y; matrix[M11] = diff.y; matrix[M12] = vec2.y; matrix[M20] = vec5.z; matrix[M21] = diff.z; matrix[M22] = vec2.z; phd_GetMatrixAngles(matrix, angle); item->Position.xPos = rope->position.x + (rope->meshSegment[Lara.Control.Rope.Segment].x >> FP_SHIFT); item->Position.yPos = rope->position.y + (rope->meshSegment[Lara.Control.Rope.Segment].y >> FP_SHIFT) + Lara.Control.Rope.Offset; item->Position.zPos = rope->position.z + (rope->meshSegment[Lara.Control.Rope.Segment].z >> FP_SHIFT); Matrix rotMatrix = Matrix::CreateFromYawPitchRoll( TO_RAD(angle[1]), TO_RAD(angle[0]), TO_RAD(angle[2]) ); rotMatrix = rotMatrix.Transpose(); item->Position.xPos += -112 * rotMatrix.m[0][2]; item->Position.yPos += -112 * rotMatrix.m[1][2]; item->Position.zPos += -112 * rotMatrix.m[2][2]; item->Position.xRot = angle[0]; item->Position.yRot = angle[1]; item->Position.zRot = angle[2]; } }