mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-05-02 09:47:58 +03:00
1294 lines
29 KiB
C++
1294 lines
29 KiB
C++
#include "framework.h"
|
|
#include "Game/Lara/lara.h"
|
|
|
|
#include "Game/Lara/lara_basic.h"
|
|
#include "Game/Lara/lara_helpers.h"
|
|
#include "Game/Lara/lara_jump.h"
|
|
#include "Game/Lara/lara_tests.h"
|
|
#include "Game/Lara/lara_monkey.h"
|
|
#include "Game/Lara/lara_crawl.h"
|
|
#include "Game/Lara/lara_objects.h"
|
|
#include "Game/Lara/lara_hang.h"
|
|
#include "Game/Lara/lara_slide.h"
|
|
#include "Game/Lara/lara_fire.h"
|
|
#include "Game/Lara/lara_surface.h"
|
|
#include "Game/Lara/lara_swim.h"
|
|
#include "Game/Lara/lara_one_gun.h"
|
|
#include "Game/Lara/lara_cheat.h"
|
|
#include "Game/Lara/lara_climb.h"
|
|
#include "Game/Lara/lara_initialise.h"
|
|
|
|
#include "Objects/TR2/Vehicles/snowmobile.h"
|
|
#include "Objects/TR3/Vehicles/biggun.h"
|
|
#include "Objects/TR3/Vehicles/kayak.h"
|
|
#include "Objects/TR3/Vehicles/minecart.h"
|
|
#include "Objects/TR3/Vehicles/quad.h"
|
|
#include "Objects/TR3/Vehicles/upv.h"
|
|
#include "Objects/TR4/Vehicles/jeep.h"
|
|
#include "Objects/TR4/Vehicles/motorbike.h"
|
|
#include "Objects/Generic/Object/rope.h"
|
|
|
|
#include "Game/animation.h"
|
|
#include "Game/camera.h"
|
|
#include "Game/collision/collide_item.h"
|
|
#include "Game/control/flipeffect.h"
|
|
#include "Game/control/volume.h"
|
|
#include "Game/effects/lara_fx.h"
|
|
#include "Game/effects/tomb4fx.h"
|
|
#include "Game/gui.h"
|
|
#include "Game/items.h"
|
|
#include "Game/misc.h"
|
|
#include "Game/savegame.h"
|
|
#include "Scripting/GameFlowScript.h"
|
|
#include "Sound/sound.h"
|
|
#include "Renderer/Renderer11.h"
|
|
|
|
using namespace TEN::Effects::Lara;
|
|
using namespace TEN::Entities::Generic;
|
|
using namespace TEN::Control::Volumes;
|
|
using std::function;
|
|
using TEN::Renderer::g_Renderer;
|
|
|
|
LaraInfo Lara;
|
|
ITEM_INFO* LaraItem;
|
|
COLL_INFO LaraCollision = {};
|
|
byte LaraNodeUnderwater[NUM_LARA_MESHES];
|
|
|
|
function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1] =
|
|
{
|
|
lara_as_walk_forward,
|
|
lara_as_run_forward,
|
|
lara_as_idle,
|
|
lara_as_jump_forward,//33
|
|
lara_as_pose,//4
|
|
lara_as_run_back,//5
|
|
lara_as_turn_right_slow,//6
|
|
lara_as_turn_left_slow,//7
|
|
lara_as_death,//8
|
|
lara_as_freefall,//9
|
|
lara_as_hang,
|
|
lara_as_reach,
|
|
lara_as_splat,
|
|
lara_as_tread,
|
|
lara_void_func,
|
|
lara_as_jump_prepare,//15
|
|
lara_as_walk_back,//16
|
|
lara_as_swim,//17
|
|
lara_as_glide,//18
|
|
lara_as_controlled_no_look,//19
|
|
lara_as_turn_right_fast,//20
|
|
lara_as_step_right,//21
|
|
lara_as_step_left,//22
|
|
lara_as_roll_back,
|
|
lara_as_slide_forward,//24
|
|
lara_as_jump_back,//25
|
|
lara_as_jump_right,//26
|
|
lara_as_jump_left,//27
|
|
lara_as_jump_up,//28
|
|
lara_as_fall_back,//29
|
|
lara_as_hangleft,//30
|
|
lara_as_hangright,//31
|
|
lara_as_slide_back,//32
|
|
lara_as_surftread,
|
|
lara_as_surfswim,
|
|
lara_as_dive,
|
|
lara_as_pushblock,//36
|
|
lara_as_pullblock,//37
|
|
lara_as_ppready,//38
|
|
lara_as_pickup,//39
|
|
lara_as_switchon,//40
|
|
lara_as_switchoff,//41
|
|
lara_as_usekey,//42
|
|
lara_as_usepuzzle,//43
|
|
lara_as_uwdeath,//44
|
|
lara_as_roll_forward,//45
|
|
lara_as_special,//46
|
|
lara_as_surfback,//47
|
|
lara_as_surfleft,//48
|
|
lara_as_surfright,//49
|
|
lara_void_func,//50
|
|
lara_void_func,//51
|
|
lara_as_swan_dive,//52
|
|
lara_as_freefall_dive,//53
|
|
lara_as_gymnast,//54
|
|
lara_as_waterout,
|
|
lara_as_climbstnc,
|
|
lara_as_climbing,
|
|
lara_as_climbleft,
|
|
lara_as_climbend,
|
|
lara_as_climbright,
|
|
lara_as_climbdown,//
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_as_wade_forward,//65
|
|
lara_as_waterroll,//66
|
|
lara_as_pickupflare,//67
|
|
lara_void_func,//68
|
|
lara_void_func,//69
|
|
lara_as_deathslide,//70
|
|
lara_as_crouch_idle,//71
|
|
lara_as_crouch_roll,//72
|
|
lara_as_sprint,
|
|
lara_as_sprint_dive,
|
|
lara_as_monkey_idle,
|
|
lara_as_monkey_forward,//76
|
|
lara_as_monkey_shimmy_left,//77
|
|
lara_as_monkey_shimmy_right,//78
|
|
lara_as_monkey_turn_180,//79
|
|
lara_as_crawl_idle,//80
|
|
lara_as_crawl_forward,//81
|
|
lara_as_monkey_turn_left,//82
|
|
lara_as_monkey_turn_right,//83
|
|
lara_as_crawl_turn_left,//84
|
|
lara_as_crawl_turn_right,//85
|
|
lara_as_crawl_back,//86
|
|
lara_as_controlled_no_look,
|
|
lara_as_controlled_no_look,
|
|
lara_as_controlled,
|
|
lara_as_ropel,
|
|
lara_as_roper,
|
|
lara_as_controlled,
|
|
lara_as_controlled,
|
|
lara_as_controlled,
|
|
lara_as_controlled_no_look,
|
|
lara_as_controlled_no_look,
|
|
lara_as_controlled,
|
|
lara_as_pickup,//98
|
|
lara_as_pole_idle,//99
|
|
lara_as_pole_up,//100
|
|
lara_as_pole_down,//101
|
|
lara_as_pole_turn_clockwise,//102
|
|
lara_as_pole_turn_counter_clockwise,//103
|
|
lara_as_pulley,//104
|
|
lara_as_crouch_turn_left,//105
|
|
lara_as_crouch_turn_right,//106
|
|
lara_as_corner,//107
|
|
lara_as_corner,//108
|
|
lara_as_corner,//109
|
|
lara_as_corner,//110
|
|
lara_as_rope,//111
|
|
lara_as_climbrope,//112
|
|
lara_as_climbroped,//113
|
|
lara_as_rope,//114
|
|
lara_as_rope,//115
|
|
lara_void_func,
|
|
lara_as_controlled,
|
|
lara_as_swimcheat,
|
|
lara_as_trpose,//119
|
|
lara_as_controlled_no_look,//120
|
|
lara_as_trwalk,//121
|
|
lara_as_trfall,//122
|
|
lara_as_trfall,//123
|
|
lara_as_null,//124
|
|
#ifdef NEW_TIGHTROPE
|
|
lara_as_trexit,//125
|
|
#else // !NEW_TIGHTROPE
|
|
lara_as_null,//125
|
|
#endif
|
|
lara_as_switchon,//126
|
|
lara_as_null,//127
|
|
lara_as_parallelbars,//128
|
|
lara_as_pbleapoff,//129
|
|
lara_as_null,//130
|
|
lara_as_controlled_no_look,//131
|
|
lara_as_controlled_no_look,//132
|
|
lara_as_null,//133
|
|
lara_as_null,//134
|
|
lara_as_null,//135
|
|
lara_as_null,//136
|
|
lara_as_null,//137
|
|
lara_as_null,//138
|
|
lara_as_null,//139
|
|
lara_as_null,//140
|
|
lara_as_null,//141
|
|
lara_as_null,//142
|
|
lara_as_null,// 143 - Unused
|
|
lara_as_null,// 144 - Unused
|
|
lara_as_null,// 145 - Unused
|
|
lara_as_controlled_no_look,
|
|
lara_as_null,
|
|
lara_as_null,
|
|
lara_as_null,
|
|
lara_as_stepoff_left,
|
|
lara_as_stepoff_right,
|
|
lara_as_turn_left_fast,
|
|
lara_as_controlled,
|
|
lara_as_controlled,
|
|
lara_as_controlled
|
|
};
|
|
|
|
function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1] =
|
|
{
|
|
lara_col_walk_forward,
|
|
lara_col_run_forward,
|
|
lara_col_idle,
|
|
lara_col_jump_forward,//3
|
|
lara_col_idle,//4
|
|
lara_col_run_back,
|
|
lara_col_turn_right_slow,
|
|
lara_col_turn_left_slow,
|
|
lara_col_death,
|
|
lara_col_freefall,//9
|
|
lara_col_hang,
|
|
lara_col_reach,
|
|
lara_col_splat,
|
|
lara_col_tread,
|
|
lara_col_land,
|
|
lara_col_jump_prepare,//15
|
|
lara_col_walk_back,
|
|
lara_col_swim,
|
|
lara_col_glide,
|
|
lara_default_col,
|
|
lara_col_turn_right_fast,
|
|
lara_col_step_right,
|
|
lara_col_step_left,
|
|
lara_col_roll_back,
|
|
lara_col_slide_forward,//24
|
|
lara_col_jump_back,//25
|
|
lara_col_jump_right,//26
|
|
lara_col_jump_left,//27
|
|
lara_col_jump_up,//28
|
|
lara_col_fall_back,//29
|
|
lara_col_hangleft,
|
|
lara_col_hangright,
|
|
lara_col_slide_back,//32
|
|
lara_col_surftread,
|
|
lara_col_surfswim,
|
|
lara_col_dive,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_col_uwdeath,
|
|
lara_col_roll_forward,
|
|
lara_void_func,
|
|
lara_col_surfback,
|
|
lara_col_surfleft,
|
|
lara_col_surfright,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_col_swan_dive,//52
|
|
lara_col_freefall_dive,//53
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_col_climbstnc,
|
|
lara_col_climbing,
|
|
lara_col_climbleft,
|
|
lara_col_climbend,
|
|
lara_col_climbright,
|
|
lara_col_climbdown,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_col_wade_forward,
|
|
lara_col_waterroll,
|
|
lara_default_col,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_col_crouch_idle,
|
|
lara_col_crouch_roll,
|
|
lara_col_sprint,
|
|
lara_col_sprint_dive,
|
|
lara_col_monkey_idle,
|
|
lara_col_monkey_forward,//76
|
|
lara_col_monkey_shimmy_left,//77
|
|
lara_col_monkey_shimmy_right,//78
|
|
lara_col_monkey_turn_180,//79
|
|
lara_col_crawl_idle,
|
|
lara_col_crawl_forward,
|
|
lara_col_monkey_turn_left,//81
|
|
lara_col_monkey_turn_right,//82
|
|
lara_col_crawl_turn_left,
|
|
lara_col_crawl_turn_right,
|
|
lara_col_crawl_back,
|
|
lara_void_func,
|
|
lara_col_crawl_to_hang,
|
|
lara_default_col,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_default_col,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_col_turnswitch,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_default_col,
|
|
lara_col_pole_idle,
|
|
lara_col_pole_up,
|
|
lara_col_pole_down,
|
|
lara_col_pole_turn_clockwise,
|
|
lara_col_pole_turn_counter_clockwise,
|
|
lara_default_col,
|
|
lara_col_crouch_turn_left,
|
|
lara_col_crouch_turn_right,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_col_rope,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_col_ropefwd,
|
|
lara_col_ropefwd,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_col_swim,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func, // 143 - Unused
|
|
lara_void_func, // 144 - Unused
|
|
lara_void_func, // 145 - Unused
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_void_func,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_col_turn_left_fast,
|
|
lara_default_col,
|
|
lara_default_col,
|
|
lara_default_col
|
|
};
|
|
|
|
void LaraControl(ITEM_INFO* item, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& info = item->data;
|
|
|
|
if (info->hasFired)
|
|
{
|
|
AlertNearbyGuards(item);
|
|
info->hasFired = false;
|
|
}
|
|
|
|
if (info->poisoned)
|
|
{
|
|
if (info->poisoned > 4096)
|
|
info->poisoned = 4096;
|
|
|
|
if (info->poisoned >= 256 && !(Wibble & 0xFF))
|
|
item->hitPoints -= info->poisoned >> 8;
|
|
}
|
|
|
|
if (info->isMoving)
|
|
{
|
|
if (info->moveCount > 90)
|
|
{
|
|
info->isMoving = false;
|
|
info->gunStatus = LG_HANDS_FREE;
|
|
}
|
|
|
|
++info->moveCount;
|
|
}
|
|
|
|
if (!info->uncontrollable)
|
|
info->locationPad = 128;
|
|
|
|
int oldX = item->pos.xPos;
|
|
int oldY = item->pos.yPos;
|
|
int oldZ = item->pos.zPos;
|
|
|
|
if (info->gunStatus == LG_HANDS_BUSY &&
|
|
item->currentAnimState == LS_IDLE &&
|
|
item->goalAnimState == LS_IDLE &&
|
|
item->animNumber == LA_STAND_IDLE &&
|
|
!item->gravityStatus)
|
|
{
|
|
info->gunStatus = LG_HANDS_FREE;
|
|
}
|
|
|
|
if (item->currentAnimState != LS_SPRINT && info->sprintTimer < LARA_SPRINT_MAX)
|
|
info->sprintTimer++;
|
|
|
|
info->isLow = false;
|
|
|
|
bool isWater = TestLaraWater(item);
|
|
bool isSwamp = TestLaraSwamp(item);
|
|
|
|
int waterDepth = GetWaterDepth(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
|
|
int waterHeight = GetWaterHeight(item->pos.xPos, item->pos.yPos, item->pos.zPos, item->roomNumber);
|
|
|
|
int heightFromWater;
|
|
if (waterHeight != NO_HEIGHT)
|
|
heightFromWater = item->pos.yPos - waterHeight;
|
|
else
|
|
heightFromWater = NO_HEIGHT;
|
|
info->waterSurfaceDist = -heightFromWater;
|
|
|
|
if (info->Vehicle == NO_ITEM)
|
|
WadeSplash(item, waterHeight, waterDepth);
|
|
|
|
if (info->Vehicle == NO_ITEM && info->ExtraAnim == -1)
|
|
{
|
|
switch (info->waterStatus)
|
|
{
|
|
case LW_ABOVE_WATER:
|
|
if (heightFromWater == NO_HEIGHT || heightFromWater < WADE_DEPTH)
|
|
break;
|
|
|
|
Camera.targetElevation = -ANGLE(22.0f);
|
|
|
|
// Water is deep enough to swim; dispatch dive.
|
|
if (waterDepth >= SWIM_DEPTH &&
|
|
!isSwamp)
|
|
{
|
|
if (isWater)
|
|
{
|
|
info->air = LARA_AIR_MAX;
|
|
info->waterStatus = LW_UNDERWATER;
|
|
item->gravityStatus = false;
|
|
item->pos.yPos += 100;
|
|
|
|
UpdateItemRoom(item, 0);
|
|
StopSoundEffect(SFX_TR4_LARA_FALL);
|
|
|
|
if (item->currentAnimState == LS_SWAN_DIVE_START)
|
|
{
|
|
info->gunStatus = LG_HANDS_FREE;
|
|
item->pos.xRot = -ANGLE(45.0f);
|
|
item->goalAnimState = LS_FREEFALL_DIVE;
|
|
AnimateLara(item);
|
|
item->fallspeed *= 2;
|
|
}
|
|
else if (item->currentAnimState == LS_SWAN_DIVE_END)
|
|
{
|
|
info->gunStatus = LG_HANDS_FREE;
|
|
item->pos.xRot = -ANGLE(85.0f);
|
|
item->goalAnimState = LS_FREEFALL_DIVE;
|
|
AnimateLara(item);
|
|
item->fallspeed *= 2;
|
|
}
|
|
else
|
|
{
|
|
item->pos.xRot = -ANGLE(45.0f);
|
|
SetAnimation(item, LA_FREEFALL_DIVE);
|
|
item->fallspeed = 3 * item->fallspeed / 2;
|
|
}
|
|
|
|
ResetLaraFlex(item);
|
|
Splash(item);
|
|
}
|
|
}
|
|
// Water is at wade depth; update water status and do special handling.
|
|
else if (heightFromWater >= WADE_DEPTH)
|
|
{
|
|
info->waterStatus = LW_WADE;
|
|
|
|
// Make splash ONLY within this particular threshold before swim depth while airborne (WadeSplash() above interferes otherwise).
|
|
if (waterDepth > (SWIM_DEPTH - STEP_SIZE) &&
|
|
!isSwamp &&
|
|
item->gravityStatus)
|
|
{
|
|
Splash(item);
|
|
item->goalAnimState = LS_IDLE;
|
|
}
|
|
// Lara is grounded; don't splash again.
|
|
else if (!item->gravityStatus)
|
|
item->goalAnimState = LS_IDLE;
|
|
else if (isSwamp)
|
|
{
|
|
if (item->currentAnimState == LS_SWAN_DIVE_START ||
|
|
item->currentAnimState == LS_SWAN_DIVE_END)
|
|
{
|
|
item->pos.yPos = waterHeight + (WALL_SIZE - 24);
|
|
}
|
|
|
|
SetAnimation(item, LA_WADE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case LW_UNDERWATER:
|
|
if (isWater ||
|
|
waterDepth == DEEP_WATER ||
|
|
abs(heightFromWater) >= STEP_SIZE ||
|
|
item->animNumber == LA_UNDERWATER_RESURFACE ||
|
|
item->animNumber == LA_ONWATER_DIVE)
|
|
{
|
|
if (!isWater)
|
|
{
|
|
if (waterDepth == DEEP_WATER || abs(heightFromWater) >= STEP_SIZE)
|
|
{
|
|
SetAnimation(item, LA_FALL_START);
|
|
item->speed = item->fallspeed / 4;
|
|
item->gravityStatus = true;
|
|
item->fallspeed = 0;
|
|
item->pos.zRot = 0;
|
|
item->pos.xRot = 0;
|
|
info->waterStatus = LW_ABOVE_WATER;
|
|
ResetLaraFlex(item);
|
|
}
|
|
else
|
|
{
|
|
SetAnimation(item, LA_UNDERWATER_RESURFACE);
|
|
item->pos.yPos = waterHeight;
|
|
item->fallspeed = 0;
|
|
item->pos.zRot = 0;
|
|
item->pos.xRot = 0;
|
|
info->waterStatus = LW_SURFACE;
|
|
info->diveCount = 11;
|
|
ResetLaraFlex(item);
|
|
|
|
UpdateItemRoom(item, -(STEPUP_HEIGHT - 3));
|
|
SoundEffect(SFX_TR4_LARA_BREATH, &item->pos, 2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetAnimation(item, LA_UNDERWATER_RESURFACE);
|
|
item->pos.yPos = waterHeight + 1;
|
|
item->fallspeed = 0;
|
|
item->pos.zRot = 0;
|
|
item->pos.xRot = 0;
|
|
info->waterStatus = LW_SURFACE;
|
|
info->diveCount = 11;
|
|
ResetLaraFlex(item);
|
|
|
|
UpdateItemRoom(item, 0);
|
|
SoundEffect(SFX_TR4_LARA_BREATH, &item->pos, 2);
|
|
}
|
|
break;
|
|
|
|
case LW_SURFACE:
|
|
if (!isWater)
|
|
{
|
|
if (heightFromWater <= WADE_DEPTH)
|
|
{
|
|
SetAnimation(item, LA_FALL_START);
|
|
item->speed = item->fallspeed / 4;
|
|
item->gravityStatus = true;
|
|
info->waterStatus = LW_ABOVE_WATER;
|
|
}
|
|
else
|
|
{
|
|
SetAnimation(item, LA_STAND_IDLE);
|
|
item->goalAnimState = LS_WADE_FORWARD; // TODO: Check if really needed? -- Lwmte, 10.11.21
|
|
info->waterStatus = LW_WADE;
|
|
|
|
AnimateItem(item);
|
|
}
|
|
|
|
item->fallspeed = 0;
|
|
item->pos.zRot = 0;
|
|
item->pos.xRot = 0;
|
|
ResetLaraFlex(item);
|
|
}
|
|
|
|
break;
|
|
|
|
case LW_WADE:
|
|
Camera.targetElevation = -ANGLE(22.0f);
|
|
|
|
if (heightFromWater >= WADE_DEPTH)
|
|
{
|
|
if (heightFromWater > SWIM_DEPTH && !isSwamp)
|
|
{
|
|
SetAnimation(item, LA_ONWATER_IDLE);
|
|
|
|
info->waterStatus = LW_SURFACE;
|
|
item->pos.yPos += 1 - heightFromWater;
|
|
item->gravityStatus = false;
|
|
item->fallspeed = 0;
|
|
item->pos.zRot = 0;
|
|
item->pos.xRot = 0;
|
|
info->diveCount = 0;
|
|
ResetLaraFlex(item);
|
|
|
|
UpdateItemRoom(item, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
info->waterStatus = LW_ABOVE_WATER;
|
|
|
|
if (item->currentAnimState == LS_WADE_FORWARD)
|
|
item->goalAnimState = LS_RUN_FORWARD;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (item->hitPoints <= 0)
|
|
{
|
|
item->hitPoints = -1;
|
|
|
|
if (info->deathCount == 0)
|
|
StopSoundTracks();
|
|
|
|
info->deathCount++;
|
|
if ((item->flags & 0x100))
|
|
{
|
|
info->deathCount++;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (info->waterStatus)
|
|
{
|
|
case LW_ABOVE_WATER:
|
|
case LW_WADE:
|
|
if (isSwamp && info->waterSurfaceDist < -(LARA_HEIGHT + 8)) // TODO: Find best height. @Sezz 2021.11.10
|
|
{
|
|
if (item->hitPoints >= 0)
|
|
{
|
|
info->air -= 6;
|
|
if (info->air < 0)
|
|
{
|
|
info->air = -1;
|
|
item->hitPoints -= 10;
|
|
}
|
|
}
|
|
}
|
|
else if (info->air < LARA_AIR_MAX && item->hitPoints >= 0)
|
|
{
|
|
if (info->Vehicle == NO_ITEM) // only for the upv !!
|
|
{
|
|
info->air += 10;
|
|
if (info->air > LARA_AIR_MAX)
|
|
info->air = LARA_AIR_MAX;
|
|
}
|
|
}
|
|
|
|
LaraAboveWater(item, coll);
|
|
|
|
break;
|
|
|
|
case LW_UNDERWATER:
|
|
if (item->hitPoints >= 0)
|
|
{
|
|
auto level = g_GameFlow->GetLevel(CurrentLevel);
|
|
if (level->LaraType != LaraType::Divesuit)
|
|
info->air--;
|
|
|
|
if (info->air < 0)
|
|
{
|
|
// if (LaraDrawType == LARA_TYPE::DIVESUIT && info->anxiety < 251)
|
|
// info->anxiety += 4;
|
|
info->air = -1;
|
|
item->hitPoints -= 5;
|
|
}
|
|
}
|
|
|
|
LaraUnderWater(item, coll);
|
|
|
|
break;
|
|
|
|
case LW_SURFACE:
|
|
if (item->hitPoints >= 0)
|
|
{
|
|
info->air += 10;
|
|
if (info->air > LARA_AIR_MAX)
|
|
info->air = LARA_AIR_MAX;
|
|
}
|
|
|
|
LaraSurface(item, coll);
|
|
|
|
break;
|
|
|
|
case LW_FLYCHEAT:
|
|
LaraCheat(item, coll);
|
|
|
|
break;
|
|
}
|
|
|
|
Statistics.Game.Distance += sqrt(
|
|
SQUARE(item->pos.xPos - oldX) +
|
|
SQUARE(item->pos.yPos - oldY) +
|
|
SQUARE(item->pos.zPos - oldZ));
|
|
}
|
|
|
|
void LaraAboveWater(ITEM_INFO* item, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& info = item->data;
|
|
|
|
coll->Setup.OldPosition.x = item->pos.xPos;
|
|
coll->Setup.OldPosition.y = item->pos.yPos;
|
|
coll->Setup.OldPosition.z = item->pos.zPos;
|
|
coll->Setup.OldAnimState = item->currentAnimState;
|
|
coll->Setup.OldAnimNumber = item->animNumber;
|
|
coll->Setup.OldFrameNumber = item->frameNumber;
|
|
|
|
coll->Setup.BadCeilingHeightUp = MAX_HEIGHT;
|
|
coll->Setup.EnableObjectPush = true;
|
|
coll->Setup.EnableSpaz = true;
|
|
coll->Setup.FloorSlopesAreWalls = false;
|
|
coll->Setup.FloorSlopesArePits = false;
|
|
coll->Setup.CeilingSlopesAreWalls = false;
|
|
coll->Setup.DeathFlagIsPit = false;
|
|
coll->Setup.NoMonkeyFlagIsWall = false;
|
|
coll->Setup.Mode = COLL_PROBE_MODE::QUADRANTS;
|
|
|
|
if (TrInput & IN_LOOK && info->look &&
|
|
info->ExtraAnim == NO_ITEM)
|
|
{
|
|
LookLeftRight();
|
|
}
|
|
else if (coll->Setup.Height > LARA_HEIGHT - LARA_HEADROOM) // TEMP HACK: Look feature will need a dedicated refactor; ResetLook() interferes with crawl flexing. @Sezz 2021.12.10
|
|
ResetLook(item);
|
|
|
|
// TODO: Move radius and height default resets above look checks when
|
|
coll->Setup.Radius = LARA_RAD;
|
|
coll->Setup.Height = LARA_HEIGHT;
|
|
info->look = true;
|
|
|
|
UpdateItemRoom(item, -LARA_HEIGHT / 2);
|
|
|
|
// Process vehicles.
|
|
if (info->Vehicle != NO_ITEM)
|
|
{
|
|
switch (g_Level.Items[info->Vehicle].objectNumber)
|
|
{
|
|
case ID_QUAD:
|
|
if (QuadBikeControl(item, coll))
|
|
return;
|
|
break;
|
|
|
|
case ID_JEEP:
|
|
if (JeepControl())
|
|
return;
|
|
break;
|
|
|
|
case ID_MOTORBIKE:
|
|
if (MotorbikeControl())
|
|
return;
|
|
break;
|
|
|
|
case ID_KAYAK:
|
|
if (KayakControl(item))
|
|
return;
|
|
break;
|
|
|
|
case ID_SNOWMOBILE:
|
|
if (SkidooControl(item, coll))
|
|
return;
|
|
break;
|
|
|
|
case ID_UPV:
|
|
if (SubControl(item, coll))
|
|
return;
|
|
break;
|
|
|
|
case ID_MINECART:
|
|
if (MineCartControl())
|
|
return;
|
|
break;
|
|
|
|
case ID_BIGGUN:
|
|
if (BigGunControl(item, coll))
|
|
return;
|
|
break;
|
|
|
|
default:
|
|
// Boats are processed like normal items in loop.
|
|
LaraGun(item);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Temp. debug stuff
|
|
//---
|
|
|
|
if (KeyMap[DIK_D])
|
|
item->hitPoints = 0;
|
|
|
|
static PHD_3DPOS posO = { 0, 0, 0, 0, 0, 0 };
|
|
static short roomNumO = item->roomNumber;
|
|
static CAMERA_INFO camO = Camera;
|
|
|
|
if (KeyMap[DIK_Q] && TrInput & IN_WALK)
|
|
{
|
|
posO = item->pos;
|
|
roomNumO = item->roomNumber;
|
|
camO = Camera;
|
|
}
|
|
else if (KeyMap[DIK_E])
|
|
{
|
|
item->pos = posO;
|
|
item->roomNumber - roomNumO;
|
|
Camera = camO;
|
|
}
|
|
|
|
// Forward 1 unit.
|
|
if (KeyMap[DIK_I])
|
|
{
|
|
item->pos.xPos += round(phd_sin(item->pos.yRot));
|
|
item->pos.zPos += round(phd_cos(item->pos.yRot));
|
|
}
|
|
// Back 1 unit.
|
|
else if (KeyMap[DIK_K])
|
|
{
|
|
item->pos.xPos += round(phd_sin(item->pos.yRot + ANGLE(180.0f)));
|
|
item->pos.zPos += round(phd_cos(item->pos.yRot + ANGLE(180.0f)));
|
|
}
|
|
// Left 1 unit.
|
|
else if (KeyMap[DIK_J])
|
|
{
|
|
item->pos.xPos += round(phd_sin(item->pos.yRot - ANGLE(90.0f)));
|
|
item->pos.zPos += round(phd_cos(item->pos.yRot - ANGLE(90.0f)));
|
|
}
|
|
// Right 1 unit.
|
|
else if (KeyMap[DIK_L])
|
|
{
|
|
item->pos.xPos += round(phd_sin(item->pos.yRot + ANGLE(90.0f)));
|
|
item->pos.zPos += round(phd_cos(item->pos.yRot + ANGLE(90.0f)));
|
|
}
|
|
|
|
//---
|
|
|
|
// Temp. debug stuff.
|
|
static bool doRoutines = true;
|
|
|
|
static bool dbT = false;
|
|
if (KeyMap[DIK_T] && !dbT)
|
|
doRoutines = !doRoutines;
|
|
dbT = KeyMap[DIK_T] ? true : false;
|
|
|
|
static bool dbU = false;
|
|
if (doRoutines || KeyMap[DIK_U] && !dbU)
|
|
{
|
|
// Handle current Lara status.
|
|
lara_control_routines[item->currentAnimState](item, coll);
|
|
|
|
HandleLaraMovementParameters(item, coll);
|
|
|
|
// Animate Lara.
|
|
AnimateLara(item);
|
|
|
|
if (info->ExtraAnim == -1)
|
|
{
|
|
// Check for collision with items.
|
|
DoObjectCollision(item, coll);
|
|
|
|
// Handle Lara collision.
|
|
if (info->Vehicle == NO_ITEM)
|
|
lara_collision_routines[item->currentAnimState](item, coll);
|
|
}
|
|
}
|
|
dbU = KeyMap[DIK_U] ? true : false;
|
|
|
|
//if (info->gunType == WEAPON_CROSSBOW && !LaserSight)
|
|
// TrInput &= ~IN_ACTION;
|
|
|
|
// Handle weapons.
|
|
LaraGun(item);
|
|
|
|
// Handle breath.
|
|
LaraBreath(item);
|
|
|
|
// Test for flags and triggers.
|
|
ProcessSectorFlags(item);
|
|
TestTriggers(item, false);
|
|
TestVolumes(item);
|
|
}
|
|
|
|
void LaraUnderWater(ITEM_INFO* item, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& info = item->data;
|
|
|
|
coll->Setup.BadFloorHeightDown = 32512;
|
|
coll->Setup.BadFloorHeightUp = -400;
|
|
coll->Setup.BadCeilingHeightDown = 400;
|
|
coll->Setup.BadCeilingHeightUp = MAX_HEIGHT;
|
|
|
|
coll->Setup.OldPosition.x = item->pos.xPos;
|
|
coll->Setup.OldPosition.y = item->pos.yPos;
|
|
coll->Setup.OldPosition.z = item->pos.zPos;
|
|
|
|
coll->Setup.FloorSlopesAreWalls = false;
|
|
coll->Setup.FloorSlopesArePits = false;
|
|
coll->Setup.CeilingSlopesAreWalls = false;
|
|
coll->Setup.DeathFlagIsPit = false;
|
|
coll->Setup.NoMonkeyFlagIsWall = false;
|
|
coll->Setup.EnableObjectPush = true;
|
|
coll->Setup.EnableSpaz = false;
|
|
coll->Setup.Mode = COLL_PROBE_MODE::QUADRANTS;
|
|
|
|
coll->Setup.Radius = LARA_RAD_UNDERWATER;
|
|
coll->Setup.Height = LARA_HEIGHT;
|
|
|
|
if (TrInput & IN_LOOK && info->look)
|
|
LookLeftRight();
|
|
else
|
|
ResetLook(item);
|
|
|
|
info->look = true;
|
|
info->poseCount = 0;
|
|
|
|
lara_control_routines[item->currentAnimState](item, coll);
|
|
|
|
auto level = g_GameFlow->GetLevel(CurrentLevel);
|
|
|
|
if (level->LaraType == LaraType::Divesuit)
|
|
{
|
|
if (info->turnRate < -ANGLE(0.5f))
|
|
info->turnRate += ANGLE(0.5f);
|
|
else if (info->turnRate > ANGLE(0.5f))
|
|
info->turnRate -= ANGLE(0.5f);
|
|
else
|
|
info->turnRate = 0;
|
|
}
|
|
else if (info->turnRate < -ANGLE(2.0f))
|
|
info->turnRate += ANGLE(2.0f);
|
|
else if (info->turnRate > ANGLE(2.0f))
|
|
info->turnRate -= ANGLE(2.0f);
|
|
else
|
|
info->turnRate = 0;
|
|
|
|
item->pos.yRot += info->turnRate;
|
|
|
|
if (level->LaraType == LaraType::Divesuit)
|
|
UpdateSubsuitAngles();
|
|
|
|
if (!info->isMoving && !(TrInput & (IN_LEFT | IN_RIGHT)))
|
|
{
|
|
if (abs(item->pos.zRot) > 0)
|
|
item->pos.zRot += item->pos.zRot / -8;
|
|
}
|
|
|
|
if (item->pos.xRot < -ANGLE(85.0f))
|
|
item->pos.xRot = -ANGLE(85.0f);
|
|
else if (item->pos.xRot > ANGLE(85.0f))
|
|
item->pos.xRot = ANGLE(85.0f);
|
|
|
|
if (level->LaraType == LaraType::Divesuit)
|
|
{
|
|
if (item->pos.zRot > ANGLE(44.0f))
|
|
item->pos.zRot = ANGLE(44.0f);
|
|
else if (item->pos.zRot < -ANGLE(44.0f))
|
|
item->pos.zRot = -ANGLE(44.0f);
|
|
}
|
|
else
|
|
{
|
|
if (item->pos.zRot > ANGLE(22.0f))
|
|
item->pos.zRot = ANGLE(22.0f);
|
|
else if (item->pos.zRot < -ANGLE(22.0f))
|
|
item->pos.zRot = -ANGLE(22.0f);
|
|
}
|
|
|
|
if (info->currentActive && info->waterStatus != LW_FLYCHEAT)
|
|
LaraWaterCurrent(coll);
|
|
|
|
AnimateLara(item);
|
|
|
|
item->pos.xPos += phd_cos(item->pos.xRot) * item->fallspeed * phd_sin(item->pos.yRot) / 4;
|
|
item->pos.yPos -= item->fallspeed * phd_sin(item->pos.xRot) / 4;
|
|
item->pos.zPos += phd_cos(item->pos.xRot) * item->fallspeed * phd_cos(item->pos.yRot) / 4;
|
|
|
|
DoObjectCollision(item, coll);
|
|
|
|
if (/*info->ExtraAnim == -1 &&*/ info->Vehicle == NO_ITEM)
|
|
lara_collision_routines[item->currentAnimState](item, coll);
|
|
|
|
UpdateItemRoom(item, 0);
|
|
|
|
LaraGun(item);
|
|
|
|
ProcessSectorFlags(item);
|
|
TestTriggers(item, false);
|
|
TestVolumes(item);
|
|
}
|
|
|
|
void LaraSurface(ITEM_INFO* item, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& info = item->data;
|
|
|
|
Camera.targetElevation = -ANGLE(22.0f);
|
|
|
|
coll->Setup.BadFloorHeightDown = 32512;
|
|
coll->Setup.BadFloorHeightUp = -128;
|
|
coll->Setup.BadCeilingHeightDown = 100;
|
|
coll->Setup.BadCeilingHeightUp = MAX_HEIGHT;
|
|
|
|
coll->Setup.OldPosition.x = item->pos.xPos;
|
|
coll->Setup.OldPosition.y = item->pos.yPos;
|
|
coll->Setup.OldPosition.z = item->pos.zPos;
|
|
|
|
coll->Setup.FloorSlopesAreWalls = false;
|
|
coll->Setup.FloorSlopesArePits = false;
|
|
coll->Setup.CeilingSlopesAreWalls = false;
|
|
coll->Setup.DeathFlagIsPit = false;
|
|
coll->Setup.NoMonkeyFlagIsWall = false;
|
|
coll->Setup.EnableObjectPush = false;
|
|
coll->Setup.EnableSpaz = false;
|
|
coll->Setup.Mode = COLL_PROBE_MODE::FREE_FORWARD;
|
|
|
|
coll->Setup.Radius = LARA_RAD;
|
|
coll->Setup.Height = LARA_HEIGHT_SURFACE;
|
|
|
|
if (TrInput & IN_LOOK && info->look)
|
|
LookLeftRight();
|
|
else
|
|
ResetLook(item);
|
|
|
|
info->look = true;
|
|
info->poseCount = 0;
|
|
|
|
lara_control_routines[item->currentAnimState](item, coll);
|
|
|
|
if (!info->isMoving && !(TrInput & (IN_LEFT | IN_RIGHT)))
|
|
{
|
|
if (abs(item->pos.zRot) > 0)
|
|
item->pos.zRot += item->pos.zRot / -8;
|
|
}
|
|
|
|
if (info->currentActive && info->waterStatus != LW_FLYCHEAT)
|
|
LaraWaterCurrent(coll);
|
|
|
|
AnimateLara(item);
|
|
|
|
item->pos.xPos += item->fallspeed * phd_sin(info->moveAngle) / 4;
|
|
item->pos.zPos += item->fallspeed * phd_cos(info->moveAngle) / 4;
|
|
|
|
DoObjectCollision(item, coll);
|
|
|
|
if (info->Vehicle == NO_ITEM)
|
|
lara_collision_routines[item->currentAnimState](item, coll);
|
|
|
|
UpdateItemRoom(item, 100);
|
|
|
|
LaraGun(item);
|
|
|
|
ProcessSectorFlags(item);
|
|
TestTriggers(item, false);
|
|
TestVolumes(item);
|
|
}
|
|
|
|
void LaraCheat(ITEM_INFO* item, COLL_INFO* coll)
|
|
{
|
|
LaraInfo*& info = item->data;
|
|
|
|
item->hitPoints = LARA_HEALTH_MAX;
|
|
LaraUnderWater(item, coll);
|
|
|
|
if (TrInput & IN_WALK && !(TrInput & IN_LOOK))
|
|
{
|
|
if (TestLaraWater(item) || (info->waterSurfaceDist > 0 && info->waterSurfaceDist != NO_HEIGHT))
|
|
{
|
|
info->waterStatus = LW_UNDERWATER;
|
|
SetAnimation(item, LA_UNDERWATER_IDLE);
|
|
ResetLaraFlex(item);
|
|
}
|
|
else
|
|
{
|
|
info->waterStatus = LW_ABOVE_WATER;
|
|
SetAnimation(item, LA_STAND_SOLID);
|
|
item->pos.zRot = 0;
|
|
item->pos.xRot = 0;
|
|
ResetLaraFlex(item);
|
|
}
|
|
|
|
info->gunStatus = LG_HANDS_FREE;
|
|
LaraInitialiseMeshes();
|
|
item->hitPoints = LARA_HEALTH_MAX;
|
|
}
|
|
}
|
|
|
|
void AnimateLara(ITEM_INFO* item)
|
|
{
|
|
LaraInfo*& info = item->data;
|
|
|
|
item->frameNumber++;
|
|
|
|
ANIM_STRUCT* anim = &g_Level.Anims[item->animNumber];
|
|
if (anim->numberChanges > 0 && GetChange(item, anim))
|
|
{
|
|
anim = &g_Level.Anims[item->animNumber];
|
|
item->currentAnimState = anim->currentAnimState;
|
|
}
|
|
|
|
if (item->frameNumber > anim->frameEnd)
|
|
{
|
|
if (anim->numberCommands > 0)
|
|
{
|
|
short* cmd = &g_Level.Commands[anim->commandIndex];
|
|
for (int i = anim->numberCommands; i > 0; i--)
|
|
{
|
|
switch (*(cmd++))
|
|
{
|
|
case COMMAND_MOVE_ORIGIN:
|
|
TranslateItem(item, cmd[0], cmd[1], cmd[2]);
|
|
UpdateItemRoom(item, -LARA_HEIGHT / 2);
|
|
cmd += 3;
|
|
break;
|
|
|
|
case COMMAND_JUMP_VELOCITY:
|
|
item->fallspeed = *(cmd++);
|
|
item->speed = *(cmd++);
|
|
item->gravityStatus = true;
|
|
if (info->calcFallSpeed)
|
|
{
|
|
item->fallspeed = info->calcFallSpeed;
|
|
info->calcFallSpeed = 0;
|
|
}
|
|
break;
|
|
|
|
case COMMAND_ATTACK_READY:
|
|
if (info->gunStatus != LG_SPECIAL)
|
|
info->gunStatus = LG_HANDS_FREE;
|
|
break;
|
|
|
|
case COMMAND_SOUND_FX:
|
|
case COMMAND_EFFECT:
|
|
cmd += 2;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
item->animNumber = anim->jumpAnimNum;
|
|
item->frameNumber = anim->jumpFrameNum;
|
|
|
|
anim = &g_Level.Anims[item->animNumber];
|
|
item->currentAnimState = anim->currentAnimState;
|
|
}
|
|
|
|
if (anim->numberCommands > 0)
|
|
{
|
|
short* cmd = &g_Level.Commands[anim->commandIndex];
|
|
int flags;
|
|
int effectID = 0;
|
|
|
|
for (int i = anim->numberCommands; i > 0; i--)
|
|
{
|
|
switch (*(cmd++))
|
|
{
|
|
case COMMAND_MOVE_ORIGIN:
|
|
cmd += 3;
|
|
break;
|
|
|
|
case COMMAND_JUMP_VELOCITY:
|
|
cmd += 2;
|
|
break;
|
|
|
|
case COMMAND_SOUND_FX:
|
|
if (item->frameNumber != *cmd)
|
|
{
|
|
cmd += 2;
|
|
break;
|
|
}
|
|
|
|
flags = cmd[1] & 0xC000;
|
|
if ( flags == (int)SOUND_PLAYCONDITION::LandAndWater ||
|
|
(flags == (int)SOUND_PLAYCONDITION::Land && (info->waterSurfaceDist >= 0 || info->waterSurfaceDist == NO_HEIGHT)) ||
|
|
(flags == (int)SOUND_PLAYCONDITION::Water && info->waterSurfaceDist < 0 && info->waterSurfaceDist != NO_HEIGHT && !TestLaraSwamp(item)))
|
|
{
|
|
SoundEffect(cmd[1] & 0x3FFF, &item->pos, 2);
|
|
}
|
|
|
|
cmd += 2;
|
|
break;
|
|
|
|
case COMMAND_EFFECT:
|
|
if (item->frameNumber != *cmd)
|
|
{
|
|
cmd += 2;
|
|
break;
|
|
}
|
|
|
|
effectID = cmd[1] & 0x3FFF;
|
|
DoFlipEffect(effectID, item);
|
|
|
|
cmd += 2;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
int lateral = anim->Xvelocity;
|
|
if (anim->Xacceleration)
|
|
lateral += anim->Xacceleration * (item->frameNumber - anim->frameBase);
|
|
lateral >>= 16;
|
|
|
|
if (item->gravityStatus)
|
|
{
|
|
if (TestLaraSwamp(item))
|
|
{
|
|
item->speed -= item->speed >> 3;
|
|
if (abs(item->speed) < 8)
|
|
{
|
|
item->speed = 0;
|
|
item->gravityStatus = false;
|
|
}
|
|
if (item->fallspeed > 128)
|
|
item->fallspeed /= 2;
|
|
item->fallspeed -= item->fallspeed / 4;
|
|
if (item->fallspeed < 4)
|
|
item->fallspeed = 4;
|
|
item->pos.yPos += item->fallspeed;
|
|
}
|
|
else
|
|
{
|
|
int velocity = (anim->velocity + anim->acceleration * (item->frameNumber - anim->frameBase - 1));
|
|
item->speed -= velocity >> 16;
|
|
item->speed += (velocity + anim->acceleration) >> 16;
|
|
item->fallspeed += (item->fallspeed >= 128 ? 1 : GRAVITY);
|
|
item->pos.yPos += std::min(LaraCollision.Middle.Floor, (int)item->fallspeed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int velocity;
|
|
|
|
if (info->waterStatus == LW_WADE && TestLaraSwamp(item))
|
|
{
|
|
velocity = (anim->velocity >> 1);
|
|
if (anim->acceleration)
|
|
velocity += (anim->acceleration * (item->frameNumber - anim->frameBase)) >> 2;
|
|
}
|
|
else
|
|
{
|
|
velocity = anim->velocity;
|
|
if (anim->acceleration)
|
|
velocity += anim->acceleration * (item->frameNumber - anim->frameBase);
|
|
}
|
|
|
|
item->speed = velocity >> 16;
|
|
}
|
|
|
|
if (info->ropePtr != -1)
|
|
DelAlignLaraToRope(item);
|
|
|
|
if (!info->isMoving)
|
|
MoveItem(item, info->moveAngle, item->speed, lateral);
|
|
|
|
// Update matrices
|
|
g_Renderer.updateLaraAnimations(true);
|
|
}
|