2021-09-27 18:18:03 +10:00
|
|
|
#include "framework.h"
|
|
|
|
#include "collide.h"
|
2021-10-18 15:22:38 +11:00
|
|
|
#include "control/control.h"
|
2021-10-16 20:50:16 +11:00
|
|
|
#include "input.h"
|
2021-09-27 18:18:03 +10:00
|
|
|
#include "items.h"
|
|
|
|
#include "level.h"
|
|
|
|
#include "lara.h"
|
2021-10-16 20:50:16 +11:00
|
|
|
#include "lara_tests.h"
|
2021-10-09 14:39:06 +11:00
|
|
|
|
2021-10-18 20:15:53 +11:00
|
|
|
// -----------------------------
|
|
|
|
// HELPER FUNCTIONS
|
|
|
|
// For State Control & Collision
|
|
|
|
// -----------------------------
|
|
|
|
|
2021-10-09 14:39:06 +11:00
|
|
|
// TODO: Some states can't make the most of this function due to missing step up/down animations.
|
|
|
|
// Try implementing leg IK as a substitute to make step animations obsolete. @Sezz 2021.10.09
|
|
|
|
void DoLaraStep(ITEM_INFO* item, COLL_INFO* coll)
|
|
|
|
{
|
2021-10-09 23:00:26 +11:00
|
|
|
if (TestLaraStepUp(item, coll))
|
2021-10-09 14:39:06 +11:00
|
|
|
{
|
|
|
|
item->goalAnimState = LS_STEP_UP;
|
|
|
|
|
2021-10-10 14:03:38 +11:00
|
|
|
if (GetChange(item, &g_Level.Anims[item->animNumber]))
|
|
|
|
{
|
|
|
|
item->pos.yPos += coll->Middle.Floor;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2021-10-09 14:39:06 +11:00
|
|
|
}
|
2021-10-09 23:00:26 +11:00
|
|
|
else if (TestLaraStepDown(item, coll))
|
2021-10-09 14:39:06 +11:00
|
|
|
{
|
2021-10-18 00:33:46 +11:00
|
|
|
item->goalAnimState = LS_STEP_DOWN;
|
2021-10-09 14:39:06 +11:00
|
|
|
|
2021-10-10 14:03:38 +11:00
|
|
|
if (GetChange(item, &g_Level.Anims[item->animNumber]))
|
|
|
|
{
|
|
|
|
item->pos.yPos += coll->Middle.Floor;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2021-10-09 14:39:06 +11:00
|
|
|
}
|
|
|
|
|
2021-10-18 20:15:53 +11:00
|
|
|
// Height difference is below threshold for step dispatch or step animation doesn't exist; translate Lara to new floor height.
|
2021-10-14 01:17:21 +11:00
|
|
|
// TODO: Try using bad heights native to each state.
|
2021-10-09 16:56:07 +11:00
|
|
|
// TODO: Follow cube root curve instead of doing this ugly thing.
|
2021-10-09 23:00:26 +11:00
|
|
|
// TODO: This might cause underirable artefacts where an object pushes Lara rapidly up a slope or a platform ascends.
|
|
|
|
// Leg IK may correct for it, but until I get that working, for any future developments that see Lara phasing below the floor:
|
2021-10-18 20:15:53 +11:00
|
|
|
// comment everything EXCEPT the last two lines. Lara will simply snap to the surface as before (although the LS_RUN and LS_WADE_FORWARD
|
|
|
|
// states had a linear transition of 50 units per frame, so not all original behaviour can be restored from here;
|
|
|
|
// as that is so, I have left commented legacy step code in their respective col functions). @Sezz 2021.10.09
|
2021-10-09 14:39:06 +11:00
|
|
|
int div = 3;
|
2021-10-09 16:56:07 +11:00
|
|
|
if (abs(coll->Middle.Floor) <= STEPUP_HEIGHT / 2 &&
|
|
|
|
abs(coll->Middle.Floor) >= div &&
|
|
|
|
coll->Middle.Floor != NO_HEIGHT)
|
2021-10-09 14:39:06 +11:00
|
|
|
{
|
|
|
|
item->pos.yPos += coll->Middle.Floor / div;
|
|
|
|
}
|
2021-10-09 16:56:07 +11:00
|
|
|
else if (abs(coll->Middle.Floor) > STEPUP_HEIGHT / 2 &&
|
|
|
|
abs(coll->Middle.Floor) >= div &&
|
|
|
|
coll->Middle.Floor != NO_HEIGHT)
|
|
|
|
{
|
2021-10-09 20:19:34 +11:00
|
|
|
if (coll->Middle.Floor >= -50)
|
2021-10-09 16:56:07 +11:00
|
|
|
item->pos.yPos += 50;
|
2021-10-09 20:19:34 +11:00
|
|
|
else if (coll->Middle.Floor <= 50)
|
2021-10-09 16:56:07 +11:00
|
|
|
item->pos.yPos -= 50;
|
|
|
|
}
|
2021-10-09 20:19:34 +11:00
|
|
|
else if (coll->Middle.Floor != NO_HEIGHT)
|
2021-10-09 14:39:06 +11:00
|
|
|
item->pos.yPos += coll->Middle.Floor;
|
|
|
|
}
|
|
|
|
|
2021-10-16 20:50:16 +11:00
|
|
|
void DoLaraCrawlVault(ITEM_INFO* item, COLL_INFO* coll)
|
2021-09-29 18:35:21 +10:00
|
|
|
{
|
2021-10-16 20:50:16 +11:00
|
|
|
if (TestLaraCrawlExitJump(item, coll))
|
2021-09-29 18:35:21 +10:00
|
|
|
{
|
2021-10-16 20:50:16 +11:00
|
|
|
if (TrInput & IN_WALK)
|
2021-10-18 00:33:46 +11:00
|
|
|
item->goalAnimState = LS_CRAWL_EXIT_FLIP;
|
|
|
|
else [[likely]]
|
|
|
|
item->goalAnimState = LS_CRAWL_EXIT_JUMP;
|
2021-10-16 20:50:16 +11:00
|
|
|
|
|
|
|
return;
|
2021-09-29 18:35:21 +10:00
|
|
|
}
|
|
|
|
|
2021-10-16 20:50:16 +11:00
|
|
|
if (TestLaraCrawlExitDownStep(item, coll))
|
|
|
|
{
|
|
|
|
if (TrInput & IN_DUCK)
|
|
|
|
item->goalAnimState = LS_STEP_DOWN;
|
2021-10-18 00:33:46 +11:00
|
|
|
else [[likely]]
|
|
|
|
item->goalAnimState = LS_CRAWL_EXIT_DOWN_STEP;
|
2021-10-16 20:50:16 +11:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TestLaraCrawlUpStep(item, coll))
|
|
|
|
{
|
|
|
|
item->goalAnimState = LS_STEP_UP;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TestLaraCrawlDownStep(item, coll))
|
|
|
|
{
|
|
|
|
item->goalAnimState = LS_STEP_DOWN;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2021-09-29 18:35:21 +10:00
|
|
|
}
|
|
|
|
|
2021-10-18 15:22:38 +11:00
|
|
|
// TODO: Keeping legacy quadrant snaps for now. @Sezz 2021.10.16
|
|
|
|
void DoLaraCrawlToHangSnap(ITEM_INFO* item, COLL_INFO* coll)
|
|
|
|
{
|
|
|
|
switch (GetQuadrant(item->pos.yRot))
|
|
|
|
{
|
|
|
|
case NORTH:
|
|
|
|
item->pos.yRot = 0;
|
|
|
|
item->pos.zPos = (item->pos.zPos & 0xFFFFFC00) + 225;
|
|
|
|
break;
|
|
|
|
case EAST:
|
|
|
|
item->pos.yRot = ANGLE(90.0f);
|
|
|
|
item->pos.xPos = (item->pos.xPos & 0xFFFFFC00) + 225;
|
|
|
|
break;
|
|
|
|
case SOUTH:
|
|
|
|
item->pos.yRot = -ANGLE(180.0f);
|
|
|
|
item->pos.zPos = (item->pos.zPos | 0x3FF) - 225;
|
|
|
|
break;
|
|
|
|
case WEST:
|
|
|
|
item->pos.yRot = -ANGLE(90.0f);
|
|
|
|
item->pos.xPos = (item->pos.xPos | 0x3FF) - 225;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-22 00:07:24 +11:00
|
|
|
// TODO: Make lean rate proportional to the turn rate, allowing for nicer aesthetics with future analog stick input.
|
|
|
|
// The following commented line makes the lean rate LINEARLY proportional, but it's visually much too subtle.
|
|
|
|
// Ideally, lean rate should be on a curve, approaching leanMax faster at Lara.turnRate values near zero
|
|
|
|
// and falling off as Lara.turnRate approaches LARA_FAST_TURN (i.e. max turn rate).
|
|
|
|
// Unfortunately I am terrible at mathematics and I don't know how to do this.
|
|
|
|
// Would a library of easing functions be helpful here? @Sezz 2021.09.26
|
|
|
|
// item->pos.zRot += (item->pos.zRot -maxLean / LARA_FAST_TURN * Lara.turnRate) / 3;
|
|
|
|
void DoLaraLean(ITEM_INFO* item, COLL_INFO* coll, int maxLean, int divisor)
|
|
|
|
{
|
|
|
|
if (item->speed)
|
|
|
|
{
|
|
|
|
if (coll->CollisionType == CT_LEFT ||
|
|
|
|
coll->CollisionType == CT_RIGHT)
|
|
|
|
{
|
|
|
|
item->pos.zRot += (maxLean / 2 - item->pos.zRot) / divisor;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
item->pos.zRot += (maxLean - item->pos.zRot) / divisor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-27 18:18:03 +10:00
|
|
|
// TODO: State dispatch to a new LS_FALL state. The issue is that goal states set in collision functions are only actuated on the following
|
|
|
|
// frame, resulting in an unacceptable delay. Changing the order in which routine functions are executed is not a viable solution. @Sezz 2021.09.26
|
|
|
|
void SetLaraFallState(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
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-10-14 19:58:29 +11:00
|
|
|
void SetLaraFallBackState(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
item->animNumber = LA_FALL_BACK;
|
|
|
|
item->currentAnimState = LS_FALL_BACK;
|
|
|
|
item->goalAnimState = LS_FALL_BACK;
|
|
|
|
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
|
|
|
|
item->fallspeed = 0;
|
|
|
|
item->gravityStatus = true;
|
|
|
|
}
|
|
|
|
|
2021-09-27 18:18:03 +10:00
|
|
|
// TODO: Get true slope direction for enhanced sliding mechanics.
|
|
|
|
// Krys, I'd like to have a look at what you did in your TRNG script. @Sezz 2021.09.26
|
|
|
|
short GetLaraSlideDirection(COLL_INFO* coll)
|
|
|
|
{
|
|
|
|
short laraAngle = ANGLE(0.0f);
|
|
|
|
|
|
|
|
if (coll->TiltX > 2)
|
|
|
|
laraAngle = -ANGLE(90.0f);
|
|
|
|
else if (coll->TiltX < -2)
|
|
|
|
laraAngle = ANGLE(90.0f);
|
|
|
|
|
|
|
|
if (coll->TiltZ > 2 && coll->TiltZ > abs(coll->TiltX))
|
|
|
|
laraAngle = ANGLE(180.0f);
|
|
|
|
else if (coll->TiltZ < -2 && -coll->TiltZ > abs(coll->TiltX))
|
|
|
|
laraAngle = ANGLE(0.0f);
|
|
|
|
|
|
|
|
return laraAngle;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: State dispatches to slide states. Same issue as with SetLaraFallState().
|
|
|
|
void SetLaraSlideState(ITEM_INFO* item, COLL_INFO* coll)
|
|
|
|
{
|
|
|
|
auto angle = GetLaraSlideDirection(coll);
|
|
|
|
auto polarity = angle - item->pos.yRot;
|
|
|
|
|
|
|
|
ShiftItem(item, coll);
|
|
|
|
|
2021-10-14 21:11:59 +11:00
|
|
|
// Slide back.
|
2021-09-27 18:18:03 +10:00
|
|
|
if (polarity < -ANGLE(90.0f) || polarity > ANGLE(90.0f))
|
|
|
|
{
|
|
|
|
Lara.moveAngle = ANGLE(180);
|
|
|
|
item->pos.yRot = angle + ANGLE(180.0f);
|
|
|
|
|
|
|
|
item->animNumber = LA_SLIDE_BACK_START;
|
|
|
|
item->goalAnimState = LS_SLIDE_BACK;
|
|
|
|
item->currentAnimState = LS_SLIDE_BACK;
|
|
|
|
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
|
|
|
|
|
|
|
|
}
|
2021-10-14 21:11:59 +11:00
|
|
|
// Slide forward.
|
2021-09-27 18:18:03 +10:00
|
|
|
else [[likely]]
|
|
|
|
{
|
|
|
|
Lara.moveAngle = 0;
|
|
|
|
item->pos.yRot = angle;
|
|
|
|
|
|
|
|
item->animNumber = LA_SLIDE_FORWARD;
|
|
|
|
item->goalAnimState = LS_SLIDE_FORWARD;
|
|
|
|
item->frameNumber = g_Level.Anims[item->animNumber].frameBase;
|
|
|
|
item->currentAnimState = LS_SLIDE_FORWARD;
|
|
|
|
}
|
2021-10-09 14:39:06 +11:00
|
|
|
}
|