mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Merge branch 'master' into lua_1.0.2
This commit is contained in:
commit
e16a98a8aa
246 changed files with 16974 additions and 15499 deletions
|
@ -6,36 +6,56 @@ Version 1.0.2
|
|||
- GiveItem, TakeItem, and SetItemCount have been reworked (e.g. SetItemCount with a value of -1 can give infinite ammo/consumables).
|
||||
- Removing Pistols with TakeItem and SetItemCount now works correctly.
|
||||
- Vec3s can now be saved and loaded in LevelVars and GameVars.
|
||||
* Adjust max turn rate of idle state.
|
||||
* Align Lara on slopes when crouching, crawling, and dying.
|
||||
* Better slope alignment for large, flat enemies (i.e. big scorpion and crocodile).
|
||||
* Lock turn rate when automatically aligning Lara to objects.
|
||||
* Don't play Lara alignment animations if the interacted object is too close.
|
||||
* Fix incorrect pole grabbing.
|
||||
* Fix zeroed forward velocity upon landing.
|
||||
* Fix 1-frame turn rate delays.
|
||||
* Fix occasional leave event calls when moving closer to volumes.
|
||||
* Fix incorrect viewport size in windowed mode.
|
||||
* Fix Lara's subway train death so that she no longer slides slowly after the animation has finished.
|
||||
|
||||
|
||||
Version 1.0.1
|
||||
=============
|
||||
|
||||
- Added antialiasing support.
|
||||
- Fix some issues with shimmying between diagonal ledges and walls.
|
||||
- Fix rope transparency.
|
||||
- Fix objects disappearing under certain angles at the edges of the screen.
|
||||
- Fix incorrect polerope and jumpswitch grabbing.
|
||||
- Fix camera behaviour with pushable blocks.
|
||||
- Fix minecart unduck on inclines.
|
||||
- Fix quadbike dismount with jump key and allow to shoot big gun with action key.
|
||||
- Fix static meshes having wrong colors on savegame reload.
|
||||
- Fix rollingball incorrectly killing Lara in water and in jump.
|
||||
- Fix resurfacing on underwater death.
|
||||
- Fix ripples not appearing on water connections higher than room bottom.
|
||||
- Fix several problems with ropes (stumbling, rope length, etc).
|
||||
- Fix several problems with teeth spikes.
|
||||
- Fix crashes when loading image files are missing.
|
||||
- Disable trigger check for puzzle holes.
|
||||
- Clear locusts and other swarm enemies on level reload.
|
||||
- Prevent title music audio from starting in a random place.
|
||||
- Update harpoon speed on room change.
|
||||
- Enable second sky layer rendering.
|
||||
- Preserve inventory and flare on level jumps.
|
||||
- Timer.Create now lets you choose the units to display remaining time.
|
||||
- Fatal script errors now boot you to the title (it will crash if the title itself has these errors).
|
||||
- SetFarView has been removed, and Flow.Level.farView is now uncapped.
|
||||
- DisplayString text will now be cleared when a level is exited or reloaded.
|
||||
- EventSequence.lua has been added and documented.
|
||||
* Added antialiasing support.
|
||||
* Added static mesh scaling support.
|
||||
* Added free rotation for teeth spikes instead of using OCB codes.
|
||||
* Fix some issues with shimmying between diagonal ledges and walls.
|
||||
* Fix rope transparency.
|
||||
* Fix objects disappearing under certain angles at the edges of the screen.
|
||||
* Fix incorrect polerope and jumpswitch grabbing.
|
||||
* Fix camera behaviour with pushable blocks.
|
||||
* Fix minecart unduck on inclines.
|
||||
* Fix quadbike dismount with jump key and allow to shoot big gun with action key.
|
||||
* Fix static meshes having wrong colors on savegame reload.
|
||||
* Fix rollingball incorrectly killing Lara in water and in jump.
|
||||
* Fix resurfacing on underwater death.
|
||||
* Fix water to ladder animation not activating in all cases.
|
||||
* Fix ripples not appearing on water connections higher than room bottom.
|
||||
* Fix several problems with ropes (stumbling, rope length, etc).
|
||||
* Fix several problems with teeth spikes.
|
||||
* Fix falling through twoblock platform on room number change.
|
||||
* Fix falling block breaking too early if placed on a vertical portal.
|
||||
* Fix crashes when loading image files are missing.
|
||||
* Disable trigger check for puzzle holes.
|
||||
* Clear locusts and other swarm enemies on level reload.
|
||||
* Enhance cobra AI and fix targeting.
|
||||
* Fully decompile HAMMER object from TR4.
|
||||
* Prevent title music audio from starting in a random place.
|
||||
* Update harpoon speed on room change.
|
||||
* Enable second sky layer rendering.
|
||||
* Preserve inventory and flare on level jumps.
|
||||
* Timer.Create now lets you choose the units to display remaining time.
|
||||
* Fatal script errors now boot you to the title (it will crash if the title itself has these errors).
|
||||
* SetFarView has been removed, and Flow.Level.farView is now uncapped.
|
||||
* DisplayString text will now be cleared when a level is exited or reloaded.
|
||||
* EventSequence.lua has been added and documented.
|
||||
|
||||
|
||||
Version 1.0
|
||||
===========
|
||||
|
|
|
@ -12,9 +12,14 @@ local InvID = Flow.InvID
|
|||
local RotationAxis = Flow.RotationAxis
|
||||
local ItemAction = Flow.ItemAction
|
||||
|
||||
-- These variables are unused for now.
|
||||
-- Intro image is a splash screen which appears before actual loading screen.
|
||||
-- If you don't want it to appear, just remove this line.
|
||||
|
||||
Flow.SetIntroImagePath("Screens\\main.jpg")
|
||||
|
||||
-- This image should be used for static title screen background (as in TR1-TR3).
|
||||
-- For now it is not implemented.
|
||||
|
||||
Flow.SetTitleScreenImagePath("Screens\\main.jpg")
|
||||
|
||||
|
||||
|
@ -38,7 +43,7 @@ Flow.AddLevel(title)
|
|||
test = Level.new()
|
||||
|
||||
test.nameKey = "level_test"
|
||||
test.scriptFile = "Scripts\\TestLevel.lua"
|
||||
test.scriptFile = "Scripts\\New_Level.lua"
|
||||
test.ambientTrack = "108"
|
||||
test.levelFile = "Data\\TestLevel.ten"
|
||||
test.loadScreenFile = "Screens\\rome.jpg"
|
||||
|
|
30
Scripts/New_Level.lua
Normal file
30
Scripts/New_Level.lua
Normal file
|
@ -0,0 +1,30 @@
|
|||
-- New level script file.
|
||||
-- To include other script files, you can use require("filename") command.
|
||||
|
||||
local Util = require("Util")
|
||||
Util.ShortenTENCalls()
|
||||
|
||||
-- Called when entering a level, either after leveljump, new game or loading game
|
||||
LevelFuncs.OnStart = function() end
|
||||
|
||||
-- Called after loading from a save
|
||||
LevelFuncs.OnLoad = function() end
|
||||
|
||||
-- Called after saving game
|
||||
LevelFuncs.OnSave = function() end
|
||||
|
||||
-- Called on every frame of the game
|
||||
-- dt stands for "delta time", and holds the time in seconds since the last call to OnControlPhase
|
||||
LevelFuncs.OnControlPhase = function(dt) end
|
||||
|
||||
-- Called when level is ended, either after leveljump, quitting to title or loading game
|
||||
LevelFuncs.OnEnd = function() end
|
||||
|
||||
|
||||
-- An example function which prints a string and leaves it on screen for 1 second.
|
||||
-- Argument should be typed in TE trigger manager window's argument text field.
|
||||
|
||||
LevelFuncs.PrintText = function(Triggerer, Argument)
|
||||
local TestText = DisplayString(Argument, 100, 100, Color.new(250,250,250))
|
||||
ShowString(TestText, 1)
|
||||
end
|
|
@ -457,8 +457,6 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
|
|||
|
||||
RumbleLaraHealthCondition(item);
|
||||
|
||||
lara->Control.IsLow = false;
|
||||
|
||||
bool isWater = TestEnvironment(ENV_FLAG_WATER, item);
|
||||
bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item);
|
||||
|
||||
|
@ -785,11 +783,9 @@ void LaraAboveWater(ItemInfo* item, CollisionInfo* coll)
|
|||
if (HandleLaraVehicle(item, coll))
|
||||
return;
|
||||
|
||||
HandleLaraMovementParameters(item, coll);
|
||||
|
||||
// Handle current Lara status.
|
||||
lara_control_routines[item->Animation.ActiveState](item, coll);
|
||||
|
||||
HandleLaraMovementParameters(item, coll);
|
||||
AnimateLara(item);
|
||||
|
||||
if (lara->ExtraAnim == NO_ITEM)
|
||||
|
@ -820,6 +816,8 @@ void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll)
|
|||
{
|
||||
auto* lara = GetLaraInfo(item);
|
||||
|
||||
lara->Control.IsLow = false;
|
||||
|
||||
Camera.targetElevation = -ANGLE(22.0f);
|
||||
|
||||
coll->Setup.Mode = CollisionProbeMode::FreeForward;
|
||||
|
@ -890,6 +888,8 @@ void LaraUnderwater(ItemInfo* item, CollisionInfo* coll)
|
|||
{
|
||||
auto* lara = GetLaraInfo(item);
|
||||
|
||||
lara->Control.IsLow = false;
|
||||
|
||||
coll->Setup.Mode = CollisionProbeMode::Quadrants;
|
||||
coll->Setup.Radius = LARA_RADIUS_UNDERWATER;
|
||||
coll->Setup.Height = LARA_HEIGHT;
|
||||
|
|
|
@ -50,7 +50,7 @@ constexpr int LARA_RADIUS = 100;
|
|||
constexpr int LARA_RADIUS_CRAWL = 200;
|
||||
constexpr int LARA_RADIUS_UNDERWATER = 300;
|
||||
constexpr int LARA_RADIUS_DEATH = 400;
|
||||
constexpr int LARA_VELOCITY = 12; // TODO: Float.
|
||||
constexpr int LARA_ALIGN_VELOCITY = 12; // TODO: Float.
|
||||
|
||||
constexpr auto LARA_FREEFALL_VELOCITY = 131.0f;
|
||||
constexpr auto LARA_DAMAGE_VELOCITY = 141.0f;
|
||||
|
|
|
@ -139,9 +139,12 @@ void lara_as_walk_forward(ItemInfo* item, CollisionInfo* coll)
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement item alignment properly someday. @Sezz 2021.11.01
|
||||
// TODO: Implement item alignment properly someday. -- Sezz 2021.11.01
|
||||
if (lara->Control.IsMoving)
|
||||
{
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TrInput & (IN_LEFT | IN_RIGHT))
|
||||
{
|
||||
|
@ -410,7 +413,7 @@ void lara_as_idle(ItemInfo* item, CollisionInfo* coll)
|
|||
(TrInput & IN_RIGHT &&
|
||||
!(TrInput & IN_RSTEP || (TrInput & IN_WALK && TrInput & IN_RIGHT))))
|
||||
{
|
||||
ModulateLaraTurnRateY(item, LARA_TURN_RATE_ACCEL, 0, LARA_SLOW_TURN_RATE_MAX);
|
||||
ModulateLaraTurnRateY(item, LARA_TURN_RATE_ACCEL, 0, LARA_SLOW_MED_TURN_RATE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1325,13 +1328,13 @@ void lara_col_turn_left_slow(ItemInfo* item, CollisionInfo* coll)
|
|||
void lara_as_death(ItemInfo* item, CollisionInfo* coll)
|
||||
{
|
||||
auto* lara = GetLaraInfo(item);
|
||||
auto* bounds = GetBoundsAccurate(item);
|
||||
|
||||
item->Animation.Velocity.z = 0.0f;
|
||||
lara->Control.CanLook = false;
|
||||
coll->Setup.EnableObjectPush = false;
|
||||
coll->Setup.EnableSpasm = false;
|
||||
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
|
||||
if (BinocularRange)
|
||||
{
|
||||
BinocularRange = 0;
|
||||
|
@ -1340,6 +1343,11 @@ void lara_as_death(ItemInfo* item, CollisionInfo* coll)
|
|||
item->MeshBits = ALL_JOINT_BITS;
|
||||
lara->Inventory.IsBusy = false;
|
||||
}
|
||||
|
||||
if (bounds->Height() <= (LARA_HEIGHT * 0.75f))
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
}
|
||||
|
||||
// State: LS_DEATH (8)
|
||||
|
@ -1421,7 +1429,10 @@ void lara_as_walk_back(ItemInfo* item, CollisionInfo* coll)
|
|||
}
|
||||
|
||||
if (lara->Control.IsMoving)
|
||||
{
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSwamp && lara->Control.WaterStatus == WaterStatus::Wade)
|
||||
{
|
||||
|
@ -1781,7 +1792,10 @@ void lara_as_step_right(ItemInfo* item, CollisionInfo* coll)
|
|||
}
|
||||
|
||||
if (lara->Control.IsMoving)
|
||||
{
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TrInput & IN_WALK) // WALK locks orientation.
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
|
@ -1872,7 +1886,10 @@ void lara_as_step_left(ItemInfo* item, CollisionInfo* coll)
|
|||
}
|
||||
|
||||
if (lara->Control.IsMoving)
|
||||
{
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TrInput & IN_WALK) // WALK locks orientation.
|
||||
ModulateLaraTurnRateY(item, 0, 0, 0);
|
||||
|
|
|
@ -42,6 +42,8 @@ void lara_as_crouch_idle(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
// TODO: Dispatch pickups from within states.
|
||||
if (item->Animation.TargetState == LS_PICKUP)
|
||||
return;
|
||||
|
@ -156,6 +158,8 @@ void lara_as_crouch_roll(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if (TrInput & (IN_LEFT | IN_RIGHT))
|
||||
{
|
||||
ModulateLaraTurnRateY(item, LARA_TURN_RATE_ACCEL, 0, LARA_CROUCH_ROLL_TURN_RATE_MAX);
|
||||
|
@ -227,6 +231,8 @@ void lara_as_crouch_turn_left(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.TargetState = LS_DEATH;
|
||||
|
@ -282,6 +288,8 @@ void lara_as_crouch_turn_right(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.TargetState = LS_DEATH;
|
||||
|
@ -337,6 +345,8 @@ void lara_as_crouch_turn_180(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if ((TrInput & IN_CROUCH || lara->Control.KeepLow) &&
|
||||
lara->Control.WaterStatus != WaterStatus::Wade)
|
||||
{
|
||||
|
@ -375,6 +385,8 @@ void lara_as_crawl_idle(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
// TODO: Dispatch pickups from within states.
|
||||
if (item->Animation.TargetState == LS_PICKUP)
|
||||
return;
|
||||
|
@ -520,6 +532,8 @@ void lara_as_crawl_forward(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.TargetState = LS_DEATH;
|
||||
|
@ -614,6 +628,8 @@ void lara_as_crawl_back(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.TargetState = LS_DEATH;
|
||||
|
@ -699,6 +715,8 @@ void lara_as_crawl_turn_left(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.TargetState = LS_DEATH;
|
||||
|
@ -758,6 +776,8 @@ void lara_as_crawl_turn_right(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.TargetState = LS_DEATH;
|
||||
|
@ -815,6 +835,8 @@ void lara_as_crawl_turn_180(ItemInfo* item, CollisionInfo* coll)
|
|||
coll->Setup.EnableSpasm = false;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
AlignLaraToSurface(item);
|
||||
|
||||
if ((TrInput & IN_CROUCH || lara->Control.KeepLow) &&
|
||||
lara->Control.WaterStatus != WaterStatus::Wade)
|
||||
{
|
||||
|
@ -842,6 +864,8 @@ void lara_col_crawl_to_hang(ItemInfo* item, CollisionInfo* coll)
|
|||
Camera.targetAngle = 0;
|
||||
Camera.targetDistance = SECTOR(1);
|
||||
|
||||
ResetLaraLean(item, 6.0f);
|
||||
|
||||
if (item->Animation.AnimNumber == LA_CRAWL_TO_HANG_END)
|
||||
{
|
||||
lara->Control.MoveAngle = item->Pose.Orientation.y;
|
||||
|
|
|
@ -938,7 +938,7 @@ void FindTargetPoint(ItemInfo* item, GameVector* target)
|
|||
{
|
||||
auto* bounds = (BOUNDING_BOX*)GetBestFrame(item);
|
||||
int x = (int)(bounds->X1 + bounds->X2) / 2;
|
||||
int y = (int) bounds->Y1 + (bounds->Y2 - bounds->Y1) / 3;
|
||||
int y = (int) bounds->Y1 + bounds->Height() / 3;
|
||||
int z = (int)(bounds->Z1 + bounds->Z2) / 2;
|
||||
|
||||
float c = phd_cos(item->Pose.Orientation.y);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#include "framework.h"
|
||||
#include "Game/Lara/lara_helpers.h"
|
||||
|
||||
#include "Flow/ScriptInterfaceFlowHandler.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/control/control.h"
|
||||
#include "Game/control/volume.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/Lara/lara_collide.h"
|
||||
#include "Flow/ScriptInterfaceFlowHandler.h"
|
||||
#include "Game/Lara/lara_fire.h"
|
||||
#include "Game/Lara/lara_tests.h"
|
||||
#include "Renderer/Renderer11.h"
|
||||
|
@ -57,8 +57,11 @@ void HandleLaraMovementParameters(ItemInfo* item, CollisionInfo* coll)
|
|||
lara->Control.RunJumpQueued = false;
|
||||
|
||||
// Reset lean.
|
||||
if (!lara->Control.IsMoving || (lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT))))
|
||||
if ((!lara->Control.IsMoving || (lara->Control.IsMoving && !(TrInput & (IN_LEFT | IN_RIGHT)))) &&
|
||||
(!lara->Control.IsLow && item->Animation.ActiveState != LS_DEATH)) // HACK: Don't interfere with surface alignment in crouch, crawl, and death states.
|
||||
{
|
||||
ResetLaraLean(item, 6.0f);
|
||||
}
|
||||
|
||||
// Reset crawl flex.
|
||||
if (!(TrInput & IN_LOOK) && coll->Setup.Height > LARA_HEIGHT - LARA_HEADROOM && // HACK
|
||||
|
@ -71,6 +74,8 @@ void HandleLaraMovementParameters(ItemInfo* item, CollisionInfo* coll)
|
|||
item->Pose.Orientation.y += lara->Control.TurnRate;
|
||||
if (!(TrInput & (IN_LEFT | IN_RIGHT)))
|
||||
lara->Control.TurnRate = 0;
|
||||
|
||||
lara->Control.IsLow = false;
|
||||
}
|
||||
|
||||
bool HandleLaraVehicle(ItemInfo* item, CollisionInfo* coll)
|
||||
|
@ -332,7 +337,7 @@ short GetLaraSlideDirection(ItemInfo* item, CollisionInfo* coll)
|
|||
// Get either:
|
||||
// a) the surface aspect angle (extended slides), or
|
||||
// b) the derived nearest cardinal direction from it (original slides).
|
||||
headingAngle = GetSurfaceAspectAngle(probe.FloorTilt.x, probe.FloorTilt.y);
|
||||
headingAngle = GetSurfaceAspectAngle(probe.FloorTilt);
|
||||
if (g_GameFlow->HasSlideExtended())
|
||||
return headingAngle;
|
||||
else
|
||||
|
@ -539,8 +544,8 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
|
|||
{
|
||||
auto probe = GetCollision(item);
|
||||
short minSlideAngle = ANGLE(33.75f);
|
||||
short steepness = GetSurfaceSteepnessAngle(probe.FloorTilt.x, probe.FloorTilt.y);
|
||||
short direction = GetSurfaceAspectAngle(probe.FloorTilt.x, probe.FloorTilt.y);
|
||||
short steepness = GetSurfaceSteepnessAngle(probe.FloorTilt);
|
||||
short direction = GetSurfaceAspectAngle(probe.FloorTilt);
|
||||
|
||||
float velocityMultiplier = 1 / (float)ANGLE(33.75f);
|
||||
int slideVelocity = std::min<int>(minVelocity + 10 * (steepness * velocityMultiplier), LARA_TERMINAL_VELOCITY);
|
||||
|
@ -555,7 +560,25 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
|
|||
//lara->ExtraVelocity.x += minVelocity;
|
||||
}
|
||||
|
||||
void SetLaraJumpDirection(ItemInfo* item, CollisionInfo* coll)
|
||||
void AlignLaraToSurface(ItemInfo* item, float alpha)
|
||||
{
|
||||
auto floorTilt = GetCollision(item).FloorTilt;
|
||||
short aspectAngle = GetSurfaceAspectAngle(floorTilt);
|
||||
short steepnessAngle = std::min(GetSurfaceSteepnessAngle(floorTilt), ANGLE(70.0f));
|
||||
|
||||
short deltaAngle = GetShortestAngularDistance(item->Pose.Orientation.y, aspectAngle);
|
||||
float sinDeltaAngle = phd_sin(deltaAngle);
|
||||
float cosDeltaAngle = phd_cos(deltaAngle);
|
||||
|
||||
auto extraRot = Vector3Shrt(
|
||||
-steepnessAngle * cosDeltaAngle,
|
||||
0,
|
||||
steepnessAngle * sinDeltaAngle
|
||||
) - Vector3Shrt(item->Pose.Orientation.x, 0, item->Pose.Orientation.z);
|
||||
item->Pose.Orientation += extraRot * alpha;
|
||||
}
|
||||
|
||||
void SetLaraJumpDirection(ItemInfo* item, CollisionInfo* coll)
|
||||
{
|
||||
auto* lara = GetLaraInfo(item);
|
||||
|
||||
|
@ -634,10 +657,8 @@ void SetLaraVault(ItemInfo* item, CollisionInfo* coll, VaultTestResult vaultResu
|
|||
|
||||
void SetLaraLand(ItemInfo* item, CollisionInfo* coll)
|
||||
{
|
||||
//item->IsAirborne = false; // TODO: Removing this avoids an unusual landing bug Core had worked around in an obscure way. I hope to find a proper solution. @Sezz 2022.02.18
|
||||
item->Animation.Velocity.z = 0;
|
||||
item->Animation.Velocity.y = 0;
|
||||
|
||||
//item->IsAirborne = false; // TODO: Removing this avoids an unusual landing bug. I hope to find a proper solution later. -- Sezz 2022.02.18
|
||||
item->Animation.Velocity.y = 0.0f;
|
||||
LaraSnapToHeight(item, coll);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ void UpdateLaraSubsuitAngles(ItemInfo* item);
|
|||
void ModulateLaraLean(ItemInfo* item, CollisionInfo* coll, short baseRate, short maxAngle);
|
||||
void ModulateLaraCrawlFlex(ItemInfo* item, short baseRate, short maxAngle);
|
||||
void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll);
|
||||
void AlignLaraToSurface(ItemInfo* item, float alpha = 0.15f);
|
||||
|
||||
void SetLaraJumpDirection(ItemInfo* item, CollisionInfo* coll);
|
||||
void SetLaraRunJumpQueue(ItemInfo* item, CollisionInfo* coll);
|
||||
|
|
|
@ -780,7 +780,7 @@ void lara_col_swan_dive(ItemInfo* item, CollisionInfo* coll)
|
|||
auto* lara = GetLaraInfo(item);
|
||||
|
||||
auto* bounds = GetBoundsAccurate(item);
|
||||
int realHeight = g_GameFlow->HasCrawlspaceSwandive() ? ((bounds->Y2 - bounds->Y1) * 0.7f) : LARA_HEIGHT;
|
||||
int realHeight = g_GameFlow->HasCrawlspaceSwandive() ? (bounds->Height() * 0.7f) : LARA_HEIGHT;
|
||||
|
||||
lara->Control.MoveAngle = item->Pose.Orientation.y;
|
||||
coll->Setup.Height = std::max(LARA_HEIGHT_CRAWL, realHeight);
|
||||
|
|
|
@ -495,10 +495,10 @@ bool TestLaraHangOnClimbableWall(ItemInfo* item, CollisionInfo* coll)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (LaraTestClimbPos(item, LARA_RADIUS, LARA_RADIUS, bounds->Y1, bounds->Y2 - bounds->Y1, &shift) &&
|
||||
LaraTestClimbPos(item, LARA_RADIUS, -LARA_RADIUS, bounds->Y1, bounds->Y2 - bounds->Y1, &shift))
|
||||
if (LaraTestClimbPos(item, LARA_RADIUS, LARA_RADIUS, bounds->Y1, bounds->Height(), &shift) &&
|
||||
LaraTestClimbPos(item, LARA_RADIUS, -LARA_RADIUS, bounds->Y1, bounds->Height(), &shift))
|
||||
{
|
||||
result = LaraTestClimbPos(item, LARA_RADIUS, 0, bounds->Y1, bounds->Y2 - bounds->Y1, &shift);
|
||||
result = LaraTestClimbPos(item, LARA_RADIUS, 0, bounds->Y1, bounds->Height(), &shift);
|
||||
if (result)
|
||||
{
|
||||
if (result != 1)
|
||||
|
@ -1004,9 +1004,7 @@ bool TestLaraLadderClimbOut(ItemInfo* item, CollisionInfo* coll) // NEW function
|
|||
{
|
||||
auto* lara = GetLaraInfo(item);
|
||||
|
||||
if (!(TrInput & IN_ACTION) ||
|
||||
!lara->Control.CanClimbLadder ||
|
||||
coll->CollisionType != CT_FRONT)
|
||||
if (!(TrInput & IN_ACTION) || !lara->Control.CanClimbLadder || coll->CollisionType != CT_FRONT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1017,6 +1015,9 @@ bool TestLaraLadderClimbOut(ItemInfo* item, CollisionInfo* coll) // NEW function
|
|||
return false;
|
||||
}
|
||||
|
||||
// HACK: Reduce probe radius, because free forward probe mode makes ladder tests to fail in some cases.
|
||||
coll->Setup.Radius *= 0.8f;
|
||||
|
||||
if (!TestLaraClimbIdle(item, coll))
|
||||
return false;
|
||||
|
||||
|
@ -1112,22 +1113,22 @@ void GetTightropeFallOff(ItemInfo* item, int regularity)
|
|||
#endif
|
||||
|
||||
// TODO: Organise all of this properly. -- Sezz 2022.07.28
|
||||
bool CheckLaraState(LaraState state, std::vector<LaraState> stateList)
|
||||
bool CheckLaraState(LaraState referenceState, const std::vector<LaraState>& stateList)
|
||||
{
|
||||
for (auto listedState : stateList)
|
||||
for (auto& state : stateList)
|
||||
{
|
||||
if (state == listedState)
|
||||
if (state == referenceState)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CheckLaraWeaponType(LaraWeaponType weaponType, std::vector<LaraWeaponType> weaponTypeList)
|
||||
bool CheckLaraWeaponType(LaraWeaponType referenceWeaponType, const std::vector<LaraWeaponType>& weaponTypeList)
|
||||
{
|
||||
for (auto listedWeaponType : weaponTypeList)
|
||||
for (auto& weaponType : weaponTypeList)
|
||||
{
|
||||
if (weaponType == listedWeaponType)
|
||||
if (weaponType == referenceWeaponType)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1295,8 +1296,7 @@ bool TestLaraLand(ItemInfo* item, CollisionInfo* coll)
|
|||
{
|
||||
int heightFromFloor = GetCollision(item).Position.Floor - item->Pose.Position.y;
|
||||
|
||||
if (item->Animation.IsAirborne &&
|
||||
item->Animation.Velocity.y >= 0 &&
|
||||
if (item->Animation.IsAirborne && item->Animation.Velocity.y >= 0 &&
|
||||
(heightFromFloor <= item->Animation.Velocity.y ||
|
||||
TestEnvironment(ENV_FLAG_SWAMP, item)))
|
||||
{
|
||||
|
@ -2484,7 +2484,7 @@ bool TestLaraSlideJump(ItemInfo* item, CollisionInfo* coll)
|
|||
auto probe = GetCollision(item);
|
||||
|
||||
short direction = GetLaraSlideDirection(item, coll);
|
||||
short steepness = GetSurfaceSteepnessAngle(probe.FloorTilt.x, probe.FloorTilt.y);
|
||||
short steepness = GetSurfaceSteepnessAngle(probe.FloorTilt);
|
||||
return (abs((short)(coll->Setup.ForwardAngle - direction)) <= abs(steepness));
|
||||
}
|
||||
|
||||
|
@ -2519,25 +2519,33 @@ bool TestLaraTightropeDismount(ItemInfo* item, CollisionInfo* coll)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool up, float offset)
|
||||
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, float offset)
|
||||
{
|
||||
static constexpr auto poleProbeCollRadius = 16.0f;
|
||||
|
||||
bool atLeastOnePoleCollided = false;
|
||||
|
||||
if (GetCollidedObjects(item, SECTOR(1), true, CollidedItems, nullptr, 0) && CollidedItems[0])
|
||||
if (GetCollidedObjects(item, SECTOR(1), true, CollidedItems, nullptr, false) &&
|
||||
CollidedItems[0] != nullptr)
|
||||
{
|
||||
auto laraBox = TO_DX_BBOX(item->Pose, GetBoundsAccurate(item));
|
||||
|
||||
// HACK: because Core implemented upward pole movement as SetPosition command, we can't precisely
|
||||
// HACK: Because Core implemented upward pole movement as a SetPosition command, we can't precisely
|
||||
// check her position. So we add a fixed height offset.
|
||||
|
||||
auto sphere = BoundingSphere(laraBox.Center + Vector3(0, (laraBox.Extents.y + poleProbeCollRadius + offset) * (up ? -1 : 1), 0), poleProbeCollRadius);
|
||||
// Offset a sphere when jumping toward pole.
|
||||
auto sphereOffset2D = Vector3::Zero;
|
||||
sphereOffset2D = TranslateVector(sphereOffset2D, item->Pose.Orientation.y, coll->Setup.Radius + item->Animation.Velocity.z);
|
||||
|
||||
auto spherePos = laraBox.Center + Vector3(0.0f, (laraBox.Extents.y + poleProbeCollRadius + offset) * (goingUp ? -1 : 1), 0.0f);
|
||||
|
||||
auto sphere = BoundingSphere(spherePos, poleProbeCollRadius);
|
||||
auto offsetSphere = BoundingSphere(spherePos + sphereOffset2D, poleProbeCollRadius);
|
||||
|
||||
//g_Renderer.AddDebugSphere(sphere.Center, 16.0f, Vector4(1, 0, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
|
||||
int i = 0;
|
||||
while (CollidedItems[i] != NULL)
|
||||
while (CollidedItems[i] != nullptr)
|
||||
{
|
||||
auto*& object = CollidedItems[i];
|
||||
i++;
|
||||
|
@ -2546,11 +2554,11 @@ bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool up, float o
|
|||
continue;
|
||||
|
||||
auto poleBox = TO_DX_BBOX(object->Pose, GetBoundsAccurate(object));
|
||||
poleBox.Extents = poleBox.Extents + Vector3(coll->Setup.Radius, 0, coll->Setup.Radius);
|
||||
poleBox.Extents = poleBox.Extents + Vector3(coll->Setup.Radius, 0.0f, coll->Setup.Radius);
|
||||
|
||||
//g_Renderer.AddDebugBox(poleBox, Vector4(0, 0, 1, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
|
||||
if (poleBox.Intersects(sphere))
|
||||
if (poleBox.Intersects(sphere) || poleBox.Intersects(offsetSphere))
|
||||
{
|
||||
atLeastOnePoleCollided = true;
|
||||
break;
|
||||
|
|
|
@ -46,7 +46,8 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll);
|
|||
void GetTightropeFallOff(ItemInfo* item, int regularity);
|
||||
#endif
|
||||
|
||||
bool CheckLaraState(LaraState state, std::vector<LaraState> stateList);
|
||||
bool CheckLaraState(LaraState referenceState, const std::vector<LaraState>& stateList);
|
||||
bool CheckLaraWeaponType(LaraWeaponType referenceWeaponType, const std::vector<LaraWeaponType>& weaponTypeList);
|
||||
bool IsStandingWeapon(ItemInfo* item, LaraWeaponType weaponType);
|
||||
bool IsVaultState(LaraState state);
|
||||
bool IsJumpState(LaraState state);
|
||||
|
@ -123,6 +124,6 @@ bool TestLaraCrawlspaceDive(ItemInfo* item, CollisionInfo* coll);
|
|||
|
||||
bool TestLaraTightropeDismount(ItemInfo* item, CollisionInfo* coll);
|
||||
|
||||
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool up, float offset = 0.0f);
|
||||
bool TestLaraPoleCollision(ItemInfo* item, CollisionInfo* coll, bool goingUp, float offset = 0.0f);
|
||||
bool TestLaraPoleUp(ItemInfo* item, CollisionInfo* coll);
|
||||
bool TestLaraPoleDown(ItemInfo* item, CollisionInfo* coll);
|
||||
|
|
|
@ -412,17 +412,16 @@ void TranslateItem(ItemInfo* item, Vector3 direction, float distance)
|
|||
|
||||
void SetAnimation(ItemInfo* item, int animIndex, int frameToStart)
|
||||
{
|
||||
int index = Objects[item->ObjectNumber].animIndex + animIndex;
|
||||
if (item->Animation.AnimNumber == animIndex)
|
||||
return;
|
||||
|
||||
int index = Objects[item->ObjectNumber].animIndex + animIndex;
|
||||
if (index < 0 || index >= g_Level.Anims.size())
|
||||
{
|
||||
TENLog(std::string("Attempted to set nonexistent animation ") + std::to_string(animIndex) + std::string(" for object ") + std::to_string(item->ObjectNumber), LogLevel::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->Animation.AnimNumber == animIndex)
|
||||
return;
|
||||
|
||||
item->Animation.AnimNumber = index;
|
||||
item->Animation.FrameNumber = g_Level.Anims[index].frameBase + frameToStart;
|
||||
item->Animation.ActiveState = g_Level.Anims[index].ActiveState;
|
||||
|
|
|
@ -1461,7 +1461,7 @@ void CalculateCamera()
|
|||
auto* bounds = GetBoundsAccurate(item);
|
||||
|
||||
int x;
|
||||
int y = ((bounds->Y1 + bounds->Y2) / 2) + item->Pose.Position.y - CLICK(1);
|
||||
int y = item->Pose.Position.y + bounds->Y2 + (3 * (bounds->Y1 - bounds->Y2) / 4);
|
||||
int z;
|
||||
|
||||
if (Camera.item)
|
||||
|
@ -1854,10 +1854,10 @@ static bool CheckStaticCollideCamera(MESH_INFO* mesh)
|
|||
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
|
||||
return false;
|
||||
|
||||
auto stat = &StaticObjects[mesh->staticNumber];
|
||||
auto extents = Vector3(abs(stat->collisionBox.X1 - stat->collisionBox.X2),
|
||||
abs(stat->collisionBox.Y1 - stat->collisionBox.Y2),
|
||||
abs(stat->collisionBox.Z1 - stat->collisionBox.Z2));
|
||||
auto bounds = GetBoundsAccurate(mesh, false);
|
||||
auto extents = Vector3(abs(bounds->X1 - bounds->X2),
|
||||
abs(bounds->Y1 - bounds->Y2),
|
||||
abs(bounds->Z1 - bounds->Z2));
|
||||
|
||||
// Check extents, if any 2 bounds are smaller than threshold, discard.
|
||||
if ((abs(extents.x) < COLL_DISCARD_THRESHOLD && abs(extents.y) < COLL_DISCARD_THRESHOLD) ||
|
||||
|
@ -1929,9 +1929,7 @@ void ItemsCollideCamera()
|
|||
for (int i = 0; i < staticList.size(); i++)
|
||||
{
|
||||
auto mesh = staticList[i];
|
||||
auto stat = &StaticObjects[mesh->staticNumber];
|
||||
|
||||
if (!mesh || !stat)
|
||||
if (!mesh)
|
||||
return;
|
||||
|
||||
auto dx = abs(LaraItem->Pose.Position.x - mesh->pos.Position.x);
|
||||
|
@ -1941,7 +1939,7 @@ void ItemsCollideCamera()
|
|||
if (dx > COLL_CANCEL_THRESHOLD || dz > COLL_CANCEL_THRESHOLD || dy > COLL_CANCEL_THRESHOLD)
|
||||
continue;
|
||||
|
||||
auto bounds = &stat->collisionBox;
|
||||
auto bounds = GetBoundsAccurate(mesh, false);
|
||||
if (TestBoundsCollideCamera(bounds, &mesh->pos, CAMERA_RADIUS))
|
||||
ItemPushCamera(bounds, &mesh->pos, rad);
|
||||
|
||||
|
|
|
@ -3,22 +3,23 @@
|
|||
|
||||
#include "Game/animation.h"
|
||||
#include "Game/control/los.h"
|
||||
#include "Game/collision/sphere.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/collision/sphere.h"
|
||||
#include "Game/effects/debris.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/effects/simple_particle.h"
|
||||
#include "Game/effects/tomb4fx.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/Lara/lara_helpers.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/effects/debris.h"
|
||||
#include "Game/effects/tomb4fx.h"
|
||||
#include "Game/effects/simple_particle.h"
|
||||
#include "Game/pickup/pickup.h"
|
||||
#include "Game/room.h"
|
||||
#include "Renderer/Renderer11.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/trmath.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "ScriptInterfaceGame.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using namespace TEN::Renderer;
|
||||
|
@ -27,6 +28,8 @@ BOUNDING_BOX GlobalCollisionBounds;
|
|||
ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS];
|
||||
MESH_INFO* CollidedMeshes[MAX_COLLIDED_OBJECTS];
|
||||
|
||||
constexpr auto ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD = 6;
|
||||
|
||||
void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
@ -38,11 +41,11 @@ void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionIn
|
|||
int collidedBits = TestCollision(item, laraItem);
|
||||
if (collidedBits)
|
||||
{
|
||||
short oldYOrient = item->Pose.Orientation.y;
|
||||
short prevYOrient = item->Pose.Orientation.y;
|
||||
|
||||
item->Pose.Orientation.y = 0;
|
||||
GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
|
||||
item->Pose.Orientation.y = oldYOrient;
|
||||
item->Pose.Orientation.y = prevYOrient;
|
||||
|
||||
int deadlyBits = *((int*)&item->ItemFlags[0]);
|
||||
auto* sphere = &CreatureSpheres[0];
|
||||
|
@ -112,26 +115,27 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
|
|||
for (int j = 0; j < room->mesh.size(); j++)
|
||||
{
|
||||
auto* mesh = &room->mesh[j];
|
||||
auto* staticMesh = &StaticObjects[mesh->staticNumber];
|
||||
auto* bbox = GetBoundsAccurate(mesh, false);
|
||||
|
||||
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (collidingItem->Pose.Position.y + radius + CLICK(0.5f) < mesh->pos.Position.y + staticMesh->collisionBox.Y1)
|
||||
if ((collidingItem->Pose.Position.y + radius + CLICK(0.5f)) < (mesh->pos.Position.y + bbox->Y1))
|
||||
continue;
|
||||
|
||||
if (collidingItem->Pose.Position.y > mesh->pos.Position.y + staticMesh->collisionBox.Y2)
|
||||
if (collidingItem->Pose.Position.y > (mesh->pos.Position.y + bbox->Y2))
|
||||
continue;
|
||||
|
||||
float s = phd_sin(mesh->pos.Orientation.y);
|
||||
float c = phd_cos(mesh->pos.Orientation.y);
|
||||
float rx = (collidingItem->Pose.Position.x - mesh->pos.Position.x) * c - s * (collidingItem->Pose.Position.z - mesh->pos.Position.z);
|
||||
float rz = (collidingItem->Pose.Position.z - mesh->pos.Position.z) * c + s * (collidingItem->Pose.Position.x - mesh->pos.Position.x);
|
||||
float sinY = phd_sin(mesh->pos.Orientation.y);
|
||||
float cosY = phd_cos(mesh->pos.Orientation.y);
|
||||
|
||||
if (radius + rx + CLICK(0.5f) < staticMesh->collisionBox.X1 || rx - radius - CLICK(0.5f) > staticMesh->collisionBox.X2)
|
||||
float rx = ((collidingItem->Pose.Position.x - mesh->pos.Position.x) * cosY) - ((collidingItem->Pose.Position.z - mesh->pos.Position.z) * sinY);
|
||||
float rz = ((collidingItem->Pose.Position.z - mesh->pos.Position.z) * cosY) + ((collidingItem->Pose.Position.x - mesh->pos.Position.x) * sinY);
|
||||
|
||||
if ((radius + rx + CLICK(0.5f) < bbox->X1) || (rx - radius - CLICK(0.5f) > bbox->X2))
|
||||
continue;
|
||||
|
||||
if (radius + rz + CLICK(0.5f) < staticMesh->collisionBox.Z1 || rz - radius - CLICK(0.5f) > staticMesh->collisionBox.Z2)
|
||||
if ((radius + rz + CLICK(0.5f) < bbox->Z1) || (rz - radius - CLICK(0.5f) > bbox->Z2))
|
||||
continue;
|
||||
|
||||
collidedMeshes[numMeshes++] = mesh;
|
||||
|
@ -159,8 +163,8 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
|
|||
item->ObjectNumber == ID_LARA && ignoreLara ||
|
||||
item->Flags & 0x8000 ||
|
||||
item->MeshBits == NO_JOINT_BITS ||
|
||||
(Objects[item->ObjectNumber].drawRoutine == NULL && item->ObjectNumber != ID_LARA) ||
|
||||
(Objects[item->ObjectNumber].collision == NULL && item->ObjectNumber != ID_LARA) ||
|
||||
(Objects[item->ObjectNumber].drawRoutine == nullptr && item->ObjectNumber != ID_LARA) ||
|
||||
(Objects[item->ObjectNumber].collision == nullptr && item->ObjectNumber != ID_LARA) ||
|
||||
onlyVisible && item->Status == ITEM_INVISIBLE ||
|
||||
item->ObjectNumber == ID_BURNING_FLOOR)
|
||||
{
|
||||
|
@ -190,14 +194,14 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
|
|||
if (dx >= -SECTOR(2) && dx <= SECTOR(2) &&
|
||||
dy >= -SECTOR(2) && dy <= SECTOR(2) &&
|
||||
dz >= -SECTOR(2) && dz <= SECTOR(2) &&
|
||||
collidingItem->Pose.Position.y + radius + CLICK(0.5f) >= item->Pose.Position.y + framePtr->boundingBox.Y1 &&
|
||||
collidingItem->Pose.Position.y - radius - CLICK(0.5f) <= item->Pose.Position.y + framePtr->boundingBox.Y2)
|
||||
(collidingItem->Pose.Position.y + radius + CLICK(0.5f)) >= (item->Pose.Position.y + framePtr->boundingBox.Y1) &&
|
||||
(collidingItem->Pose.Position.y - radius - CLICK(0.5f)) <= (item->Pose.Position.y + framePtr->boundingBox.Y2))
|
||||
{
|
||||
float s = phd_sin(item->Pose.Orientation.y);
|
||||
float c = phd_cos(item->Pose.Orientation.y);
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
float cosY = phd_cos(item->Pose.Orientation.y);
|
||||
|
||||
int rx = dx * c - s * dz;
|
||||
int rz = dz * c + s * dx;
|
||||
int rx = (dx * cosY) - (dz * sinY);
|
||||
int rz = (dz * cosY) + (dx * sinY);
|
||||
|
||||
if (item->ObjectNumber == ID_TURN_SWITCH)
|
||||
{
|
||||
|
@ -207,36 +211,39 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
|
|||
framePtr->boundingBox.Z1 = CLICK(1);
|
||||
}
|
||||
|
||||
if (radius + rx + CLICK(0.5f) >= framePtr->boundingBox.X1 && rx - radius - CLICK(0.5f) <= framePtr->boundingBox.X2)
|
||||
if ((radius + rx + CLICK(0.5f)) >= framePtr->boundingBox.X1 &&
|
||||
(rx - radius - CLICK(0.5f)) <= framePtr->boundingBox.X2)
|
||||
{
|
||||
if (radius + rz + CLICK(0.5f) >= framePtr->boundingBox.Z1 && rz - radius - CLICK(0.5f) <= framePtr->boundingBox.Z2)
|
||||
if ((radius + rz + CLICK(0.5f)) >= framePtr->boundingBox.Z1 &&
|
||||
(rz - radius - CLICK(0.5f)) <= framePtr->boundingBox.Z2)
|
||||
{
|
||||
collidedItems[numItems++] = item;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (collidingItem->Pose.Position.y + radius + 128 >= item->Pose.Position.y + framePtr->boundingBox.Y1 &&
|
||||
collidingItem->Pose.Position.y - radius - 128 <= item->Pose.Position.y + framePtr->boundingBox.Y2)
|
||||
if ((collidingItem->Pose.Position.y + radius + CLICK(0.5f)) >= (item->Pose.Position.y + framePtr->boundingBox.Y1) &&
|
||||
(collidingItem->Pose.Position.y - radius - CLICK(0.5f)) <= (item->Pose.Position.y + framePtr->boundingBox.Y2))
|
||||
{
|
||||
float s = phd_sin(item->Pose.Orientation.y);
|
||||
float c = phd_cos(item->Pose.Orientation.y);
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
float cosY = phd_cos(item->Pose.Orientation.y);
|
||||
|
||||
int rx = dx * c - s * dz;
|
||||
int rz = dz * c + s * dx;
|
||||
int rx = (dx * cosY) - (dz * sinY);
|
||||
int rz = (dz * cosY) + (dx * sinY);
|
||||
|
||||
if (item->ObjectNumber == ID_TURN_SWITCH)
|
||||
{
|
||||
framePtr->boundingBox.X1 = -256;
|
||||
framePtr->boundingBox.X2 = 256;
|
||||
framePtr->boundingBox.Z1 = -256;
|
||||
framePtr->boundingBox.Z1 = 256;
|
||||
framePtr->boundingBox.X1 = -CLICK(1);
|
||||
framePtr->boundingBox.X2 = CLICK(1);
|
||||
framePtr->boundingBox.Z1 = -CLICK(1);
|
||||
framePtr->boundingBox.Z1 = CLICK(1);
|
||||
}
|
||||
|
||||
if (radius + rx + 128 >= framePtr->boundingBox.X1 && rx - radius - 128 <= framePtr->boundingBox.X2)
|
||||
if ((radius + rx + CLICK(0.5f)) >= framePtr->boundingBox.X1 &&
|
||||
(rx - radius - CLICK(0.5f)) <= framePtr->boundingBox.X2)
|
||||
{
|
||||
if (radius + rz + 128 >= framePtr->boundingBox.Z1 && rz - radius - 128 <= framePtr->boundingBox.Z2)
|
||||
if ((radius + rz + CLICK(0.5f)) >= framePtr->boundingBox.Z1 &&
|
||||
(rz - radius - CLICK(0.5f)) <= framePtr->boundingBox.Z2)
|
||||
{
|
||||
collidedItems[numItems++] = item;
|
||||
|
||||
|
@ -249,10 +256,11 @@ bool GetCollidedObjects(ItemInfo* collidingItem, int radius, bool onlyVisible, I
|
|||
}
|
||||
|
||||
itemNumber = item->NextItem;
|
||||
} while (itemNumber != NO_ITEM);
|
||||
}
|
||||
while (itemNumber != NO_ITEM);
|
||||
}
|
||||
|
||||
collidedItems[numItems] = NULL;
|
||||
collidedItems[numItems] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,20 +277,22 @@ bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, Collision
|
|||
if (item->Pose.Position.y + GlobalCollisionBounds.Y1 >= framePtr->boundingBox.Y2)
|
||||
return false;
|
||||
|
||||
float s = phd_sin(item->Pose.Orientation.y);
|
||||
float c = phd_cos(item->Pose.Orientation.y);
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
float cosY = phd_cos(item->Pose.Orientation.y);
|
||||
|
||||
int dx = laraItem->Pose.Position.x - item->Pose.Position.x;
|
||||
int dz = laraItem->Pose.Position.z - item->Pose.Position.z;
|
||||
|
||||
int x = c * dx - s * dz;
|
||||
int z = c * dz + s * dx;
|
||||
int x = (dx * cosY) - (dz * sinY);
|
||||
int z = (dz * cosY) + (dx * sinY);
|
||||
|
||||
if (x < GlobalCollisionBounds.X1 - coll->Setup.Radius ||
|
||||
x > GlobalCollisionBounds.X2 + coll->Setup.Radius ||
|
||||
z < GlobalCollisionBounds.Z1 - coll->Setup.Radius ||
|
||||
z > GlobalCollisionBounds.Z2 + coll->Setup.Radius)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -294,15 +304,15 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
|
|||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto s = (i != 1) ? phd_sin(coll->Setup.ForwardAngle + ANGLE((i * 90) - 90)) : 0;
|
||||
auto c = (i != 1) ? phd_cos(coll->Setup.ForwardAngle + ANGLE((i * 90) - 90)) : 0;
|
||||
auto sinForwardAngle = (i != 1) ? (phd_sin(coll->Setup.ForwardAngle + ANGLE((i * 90.0f) - 90.0f))) : 0;
|
||||
auto cosForwardAngle = (i != 1) ? (phd_cos(coll->Setup.ForwardAngle + ANGLE((i * 90.0f) - 90.0f))) : 0;
|
||||
|
||||
auto x = item->Pose.Position.x + (s * (coll->Setup.Radius));
|
||||
auto y = item->Pose.Position.y - height - STEP_SIZE;
|
||||
auto z = item->Pose.Position.z + (c * (coll->Setup.Radius));
|
||||
auto x = item->Pose.Position.x + (sinForwardAngle * (coll->Setup.Radius));
|
||||
auto y = item->Pose.Position.y - height - CLICK(1);
|
||||
auto z = item->Pose.Position.z + (cosForwardAngle * (coll->Setup.Radius));
|
||||
|
||||
auto origin = Vector3(x, y, z);
|
||||
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(coll->Setup.ForwardAngle), 0, 0);
|
||||
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(coll->Setup.ForwardAngle), 0.0f, 0.0f);
|
||||
auto direction = (Matrix::CreateTranslation(Vector3::UnitZ) * mxR).Translation();
|
||||
|
||||
// g_Renderer.AddDebugSphere(origin, 16, Vector4::One, RENDERER_DEBUG_PAGE::DIMENSION_STATS);
|
||||
|
@ -312,10 +322,10 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
|
|||
short itemNumber = g_Level.Rooms[i].itemNumber;
|
||||
while (itemNumber != NO_ITEM)
|
||||
{
|
||||
auto item2 = &g_Level.Items[itemNumber];
|
||||
auto obj = &Objects[item2->ObjectNumber];
|
||||
auto* item2 = &g_Level.Items[itemNumber];
|
||||
auto* object = &Objects[item2->ObjectNumber];
|
||||
|
||||
if (obj->isPickup || obj->collision == nullptr || !item2->Collidable || item2->Status == ITEM_INVISIBLE)
|
||||
if (object->isPickup || object->collision == nullptr || !item2->Collidable || item2->Status == ITEM_INVISIBLE)
|
||||
{
|
||||
itemNumber = item2->NextItem;
|
||||
continue;
|
||||
|
@ -338,17 +348,17 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
|
|||
|
||||
for (int j = 0; j < g_Level.Rooms[i].mesh.size(); j++)
|
||||
{
|
||||
auto mesh = &g_Level.Rooms[i].mesh[j];
|
||||
auto* mesh = &g_Level.Rooms[i].mesh[j];
|
||||
|
||||
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (phd_Distance(&item->Pose, &mesh->pos) < COLLISION_CHECK_DISTANCE)
|
||||
{
|
||||
auto box = TO_DX_BBOX(mesh->pos, &StaticObjects[mesh->staticNumber].collisionBox);
|
||||
float dist;
|
||||
auto box = TO_DX_BBOX(mesh->pos, GetBoundsAccurate(mesh, false));
|
||||
float distance;
|
||||
|
||||
if (box.Intersects(origin, direction, dist) && dist < coll->Setup.Radius * 2)
|
||||
if (box.Intersects(origin, direction, distance) && distance < coll->Setup.Radius * 2)
|
||||
{
|
||||
coll->HitStatic = true;
|
||||
return;
|
||||
|
@ -361,29 +371,29 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
|
|||
|
||||
bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ItemInfo* item, ItemInfo* laraItem)
|
||||
{
|
||||
auto rotRel = laraItem->Pose.Orientation - item->Pose.Orientation;
|
||||
auto deltaOrient = laraItem->Pose.Orientation - item->Pose.Orientation;
|
||||
|
||||
if (rotRel.x < bounds->rotX1)
|
||||
if (deltaOrient.x < bounds->rotX1)
|
||||
return false;
|
||||
|
||||
if (rotRel.x > bounds->rotX2)
|
||||
if (deltaOrient.x > bounds->rotX2)
|
||||
return false;
|
||||
|
||||
if (rotRel.y < bounds->rotY1)
|
||||
if (deltaOrient.y < bounds->rotY1)
|
||||
return false;
|
||||
|
||||
if (rotRel.y > bounds->rotY2)
|
||||
if (deltaOrient.y > bounds->rotY2)
|
||||
return false;
|
||||
|
||||
if (rotRel.z < bounds->rotZ1)
|
||||
if (deltaOrient.z < bounds->rotZ1)
|
||||
return false;
|
||||
|
||||
if (rotRel.z > bounds->rotZ2)
|
||||
if (deltaOrient.z > bounds->rotZ2)
|
||||
return false;
|
||||
|
||||
auto pos = (laraItem->Pose.Position - item->Pose.Position).ToVector3();
|
||||
|
||||
Matrix matrix = Matrix::CreateFromYawPitchRoll(
|
||||
auto matrix = Matrix::CreateFromYawPitchRoll(
|
||||
TO_RAD(item->Pose.Orientation.y),
|
||||
TO_RAD(item->Pose.Orientation.x),
|
||||
TO_RAD(item->Pose.Orientation.z)
|
||||
|
@ -401,34 +411,35 @@ bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ItemInfo* item, ItemInfo*
|
|||
if (pos.x < bounds->boundingBox.X1 || pos.x > bounds->boundingBox.X2 ||
|
||||
pos.y < bounds->boundingBox.Y1 || pos.y > bounds->boundingBox.Y2 ||
|
||||
pos.z < bounds->boundingBox.Z1 || pos.z > bounds->boundingBox.Z2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AlignLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
|
||||
{
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
|
||||
laraItem->Pose.Orientation = item->Pose.Orientation;
|
||||
|
||||
Matrix matrix = Matrix::CreateFromYawPitchRoll(
|
||||
auto matrix = Matrix::CreateFromYawPitchRoll(
|
||||
TO_RAD(item->Pose.Orientation.y),
|
||||
TO_RAD(item->Pose.Orientation.x),
|
||||
TO_RAD(item->Pose.Orientation.z)
|
||||
);
|
||||
|
||||
Vector3 pos = Vector3::Transform(Vector3(vec->x, vec->y, vec->z), matrix);
|
||||
Vector3 newPos = item->Pose.Position.ToVector3() + pos;
|
||||
auto pos = Vector3::Transform(vec->ToVector3(), matrix);
|
||||
auto newPos = item->Pose.Position.ToVector3() + pos;
|
||||
|
||||
int height = GetCollision(newPos.x, newPos.y, newPos.z, laraItem->RoomNumber).Position.Floor;
|
||||
if ((laraItem->Pose.Position.y - height) <= CLICK(2))
|
||||
{
|
||||
laraItem->Pose.Position.x = newPos.x;
|
||||
laraItem->Pose.Position.y = newPos.y;
|
||||
laraItem->Pose.Position.z = newPos.z;
|
||||
laraItem->Pose.Position = Vector3Int(newPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
if (lara->Control.IsMoving)
|
||||
{
|
||||
lara->Control.IsMoving = false;
|
||||
|
@ -442,9 +453,8 @@ bool MoveLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
|
|||
{
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
|
||||
auto dest = PHD_3DPOS(item->Pose.Orientation);
|
||||
|
||||
Vector3 pos = Vector3(vec->x, vec->y, vec->z);
|
||||
auto target = PHD_3DPOS(item->Pose.Orientation);
|
||||
auto pos = vec->ToVector3();
|
||||
|
||||
Matrix matrix = Matrix::CreateFromYawPitchRoll(
|
||||
TO_RAD(item->Pose.Orientation.y),
|
||||
|
@ -453,24 +463,23 @@ bool MoveLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
|
|||
);
|
||||
|
||||
pos = Vector3::Transform(pos, matrix);
|
||||
target.Position = item->Pose.Position + Vector3Int(pos);
|
||||
|
||||
dest.Position.x = item->Pose.Position.x + pos.x;
|
||||
dest.Position.y = item->Pose.Position.y + pos.y;
|
||||
dest.Position.z = item->Pose.Position.z + pos.z;
|
||||
if (!Objects[item->ObjectNumber].isPickup)
|
||||
return Move3DPosTo3DPos(&laraItem->Pose, &target, LARA_ALIGN_VELOCITY, ANGLE(2.0f));
|
||||
|
||||
if (item->ObjectNumber != ID_FLARE_ITEM && item->ObjectNumber != ID_BURNING_TORCH_ITEM)
|
||||
return Move3DPosTo3DPos(&laraItem->Pose, &dest, LARA_VELOCITY, ANGLE(2.0f));
|
||||
// Prevent picking up items which can result in so called "flare pickup bug"
|
||||
|
||||
int height = GetCollision(dest.Position.x, dest.Position.y, dest.Position.z, laraItem->RoomNumber).Position.Floor;
|
||||
int height = GetCollision(target.Position.x, target.Position.y, target.Position.z, laraItem->RoomNumber).Position.Floor;
|
||||
if (abs(height - laraItem->Pose.Position.y) <= CLICK(2))
|
||||
{
|
||||
auto direction = dest.Position - laraItem->Pose.Position;
|
||||
auto direction = target.Position - laraItem->Pose.Position;
|
||||
|
||||
float distance = sqrt(pow(direction.x, 2) + pow(direction.y, 2) + pow(direction.z, 2));
|
||||
float distance = sqrt(SQUARE(direction.x) + SQUARE(direction.y) + SQUARE(direction.z));
|
||||
if (distance < CLICK(0.5f))
|
||||
return true;
|
||||
|
||||
return Move3DPosTo3DPos(&laraItem->Pose, &dest, LARA_VELOCITY, ANGLE(2.0f));
|
||||
return Move3DPosTo3DPos(&laraItem->Pose, &target, LARA_ALIGN_VELOCITY, ANGLE(2.0f));
|
||||
}
|
||||
|
||||
if (lara->Control.IsMoving)
|
||||
|
@ -484,20 +493,21 @@ bool MoveLaraPosition(Vector3Int* vec, ItemInfo* item, ItemInfo* laraItem)
|
|||
|
||||
static bool ItemCollide(int value, int radius)
|
||||
{
|
||||
return value >= -radius && value <= radius;
|
||||
return (value >= -radius && value <= radius);
|
||||
}
|
||||
|
||||
static bool ItemInRange(int x, int z, int radius)
|
||||
{
|
||||
return (pow(x, 2) + pow(z, 2)) <= pow(radius, 2);
|
||||
return ((SQUARE(x) + SQUARE(z)) <= SQUARE(radius));
|
||||
}
|
||||
|
||||
bool ItemNearLara(PHD_3DPOS* pos, int radius)
|
||||
{
|
||||
GameVector target;
|
||||
target.x = pos->Position.x - LaraItem->Pose.Position.x;
|
||||
target.y = pos->Position.y - LaraItem->Pose.Position.y;
|
||||
target.z = pos->Position.z - LaraItem->Pose.Position.z;
|
||||
auto target = GameVector(
|
||||
pos->Position.x - LaraItem->Pose.Position.x,
|
||||
pos->Position.y - LaraItem->Pose.Position.y,
|
||||
pos->Position.z - LaraItem->Pose.Position.z
|
||||
);
|
||||
|
||||
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
|
||||
return false;
|
||||
|
@ -535,22 +545,24 @@ bool ItemNearTarget(PHD_3DPOS* src, ItemInfo* target, int radius)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Move3DPosTo3DPos(PHD_3DPOS* src, PHD_3DPOS* dest, int velocity, short angleAdd)
|
||||
bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short angleAdd)
|
||||
{
|
||||
auto direction = dest->Position - src->Position;
|
||||
int distance = sqrt(pow(direction.x, 2) + pow(direction.y, 2) + pow(direction.z, 2));
|
||||
auto direction = target->Position - origin->Position;
|
||||
int distance = sqrt(SQUARE(direction.x) + SQUARE(direction.y) + SQUARE(direction.z));
|
||||
|
||||
if (velocity < distance)
|
||||
src->Position += direction * velocity / distance;
|
||||
origin->Position += direction * velocity / distance;
|
||||
else
|
||||
src->Position = dest->Position;
|
||||
origin->Position = target->Position;
|
||||
|
||||
if (!Lara.Control.IsMoving)
|
||||
{
|
||||
if (Lara.Control.WaterStatus != WaterStatus::Underwater)
|
||||
bool shouldAnimate = (distance - velocity) > (velocity * ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD);
|
||||
|
||||
if (shouldAnimate && Lara.Control.WaterStatus != WaterStatus::Underwater)
|
||||
{
|
||||
int angle = mGetAngle(dest->Position.x, dest->Position.z, src->Position.x, src->Position.z);
|
||||
int direction = (GetQuadrant(angle) - GetQuadrant(dest->Orientation.y)) & 3;
|
||||
int angle = mGetAngle(target->Position.x, target->Position.z, origin->Position.x, origin->Position.z);
|
||||
int direction = (GetQuadrant(angle) - GetQuadrant(target->Orientation.y)) & 3;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
|
@ -581,42 +593,42 @@ bool Move3DPosTo3DPos(PHD_3DPOS* src, PHD_3DPOS* dest, int velocity, short angle
|
|||
Lara.Control.Count.PositionAdjust = 0;
|
||||
}
|
||||
|
||||
short deltaAngle = dest->Orientation.x - src->Orientation.x;
|
||||
short deltaAngle = target->Orientation.x - origin->Orientation.x;
|
||||
if (deltaAngle > angleAdd)
|
||||
src->Orientation.x += angleAdd;
|
||||
origin->Orientation.x += angleAdd;
|
||||
else if (deltaAngle < -angleAdd)
|
||||
src->Orientation.x -= angleAdd;
|
||||
origin->Orientation.x -= angleAdd;
|
||||
else
|
||||
src->Orientation.x = dest->Orientation.x;
|
||||
origin->Orientation.x = target->Orientation.x;
|
||||
|
||||
deltaAngle = dest->Orientation.y - src->Orientation.y;
|
||||
deltaAngle = target->Orientation.y - origin->Orientation.y;
|
||||
if (deltaAngle > angleAdd)
|
||||
src->Orientation.y += angleAdd;
|
||||
origin->Orientation.y += angleAdd;
|
||||
else if (deltaAngle < -angleAdd)
|
||||
src->Orientation.y -= angleAdd;
|
||||
origin->Orientation.y -= angleAdd;
|
||||
else
|
||||
src->Orientation.y = dest->Orientation.y;
|
||||
origin->Orientation.y = target->Orientation.y;
|
||||
|
||||
deltaAngle = dest->Orientation.z - src->Orientation.z;
|
||||
deltaAngle = target->Orientation.z - origin->Orientation.z;
|
||||
if (deltaAngle > angleAdd)
|
||||
src->Orientation.z += angleAdd;
|
||||
origin->Orientation.z += angleAdd;
|
||||
else if (deltaAngle < -angleAdd)
|
||||
src->Orientation.z -= angleAdd;
|
||||
origin->Orientation.z -= angleAdd;
|
||||
else
|
||||
src->Orientation.z = dest->Orientation.z;
|
||||
origin->Orientation.z = target->Orientation.z;
|
||||
|
||||
return (src->Position == dest->Position && src->Orientation == dest->Orientation);
|
||||
return (origin->Position == target->Position && origin->Orientation == target->Orientation);
|
||||
}
|
||||
|
||||
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
|
||||
{
|
||||
auto bounds = (BOUNDING_BOX*)GetBestFrame(item);
|
||||
auto laraBounds = (BOUNDING_BOX*)GetBestFrame(laraItem);
|
||||
auto* bounds = (BOUNDING_BOX*)GetBestFrame(item);
|
||||
auto* laraBounds = (BOUNDING_BOX*)GetBestFrame(laraItem);
|
||||
|
||||
if (item->Pose.Position.y + bounds->Y2 <= laraItem->Pose.Position.y + laraBounds->Y1)
|
||||
if ((item->Pose.Position.y + bounds->Y2) <= (laraItem->Pose.Position.y + laraBounds->Y1))
|
||||
return false;
|
||||
|
||||
if (item->Pose.Position.y + bounds->Y1 >= laraItem->Pose.Position.y + laraBounds->Y2)
|
||||
if ((item->Pose.Position.y + bounds->Y1) >= (laraItem->Pose.Position.y + laraBounds->Y2))
|
||||
return false;
|
||||
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
|
@ -640,16 +652,16 @@ bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
|
|||
|
||||
bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius)
|
||||
{
|
||||
auto bounds = StaticObjects[mesh->staticNumber].collisionBox;
|
||||
auto bounds = GetBoundsAccurate(mesh, false);
|
||||
|
||||
if (!(bounds.Z2 != 0 || bounds.Z1 != 0 || bounds.X1 != 0 || bounds.X2 != 0 || bounds.Y1 != 0 || bounds.Y2 != 0))
|
||||
if (!(bounds->Z2 != 0 || bounds->Z1 != 0 || bounds->X1 != 0 || bounds->X2 != 0 || bounds->Y1 != 0 || bounds->Y2 != 0))
|
||||
return false;
|
||||
|
||||
auto* frame = GetBestFrame(item);
|
||||
if (mesh->pos.Position.y + bounds.Y2 <= item->Pose.Position.y + frame->boundingBox.Y1)
|
||||
if (mesh->pos.Position.y + bounds->Y2 <= item->Pose.Position.y + frame->boundingBox.Y1)
|
||||
return false;
|
||||
|
||||
if (mesh->pos.Position.y + bounds.Y1 >= item->Pose.Position.y + frame->boundingBox.Y2)
|
||||
if (mesh->pos.Position.y + bounds->Y1 >= item->Pose.Position.y + frame->boundingBox.Y2)
|
||||
return false;
|
||||
|
||||
float sinY = phd_sin(mesh->pos.Orientation.y);
|
||||
|
@ -660,10 +672,10 @@ bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius)
|
|||
int dx = cosY * x - sinY * z;
|
||||
int dz = cosY * z + sinY * x;
|
||||
|
||||
if (dx <= radius + bounds.X2 &&
|
||||
dx >= bounds.X1 - radius &&
|
||||
dz <= radius + bounds.Z2 &&
|
||||
dz >= bounds.Z1 - radius)
|
||||
if (dx <= radius + bounds->X2 &&
|
||||
dx >= bounds->X1 - radius &&
|
||||
dz <= radius + bounds->Z2 &&
|
||||
dz >= bounds->Z1 - radius)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -671,19 +683,20 @@ bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spasmEnabled, char bigPush) // previously ItemPushLara
|
||||
// NOTE: Previously ItemPushLara().
|
||||
bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spasmEnabled, char bigPush)
|
||||
{
|
||||
// Get item's rotation
|
||||
// Get item's rotation.
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
float cosY = phd_cos(item->Pose.Orientation.y);
|
||||
|
||||
// Get vector from item to Lara
|
||||
// Get vector from item to Lara.
|
||||
int dx = item2->Pose.Position.x - item->Pose.Position.x;
|
||||
int dz = item2->Pose.Position.z - item->Pose.Position.z;
|
||||
|
||||
// Rotate Lara vector into item frame
|
||||
int rx = cosY * dx - sinY * dz;
|
||||
int rz = cosY * dz + sinY * dx;
|
||||
// Rotate Lara vector into item frame.
|
||||
int rx = (dx * cosY) - (dz * sinY);
|
||||
int rz = (dz * cosY) + (dx * sinY);
|
||||
|
||||
BOUNDING_BOX* bounds;
|
||||
if (bigPush & 2)
|
||||
|
@ -731,13 +744,13 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
|||
|
||||
auto* lara = item2->IsLara() ? GetLaraInfo(item2) : nullptr;
|
||||
|
||||
if (lara != nullptr && spasmEnabled && bounds->Y2 - bounds->Y1 > CLICK(1))
|
||||
if (lara != nullptr && spasmEnabled && (bounds->Y2 - bounds->Y1) > CLICK(1))
|
||||
{
|
||||
rx = (bounds->X1 + bounds->X2) / 2;
|
||||
rz = (bounds->Z1 + bounds->Z2) / 2;
|
||||
|
||||
dx -= cosY * rx + sinY * rz;
|
||||
dz -= cosY * rz - sinY * rx;
|
||||
dx -= (rx * cosY) + (rz * sinY);
|
||||
dz -= (rz * cosY) - (rx * sinY);
|
||||
|
||||
lara->HitDirection = (item2->Pose.Orientation.y - phd_atan(dz, dz) - ANGLE(135.0f)) / ANGLE(90.0f);
|
||||
DoDamage(item2, 0); // Dummy hurt call. Only for ooh sound!
|
||||
|
@ -784,7 +797,7 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
|||
|
||||
bool ItemPushStatic(ItemInfo* item, MESH_INFO* mesh, CollisionInfo* coll) // previously ItemPushLaraStatic
|
||||
{
|
||||
auto bounds = StaticObjects[mesh->staticNumber].collisionBox;
|
||||
auto bounds = GetBoundsAccurate(mesh, false);
|
||||
|
||||
float sinY = phd_sin(mesh->pos.Orientation.y);
|
||||
float cosY = phd_cos(mesh->pos.Orientation.y);
|
||||
|
@ -793,10 +806,10 @@ bool ItemPushStatic(ItemInfo* item, MESH_INFO* mesh, CollisionInfo* coll) // pre
|
|||
auto dz = item->Pose.Position.z - mesh->pos.Position.z;
|
||||
auto rx = cosY * dx - sinY * dz;
|
||||
auto rz = cosY * dz + sinY * dx;
|
||||
auto minX = bounds.X1 - coll->Setup.Radius;
|
||||
auto maxX = bounds.X2 + coll->Setup.Radius;
|
||||
auto minZ = bounds.Z1 - coll->Setup.Radius;
|
||||
auto maxZ = bounds.Z2 + coll->Setup.Radius;
|
||||
auto minX = bounds->X1 - coll->Setup.Radius;
|
||||
auto maxX = bounds->X2 + coll->Setup.Radius;
|
||||
auto minZ = bounds->Z1 - coll->Setup.Radius;
|
||||
auto maxZ = bounds->Z2 + coll->Setup.Radius;
|
||||
|
||||
if (abs(dx) > SECTOR(4.5f) || abs(dz) > SECTOR(4.5f) ||
|
||||
rx <= minX || rx >= maxX ||
|
||||
|
@ -862,7 +875,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
|
|||
{
|
||||
for (int j = 0; j < g_Level.Rooms[i].mesh.size(); j++)
|
||||
{
|
||||
auto mesh = &g_Level.Rooms[i].mesh[j];
|
||||
auto* mesh = &g_Level.Rooms[i].mesh[j];
|
||||
|
||||
// Only process meshes which are visible and solid
|
||||
if ((mesh->flags & StaticMeshFlags::SM_VISIBLE) && (mesh->flags & StaticMeshFlags::SM_SOLID))
|
||||
|
@ -870,7 +883,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
|
|||
if (phd_Distance(&item->Pose, &mesh->pos) < COLLISION_CHECK_DISTANCE)
|
||||
{
|
||||
auto staticInfo = &StaticObjects[mesh->staticNumber];
|
||||
if (CollideSolidBounds(item, staticInfo->collisionBox, mesh->pos, coll))
|
||||
if (CollideSolidBounds(item, GetBoundsAccurate(mesh, false), mesh->pos, coll))
|
||||
coll->HitStatic = true;
|
||||
}
|
||||
}
|
||||
|
@ -878,12 +891,12 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
|
|||
}
|
||||
}
|
||||
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, CollisionInfo* coll)
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, CollisionInfo* coll)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// Get DX static bounds in global coords
|
||||
auto staticBounds = TO_DX_BBOX(pos, &box);
|
||||
auto staticBounds = TO_DX_BBOX(pos, box);
|
||||
|
||||
// Get local TR bounds and DX item bounds in global coords
|
||||
auto itemBBox = GetBoundsAccurate(item);
|
||||
|
@ -911,7 +924,7 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, Collisi
|
|||
|
||||
// Calculate vertical item coll bounds according to either height (land mode) or precise bounds (water mode).
|
||||
// Water mode needs special processing because height calc in original engines is inconsistent in such cases.
|
||||
if (g_Level.Rooms[item->RoomNumber].flags & ENV_FLAG_WATER)
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item))
|
||||
{
|
||||
collBox.Y1 = itemBBox->Y1;
|
||||
collBox.Y2 = itemBBox->Y2;
|
||||
|
@ -945,7 +958,7 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, Collisi
|
|||
};
|
||||
|
||||
// Determine collision box vertical dimensions
|
||||
auto height = collBox.Y2 - collBox.Y1;
|
||||
auto height = collBox.Height();
|
||||
auto center = item->Pose.Position.y - height / 2;
|
||||
|
||||
// Do a series of angular tests with 90 degree steps to determine top/bottom collision.
|
||||
|
@ -1022,21 +1035,21 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, Collisi
|
|||
|
||||
// Determine identity rotation/distance
|
||||
auto distance = Vector3(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
|
||||
auto c = phd_cos(pos.Orientation.y);
|
||||
auto s = phd_sin(pos.Orientation.y);
|
||||
auto sinY = phd_sin(pos.Orientation.y);
|
||||
auto cosY = phd_cos(pos.Orientation.y);
|
||||
|
||||
// Rotate item to collision bounds identity
|
||||
auto x = round(distance.x * c - distance.z * s) + pos.Position.x;
|
||||
auto x = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
|
||||
auto y = item->Pose.Position.y;
|
||||
auto z = round(distance.x * s + distance.z * c) + pos.Position.z;
|
||||
auto z = round(distance.x * sinY + distance.z * cosY) + pos.Position.z;
|
||||
|
||||
// Determine identity static collision bounds
|
||||
auto XMin = pos.Position.x + box.X1;
|
||||
auto XMax = pos.Position.x + box.X2;
|
||||
auto YMin = pos.Position.y + box.Y1;
|
||||
auto YMax = pos.Position.y + box.Y2;
|
||||
auto ZMin = pos.Position.z + box.Z1;
|
||||
auto ZMax = pos.Position.z + box.Z2;
|
||||
auto XMin = pos.Position.x + box->X1;
|
||||
auto XMax = pos.Position.x + box->X2;
|
||||
auto YMin = pos.Position.y + box->Y1;
|
||||
auto YMax = pos.Position.y + box->Y2;
|
||||
auto ZMin = pos.Position.z + box->Z1;
|
||||
auto ZMax = pos.Position.z + box->Z2;
|
||||
|
||||
// Determine item collision bounds
|
||||
auto inXMin = x + collBox.X1;
|
||||
|
@ -1074,8 +1087,8 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, Collisi
|
|||
|
||||
// Rotate previous collision position to identity
|
||||
distance = Vector3(coll->Setup.OldPosition.x, coll->Setup.OldPosition.y, coll->Setup.OldPosition.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
|
||||
auto ox = round(distance.x * c - distance.z * s) + pos.Position.x;
|
||||
auto oz = round(distance.x * s + distance.z * c) + pos.Position.z;
|
||||
auto ox = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
|
||||
auto oz = round(distance.x * sinY + distance.z * cosY) + pos.Position.z;
|
||||
|
||||
// Calculate collisison type based on identity rotation
|
||||
switch (GetQuadrant(coll->Setup.ForwardAngle - pos.Orientation.y))
|
||||
|
@ -1171,12 +1184,12 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, Collisi
|
|||
|
||||
// Determine final shifts rotation/distance
|
||||
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
|
||||
c = phd_cos(-pos.Orientation.y);
|
||||
s = phd_sin(-pos.Orientation.y);
|
||||
sinY = phd_sin(-pos.Orientation.y);
|
||||
cosY = phd_cos(-pos.Orientation.y);
|
||||
|
||||
// Calculate final shifts rotation/distance
|
||||
coll->Shift.x = (round(distance.x * c - distance.z * s) + pos.Position.x) - item->Pose.Position.x;
|
||||
coll->Shift.z = (round(distance.x * s + distance.z * c) + pos.Position.z) - item->Pose.Position.z;
|
||||
coll->Shift.x = (round(distance.x * cosY - distance.z * sinY) + pos.Position.x) - item->Pose.Position.x;
|
||||
coll->Shift.z = (round(distance.x * sinY + distance.z * cosY) + pos.Position.z) - item->Pose.Position.z;
|
||||
|
||||
if (coll->Shift.x == 0 && coll->Shift.z == 0)
|
||||
coll->CollisionType = CT_NONE; // Paranoid
|
||||
|
@ -1188,27 +1201,27 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, Collisi
|
|||
return true;
|
||||
}
|
||||
|
||||
void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv) // previously DoProperDetection
|
||||
// NOTE: Previously DoProperDetection().
|
||||
void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv)
|
||||
{
|
||||
int bs, yAngle;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
auto oldCollResult = GetCollision(x, y, z, item->RoomNumber);
|
||||
auto prevCollResult = GetCollision(x, y, z, item->RoomNumber);
|
||||
auto collResult = GetCollision(item);
|
||||
|
||||
auto* bounds = GetBoundsAccurate(item);
|
||||
int radius = abs(bounds->Y2 - bounds->Y1);
|
||||
int radius = bounds->Height();
|
||||
|
||||
item->Pose.Position.y += radius;
|
||||
|
||||
if (item->Pose.Position.y >= collResult.Position.Floor)
|
||||
{
|
||||
bs = 0;
|
||||
int bs = 0;
|
||||
|
||||
if (collResult.Position.FloorSlope && oldCollResult.Position.Floor < collResult.Position.Floor)
|
||||
if (collResult.Position.FloorSlope && prevCollResult.Position.Floor < collResult.Position.Floor)
|
||||
{
|
||||
yAngle = (long)((unsigned short)item->Pose.Orientation.y);
|
||||
int yAngle = (long)((unsigned short)item->Pose.Orientation.y);
|
||||
|
||||
if (collResult.FloorTilt.x < 0)
|
||||
{
|
||||
if (yAngle >= ANGLE(180.0f))
|
||||
|
@ -1601,7 +1614,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (yv >= 0)
|
||||
{
|
||||
oldCollResult = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
|
||||
prevCollResult = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
|
||||
collResult = GetCollision(item);
|
||||
|
||||
// Bounce off floor.
|
||||
|
@ -1610,7 +1623,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
// was always set to 0 by GetHeight() function which was called before the check.
|
||||
// Possibly a mistake or unfinished feature by Core? -- Lwmte, 27.08.21
|
||||
|
||||
if (item->Pose.Position.y >= oldCollResult.Position.Floor)
|
||||
if (item->Pose.Position.y >= prevCollResult.Position.Floor)
|
||||
{
|
||||
// Hit the floor; bounce and slow down.
|
||||
if (item->Animation.Velocity.y > 0)
|
||||
|
@ -1644,7 +1657,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
|
||||
item->Pose.Position.y = oldCollResult.Position.Floor;
|
||||
item->Pose.Position.y = prevCollResult.Position.Floor;
|
||||
}
|
||||
}
|
||||
// else
|
||||
|
@ -1709,10 +1722,10 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
|||
laraItem->HitStatus = false;
|
||||
coll->HitStatic = false;
|
||||
|
||||
bool playerCollision = laraItem->IsLara();
|
||||
bool harmless = !playerCollision && (laraItem->Data.is<KayakInfo>() || laraItem->Data.is<UPVInfo>());
|
||||
bool doPlayerCollision = laraItem->IsLara();
|
||||
bool harmless = !doPlayerCollision && (laraItem->Data.is<KayakInfo>() || laraItem->Data.is<UPVInfo>());
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
GetLaraInfo(laraItem)->HitDirection = -1;
|
||||
|
||||
|
@ -1751,7 +1764,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
|||
if (phd_Distance(&item->Pose, &laraItem->Pose) >= COLLISION_CHECK_DISTANCE)
|
||||
continue;
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
// Objects' own collision routines were almost universally written only for
|
||||
// managing collisions with Lara and nothing else. Until all of these routines
|
||||
|
@ -1796,7 +1809,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
|||
item->RoomNumber, 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (coll->Setup.EnableObjectPush)
|
||||
ItemPushItem(item, laraItem, coll, false, 1);
|
||||
}
|
||||
}
|
||||
|
@ -1810,7 +1823,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
|||
|
||||
// For Lara, solid static mesh collisions are directly managed by GetCollisionInfo,
|
||||
// so we bypass them here to avoid interference.
|
||||
if (playerCollision && (mesh->flags & StaticMeshFlags::SM_SOLID))
|
||||
if (doPlayerCollision && (mesh->flags & StaticMeshFlags::SM_SOLID))
|
||||
continue;
|
||||
|
||||
if (phd_Distance(&mesh->pos, &laraItem->Pose) >= COLLISION_CHECK_DISTANCE)
|
||||
|
@ -1822,7 +1835,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
|||
coll->HitStatic = true;
|
||||
|
||||
// HACK: Shatter statics only by non-harmless vehicles.
|
||||
if (!playerCollision &&
|
||||
if (!doPlayerCollision &&
|
||||
!harmless && abs(laraItem->Animation.Velocity.z) > VEHICLE_COLLISION_TERMINAL_VELOCITY &&
|
||||
StaticObjects[mesh->staticNumber].shatterType != SHT_NONE)
|
||||
{
|
||||
|
@ -1840,7 +1853,7 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
|||
}
|
||||
}
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
if (lara->HitDirection == -1)
|
||||
|
@ -1880,14 +1893,12 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
|
|||
if (!TestCollision(item, laraItem))
|
||||
return;
|
||||
|
||||
bool playerCollision = laraItem->IsLara();
|
||||
bool waterPlayerCollision = playerCollision && GetLaraInfo(laraItem)->Control.WaterStatus >= WaterStatus::TreadWater;
|
||||
bool doPlayerCollision = laraItem->IsLara();
|
||||
bool waterPlayerCollision = doPlayerCollision && GetLaraInfo(laraItem)->Control.WaterStatus >= WaterStatus::TreadWater;
|
||||
|
||||
if (waterPlayerCollision || coll->Setup.EnableObjectPush)
|
||||
{
|
||||
ItemPushItem(item, laraItem, coll, coll->Setup.EnableSpasm, 0);
|
||||
}
|
||||
else if (playerCollision && coll->Setup.EnableSpasm)
|
||||
else if (doPlayerCollision && coll->Setup.EnableSpasm)
|
||||
{
|
||||
int x = laraItem->Pose.Position.x - item->Pose.Position.x;
|
||||
int z = laraItem->Pose.Position.z - item->Pose.Position.z;
|
||||
|
@ -1899,12 +1910,12 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
|
|||
int rx = (frame->boundingBox.X1 + frame->boundingBox.X2) / 2;
|
||||
int rz = (frame->boundingBox.X2 + frame->boundingBox.Z2) / 2;
|
||||
|
||||
if (frame->boundingBox.Y2 - frame->boundingBox.Y1 > STEP_SIZE)
|
||||
if (frame->boundingBox.Height() > STEP_SIZE)
|
||||
{
|
||||
int angle = (laraItem->Pose.Orientation.y - phd_atan(z - cosY * rx - sinY * rz, x - cosY * rx + sinY * rz) - ANGLE(135.0f)) / ANGLE(90.0f);
|
||||
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
|
||||
int angle = (laraItem->Pose.Orientation.y - phd_atan(z - cosY * rx - sinY * rz, x - cosY * rx + sinY * rz) - ANGLE(135.0f)) / ANGLE(90.0f);
|
||||
|
||||
lara->HitDirection = (short)angle;
|
||||
|
||||
// TODO: check if a second Lara.hitFrame++; is required there !
|
||||
|
|
|
@ -46,7 +46,7 @@ bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius);
|
|||
bool ItemPushItem(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll, bool spasmEnabled, char bigPush);
|
||||
bool ItemPushStatic(ItemInfo* laraItem, MESH_INFO* mesh, CollisionInfo* coll);
|
||||
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX box, PHD_3DPOS pos, CollisionInfo* coll);
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, CollisionInfo* coll);
|
||||
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll);
|
||||
|
||||
void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
|
|
|
@ -219,78 +219,72 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
{
|
||||
// Player collision has several more precise checks for bridge collisions.
|
||||
// Therefore, we should differentiate these code paths.
|
||||
bool playerCollision = item->IsLara();
|
||||
bool doPlayerCollision = item->IsLara();
|
||||
|
||||
// Reset collision parameters.
|
||||
coll->CollisionType = CT_NONE;
|
||||
coll->Shift.x = 0;
|
||||
coll->Shift.y = 0;
|
||||
coll->Shift.z = 0;
|
||||
coll->CollisionType = CollisionType::CT_NONE;
|
||||
coll->Shift = Vector3Int::Zero;
|
||||
|
||||
// Offset base probe position by provided offset, if any.
|
||||
int xPos = item->Pose.Position.x + offset.x;
|
||||
int yPos = item->Pose.Position.y + offset.y;
|
||||
int zPos = item->Pose.Position.z + offset.z;
|
||||
auto entityPos = item->Pose.Position + offset;
|
||||
|
||||
// Specify base probe position, Y position being bounds top side.
|
||||
int x = xPos;
|
||||
int y = yPos - coll->Setup.Height;
|
||||
int z = zPos;
|
||||
// Specify base probe position, with Y position being bounds top side.
|
||||
auto probePos = Vector3Int(entityPos.x, entityPos.y - coll->Setup.Height, entityPos.z);
|
||||
|
||||
// Define side probe offsets.
|
||||
int xfront, xright, xleft, zfront, zright, zleft;
|
||||
// Declare side probe offsets.
|
||||
int xFront, zFront, xRight, zRight, xLeft, zLeft;
|
||||
|
||||
// Get nearest 90-degree snapped angle (quadrant).
|
||||
auto quadrant = GetQuadrant(coll->Setup.ForwardAngle);
|
||||
|
||||
// Get side probe offsets depending on quadrant.
|
||||
// If unconstrained mode is specified, don't use quadrant.
|
||||
switch (coll->Setup.Mode == CollisionProbeMode::Quadrants ? quadrant : -1)
|
||||
switch ((coll->Setup.Mode == CollisionProbeMode::Quadrants) ? quadrant : -1)
|
||||
{
|
||||
case 0:
|
||||
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
zfront = coll->Setup.Radius;
|
||||
xleft = -coll->Setup.Radius;
|
||||
zleft = coll->Setup.Radius;
|
||||
xright = coll->Setup.Radius;
|
||||
zright = coll->Setup.Radius;
|
||||
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
zFront = coll->Setup.Radius;
|
||||
xLeft = -coll->Setup.Radius;
|
||||
zLeft = coll->Setup.Radius;
|
||||
xRight = coll->Setup.Radius;
|
||||
zRight = coll->Setup.Radius;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
xfront = coll->Setup.Radius;
|
||||
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
xleft = coll->Setup.Radius;
|
||||
zleft = coll->Setup.Radius;
|
||||
xright = coll->Setup.Radius;
|
||||
zright = -coll->Setup.Radius;
|
||||
xFront = coll->Setup.Radius;
|
||||
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
xLeft = coll->Setup.Radius;
|
||||
zLeft = coll->Setup.Radius;
|
||||
xRight = coll->Setup.Radius;
|
||||
zRight = -coll->Setup.Radius;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
zfront = -coll->Setup.Radius;
|
||||
xleft = coll->Setup.Radius;
|
||||
zleft = -coll->Setup.Radius;
|
||||
xright = -coll->Setup.Radius;
|
||||
zright = -coll->Setup.Radius;
|
||||
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
zFront = -coll->Setup.Radius;
|
||||
xLeft = coll->Setup.Radius;
|
||||
zLeft = -coll->Setup.Radius;
|
||||
xRight = -coll->Setup.Radius;
|
||||
zRight = -coll->Setup.Radius;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
xfront = -coll->Setup.Radius;
|
||||
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
xleft = -coll->Setup.Radius;
|
||||
zleft = -coll->Setup.Radius;
|
||||
xright = -coll->Setup.Radius;
|
||||
zright = coll->Setup.Radius;
|
||||
xFront = -coll->Setup.Radius;
|
||||
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
xLeft = -coll->Setup.Radius;
|
||||
zLeft = -coll->Setup.Radius;
|
||||
xRight = -coll->Setup.Radius;
|
||||
zRight = coll->Setup.Radius;
|
||||
break;
|
||||
|
||||
// No valid quadrant; return true probe offsets from object rotation.
|
||||
default:
|
||||
// No valid quadrant, return true probe offsets from object rotation.
|
||||
xfront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
zfront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
xleft = (xfront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_sin(coll->Setup.ForwardAngle - ANGLE(90)) * coll->Setup.Radius;
|
||||
zleft = (zfront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_cos(coll->Setup.ForwardAngle - ANGLE(90)) * coll->Setup.Radius;
|
||||
xright = (xfront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_sin(coll->Setup.ForwardAngle + ANGLE(90)) * coll->Setup.Radius;
|
||||
zright = (zfront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_cos(coll->Setup.ForwardAngle + ANGLE(90)) * coll->Setup.Radius;
|
||||
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||
xLeft = (xFront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_sin(coll->Setup.ForwardAngle - ANGLE(90.0f)) * coll->Setup.Radius;
|
||||
zLeft = (zFront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_cos(coll->Setup.ForwardAngle - ANGLE(90.0f)) * coll->Setup.Radius;
|
||||
xRight = (xFront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_sin(coll->Setup.ForwardAngle + ANGLE(90.0f)) * coll->Setup.Radius;
|
||||
zRight = (zFront * (coll->Setup.Mode == CollisionProbeMode::FreeForward ? 0.5f : 1.0f)) + phd_cos(coll->Setup.ForwardAngle + ANGLE(90.0f)) * coll->Setup.Radius;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -302,7 +296,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
|
||||
// TEST 1: TILT AND NEAREST LEDGE CALCULATION
|
||||
|
||||
auto collResult = GetCollision(x, item->Pose.Position.y, z, item->RoomNumber);
|
||||
auto collResult = GetCollision(probePos.x, item->Pose.Position.y, probePos.z, item->RoomNumber);
|
||||
coll->FloorTilt = collResult.FloorTilt;
|
||||
coll->CeilingTilt = collResult.CeilingTilt;
|
||||
coll->NearestLedgeAngle = GetNearestLedgeAngle(item, coll, coll->NearestLedgeDistance);
|
||||
|
@ -313,24 +307,28 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
|
||||
// TEST 2: CENTERPOINT PROBE
|
||||
|
||||
collResult = GetCollision(x, y, z, item->RoomNumber);
|
||||
auto topRoomNumber = collResult.RoomNumber; // Keep top room number as we need it to re-probe from origin room
|
||||
|
||||
if (playerCollision)
|
||||
collResult = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber);
|
||||
auto topRoomNumber = collResult.RoomNumber; // Keep top room number as we need it to re-probe from origin room.
|
||||
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
tfLocation = GetRoom(item->Location, x, y, z);
|
||||
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
||||
tfLocation = GetRoom(item->Location, probePos.x, probePos.y, probePos.z);
|
||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
|
||||
tcLocation = GetRoom(item->Location, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
||||
tcLocation = GetRoom(item->Location, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = collResult.Position.Floor;
|
||||
ceiling = GetCeiling(collResult.Block, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
}
|
||||
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
||||
if (ceiling != NO_HEIGHT) ceiling -= y;
|
||||
|
||||
if (height != NO_HEIGHT)
|
||||
height -= (doPlayerCollision ? entityPos.y : probePos.y);
|
||||
|
||||
if (ceiling != NO_HEIGHT)
|
||||
ceiling -= probePos.y;
|
||||
|
||||
coll->Middle = collResult.Position;
|
||||
coll->Middle.Floor = height;
|
||||
|
@ -338,14 +336,14 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
|
||||
// TEST 3: FRONTAL PROBE
|
||||
|
||||
x = xPos + xfront;
|
||||
z = zPos + zfront;
|
||||
probePos.x = entityPos.x + xFront;
|
||||
probePos.z = entityPos.z + zFront;
|
||||
|
||||
g_Renderer.AddDebugSphere(Vector3(x, y, z), 32, Vector4(1, 0, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
g_Renderer.AddDebugSphere(probePos.ToVector3(), 32, Vector4(1, 0, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
|
||||
collResult = GetCollision(x, y, z, topRoomNumber);
|
||||
collResult = GetCollision(probePos.x, probePos.y, probePos.z, topRoomNumber);
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
if (resetRoom)
|
||||
{
|
||||
|
@ -354,34 +352,38 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
topRoomNumber = item->RoomNumber;
|
||||
}
|
||||
|
||||
tfLocation = GetRoom(tfLocation, x, y, z);
|
||||
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
||||
tfLocation = GetRoom(tfLocation, probePos.x, probePos.y, probePos.z);
|
||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
|
||||
tcLocation = GetRoom(tcLocation, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
||||
tcLocation = GetRoom(tcLocation, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = collResult.Position.Floor;
|
||||
ceiling = GetCeiling(collResult.Block, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
}
|
||||
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
||||
if (ceiling != NO_HEIGHT) ceiling -= y;
|
||||
|
||||
if (height != NO_HEIGHT)
|
||||
height -= (doPlayerCollision ? entityPos.y : probePos.y);
|
||||
|
||||
if (ceiling != NO_HEIGHT)
|
||||
ceiling -= probePos.y;
|
||||
|
||||
coll->Front = collResult.Position;
|
||||
coll->Front.Floor = height;
|
||||
coll->Front.Ceiling = ceiling;
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
tfLocation = GetRoom(tfLocation, x + xfront, y, z + zfront);
|
||||
height = GetFloorHeight(tfLocation, x + xfront, z + zfront).value_or(NO_HEIGHT);
|
||||
tfLocation = GetRoom(tfLocation, probePos.x + xFront, probePos.y, probePos.z + zFront);
|
||||
height = GetFloorHeight(tfLocation, probePos.x + xFront, probePos.z + zFront).value_or(NO_HEIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = GetCollision(x + xfront, y, z + zfront, topRoomNumber).Position.Floor;
|
||||
}
|
||||
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
||||
height = GetCollision(probePos.x + xFront, probePos.y, probePos.z + zFront, topRoomNumber).Position.Floor;
|
||||
|
||||
if (height != NO_HEIGHT)
|
||||
height -= (doPlayerCollision ? entityPos.y : probePos.y);
|
||||
|
||||
if (coll->Setup.BlockFloorSlopeUp &&
|
||||
coll->Front.FloorSlope &&
|
||||
|
@ -409,35 +411,39 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
coll->Front.Floor = STOP_SIZE;
|
||||
}
|
||||
else if (coll->Setup.BlockMonkeySwingEdge &&
|
||||
!GetCollision(x, y + coll->Setup.Height, z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
!GetCollision(probePos.x, probePos.y + coll->Setup.Height, probePos.z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
{
|
||||
coll->Front.Floor = MAX_HEIGHT;
|
||||
}
|
||||
|
||||
// TEST 4: MIDDLE-LEFT PROBE
|
||||
|
||||
x = xPos + xleft;
|
||||
z = zPos + zleft;
|
||||
probePos.x = entityPos.x + xLeft;
|
||||
probePos.z = entityPos.z + zLeft;
|
||||
|
||||
g_Renderer.AddDebugSphere(Vector3(x, y, z), 32, Vector4(0, 0, 1, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
g_Renderer.AddDebugSphere(probePos.ToVector3(), 32, Vector4(0, 0, 1, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
|
||||
collResult = GetCollision(x, y, z, item->RoomNumber);
|
||||
collResult = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber);
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
lrfLocation = GetRoom(item->Location, x, y, z);
|
||||
height = GetFloorHeight(lrfLocation, x, z).value_or(NO_HEIGHT);
|
||||
lrfLocation = GetRoom(item->Location, probePos.x, probePos.y, probePos.z);
|
||||
height = GetFloorHeight(lrfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
|
||||
lrcLocation = GetRoom(item->Location, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeilingHeight(lrcLocation, x, z).value_or(NO_HEIGHT);
|
||||
lrcLocation = GetRoom(item->Location, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
ceiling = GetCeilingHeight(lrcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = collResult.Position.Floor;
|
||||
ceiling = GetCeiling(collResult.Block, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
}
|
||||
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
||||
if (ceiling != NO_HEIGHT) ceiling -= y;
|
||||
|
||||
if (height != NO_HEIGHT)
|
||||
height -= (doPlayerCollision ? entityPos.y : probePos.y);
|
||||
|
||||
if (ceiling != NO_HEIGHT)
|
||||
ceiling -= probePos.y;
|
||||
|
||||
coll->MiddleLeft = collResult.Position;
|
||||
coll->MiddleLeft.Floor = height;
|
||||
|
@ -467,30 +473,34 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
coll->MiddleLeft.Floor = STOP_SIZE;
|
||||
}
|
||||
else if (coll->Setup.BlockMonkeySwingEdge &&
|
||||
!GetCollision(x, y + coll->Setup.Height, z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
!GetCollision(probePos.x, probePos.y + coll->Setup.Height, probePos.z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
{
|
||||
coll->MiddleLeft.Floor = MAX_HEIGHT;
|
||||
}
|
||||
|
||||
// TEST 5: FRONT-LEFT PROBE
|
||||
|
||||
collResult = GetCollision(x, y, z, topRoomNumber); // We use plain x/z values here, proposed by Choco
|
||||
collResult = GetCollision(probePos.x, probePos.y, probePos.z, topRoomNumber); // Use plain X/Z values here as proposed by Choco.
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
tfLocation = GetRoom(tfLocation, x, y, z);
|
||||
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
||||
tfLocation = GetRoom(tfLocation, probePos.x, probePos.y, probePos.z);
|
||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
|
||||
tcLocation = GetRoom(tcLocation, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
||||
tcLocation = GetRoom(tcLocation, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = collResult.Position.Floor;
|
||||
ceiling = GetCeiling(collResult.Block, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
}
|
||||
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
||||
if (ceiling != NO_HEIGHT) ceiling -= y;
|
||||
|
||||
if (height != NO_HEIGHT)
|
||||
height -= (doPlayerCollision ? entityPos.y : probePos.y);
|
||||
|
||||
if (ceiling != NO_HEIGHT)
|
||||
ceiling -= probePos.y;
|
||||
|
||||
coll->FrontLeft = collResult.Position;
|
||||
coll->FrontLeft.Floor = height;
|
||||
|
@ -520,35 +530,39 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
coll->FrontLeft.Floor = STOP_SIZE;
|
||||
}
|
||||
else if (coll->Setup.BlockMonkeySwingEdge &&
|
||||
!GetCollision(x, y + coll->Setup.Height, z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
!GetCollision(probePos.x, probePos.y + coll->Setup.Height, probePos.z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
{
|
||||
coll->FrontLeft.Floor = MAX_HEIGHT;
|
||||
}
|
||||
|
||||
// TEST 6: MIDDLE-RIGHT PROBE
|
||||
|
||||
x = xPos + xright;
|
||||
z = zPos + zright;
|
||||
probePos.x = entityPos.x + xRight;
|
||||
probePos.z = entityPos.z + zRight;
|
||||
|
||||
g_Renderer.AddDebugSphere(Vector3(x, y, z), 32, Vector4(0, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
g_Renderer.AddDebugSphere(probePos.ToVector3(), 32, Vector4(0, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
|
||||
collResult = GetCollision(x, y, z, item->RoomNumber);
|
||||
collResult = GetCollision(probePos.x, probePos.y, probePos.z, item->RoomNumber);
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
lrfLocation = GetRoom(item->Location, x, y, z);
|
||||
height = GetFloorHeight(lrfLocation, x, z).value_or(NO_HEIGHT);
|
||||
lrfLocation = GetRoom(item->Location, probePos.x, probePos.y, probePos.z);
|
||||
height = GetFloorHeight(lrfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
|
||||
lrcLocation = GetRoom(item->Location, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeilingHeight(lrcLocation, x, z).value_or(NO_HEIGHT);
|
||||
lrcLocation = GetRoom(item->Location, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
ceiling = GetCeilingHeight(lrcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = collResult.Position.Floor;
|
||||
ceiling = GetCeiling(collResult.Block, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
}
|
||||
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
||||
if (ceiling != NO_HEIGHT) ceiling -= y;
|
||||
|
||||
if (height != NO_HEIGHT)
|
||||
height -= (doPlayerCollision ? entityPos.y : probePos.y);
|
||||
|
||||
if (ceiling != NO_HEIGHT)
|
||||
ceiling -= probePos.y;
|
||||
|
||||
coll->MiddleRight = collResult.Position;
|
||||
coll->MiddleRight.Floor = height;
|
||||
|
@ -578,30 +592,34 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
coll->MiddleRight.Floor = STOP_SIZE;
|
||||
}
|
||||
else if (coll->Setup.BlockMonkeySwingEdge &&
|
||||
!GetCollision(x, y + coll->Setup.Height, z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
!GetCollision(probePos.x, probePos.y + coll->Setup.Height, probePos.z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
{
|
||||
coll->MiddleRight.Floor = MAX_HEIGHT;
|
||||
}
|
||||
|
||||
// TEST 7: FRONT-RIGHT PROBE
|
||||
|
||||
collResult = GetCollision(x, y, z, topRoomNumber);
|
||||
collResult = GetCollision(probePos.x, probePos.y, probePos.z, topRoomNumber);
|
||||
|
||||
if (playerCollision)
|
||||
if (doPlayerCollision)
|
||||
{
|
||||
tfLocation = GetRoom(tfLocation, x, y, z);
|
||||
height = GetFloorHeight(tfLocation, x, z).value_or(NO_HEIGHT);
|
||||
tfLocation = GetRoom(tfLocation, probePos.x, probePos.y, probePos.z);
|
||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
|
||||
tcLocation = GetRoom(tcLocation, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeilingHeight(tcLocation, x, z).value_or(NO_HEIGHT);
|
||||
tcLocation = GetRoom(tcLocation, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
height = collResult.Position.Floor;
|
||||
ceiling = GetCeiling(collResult.Block, x, y - item->Animation.Velocity.y, z);
|
||||
ceiling = GetCeiling(collResult.Block, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
||||
}
|
||||
if (height != NO_HEIGHT) height -= (playerCollision ? yPos : y);
|
||||
if (ceiling != NO_HEIGHT) ceiling -= y;
|
||||
|
||||
if (height != NO_HEIGHT)
|
||||
height -= (doPlayerCollision ? entityPos.y : probePos.y);
|
||||
|
||||
if (ceiling != NO_HEIGHT)
|
||||
ceiling -= probePos.y;
|
||||
|
||||
coll->FrontRight = collResult.Position;
|
||||
coll->FrontRight.Floor = height;
|
||||
|
@ -631,7 +649,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
coll->FrontRight.Floor = STOP_SIZE;
|
||||
}
|
||||
else if (coll->Setup.BlockMonkeySwingEdge &&
|
||||
!GetCollision(x, y + coll->Setup.Height, z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
!GetCollision(probePos.x, probePos.y + coll->Setup.Height, probePos.z, item->RoomNumber).BottomBlock->Flags.Monkeyswing)
|
||||
{
|
||||
coll->FrontRight.Floor = MAX_HEIGHT;
|
||||
}
|
||||
|
@ -645,18 +663,14 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
|
||||
if (coll->Middle.Floor == NO_HEIGHT)
|
||||
{
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
|
||||
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
|
||||
coll->Shift = coll->Setup.OldPosition - entityPos;
|
||||
coll->CollisionType = CT_FRONT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->Middle.Floor - coll->Middle.Ceiling <= 0)
|
||||
{
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
|
||||
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
|
||||
coll->Shift = coll->Setup.OldPosition - entityPos;
|
||||
coll->CollisionType = CT_CLAMP;
|
||||
return;
|
||||
}
|
||||
|
@ -675,8 +689,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
{
|
||||
if (coll->Front.HasDiagonalSplit())
|
||||
{
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -684,28 +698,26 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
{
|
||||
case 0:
|
||||
case 2:
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
|
||||
coll->Shift.z = FindGridShift(zPos + zfront, zPos);
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x;
|
||||
coll->Shift.z = FindGridShift(entityPos.z + zFront, entityPos.z);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
coll->Shift.x = FindGridShift(xPos + xfront, xPos);
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
|
||||
coll->Shift.x = FindGridShift(entityPos.x + xFront, entityPos.x);
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
coll->CollisionType = (coll->CollisionType == CT_TOP ? CT_TOP_FRONT : CT_FRONT);
|
||||
coll->CollisionType = ((coll->CollisionType == CT_TOP) ? CT_TOP_FRONT : CT_FRONT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->Front.Ceiling > coll->Setup.LowerCeilingBound ||
|
||||
coll->Front.Ceiling < coll->Setup.UpperCeilingBound)
|
||||
{
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
|
||||
coll->Shift.y = coll->Setup.OldPosition.y - yPos;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
|
||||
coll->Shift = coll->Setup.OldPosition - entityPos;
|
||||
coll->CollisionType = CT_TOP_FRONT;
|
||||
return;
|
||||
}
|
||||
|
@ -721,8 +733,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
// HACK: Force slight push-out to the left side to avoid stucking
|
||||
TranslateItem(item, coll->Setup.ForwardAngle + ANGLE(8.0f), item->Animation.Velocity.z);
|
||||
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -730,34 +742,28 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
{
|
||||
case 0:
|
||||
case 2:
|
||||
coll->Shift.x = FindGridShift(xPos + xleft, xPos + xfront);
|
||||
coll->Shift.x = FindGridShift(entityPos.x + xLeft, entityPos.x + xFront);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
coll->Shift.z = FindGridShift(zPos + zleft, zPos + zfront);
|
||||
coll->Shift.z = FindGridShift(entityPos.z + zLeft, entityPos.z + zFront);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (coll->DiagonalStepAtLeft())
|
||||
{
|
||||
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
|
||||
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90.0f); // NOTE: Different from quadrant!
|
||||
quarter %= 2;
|
||||
|
||||
if (coll->MiddleLeft.HasFlippedDiagonalSplit())
|
||||
{
|
||||
if (quarter) coll->CollisionType = CT_LEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!quarter) coll->CollisionType = CT_LEFT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
coll->CollisionType = CT_LEFT;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -770,11 +776,11 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
{
|
||||
if (coll->TriangleAtRight() && !coll->MiddleRight.FloorSlope)
|
||||
{
|
||||
// HACK: Force slight push-out to the right side to avoid stucking
|
||||
// HACK: Force slight push out to the right side to avoid getting stuck.
|
||||
TranslateItem(item, coll->Setup.ForwardAngle - ANGLE(8.0f), item->Animation.Velocity.z);
|
||||
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - xPos;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - zPos;
|
||||
coll->Shift.x = coll->Setup.OldPosition.x - entityPos.x;
|
||||
coll->Shift.z = coll->Setup.OldPosition.z - entityPos.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -782,85 +788,64 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, Vector3Int offset, bo
|
|||
{
|
||||
case 0:
|
||||
case 2:
|
||||
coll->Shift.x = FindGridShift(xPos + xright, xPos + xfront);
|
||||
coll->Shift.x = FindGridShift(entityPos.x + xRight, entityPos.x + xFront);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
coll->Shift.z = FindGridShift(zPos + zright, zPos + zfront);
|
||||
coll->Shift.z = FindGridShift(entityPos.z + zRight, entityPos.z + zFront);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (coll->DiagonalStepAtRight())
|
||||
{
|
||||
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90); // different from quadrant!
|
||||
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90.0f); // NOTE: Different from quadrant!
|
||||
quarter %= 2;
|
||||
|
||||
if (coll->MiddleRight.HasFlippedDiagonalSplit())
|
||||
{
|
||||
if (quarter) coll->CollisionType = CT_RIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!quarter) coll->CollisionType = CT_RIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
coll->CollisionType = CT_RIGHT;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// New function for rotating item along XZ slopes.
|
||||
// (int radiusDivide) is for radiusZ, else the MaxZ is too high and cause rotation problem !
|
||||
// Dont need to set a value in radiusDivisor if you dont need it (radiusDivisor is set to 1 by default).
|
||||
// Warning: dont set it to 0 !!!!
|
||||
void CalculateItemRotationToSurface(ItemInfo* item, float radiusDivisor, short xOffset, short zOffset)
|
||||
void AlignEntityToSurface(ItemInfo* item, Vector2 radius, float tiltConstraintAngle, Vector3Shrt tiltOffset)
|
||||
{
|
||||
if (!radiusDivisor)
|
||||
{
|
||||
TENLog(std::string("CalculateItemRotationToSurface() attempted division by zero!"), LogLevel::Warning);
|
||||
return;
|
||||
}
|
||||
// Reduce probe radii for stability.
|
||||
auto halvedRadius = radius / 2;
|
||||
|
||||
auto pos = GameVector(
|
||||
item->Pose.Position.x,
|
||||
item->Pose.Position.y,
|
||||
item->Pose.Position.z,
|
||||
item->RoomNumber
|
||||
// Probe heights at points around the entity.
|
||||
int frontHeight = GetCollision(item, item->Pose.Orientation.y, halvedRadius.y).Position.Floor;
|
||||
int backHeight = GetCollision(item, item->Pose.Orientation.y + ANGLE(180.0f), halvedRadius.y).Position.Floor;
|
||||
int leftHeight = GetCollision(item, item->Pose.Orientation.y - ANGLE(90.0f), halvedRadius.x).Position.Floor;
|
||||
int rightHeight = GetCollision(item, item->Pose.Orientation.y + ANGLE(90.0f), halvedRadius.x).Position.Floor;
|
||||
|
||||
// Calculate height differences.
|
||||
int forwardHeightDif = backHeight - frontHeight;
|
||||
int lateralHeightDif = rightHeight - leftHeight;
|
||||
|
||||
// Don't align if height differences are too significant.
|
||||
if ((abs(forwardHeightDif) > STEPUP_HEIGHT) || (abs(lateralHeightDif) > STEPUP_HEIGHT))
|
||||
return;
|
||||
|
||||
// Calculate and apply tilts.
|
||||
auto tiltedOrient = Vector3Shrt(
|
||||
phd_atan(radius.y * 2, forwardHeightDif) + tiltOffset.x,
|
||||
0,
|
||||
phd_atan(radius.x * 2, lateralHeightDif) + tiltOffset.z
|
||||
);
|
||||
|
||||
auto* bounds = GetBoundsAccurate(item);
|
||||
auto radiusX = bounds->X2;
|
||||
auto radiusZ = bounds->Z2 / radiusDivisor; // Need divide in any case else it's too much !
|
||||
if (abs(tiltedOrient.x) <= ANGLE(tiltConstraintAngle))
|
||||
item->Pose.Orientation.x = tiltedOrient.x;
|
||||
|
||||
auto ratioXZ = radiusZ / radiusX;
|
||||
auto frontX = phd_sin(item->Pose.Orientation.y) * radiusZ;
|
||||
auto frontZ = phd_cos(item->Pose.Orientation.y) * radiusZ;
|
||||
auto leftX = -frontZ * ratioXZ;
|
||||
auto leftZ = frontX * ratioXZ;
|
||||
auto rightX = frontZ * ratioXZ;
|
||||
auto rightZ = -frontX * ratioXZ;
|
||||
|
||||
auto frontHeight = GetCollision(pos.x + frontX, pos.y, pos.z + frontZ, pos.roomNumber).Position.Floor;
|
||||
auto backHeight = GetCollision(pos.x - frontX, pos.y, pos.z - frontZ, pos.roomNumber).Position.Floor;
|
||||
auto leftHeight = GetCollision(pos.x + leftX, pos.y, pos.z + leftZ, pos.roomNumber).Position.Floor;
|
||||
auto rightHeight = GetCollision(pos.x + rightX, pos.y, pos.z + rightZ, pos.roomNumber).Position.Floor;
|
||||
|
||||
auto frontHDif = backHeight - frontHeight;
|
||||
auto sideHDif = rightHeight - leftHeight;
|
||||
|
||||
// Don't align if height differences are too large
|
||||
if ((abs(frontHDif) > STEPUP_HEIGHT) || (abs(sideHDif) > STEPUP_HEIGHT))
|
||||
return;
|
||||
|
||||
// NOTE: float(atan2()) is required, else warning about double !
|
||||
item->Pose.Orientation.x = ANGLE(float(atan2(frontHDif, 2 * radiusZ)) / RADIAN) + xOffset;
|
||||
item->Pose.Orientation.z = ANGLE(float(atan2(sideHDif, 2 * radiusX)) / RADIAN) + zOffset;
|
||||
if (abs(tiltedOrient.z) <= ANGLE(tiltConstraintAngle))
|
||||
item->Pose.Orientation.z = tiltedOrient.z;
|
||||
}
|
||||
|
||||
int GetQuadrant(short angle)
|
||||
|
@ -890,7 +875,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
|
|||
|
||||
// Determine two Y points to test (lower and higher).
|
||||
// 1/10 headroom crop is needed to avoid possible issues with tight diagonal headrooms.
|
||||
int headroom = abs(bounds->Y2 - bounds->Y1) / 20.0f;
|
||||
int headroom = bounds->Height() / 20.0f;
|
||||
int yPoints[2] = { item->Pose.Position.y + bounds->Y1 + headroom,
|
||||
item->Pose.Position.y + bounds->Y2 - headroom };
|
||||
|
||||
|
@ -1453,15 +1438,15 @@ int GetWaterHeight(ItemInfo* item)
|
|||
return GetWaterHeight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
|
||||
}
|
||||
|
||||
short GetSurfaceSteepnessAngle(float xTilt, float zTilt)
|
||||
short GetSurfaceSteepnessAngle(Vector2 tilt)
|
||||
{
|
||||
short qtrBlockAngleIncrement = ANGLE(45.0f) / 4;
|
||||
return (short)sqrt(pow(xTilt * qtrBlockAngleIncrement, 2) + pow(zTilt * qtrBlockAngleIncrement, 2));
|
||||
return (short)sqrt(pow(tilt.x * qtrBlockAngleIncrement, 2) + pow(tilt.y * qtrBlockAngleIncrement, 2));
|
||||
}
|
||||
|
||||
short GetSurfaceAspectAngle(float xTilt, float zTilt)
|
||||
short GetSurfaceAspectAngle(Vector2 tilt)
|
||||
{
|
||||
return (short)phd_atan(-zTilt, -xTilt);
|
||||
return (short)phd_atan(-tilt.y, -tilt.x);
|
||||
}
|
||||
|
||||
bool TestEnvironment(RoomEnvFlags environmentType, int x, int y, int z, int roomNumber)
|
||||
|
|
|
@ -152,10 +152,10 @@ void SnapItemToLedge(ItemInfo* item, CollisionInfo* coll, float offsetMultiplier
|
|||
void SnapItemToLedge(ItemInfo* item, CollisionInfo* coll, short angle, float offsetMultiplier = 0.0f);
|
||||
void SnapItemToGrid(ItemInfo* item, CollisionInfo* coll);
|
||||
|
||||
void CalculateItemRotationToSurface(ItemInfo* item, float radiusDivisor = 1.0f, short xOffset = 0, short zOffset = 0);
|
||||
void AlignEntityToSurface(ItemInfo* item, Vector2 radius, float tiltConstraintAngle = 45.0f, Vector3Shrt tiltOffset = Vector3Shrt::Zero);
|
||||
|
||||
short GetSurfaceAspectAngle(float xTilt, float zTilt);
|
||||
short GetSurfaceSteepnessAngle(float xTilt, float zTilt);
|
||||
short GetSurfaceAspectAngle(Vector2 tilt);
|
||||
short GetSurfaceSteepnessAngle(Vector2 tilt);
|
||||
|
||||
bool TestEnvironment(RoomEnvFlags environmentType, int x, int y, int z, int roomNumber);
|
||||
bool TestEnvironment(RoomEnvFlags environmentType, ItemInfo* item);
|
||||
|
|
|
@ -8,14 +8,15 @@
|
|||
#include "Game/control/control.h"
|
||||
#include "Game/control/lot.h"
|
||||
#include "Game/effects/tomb4fx.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/Lara/lara_helpers.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/room.h"
|
||||
#include "Specific/setup.h"
|
||||
#include "Specific/trmath.h"
|
||||
#include "Objects/objectslist.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Objects/TR5/Object/tr5_pushableblock.h"
|
||||
#include "Renderer/Renderer11.h"
|
||||
|
||||
|
@ -296,19 +297,17 @@ void CreatureKill(ItemInfo* item, int killAnim, int killState, int laraKillState
|
|||
*/
|
||||
}
|
||||
|
||||
short CreatureEffect2(ItemInfo* item, BITE_INFO* bite, short damage, short angle, std::function<CreatureEffectFunction> func)
|
||||
short CreatureEffect2(ItemInfo* item, BiteInfo bite, short velocity, short angle, std::function<CreatureEffectFunction> func)
|
||||
{
|
||||
auto pos = Vector3Int(bite->x, bite->y, bite->z);
|
||||
GetJointAbsPosition(item, &pos, bite->meshNum);
|
||||
|
||||
return func(pos.x, pos.y, pos.z, damage, angle, item->RoomNumber);
|
||||
auto pos = Vector3Int(bite.Position);
|
||||
GetJointAbsPosition(item, &pos, bite.meshNum);
|
||||
return func(pos.x, pos.y, pos.z, velocity, angle, item->RoomNumber);
|
||||
}
|
||||
|
||||
short CreatureEffect(ItemInfo* item, BITE_INFO* bite, std::function<CreatureEffectFunction> func)
|
||||
short CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func)
|
||||
{
|
||||
auto pos = Vector3Int(bite->x, bite->y, bite->z);
|
||||
GetJointAbsPosition(item, &pos, bite->meshNum);
|
||||
|
||||
auto pos = Vector3Int(bite.Position);
|
||||
GetJointAbsPosition(item, &pos, bite.meshNum);
|
||||
return func(pos.x, pos.y, pos.z, item->Animation.Velocity.z, item->Pose.Orientation.y, item->RoomNumber);
|
||||
}
|
||||
|
||||
|
@ -1390,7 +1389,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
|||
item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box;
|
||||
|
||||
room = &g_Level.Rooms[aiObject->roomNumber];
|
||||
aiObject->boxNumber = GetSector(room, aiObject->x - room->x, aiObject->z - room->z)->Box;
|
||||
aiObject->boxNumber = GetSector(room, aiObject->pos.Position.x - room->x, aiObject->pos.Position.z - room->z)->Box;
|
||||
|
||||
if (item->BoxNumber == NO_BOX || aiObject->boxNumber == NO_BOX)
|
||||
return;
|
||||
|
@ -1411,10 +1410,10 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
|||
|
||||
aiItem->ObjectNumber = foundObject->objectNumber;
|
||||
aiItem->RoomNumber = foundObject->roomNumber;
|
||||
aiItem->Pose.Position.x = foundObject->x;
|
||||
aiItem->Pose.Position.y = foundObject->y;
|
||||
aiItem->Pose.Position.z = foundObject->z;
|
||||
aiItem->Pose.Orientation.y = foundObject->yRot;
|
||||
aiItem->Pose.Position.x = foundObject->pos.Position.x;
|
||||
aiItem->Pose.Position.y = foundObject->pos.Position.y;
|
||||
aiItem->Pose.Position.z = foundObject->pos.Position.z;
|
||||
aiItem->Pose.Orientation.y = foundObject->pos.Orientation.y;
|
||||
aiItem->Flags = foundObject->flags;
|
||||
aiItem->TriggerFlags = foundObject->triggerFlags;
|
||||
aiItem->BoxNumber = foundObject->boxNumber;
|
||||
|
@ -1436,8 +1435,9 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
|
|||
auto* creature = GetCreatureInfo(item);
|
||||
auto* object = &Objects[item->ObjectNumber];
|
||||
|
||||
// TODO: Deal with LaraItem global.
|
||||
auto* enemy = creature->Enemy;
|
||||
if (!enemy)
|
||||
if (enemy == nullptr)
|
||||
{
|
||||
enemy = LaraItem;
|
||||
creature->Enemy = LaraItem;
|
||||
|
@ -1475,7 +1475,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
|
|||
auto probe = GetCollision(floor, enemy->Pose.Position.x, enemy->Pose.Position.y, enemy->Pose.Position.z);
|
||||
auto bounds = GetBoundsAccurate(item);
|
||||
|
||||
reachable = abs(enemy->Pose.Position.y - probe.Position.Floor) < abs(bounds->Y2 - bounds->Y1);
|
||||
reachable = abs(enemy->Pose.Position.y - probe.Position.Floor) < bounds->Height();
|
||||
}
|
||||
|
||||
if (floor && reachable)
|
||||
|
@ -1532,8 +1532,11 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
|
|||
vector.z = abs(vector.z);
|
||||
|
||||
// Makes Lara smaller.
|
||||
if (enemy == LaraItem && ((LaraInfo*)enemy)->Control.IsLow)
|
||||
vector.y -= STEPUP_HEIGHT;
|
||||
if (enemy->IsLara())
|
||||
{
|
||||
if (GetLaraInfo(enemy)->Control.IsLow)
|
||||
vector.y -= STEPUP_HEIGHT;
|
||||
}
|
||||
|
||||
if (vector.x > vector.z)
|
||||
AI->xAngle = phd_atan(vector.x + (vector.z >> 1), vector.y);
|
||||
|
@ -1544,7 +1547,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
|
|||
AI->bite = (AI->ahead && enemy->HitPoints > 0 && abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(2));
|
||||
}
|
||||
|
||||
void CreatureMood(ItemInfo* item, AI_INFO* AI, int violent)
|
||||
void CreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent)
|
||||
{
|
||||
if (!item->Data)
|
||||
return;
|
||||
|
@ -1592,7 +1595,7 @@ void CreatureMood(ItemInfo* item, AI_INFO* AI, int violent)
|
|||
{
|
||||
if (EscapeBox(item, enemy, boxNumber))
|
||||
TargetBox(LOT, boxNumber);
|
||||
else if (AI->zoneNumber == AI->enemyZone && StalkBox(item, enemy, boxNumber) && !violent)
|
||||
else if (AI->zoneNumber == AI->enemyZone && StalkBox(item, enemy, boxNumber) && !isViolent)
|
||||
{
|
||||
TargetBox(LOT, boxNumber);
|
||||
creature->Mood = MoodType::Stalk;
|
||||
|
@ -1694,7 +1697,7 @@ void CreatureMood(ItemInfo* item, AI_INFO* AI, int violent)
|
|||
}
|
||||
}
|
||||
|
||||
void GetCreatureMood(ItemInfo* item, AI_INFO* AI, int isViolent)
|
||||
void GetCreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent)
|
||||
{
|
||||
if (!item->Data)
|
||||
return;
|
||||
|
@ -2082,15 +2085,13 @@ void InitialiseItemBoxData()
|
|||
if (!(g_Level.Boxes[floor->Box].flags & BLOCKED))
|
||||
{
|
||||
int floorHeight = floor->FloorHeight(mesh.pos.Position.x, mesh.pos.Position.z);
|
||||
auto* staticInfo = &StaticObjects[mesh.staticNumber];
|
||||
auto bbox = GetBoundsAccurate(&mesh, false);
|
||||
|
||||
if (floorHeight <= mesh.pos.Position.y - staticInfo->collisionBox.Y2 + CLICK(2) &&
|
||||
floorHeight < mesh.pos.Position.y - staticInfo->collisionBox.Y1)
|
||||
if (floorHeight <= mesh.pos.Position.y - bbox->Y2 + CLICK(2) &&
|
||||
floorHeight < mesh.pos.Position.y - bbox->Y1)
|
||||
{
|
||||
if (staticInfo->collisionBox.X1 == 0 || staticInfo->collisionBox.X2 == 0 ||
|
||||
staticInfo->collisionBox.Z1 == 0 || staticInfo->collisionBox.Z2 == 0 ||
|
||||
((staticInfo->collisionBox.X1 < 0) ^ (staticInfo->collisionBox.X2 < 0)) &&
|
||||
((staticInfo->collisionBox.Z1 < 0) ^ (staticInfo->collisionBox.Z2 < 0)))
|
||||
if (bbox->X1 == 0 || bbox->X2 == 0 || bbox->Z1 == 0 || bbox->Z2 == 0 ||
|
||||
((bbox->X1 < 0) ^ (bbox->X2 < 0)) && ((bbox->Z1 < 0) ^ (bbox->Z2 < 0)))
|
||||
{
|
||||
floor->Stopper = true;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
#include "Specific/phd_global.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/phd_global.h"
|
||||
|
||||
struct ItemInfo;
|
||||
struct BITE_INFO;
|
||||
struct BiteInfo;
|
||||
struct CreatureInfo;
|
||||
struct ItemInfo;
|
||||
struct LOTInfo;
|
||||
|
||||
enum TARGET_TYPE
|
||||
|
@ -93,26 +93,26 @@ struct OVERLAP
|
|||
int flags;
|
||||
};
|
||||
|
||||
struct BITE_INFO
|
||||
struct BiteInfo
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
int meshNum;
|
||||
Vector3 Position = Vector3::Zero;
|
||||
int meshNum = 0;
|
||||
|
||||
BITE_INFO()
|
||||
BiteInfo()
|
||||
{
|
||||
this->x = 0;
|
||||
this->y = 0;
|
||||
this->z = 0;
|
||||
this->Position = Vector3::Zero;
|
||||
this->meshNum = 0;
|
||||
}
|
||||
|
||||
BITE_INFO(int xpos, int ypos, int zpos, int meshNumber)
|
||||
BiteInfo(Vector3 pos, int meshNumber)
|
||||
{
|
||||
this->x = xpos;
|
||||
this->y = ypos;
|
||||
this->z = zpos;
|
||||
this->Position = pos;
|
||||
this->meshNum = meshNumber;
|
||||
}
|
||||
|
||||
BiteInfo(float xPos, float yPos, float zPos, int meshNumber)
|
||||
{
|
||||
this->Position = Vector3(xPos, yPos, zPos);
|
||||
this->meshNum = meshNumber;
|
||||
}
|
||||
};
|
||||
|
@ -121,8 +121,8 @@ struct BITE_INFO
|
|||
|
||||
constexpr auto BOX_BLOCKED = (1 << 14); // unpassable for other enemies, always set for movable blocks & closed doors
|
||||
constexpr auto BOX_LAST = (1 << 15); // unpassable by large enemies (T-Rex, Centaur, etc), always set behind doors
|
||||
constexpr auto TIMID = 0;
|
||||
constexpr auto VIOLENT = 1;
|
||||
|
||||
|
||||
constexpr auto REVERSE = 0x4000;
|
||||
constexpr auto BLOCKABLE = 0x8000;
|
||||
constexpr auto BLOCKED = 0x4000;
|
||||
|
@ -146,8 +146,8 @@ constexpr auto CLIP_BOTTOM = 0x8;
|
|||
constexpr auto SECONDARY_CLIP = 0x10;
|
||||
constexpr auto ALL_CLIP = (CLIP_LEFT | CLIP_RIGHT | CLIP_TOP | CLIP_BOTTOM);
|
||||
|
||||
void GetCreatureMood(ItemInfo* item, AI_INFO* AI, int violent);
|
||||
void CreatureMood(ItemInfo* item, AI_INFO* AI, int violent);
|
||||
void GetCreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent);
|
||||
void CreatureMood(ItemInfo* item, AI_INFO* AI, bool isViolent);
|
||||
void FindAITargetObject(CreatureInfo* creature, short objectNumber);
|
||||
void GetAITarget(CreatureInfo* creature);
|
||||
int CreatureVault(short itemNumber, short angle, int vault, int shift);
|
||||
|
@ -160,8 +160,8 @@ short AIGuard(CreatureInfo* creature);
|
|||
void AlertNearbyGuards(ItemInfo* item);
|
||||
void AlertAllGuards(short itemNumber);
|
||||
void CreatureKill(ItemInfo* item, int killAnim, int killState, int laraKillState);
|
||||
short CreatureEffect2(ItemInfo* item, BITE_INFO* bite, short damage, short angle, std::function<CreatureEffectFunction> func);
|
||||
short CreatureEffect(ItemInfo* item, BITE_INFO* bite, std::function<CreatureEffectFunction> func);
|
||||
short CreatureEffect2(ItemInfo* item, BiteInfo bite, short velocity, short angle, std::function<CreatureEffectFunction> func);
|
||||
short CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func);
|
||||
void CreatureUnderwater(ItemInfo* item, int depth);
|
||||
void CreatureFloat(short itemNumber);
|
||||
void CreatureJoint(ItemInfo* item, short joint, short required);
|
||||
|
@ -185,6 +185,5 @@ void CreatureSwitchRoom(short itemNumber);
|
|||
void AdjustStopperFlag(ItemInfo* item, int direction, bool set);
|
||||
void InitialiseItemBoxData();
|
||||
|
||||
|
||||
void DrawBox(int boxIndex, Vector3 color);
|
||||
void DrawNearbyPathfinding(int boxIndex);
|
||||
void DrawNearbyPathfinding(int boxIndex);
|
||||
|
|
|
@ -325,8 +325,9 @@ void FloorShake(ItemInfo* item)
|
|||
|
||||
void Turn180(ItemInfo* item)
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(180.0f);
|
||||
item->Pose.Orientation.x = -item->Pose.Orientation.x;
|
||||
item->Pose.Orientation.y += ANGLE(180.0f);
|
||||
item->Pose.Orientation.z = -item->Pose.Orientation.z;
|
||||
}
|
||||
|
||||
void FinishLevel(ItemInfo* item)
|
||||
|
|
|
@ -315,7 +315,7 @@ int ObjectOnLOS2(GameVector* start, GameVector* end, Vector3Int* vec, MESH_INFO*
|
|||
pos.Position.z = meshp->pos.Position.z;
|
||||
pos.Orientation.y = meshp->pos.Orientation.y;
|
||||
|
||||
if (DoRayBox(start, end, &StaticObjects[meshp->staticNumber].collisionBox, &pos, vec, -1 - meshp->staticNumber))
|
||||
if (DoRayBox(start, end, GetBoundsAccurate(meshp, false), &pos, vec, -1 - meshp->staticNumber))
|
||||
{
|
||||
*mesh = meshp;
|
||||
end->roomNumber = LosRooms[r];
|
||||
|
@ -333,7 +333,7 @@ int ObjectOnLOS2(GameVector* start, GameVector* end, Vector3Int* vec, MESH_INFO*
|
|||
if ((priorityObject != GAME_OBJECT_ID::ID_NO_OBJECT) && (item->ObjectNumber != priorityObject))
|
||||
continue;
|
||||
|
||||
if ((item->ObjectNumber != ID_LARA) && (Objects[item->ObjectNumber].collision == NULL))
|
||||
if ((item->ObjectNumber != ID_LARA) && (Objects[item->ObjectNumber].collision == nullptr))
|
||||
continue;
|
||||
|
||||
if ((item->ObjectNumber == ID_LARA) && (priorityObject != ID_LARA))
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Game/camera.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
@ -18,7 +19,7 @@ std::vector<CreatureInfo*> ActiveCreatures;
|
|||
void InitialiseLOTarray(int itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = (CreatureInfo*)item->Data;
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
if(!creature->LOT.Initialised)
|
||||
{
|
||||
|
@ -31,7 +32,7 @@ int EnableEntityAI(short itemNum, int always, bool makeTarget)
|
|||
{
|
||||
ItemInfo* item = &g_Level.Items[itemNum];
|
||||
|
||||
if (item->Data.is<CreatureInfo>())
|
||||
if (item->IsCreature())
|
||||
return true;
|
||||
|
||||
/*
|
||||
|
@ -99,12 +100,12 @@ int EnableEntityAI(short itemNum, int always, bool makeTarget)
|
|||
|
||||
void DisableEntityAI(short itemNumber)
|
||||
{
|
||||
ItemInfo* item = &g_Level.Items[itemNumber];
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
if (!item->IsCreature())
|
||||
return;
|
||||
|
||||
auto* creature = (CreatureInfo*)item->Data;
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
creature->ItemNumber = NO_ITEM;
|
||||
KillItem(creature->AITargetNumber);
|
||||
ActiveCreatures.erase(std::find(ActiveCreatures.begin(), ActiveCreatures.end(), creature));
|
||||
|
@ -113,11 +114,11 @@ void DisableEntityAI(short itemNumber)
|
|||
|
||||
void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
||||
{
|
||||
ItemInfo* item = &g_Level.Items[itemNum];
|
||||
ObjectInfo* obj = &Objects[item->ObjectNumber];
|
||||
auto* item = &g_Level.Items[itemNum];
|
||||
auto* obj = &Objects[item->ObjectNumber];
|
||||
|
||||
item->Data = CreatureInfo();
|
||||
CreatureInfo* creature = item->Data;
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
InitialiseLOTarray(itemNum);
|
||||
creature->ItemNumber = itemNum;
|
||||
creature->Mood = MoodType::Bored;
|
||||
|
@ -158,22 +159,22 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
{
|
||||
default:
|
||||
case ZONE_NULL:
|
||||
creature->LOT.Step = SECTOR(1) - CLICK(3);
|
||||
creature->LOT.Drop = -(SECTOR(1) - CLICK(3));
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
obj->zoneType = ZONE_BASIC; // only entity that use CreatureActive() will reach InitialiseSlot() !
|
||||
break;
|
||||
|
||||
case ZONE_SKELLY:
|
||||
// Can jump
|
||||
creature->LOT.Step = SECTOR(1) - CLICK(3);
|
||||
creature->LOT.Drop = -(SECTOR(1) - CLICK(3));
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
creature->LOT.CanJump = true;
|
||||
creature->LOT.Zone = ZONE_SKELLY;
|
||||
break;
|
||||
|
||||
case ZONE_BASIC:
|
||||
creature->LOT.Step = SECTOR(1) - CLICK(3);
|
||||
creature->LOT.Drop = -(SECTOR(1) - CLICK(3));
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
creature->LOT.Zone = ZONE_BASIC;
|
||||
break;
|
||||
|
||||
|
@ -195,7 +196,6 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
{
|
||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // is more slow than the other underwater entity
|
||||
creature->LOT.IsAmphibious = true; // crocodile can walk and swim.
|
||||
creature->LOT.Zone = ZONE_FLYER;
|
||||
}
|
||||
else if (item->ObjectNumber == ID_BIG_RAT)
|
||||
{
|
||||
|
@ -206,7 +206,6 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
{
|
||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ZONE_HUMAN_CLASSIC:
|
||||
|
@ -235,8 +234,8 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
|
||||
case ZONE_HUMAN_LONGJUMP_AND_MONKEY:
|
||||
// Can climb, jump, monkey, long jump
|
||||
creature->LOT.Step = 1792;
|
||||
creature->LOT.Drop = -1792;
|
||||
creature->LOT.Step = SECTOR(1) + CLICK(3);
|
||||
creature->LOT.Drop = -(SECTOR(1) + CLICK(3));
|
||||
creature->LOT.CanJump = true;
|
||||
creature->LOT.CanMonkey = true;
|
||||
creature->LOT.Zone = ZONE_VON_CROY;
|
||||
|
@ -253,8 +252,13 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
creature->LOT.Zone = ZONE_BASIC;
|
||||
break;
|
||||
|
||||
case ZONE_APE:
|
||||
creature->LOT.Step = CLICK(2);
|
||||
creature->LOT.Drop = -SECTOR(1);
|
||||
break;
|
||||
|
||||
case ZONE_SOPHIALEE:
|
||||
creature->LOT.Step = CLICK(4);
|
||||
creature->LOT.Step = SECTOR(1);
|
||||
creature->LOT.Drop = -CLICK(3);
|
||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
||||
break;
|
||||
|
@ -269,9 +273,8 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
|
||||
void SetBaddyTarget(short itemNum, short target)
|
||||
{
|
||||
ItemInfo* item = &g_Level.Items[itemNum];
|
||||
|
||||
CreatureInfo* creature = item->Data;
|
||||
auto* item = &g_Level.Items[itemNum];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
creature->AITargetNumber = target;
|
||||
|
||||
|
@ -289,7 +292,7 @@ void ClearLOT(LOTInfo* LOT)
|
|||
LOT->TargetBox = NO_BOX;
|
||||
LOT->RequiredBox = NO_BOX;
|
||||
|
||||
BOX_NODE* node = LOT->Node.data();
|
||||
auto* node = LOT->Node.data();
|
||||
for(auto& node : LOT->Node)
|
||||
{
|
||||
node.exitBox = NO_BOX;
|
||||
|
@ -300,8 +303,8 @@ void ClearLOT(LOTInfo* LOT)
|
|||
|
||||
void CreateZone(ItemInfo* item)
|
||||
{
|
||||
CreatureInfo* creature = (CreatureInfo*)item->Data;
|
||||
ROOM_INFO* r = &g_Level.Rooms[item->RoomNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
auto* r = &g_Level.Rooms[item->RoomNumber];
|
||||
|
||||
item->BoxNumber = GetSector(r, item->Pose.Position.x - r->x, item->Pose.Position.z - r->z)->Box;
|
||||
|
||||
|
@ -325,7 +328,7 @@ void CreateZone(ItemInfo* item)
|
|||
int zoneNumber = zone[item->BoxNumber];
|
||||
int flippedZoneNumber = flippedZone[item->BoxNumber];
|
||||
|
||||
BOX_NODE* node = creature->LOT.Node.data();
|
||||
auto* node = creature->LOT.Node.data();
|
||||
creature->LOT.ZoneCount = 0;
|
||||
|
||||
for (int i = 0; i < g_Level.Boxes.size(); i++)
|
||||
|
|
|
@ -100,13 +100,19 @@ namespace TEN::Control::Volumes
|
|||
{
|
||||
if (volume->Status == TriggerStatus::Inside)
|
||||
{
|
||||
volume->Triggerer = nullptr;
|
||||
volume->Status = TriggerStatus::Leaving;
|
||||
if (!set->OnLeave.Function.empty() && set->OnLeave.CallCounter != 0)
|
||||
// Only fire leave event when a certain timeout has passed.
|
||||
// This helps to filter out borderline cases when moving around volumes.
|
||||
|
||||
if (GameTimer - volume->Timeout > VOLUME_LEAVE_TIMEOUT)
|
||||
{
|
||||
g_GameScript->ExecuteFunction(set->OnLeave.Function, triggerer, set->OnLeave.Argument);
|
||||
if (set->OnLeave.CallCounter != NO_CALL_COUNTER)
|
||||
set->OnLeave.CallCounter--;
|
||||
volume->Triggerer = nullptr;
|
||||
volume->Status = TriggerStatus::Leaving;
|
||||
if (!set->OnLeave.Function.empty() && set->OnLeave.CallCounter != 0)
|
||||
{
|
||||
g_GameScript->ExecuteFunction(set->OnLeave.Function, triggerer, set->OnLeave.Argument);
|
||||
if (set->OnLeave.CallCounter != NO_CALL_COUNTER)
|
||||
set->OnLeave.CallCounter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -129,8 +135,7 @@ namespace TEN::Control::Volumes
|
|||
|
||||
void TestVolumes(short roomNumber, MESH_INFO* mesh)
|
||||
{
|
||||
auto* staticInfo = &StaticObjects[mesh->staticNumber];
|
||||
auto bbox = TO_DX_BBOX(mesh->pos, &staticInfo->collisionBox);
|
||||
auto bbox = TO_DX_BBOX(mesh->pos, GetBoundsAccurate(mesh, false));
|
||||
|
||||
TestVolumes(roomNumber, bbox, TriggerVolumeActivators::Static, mesh);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ constexpr auto NO_EVENT_SET = -1;
|
|||
constexpr auto NO_CALL_COUNTER = -1;
|
||||
|
||||
constexpr auto VOLUME_BUSY_TIMEOUT = 10;
|
||||
constexpr auto VOLUME_LEAVE_TIMEOUT = 5;
|
||||
|
||||
enum class TriggerStatus
|
||||
{
|
||||
|
|
|
@ -63,6 +63,7 @@ void ShatterObject(SHATTER_ITEM* item, MESH_INFO* mesh, int num, short roomNumbe
|
|||
{
|
||||
int meshIndex = 0;
|
||||
short yRot = 0;
|
||||
float scale;
|
||||
Vector3 pos;
|
||||
bool isStatic;
|
||||
|
||||
|
@ -72,6 +73,7 @@ void ShatterObject(SHATTER_ITEM* item, MESH_INFO* mesh, int num, short roomNumbe
|
|||
meshIndex = StaticObjects[mesh->staticNumber].meshNumber;
|
||||
yRot = mesh->pos.Orientation.y;
|
||||
pos = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z);
|
||||
scale = mesh->scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -79,6 +81,7 @@ void ShatterObject(SHATTER_ITEM* item, MESH_INFO* mesh, int num, short roomNumbe
|
|||
meshIndex = item->meshIndex;
|
||||
yRot = item->yRot;
|
||||
pos = Vector3(item->sphere.x, item->sphere.y, item->sphere.z);
|
||||
scale = 1.0f;
|
||||
}
|
||||
|
||||
auto fragmentsMesh = &g_Level.Meshes[meshIndex];
|
||||
|
@ -118,9 +121,9 @@ void ShatterObject(SHATTER_ITEM* item, MESH_INFO* mesh, int num, short roomNumbe
|
|||
|
||||
Matrix rotationMatrix = Matrix::CreateFromYawPitchRoll(TO_RAD(yRot), 0, 0);
|
||||
|
||||
Vector3 pos1 = fragmentsMesh->positions[poly->indices[indices[j * 3 + 0]]];
|
||||
Vector3 pos2 = fragmentsMesh->positions[poly->indices[indices[j * 3 + 1]]];
|
||||
Vector3 pos3 = fragmentsMesh->positions[poly->indices[indices[j * 3 + 2]]];
|
||||
Vector3 pos1 = fragmentsMesh->positions[poly->indices[indices[j * 3 + 0]]] * scale;
|
||||
Vector3 pos2 = fragmentsMesh->positions[poly->indices[indices[j * 3 + 1]]] * scale;
|
||||
Vector3 pos3 = fragmentsMesh->positions[poly->indices[indices[j * 3 + 2]]] * scale;
|
||||
|
||||
Vector2 uv1 = poly->textureCoordinates[indices[j * 3 + 0]];
|
||||
Vector2 uv2 = poly->textureCoordinates[indices[j * 3 + 1]];
|
||||
|
|
|
@ -1452,8 +1452,6 @@ void ExplodingDeath(short itemNumber, short flags)
|
|||
obj = &Objects[ID_LARA_SKIN];
|
||||
else
|
||||
obj = &Objects[item->ObjectNumber];
|
||||
|
||||
ANIM_FRAME* frame = GetBestFrame(item);
|
||||
|
||||
Matrix world = Matrix::CreateFromYawPitchRoll(
|
||||
TO_RAD(item->Pose.Orientation.y),
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
#pragma once
|
||||
#include <variant>
|
||||
#include <functional>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <variant>
|
||||
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/itemdata/door_data.h"
|
||||
#include "Game/Lara/lara_struct.h"
|
||||
#include "Objects/TR2/Vehicles/speedboat_info.h"
|
||||
#include "Objects/TR2/Vehicles/skidoo_info.h"
|
||||
#include "Objects/TR3/Vehicles/minecart_info.h"
|
||||
#include "Objects/TR2/Vehicles/speedboat_info.h"
|
||||
#include "Objects/TR3/Vehicles/big_gun_info.h"
|
||||
#include "Objects/TR3/Vehicles/kayak_info.h"
|
||||
#include "Objects/TR3/Vehicles/minecart_info.h"
|
||||
#include "Objects/TR3/Vehicles/quad_bike_info.h"
|
||||
#include "Objects/TR3/Vehicles/rubber_boat_info.h"
|
||||
#include "Objects/TR3/Vehicles/upv_info.h"
|
||||
#include "Objects/TR4/Entity/tr4_wraith_info.h"
|
||||
#include "Objects/TR4/Vehicles/jeep_info.h"
|
||||
#include "Objects/TR4/Vehicles/motorbike_info.h"
|
||||
#include "Objects/TR4/Entity/tr4_wraith_info.h"
|
||||
#include "Objects/TR5/Entity/tr5_laserhead_info.h"
|
||||
#include "Objects/TR5/Object/tr5_pushableblock_info.h"
|
||||
#include "Specific/phd_global.h"
|
||||
|
@ -25,6 +26,7 @@ template<class... Ts> struct visitor : Ts... { using Ts::operator()...; };
|
|||
template<class... Ts> visitor(Ts...)->visitor<Ts...>; // line not needed in C++20...
|
||||
|
||||
using namespace TEN::Entities::TR4;
|
||||
using namespace TEN::Entities::TR5;
|
||||
using namespace TEN::Entities::Vehicles;
|
||||
|
||||
struct ItemInfo;
|
||||
|
|
|
@ -17,65 +17,9 @@ using namespace TEN::Floordata;
|
|||
using namespace TEN::Input;
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
void ItemInfo::SetBits(JointBitType type, std::vector<int> jointIndices)
|
||||
{
|
||||
for (int i = 0; i < jointIndices.size(); i++)
|
||||
{
|
||||
unsigned int jointBit = unsigned int(1) << jointIndices[i];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case JointBitType::Touch:
|
||||
this->TouchBits |= jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::Mesh:
|
||||
this->MeshBits |= jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::MeshSwap:
|
||||
this->MeshSwapBits |= jointBit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ItemInfo::SetBits(JointBitType type, int jointIndex)
|
||||
{
|
||||
return SetBits(type, std::vector{ jointIndex });
|
||||
}
|
||||
|
||||
void ItemInfo::ClearBits(JointBitType type, std::vector<int> jointIndices)
|
||||
{
|
||||
for (int i = 0; i < jointIndices.size(); i++)
|
||||
{
|
||||
unsigned int jointBit = unsigned int(1) << jointIndices[i];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case JointBitType::Touch:
|
||||
this->TouchBits &= ~jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::Mesh:
|
||||
this->MeshBits &= ~jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::MeshSwap:
|
||||
this->MeshSwapBits &= ~jointBit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ItemInfo::ClearBits(JointBitType type, int jointIndex)
|
||||
{
|
||||
return ClearBits(type, std::vector{ jointIndex });
|
||||
}
|
||||
|
||||
bool ItemInfo::TestBits(JointBitType type, std::vector<int> jointIndices)
|
||||
{
|
||||
for (int i = 0; i < jointIndices.size(); i++)
|
||||
for (size_t i = 0; i < jointIndices.size(); i++)
|
||||
{
|
||||
unsigned int jointBit = unsigned int(1) << jointIndices[i];
|
||||
|
||||
|
@ -109,6 +53,93 @@ bool ItemInfo::TestBits(JointBitType type, int jointIndex)
|
|||
return TestBits(type, std::vector{ jointIndex });
|
||||
}
|
||||
|
||||
void ItemInfo::SetBits(JointBitType type, std::vector<int> jointIndices)
|
||||
{
|
||||
for (size_t i = 0; i < jointIndices.size(); i++)
|
||||
{
|
||||
unsigned int jointBit = unsigned int(1) << jointIndices[i];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case JointBitType::Touch:
|
||||
this->TouchBits |= jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::Mesh:
|
||||
this->MeshBits |= jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::MeshSwap:
|
||||
this->MeshSwapBits |= jointBit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ItemInfo::SetBits(JointBitType type, int jointIndex)
|
||||
{
|
||||
return SetBits(type, std::vector{ jointIndex });
|
||||
}
|
||||
|
||||
void ItemInfo::ClearBits(JointBitType type, std::vector<int> jointIndices)
|
||||
{
|
||||
for (size_t i = 0; i < jointIndices.size(); i++)
|
||||
{
|
||||
unsigned int jointBit = unsigned int(1) << jointIndices[i];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case JointBitType::Touch:
|
||||
this->TouchBits &= ~jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::Mesh:
|
||||
this->MeshBits &= ~jointBit;
|
||||
break;
|
||||
|
||||
case JointBitType::MeshSwap:
|
||||
this->MeshSwapBits &= ~jointBit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ItemInfo::ClearBits(JointBitType type, int jointIndex)
|
||||
{
|
||||
return ClearBits(type, std::vector{ jointIndex });
|
||||
}
|
||||
|
||||
bool ItemInfo::TestOcb(short ocbFlags)
|
||||
{
|
||||
return ((TriggerFlags & ocbFlags) == ocbFlags);
|
||||
}
|
||||
|
||||
void ItemInfo::RemoveOcb(short ocbFlags)
|
||||
{
|
||||
TriggerFlags &= ~ocbFlags;
|
||||
}
|
||||
|
||||
void ItemInfo::ClearAllOcb()
|
||||
{
|
||||
TriggerFlags = NULL;
|
||||
}
|
||||
|
||||
bool ItemInfo::TestFlags(short id, short value)
|
||||
{
|
||||
if (id < 0 || id > 7)
|
||||
return false;
|
||||
|
||||
return (ItemFlags[id] == value);
|
||||
}
|
||||
|
||||
void ItemInfo::SetFlags(short id, short value)
|
||||
{
|
||||
if (id < 0 || id > 7)
|
||||
return;
|
||||
|
||||
ItemFlags[id] = value;
|
||||
}
|
||||
|
||||
bool ItemInfo::IsLara()
|
||||
{
|
||||
return this->Data.is<LaraInfo*>();
|
||||
|
@ -122,8 +153,6 @@ bool ItemInfo::IsCreature()
|
|||
void ClearItem(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||
|
||||
item->Collidable = true;
|
||||
item->Data = nullptr;
|
||||
item->StartPose = item->Pose;
|
||||
|
@ -330,11 +359,11 @@ void KillEffect(short fxNumber)
|
|||
NextFxActive = fx->nextActive;
|
||||
else
|
||||
{
|
||||
for (short linknum = NextFxActive; linknum != NO_ITEM; linknum = EffectList[linknum].nextActive)
|
||||
for (short linkNumber = NextFxActive; linkNumber != NO_ITEM; linkNumber = EffectList[linkNumber].nextActive)
|
||||
{
|
||||
if (EffectList[linknum].nextActive == fxNumber)
|
||||
if (EffectList[linkNumber].nextActive == fxNumber)
|
||||
{
|
||||
EffectList[linknum].nextActive = fx->nextActive;
|
||||
EffectList[linkNumber].nextActive = fx->nextActive;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -344,11 +373,11 @@ void KillEffect(short fxNumber)
|
|||
g_Level.Rooms[fx->roomNumber].fxNumber = fx->nextFx;
|
||||
else
|
||||
{
|
||||
for (short linknum = g_Level.Rooms[fx->roomNumber].fxNumber; linknum != NO_ITEM; linknum = EffectList[linknum].nextFx)
|
||||
for (short linkNumber = g_Level.Rooms[fx->roomNumber].fxNumber; linkNumber != NO_ITEM; linkNumber = EffectList[linkNumber].nextFx)
|
||||
{
|
||||
if (EffectList[linknum].nextFx == fxNumber)
|
||||
if (EffectList[linkNumber].nextFx == fxNumber)
|
||||
{
|
||||
EffectList[linknum].nextFx = fx->nextFx;
|
||||
EffectList[linkNumber].nextFx = fx->nextFx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +388,7 @@ void KillEffect(short fxNumber)
|
|||
}
|
||||
}
|
||||
|
||||
short CreateNewEffect(short roomNum)
|
||||
short CreateNewEffect(short roomNumber)
|
||||
{
|
||||
short fxNumber = NextFxFree;
|
||||
|
||||
|
@ -368,8 +397,9 @@ short CreateNewEffect(short roomNum)
|
|||
auto* fx = &EffectList[NextFxFree];
|
||||
NextFxFree = fx->nextFx;
|
||||
|
||||
auto* room = &g_Level.Rooms[roomNum];
|
||||
fx->roomNumber = roomNum;
|
||||
auto* room = &g_Level.Rooms[roomNumber];
|
||||
|
||||
fx->roomNumber = roomNumber;
|
||||
fx->nextFx = room->fxNumber;
|
||||
room->fxNumber = fxNumber;
|
||||
fx->nextActive = NextFxActive;
|
||||
|
@ -456,9 +486,6 @@ void InitialiseItem(short itemNumber)
|
|||
item->Animation.TargetState = g_Level.Anims[item->Animation.AnimNumber].ActiveState;
|
||||
item->Animation.ActiveState = g_Level.Anims[item->Animation.AnimNumber].ActiveState;
|
||||
|
||||
item->Pose.Orientation.z = 0;
|
||||
item->Pose.Orientation.x = 0;
|
||||
|
||||
item->Animation.Velocity.y = 0;
|
||||
item->Animation.Velocity.z = 0;
|
||||
|
||||
|
|
|
@ -114,12 +114,19 @@ struct ItemInfo
|
|||
std::string LuaCallbackOnCollidedWithObjectName;
|
||||
std::string LuaCallbackOnCollidedWithRoomName;
|
||||
|
||||
bool TestBits(JointBitType type, std::vector<int> jointIndices);
|
||||
bool TestBits(JointBitType type, int jointIndex);
|
||||
void SetBits(JointBitType type, std::vector<int> jointIndices);
|
||||
void SetBits(JointBitType type, int jointIndex);
|
||||
void ClearBits(JointBitType type, std::vector<int> jointIndices);
|
||||
void ClearBits(JointBitType type, int jointIndex);
|
||||
bool TestBits(JointBitType type, std::vector<int> jointIndices);
|
||||
bool TestBits(JointBitType type, int jointIndex);
|
||||
|
||||
bool TestOcb(short ocbFlags);
|
||||
void RemoveOcb(short ocbFlags);
|
||||
void ClearAllOcb();
|
||||
|
||||
bool TestFlags(short id, short value);
|
||||
void SetFlags(short id, short value);
|
||||
|
||||
bool IsLara();
|
||||
bool IsCreature();
|
||||
|
|
|
@ -12,7 +12,7 @@ using std::vector;
|
|||
|
||||
CreatureInfo* GetCreatureInfo(ItemInfo* item)
|
||||
{
|
||||
return (CreatureInfo*)item->Data;
|
||||
return static_cast<CreatureInfo*>(item->Data);
|
||||
}
|
||||
|
||||
void TargetNearestEntity(ItemInfo* item, CreatureInfo* creature)
|
||||
|
@ -33,7 +33,7 @@ void TargetNearestEntity(ItemInfo* item, CreatureInfo* creature)
|
|||
int y = target->Pose.Position.y - item->Pose.Position.y;
|
||||
int z = target->Pose.Position.z - item->Pose.Position.z;
|
||||
|
||||
int distance = pow(x, 2) + pow(y, 2) + pow(z, 2);
|
||||
int distance = SQUARE(x) + SQUARE(y) + SQUARE(z);
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
creature->Enemy = target;
|
||||
|
|
|
@ -4,20 +4,22 @@
|
|||
#include "Game/animation.h"
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/effects/tomb4fx.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/effects/explosion.h"
|
||||
#include "Game/effects/bubble.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/items.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
#define SHARD_DAMAGE 30
|
||||
#define ROCKET_DAMAGE 100
|
||||
#define DIVER_HARPOON_DAMAGE 50
|
||||
using namespace TEN::Effects::Explosion;
|
||||
|
||||
#define SHARD_VELOCITY 250
|
||||
#define ROCKET_VELOCITY 220
|
||||
#define NATLA_GUN_VELOCITY 400
|
||||
#define MUTANT_SHARD_DAMAGE 30
|
||||
#define MUTANT_BOMB_DAMAGE 100
|
||||
#define DIVER_HARPOON_DAMAGE 50
|
||||
#define KNIFE_DAMAGE 50
|
||||
|
||||
void ShootAtLara(FX_INFO *fx)
|
||||
{
|
||||
|
@ -33,91 +35,97 @@ void ShootAtLara(FX_INFO *fx)
|
|||
fx->pos.Orientation.y = phd_atan(z, x);
|
||||
|
||||
// Random scatter (only a little bit else it's too hard to avoid).
|
||||
fx->pos.Orientation.x += (GetRandomControl() - 0x4000) / 0x40;
|
||||
fx->pos.Orientation.y += (GetRandomControl() - 0x4000) / 0x40;
|
||||
fx->pos.Orientation.x += (GetRandomControl() - ANGLE(90.0f)) / 64;
|
||||
fx->pos.Orientation.y += (GetRandomControl() - ANGLE(90.0f)) / 64;
|
||||
}
|
||||
|
||||
void ControlMissile(short fxNumber)
|
||||
{
|
||||
auto* fx = &EffectList[fxNumber];
|
||||
auto* fx = &EffectList[fxNumber]; // TODO: add fx->target (ItemInfo* target) to get the actual target (if it was really it)
|
||||
auto isUnderwater = TestEnvironment(ENV_FLAG_WATER, fx->roomNumber);
|
||||
auto soundFxType = isUnderwater ? SoundEnvironment::Water : SoundEnvironment::Land;
|
||||
|
||||
if (fx->objectNumber == ID_SCUBA_HARPOON && !TestEnvironment(ENV_FLAG_WATER, fx->roomNumber) && fx->pos.Orientation.x > -0x3000)
|
||||
if (fx->objectNumber == ID_SCUBA_HARPOON && isUnderwater
|
||||
&& fx->pos.Orientation.x > ANGLE(-67.5f))
|
||||
{
|
||||
fx->pos.Orientation.x -= ANGLE(1.0f);
|
||||
}
|
||||
|
||||
fx->pos.Position.y += fx->speed * phd_sin(-fx->pos.Orientation.x);
|
||||
int velocity = fx->speed * phd_cos(fx->pos.Orientation.x);
|
||||
fx->pos.Position.z += velocity * phd_cos(fx->pos.Orientation.y);
|
||||
fx->pos.Position.y += fx->speed * phd_sin(-fx->pos.Orientation.x);
|
||||
fx->pos.Position.x += velocity * phd_sin(fx->pos.Orientation.y);
|
||||
|
||||
auto probe = GetCollision(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, fx->roomNumber);
|
||||
auto hitLara = ItemNearLara(&fx->pos, 200);
|
||||
|
||||
// Check for hitting something.
|
||||
if (fx->pos.Position.y >= probe.Position.Floor ||
|
||||
fx->pos.Position.y <= probe.Position.Ceiling)
|
||||
fx->pos.Position.y <= probe.Position.Ceiling ||
|
||||
hitLara)
|
||||
{
|
||||
if (/*fx->objectNumber == KNIFE ||*/ fx->objectNumber == ID_SCUBA_HARPOON)
|
||||
if (fx->objectNumber == ID_KNIFETHROWER_KNIFE ||
|
||||
fx->objectNumber == ID_SCUBA_HARPOON ||
|
||||
fx->objectNumber == ID_PROJ_SHARD)
|
||||
{
|
||||
// Change shard into ricochet.
|
||||
// fx->speed = 0;
|
||||
// fx->frameNumber = -GetRandomControl()/11000;
|
||||
// fx->counter = 6;
|
||||
// fx->objectNumber = RICOCHET1;
|
||||
SoundEffect((fx->objectNumber == ID_SCUBA_HARPOON) ? SFX_TR4_WEAPON_RICOCHET : SFX_TR2_CIRCLE_BLADE_HIT, &fx->pos);
|
||||
SoundEffect((fx->objectNumber == ID_SCUBA_HARPOON) ? SFX_TR4_WEAPON_RICOCHET : SFX_TR2_CIRCLE_BLADE_HIT, &fx->pos, soundFxType);
|
||||
}
|
||||
/*else if (fx->objectNumber == DRAGON_FIRE)
|
||||
else if (fx->objectNumber == ID_PROJ_BOMB)
|
||||
{
|
||||
AddDynamicLight(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, 14, 11);
|
||||
KillEffect(fx_number);
|
||||
}*/
|
||||
return;
|
||||
SoundEffect(SFX_TR1_ATLANTEAN_EXPLODE, &fx->pos, soundFxType);
|
||||
TriggerExplosionSparks(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, NULL, false, isUnderwater, fx->roomNumber);
|
||||
}
|
||||
|
||||
if (hitLara)
|
||||
{
|
||||
if (fx->objectNumber == ID_KNIFETHROWER_KNIFE)
|
||||
{
|
||||
DoDamage(LaraItem, KNIFE_DAMAGE); // TODO: Make ControlMissile() not use LaraItem global. -- TokyoSU, 5/8/2022
|
||||
KillEffect(fxNumber);
|
||||
}
|
||||
else if (fx->objectNumber == ID_SCUBA_HARPOON)
|
||||
{
|
||||
DoDamage(LaraItem, DIVER_HARPOON_DAMAGE); // TODO: Make ControlMissile() not use LaraItem global. -- TokyoSU, 5/8/2022
|
||||
KillEffect(fxNumber);
|
||||
}
|
||||
else if (fx->objectNumber == ID_PROJ_BOMB)
|
||||
{
|
||||
DoDamage(LaraItem, MUTANT_BOMB_DAMAGE); // TODO: Make ControlMissile() not use LaraItem global. -- TokyoSU, 5/8/2022
|
||||
KillEffect(fxNumber);
|
||||
}
|
||||
else if (fx->objectNumber == ID_PROJ_SHARD)
|
||||
{
|
||||
TriggerBlood(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, NULL, 10);
|
||||
SoundEffect(SFX_TR4_BLOOD_LOOP, &fx->pos, soundFxType);
|
||||
DoDamage(LaraItem, MUTANT_SHARD_DAMAGE);
|
||||
KillEffect(fxNumber);
|
||||
}
|
||||
|
||||
LaraItem->HitStatus = 1; // TODO: Make ControlMissile() not use LaraItem global. -- TokyoSU, 5/8/2022
|
||||
fx->pos.Orientation.y = LaraItem->Pose.Orientation.y;
|
||||
fx->speed = LaraItem->Animation.Velocity.z;
|
||||
fx->frameNumber = fx->counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (probe.RoomNumber != fx->roomNumber)
|
||||
EffectNewRoom(fxNumber, probe.RoomNumber);
|
||||
|
||||
// Check for hitting Lara.
|
||||
/*if (fx->objectNumber == DRAGON_FIRE)
|
||||
{
|
||||
if (ItemNearLara(&fx->pos, 350))
|
||||
{
|
||||
DoDamage(LaraItem, 3);
|
||||
LaraBurn(LaraItem);
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
else if (ItemNearLara(&fx->pos, 200))
|
||||
{
|
||||
/*if (fx->objectNumber == KNIFE)
|
||||
{
|
||||
DoDamage(LaraItem, KNIFE_DAMAGE);
|
||||
KillEffect(fx_number);
|
||||
}
|
||||
else*/ if (fx->objectNumber == ID_SCUBA_HARPOON)
|
||||
{
|
||||
DoDamage(LaraItem, DIVER_HARPOON_DAMAGE);
|
||||
KillEffect(fxNumber);
|
||||
}
|
||||
if (fx->objectNumber == ID_KNIFETHROWER_KNIFE)
|
||||
fx->pos.Orientation.z += ANGLE(3.0f); // update knife rotation overtime
|
||||
|
||||
LaraItem->HitStatus = 1;
|
||||
|
||||
fx->pos.Orientation.y = LaraItem->Pose.Orientation.y;
|
||||
fx->speed = LaraItem->Animation.Velocity.z;
|
||||
fx->frameNumber = fx->counter = 0;
|
||||
switch (fx->objectNumber)
|
||||
{
|
||||
case ID_SCUBA_HARPOON:
|
||||
if (TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, fx->roomNumber))
|
||||
CreateBubble(&fx->pos.Position, fx->roomNumber, 1, 0, 0, 0, 0, 0);
|
||||
break;
|
||||
|
||||
case ID_PROJ_BOMB:
|
||||
TriggerDynamicLight(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, 14, 180, 100, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
// Create bubbles in wake of harpoon bolt.
|
||||
//if (fx->objectNumber == ID_SCUBA_HARPOON && g_Level.Rooms[fx->roomNumber].flags & 1)
|
||||
// CreateBubble(&fx->pos, fx->roomNumber, 1, 0);
|
||||
/*else if (fx->objectNumber == DRAGON_FIRE && !fx->counter--)
|
||||
{
|
||||
AddDynamicLight(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, 14, 11);
|
||||
SoundEffect(305, &fx->pos);
|
||||
KillEffect(fx_number);
|
||||
}
|
||||
else if (fx->objectNumber == KNIFE)
|
||||
fx->pos.Orientation.z += 30 * ONE_DEGREE;*/
|
||||
}
|
||||
|
||||
void ControlNatlaGun(short fxNumber)
|
||||
|
@ -175,7 +183,7 @@ short ShardGun(int x, int y, int z, short velocity, short yRot, short roomNumber
|
|||
fx->roomNumber = roomNumber;
|
||||
fx->pos.Orientation.x = fx->pos.Orientation.z = 0;
|
||||
fx->pos.Orientation.y = yRot;
|
||||
fx->speed = SHARD_VELOCITY;
|
||||
fx->speed = velocity;
|
||||
fx->frameNumber = 0;
|
||||
fx->objectNumber = ID_PROJ_SHARD;
|
||||
fx->color = Vector4::One;
|
||||
|
@ -198,7 +206,7 @@ short BombGun(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
|||
fx->roomNumber = roomNumber;
|
||||
fx->pos.Orientation.x = fx->pos.Orientation.z = 0;
|
||||
fx->pos.Orientation.y = yRot;
|
||||
fx->speed = ROCKET_VELOCITY;
|
||||
fx->speed = velocity;
|
||||
fx->frameNumber = 0;
|
||||
fx->objectNumber = ID_PROJ_BOMB;
|
||||
fx->color = Vector4::One;
|
||||
|
@ -221,7 +229,7 @@ short NatlaGun(int x, int y, int z, short velocity, short yRot, short roomNumber
|
|||
fx->roomNumber = roomNumber;
|
||||
fx->pos.Orientation.x = fx->pos.Orientation.z = 0;
|
||||
fx->pos.Orientation.y = yRot;
|
||||
fx->speed = NATLA_GUN_VELOCITY;
|
||||
fx->speed = velocity;
|
||||
fx->frameNumber = 0;
|
||||
fx->objectNumber = ID_PROJ_NATLA;
|
||||
fx->color = Vector4::One;
|
||||
|
|
|
@ -6,5 +6,5 @@ void ControlMissile(short fxNumber);
|
|||
void ControlNatlaGun(short fxNumber);
|
||||
|
||||
short ShardGun(int x, int y, int z, short velocity, short yRot, short roomNumber);
|
||||
short BombGun(int x, int y, int z, short velocity, short yRot, short roomNumber); // RocketGun = BombGun
|
||||
short BombGun(int x, int y, int z, short velocity, short yRot, short roomNumber);
|
||||
short NatlaGun(int x, int y, int z, short velocity, short yRot, short roomNumber);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "Game/misc.h"
|
||||
#include "Sound/sound.h"
|
||||
|
||||
bool ShotLara(ItemInfo* item, AI_INFO* AI, BITE_INFO* gun, short extraRotation, int damage)
|
||||
bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, int damage)
|
||||
{
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
auto* enemy = creature->Enemy;
|
||||
|
@ -42,12 +42,12 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BITE_INFO* gun, short extraRotation,
|
|||
|
||||
if (damage)
|
||||
{
|
||||
if (enemy == LaraItem)
|
||||
if (enemy->IsLara())
|
||||
{
|
||||
if (hit)
|
||||
{
|
||||
DoDamage(enemy, damage);
|
||||
CreatureEffect(item, gun, &GunHit);
|
||||
DoDamage(LaraItem, damage);
|
||||
}
|
||||
else if (targetable)
|
||||
CreatureEffect(item, gun, &GunMiss);
|
||||
|
@ -64,8 +64,9 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BITE_INFO* gun, short extraRotation,
|
|||
if (random > 14)
|
||||
random = 0;
|
||||
|
||||
Vector3Int pos = { 0, 0, 0 };
|
||||
auto pos = Vector3Int::Zero;
|
||||
GetJointAbsPosition(enemy, &pos, random);
|
||||
|
||||
DoBloodSplat(pos.x, pos.y, pos.z, (GetRandomControl() & 3) + 4, enemy->Pose.Orientation.y, enemy->RoomNumber);
|
||||
}
|
||||
}
|
||||
|
@ -78,21 +79,22 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BITE_INFO* gun, short extraRotation,
|
|||
|
||||
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
||||
{
|
||||
GameVector pos;
|
||||
pos.x = LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF;
|
||||
pos.y = LaraItem->Floor;
|
||||
pos.z = LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF;
|
||||
pos.roomNumber = LaraItem->RoomNumber;
|
||||
auto pos = GameVector(
|
||||
LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
|
||||
LaraItem->Floor,
|
||||
LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
|
||||
LaraItem->RoomNumber
|
||||
);
|
||||
|
||||
Richochet((PHD_3DPOS*)&pos);
|
||||
|
||||
return GunShot(x, y, z, velocity, yRot, roomNumber);
|
||||
}
|
||||
|
||||
short GunHit(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
||||
{
|
||||
Vector3Int pos = { 0, 0, 0 };
|
||||
auto pos = Vector3Int::Zero;
|
||||
GetLaraJointPosition(&pos, (25 * GetRandomControl()) >> 15);
|
||||
|
||||
DoBloodSplat(pos.x, pos.y, pos.z, (GetRandomControl() & 3) + 3, LaraItem->Pose.Orientation.y, LaraItem->RoomNumber);
|
||||
return GunShot(x, y, z, velocity, yRot, roomNumber);
|
||||
}
|
||||
|
@ -102,59 +104,73 @@ short GunShot(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool Targetable(ItemInfo* item, AI_INFO* AI)
|
||||
bool Targetable(ItemInfo* item, AI_INFO* AI)
|
||||
{
|
||||
// Discard it entity is not a creature (only creatures can use Targetable())
|
||||
// or if the target is not visible.
|
||||
if (!item->IsCreature() || !AI->ahead || AI->distance >= SQUARE(MAX_VISIBILITY_DISTANCE))
|
||||
return false;
|
||||
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
auto* enemy = creature->Enemy;
|
||||
|
||||
if (enemy == NULL || enemy->HitPoints <= 0 || !AI->ahead || AI->distance >= pow(MAX_VISIBILITY_DISTANCE, 2))
|
||||
if (creature->Enemy == nullptr)
|
||||
return false;
|
||||
|
||||
if (!enemy->Data.is<CreatureInfo>() && !enemy->IsLara())
|
||||
// Only Lara or a creature may be targeted.
|
||||
if ((!enemy->IsCreature() && !enemy->IsLara()) || enemy->HitPoints <= 0)
|
||||
return false;
|
||||
|
||||
auto* bounds = (BOUNDING_BOX*)GetBestFrame(item);
|
||||
auto& bounds = GetBestFrame(item)->boundingBox;
|
||||
auto& boundsTarget = GetBestFrame(enemy)->boundingBox;
|
||||
|
||||
GameVector start;
|
||||
start.x = item->Pose.Position.x;
|
||||
start.y = (item->ObjectNumber == ID_SNIPER) ? (item->Pose.Position.y - CLICK(3)) : (item->Pose.Position.y + ((bounds->Y2 + 3 * bounds->Y1) / 4));
|
||||
start.z = item->Pose.Position.z;
|
||||
start.roomNumber = item->RoomNumber;
|
||||
|
||||
bounds = (BOUNDING_BOX*)GetBestFrame(enemy);
|
||||
|
||||
GameVector target;
|
||||
target.x = enemy->Pose.Position.x;
|
||||
target.y = enemy->Pose.Position.y + ((bounds->Y2 + 3 * bounds->Y1) / 4);
|
||||
target.z = enemy->Pose.Position.z;
|
||||
|
||||
return LOS(&start, &target);
|
||||
auto origin = GameVector(
|
||||
item->Pose.Position.x,
|
||||
(item->ObjectNumber == ID_SNIPER) ? (item->Pose.Position.y - CLICK(3)) : (item->Pose.Position.y + ((bounds.Y2 + 3 * bounds.Y1) / 4)),
|
||||
item->Pose.Position.z,
|
||||
item->RoomNumber
|
||||
);
|
||||
auto target = GameVector(
|
||||
enemy->Pose.Position.x,
|
||||
enemy->Pose.Position.y + ((boundsTarget.Y2 + 3 * boundsTarget.Y1) / 4),
|
||||
enemy->Pose.Position.z,
|
||||
enemy->RoomNumber // TODO: Check why this line didn't exist in the first place. -- TokyoSU 2022.08.05
|
||||
);
|
||||
return LOS(&origin, &target);
|
||||
}
|
||||
|
||||
bool TargetVisible(ItemInfo* item, AI_INFO* AI)
|
||||
bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngle)
|
||||
{
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
auto* enemy = creature->Enemy;
|
||||
if (!item->IsCreature() || AI->distance >= SQUARE(MAX_VISIBILITY_DISTANCE))
|
||||
return false;
|
||||
|
||||
if (enemy != NULL)
|
||||
// Check just in case.
|
||||
auto* creatureInfo = GetCreatureInfo(item);
|
||||
if (creatureInfo == nullptr)
|
||||
return false;
|
||||
|
||||
auto* enemy = creatureInfo->Enemy;
|
||||
if (enemy == nullptr || enemy->HitPoints == 0)
|
||||
return false;
|
||||
|
||||
short angle = AI->angle - creatureInfo->JointRotation[2];
|
||||
if (angle > -ANGLE(maxAngle) && angle < ANGLE(maxAngle))
|
||||
{
|
||||
short angle = AI->angle - creature->JointRotation[2];
|
||||
if (enemy->HitPoints != 0 && angle > -ANGLE(45.0f) && angle < ANGLE(45.0f) && AI->distance < pow(MAX_VISIBILITY_DISTANCE, 2))
|
||||
{
|
||||
GameVector start;
|
||||
start.x = item->Pose.Position.x;
|
||||
start.y = item->Pose.Position.y - CLICK(3);
|
||||
start.z = item->Pose.Position.z;
|
||||
start.roomNumber = item->RoomNumber;
|
||||
GameVector start;
|
||||
GameVector target;
|
||||
auto& bounds = GetBestFrame(enemy)->boundingBox;
|
||||
|
||||
GameVector target;
|
||||
target.x = enemy->Pose.Position.x;
|
||||
auto* bounds = (BOUNDING_BOX*)GetBestFrame(enemy);
|
||||
target.y = enemy->Pose.Position.y + ((((bounds->Y1 * 2) + bounds->Y1) + bounds->Y2) / 4);
|
||||
target.z = enemy->Pose.Position.z;
|
||||
start.x = item->Pose.Position.x;
|
||||
start.y = item->Pose.Position.y - CLICK(3);
|
||||
start.z = item->Pose.Position.z;
|
||||
start.roomNumber = item->RoomNumber;
|
||||
|
||||
return LOS(&start, &target);
|
||||
}
|
||||
target.x = enemy->Pose.Position.x;
|
||||
target.y = enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4);
|
||||
target.z = enemy->Pose.Position.z;
|
||||
target.roomNumber = enemy->RoomNumber; // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022
|
||||
|
||||
return LOS(&start, &target);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
constexpr auto MAX_VISIBILITY_DISTANCE = SECTOR(8);
|
||||
|
||||
bool ShotLara(ItemInfo* item, AI_INFO* AI, BITE_INFO* bite, short extraRotation, int damage);
|
||||
bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, int damage);
|
||||
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber);
|
||||
short GunHit(int x, int y, int z, short velocity, short yRot, short roomNumber);
|
||||
short GunShot(int x, int y, int z, short velocity, short yRot, short roomNumber);
|
||||
bool Targetable(ItemInfo* item, AI_INFO* AI);
|
||||
bool TargetVisible(ItemInfo* item, AI_INFO* AI);
|
||||
bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngle = 45.0f);
|
||||
|
|
|
@ -825,36 +825,27 @@ BOUNDING_BOX* FindPlinth(ItemInfo* item)
|
|||
{
|
||||
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||
|
||||
int found = -1;
|
||||
for (int i = 0; i < room->mesh.size(); i++)
|
||||
{
|
||||
auto* mesh = &room->mesh[i];
|
||||
|
||||
if (mesh->flags & StaticMeshFlags::SM_VISIBLE)
|
||||
{
|
||||
if (item->Pose.Position.x == mesh->pos.Position.x && item->Pose.Position.z == mesh->pos.Position.z)
|
||||
{
|
||||
auto* frame = (BOUNDING_BOX*)GetBestFrame(item);
|
||||
auto* staticInfo = &StaticObjects[mesh->staticNumber];
|
||||
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (frame->X1 <= staticInfo->collisionBox.X2 &&
|
||||
frame->X2 >= staticInfo->collisionBox.X1 &&
|
||||
frame->Z1 <= staticInfo->collisionBox.Z2 &&
|
||||
frame->Z2 >= staticInfo->collisionBox.Z1 &&
|
||||
(staticInfo->collisionBox.X1 || staticInfo->collisionBox.X2))
|
||||
{
|
||||
found = mesh->staticNumber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item->Pose.Position.x != mesh->pos.Position.x || item->Pose.Position.z != mesh->pos.Position.z)
|
||||
continue;
|
||||
|
||||
auto* frame = (BOUNDING_BOX*)GetBestFrame(item);
|
||||
auto* bbox = GetBoundsAccurate(mesh, false);
|
||||
|
||||
if (frame->X1 <= bbox->X2 && frame->X2 >= bbox->X1 &&
|
||||
frame->Z1 <= bbox->Z2 && frame->Z2 >= bbox->Z1 &&
|
||||
(bbox->X1 || bbox->X2))
|
||||
return bbox;
|
||||
}
|
||||
|
||||
if (found != -1)
|
||||
return &StaticObjects[found].collisionBox;
|
||||
|
||||
if (room->itemNumber == NO_ITEM)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
short itemNumber = room->itemNumber;
|
||||
for (itemNumber = room->itemNumber; itemNumber != NO_ITEM; itemNumber = g_Level.Items[itemNumber].NextItem)
|
||||
|
@ -873,7 +864,7 @@ BOUNDING_BOX* FindPlinth(ItemInfo* item)
|
|||
}
|
||||
|
||||
if (itemNumber == NO_ITEM)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
else
|
||||
return (BOUNDING_BOX*)GetBestFrame(&g_Level.Items[itemNumber]);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#include "Game/items.h"
|
||||
#include "Renderer/Renderer11.h"
|
||||
|
||||
using namespace TEN::Renderer;
|
||||
using namespace TEN::Floordata;
|
||||
using namespace TEN::Renderer;
|
||||
|
||||
byte FlipStatus = 0;
|
||||
int FlipStats[MAX_FLIPMAP];
|
||||
|
@ -142,7 +142,13 @@ int IsRoomOutside(int x, int y, int z)
|
|||
if (y < probe.Position.Ceiling)
|
||||
return NO_ROOM;
|
||||
|
||||
return ((room->flags & (ENV_FLAG_WIND | ENV_FLAG_WATER)) != 0 ? probe.RoomNumber : NO_ROOM);
|
||||
if (TestEnvironmentFlags(ENV_FLAG_WATER, room->flags) ||
|
||||
TestEnvironmentFlags(ENV_FLAG_WIND, room->flags))
|
||||
{
|
||||
return probe.RoomNumber;
|
||||
}
|
||||
|
||||
return NO_ROOM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,18 +167,28 @@ FloorInfo* GetSector(ROOM_INFO* room, int x, int z)
|
|||
return &room->floor[index];
|
||||
}
|
||||
|
||||
BOUNDING_BOX* GetBoundsAccurate(const MESH_INFO* mesh, bool visibility)
|
||||
{
|
||||
static BOUNDING_BOX result;
|
||||
|
||||
if (visibility)
|
||||
result = StaticObjects[mesh->staticNumber].visibilityBox * mesh->scale;
|
||||
else
|
||||
result = StaticObjects[mesh->staticNumber].collisionBox * mesh->scale;
|
||||
|
||||
return &result;
|
||||
}
|
||||
|
||||
bool IsPointInRoom(Vector3Int pos, int roomNumber)
|
||||
{
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
int z = pos.z;
|
||||
auto* room = &g_Level.Rooms[roomNumber];
|
||||
int xSector = (x - room->x) / SECTOR(1);
|
||||
int zSector = (z - room->z) / SECTOR(1);
|
||||
|
||||
if ((xSector >= 0 && xSector <= room->xSize - 1) &&
|
||||
(zSector >= 0 && zSector <= room->zSize - 1) &&
|
||||
(y <= room->minfloor && y >= room->maxceiling)) // up is negative y axis, hence y should be "less" than floor
|
||||
int xSector = (pos.x - room->x) / SECTOR(1);
|
||||
int zSector = (pos.z - room->z) / SECTOR(1);
|
||||
|
||||
if ((xSector >= 0 && xSector <= (room->xSize - 1)) &&
|
||||
(zSector >= 0 && zSector <= (room->zSize - 1)) &&
|
||||
(pos.y <= room->minfloor && pos.y >= room->maxceiling)) // Up is -y, hence y should be "less" than floor.
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -192,39 +208,40 @@ int FindRoomNumber(Vector3Int position)
|
|||
Vector3Int GetRoomCenter(int roomNumber)
|
||||
{
|
||||
auto* room = &g_Level.Rooms[roomNumber];
|
||||
|
||||
auto halfLength = SECTOR(room->xSize) / 2;
|
||||
auto halfDepth = SECTOR(room->zSize) / 2;
|
||||
auto halfHeight = (room->maxceiling - room->minfloor) / 2;
|
||||
|
||||
Vector3Int center;
|
||||
center.x = room->x + halfLength;
|
||||
center.y = room->minfloor + halfHeight;
|
||||
center.z = room->z + halfDepth;
|
||||
|
||||
auto center = Vector3Int(
|
||||
room->x + halfLength,
|
||||
room->minfloor + halfHeight,
|
||||
room->z + halfDepth
|
||||
);
|
||||
return center;
|
||||
}
|
||||
|
||||
std::set<int> GetRoomList(int roomNumber)
|
||||
{
|
||||
std::set<int> result;
|
||||
std::set<int> roomNumberList;
|
||||
|
||||
if (g_Level.Rooms.size() <= roomNumber)
|
||||
return result;
|
||||
return roomNumberList;
|
||||
|
||||
result.insert(roomNumber);
|
||||
roomNumberList.insert(roomNumber);
|
||||
|
||||
auto* room = &g_Level.Rooms[roomNumber];
|
||||
for (int i = 0; i < room->doors.size(); i++)
|
||||
result.insert(room->doors[i].room);
|
||||
for (size_t i = 0; i < room->doors.size(); i++)
|
||||
roomNumberList.insert(room->doors[i].room);
|
||||
|
||||
for (auto i : result)
|
||||
for (int roomNumber : roomNumberList)
|
||||
{
|
||||
room = &g_Level.Rooms[i];
|
||||
for (int j = 0; j < room->doors.size(); j++)
|
||||
result.insert(room->doors[j].room);
|
||||
room = &g_Level.Rooms[roomNumber];
|
||||
for (size_t j = 0; j < room->doors.size(); j++)
|
||||
roomNumberList.insert(room->doors[j].room);
|
||||
}
|
||||
|
||||
return result;
|
||||
return roomNumberList;
|
||||
}
|
||||
|
||||
void InitializeNeighborRoomList()
|
||||
|
@ -235,8 +252,8 @@ void InitializeNeighborRoomList()
|
|||
|
||||
room->neighbors.clear();
|
||||
|
||||
auto roomList = GetRoomList(i);
|
||||
for (int n : roomList)
|
||||
room->neighbors.push_back(n);
|
||||
auto roomNumberList = GetRoomList(i);
|
||||
for (int roomNumber : roomNumberList)
|
||||
room->neighbors.push_back(roomNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ struct ROOM_LIGHT
|
|||
struct MESH_INFO
|
||||
{
|
||||
PHD_3DPOS pos;
|
||||
float scale;
|
||||
short staticNumber;
|
||||
short flags;
|
||||
Vector4 color;
|
||||
|
@ -109,6 +110,9 @@ struct ROOM_INFO
|
|||
short fxNumber;
|
||||
bool boundActive;
|
||||
|
||||
std::string name;
|
||||
std::vector<std::string> tags;
|
||||
|
||||
std::vector<FloorInfo> floor;
|
||||
std::vector<ROOM_LIGHT> lights;
|
||||
std::vector<MESH_INFO> mesh;
|
||||
|
@ -138,10 +142,11 @@ void AddRoomFlipItems(ROOM_INFO* room);
|
|||
void RemoveRoomFlipItems(ROOM_INFO* room);
|
||||
bool IsObjectInRoom(short roomNumber, short objectNumber);
|
||||
bool IsPointInRoom(Vector3Int pos, int roomNumber);
|
||||
int FindRoomNumber(Vector3Int position);
|
||||
int FindRoomNumber(Vector3Int pos);
|
||||
Vector3Int GetRoomCenter(int roomNumber);
|
||||
int IsRoomOutside(int x, int y, int z);
|
||||
std::set<int> GetRoomList(int roomNumber);
|
||||
void InitializeNeighborRoomList();
|
||||
|
||||
BOUNDING_BOX* GetBoundsAccurate(const MESH_INFO* mesh, bool visibility);
|
||||
FloorInfo* GetSector(ROOM_INFO* room, int x, int z);
|
||||
|
|
|
@ -813,6 +813,7 @@ bool SaveGame::Save(int slot)
|
|||
Save::StaticMeshInfoBuilder staticMesh{ fbb };
|
||||
|
||||
staticMesh.add_pose(&FromPHD(room->mesh[j].pos));
|
||||
staticMesh.add_scale(room->mesh[j].scale);
|
||||
staticMesh.add_color(&FromVector4(room->mesh[j].color));
|
||||
|
||||
staticMesh.add_flags(room->mesh[j].flags);
|
||||
|
@ -1244,6 +1245,7 @@ bool SaveGame::Load(int slot)
|
|||
int number = staticMesh->number();
|
||||
|
||||
room->mesh[number].pos = ToPHD(staticMesh->pose());
|
||||
room->mesh[number].scale = staticMesh->scale();
|
||||
room->mesh[number].color = ToVector4(staticMesh->color());
|
||||
|
||||
room->mesh[number].flags = staticMesh->flags();
|
||||
|
|
|
@ -70,9 +70,9 @@ namespace TEN::Entities::TR4
|
|||
locust->pos.Orientation.y = (GetRandomControl() & 0x7FF) + angles.y - ANGLE(5.6f);
|
||||
locust->roomNumber = item->RoomNumber;
|
||||
locust->randomRotation = (GetRandomControl() & 0x1F) + 0x10;
|
||||
locust->escapeYrot = (GetRandomControl() & 0x1FF);
|
||||
locust->escapeXrot = ((GetRandomControl() & 0x7) + 0xF) * 20;
|
||||
locust->counter = 0;
|
||||
locust->escapeYrot = GetRandomControl() & 0x1FF;
|
||||
locust->escapeXrot = (GetRandomControl() & 0x1F) + 16;
|
||||
locust->counter = 20 * ((GetRandomControl() & 0x7) + 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,15 +120,10 @@ namespace TEN::Entities::TR4
|
|||
if (locust->on)
|
||||
{
|
||||
// NOTE: not present in original TR4 code
|
||||
//if (LaraItem == nullptr)
|
||||
// LaraItem = LaraItem;
|
||||
|
||||
if ((Lara.Control.KeepLow || LaraItem->HitPoints <= 0) &&
|
||||
locust->counter >= 90 &&
|
||||
!(GetRandomControl() & 7))
|
||||
{
|
||||
//if (locust->target == nullptr)
|
||||
// locust->target = LaraItem;
|
||||
if ((Lara.Burn || LaraItem->HitPoints <= 0) && locust->counter >= 90 && !(GetRandomControl() & 7))
|
||||
locust->counter = 90;
|
||||
}
|
||||
|
||||
locust->counter--;
|
||||
if (locust->counter == 0)
|
||||
|
@ -149,7 +144,7 @@ namespace TEN::Entities::TR4
|
|||
LaraItem->Pose.Position.y - locust->escapeYrot - locust->pos.Position.y,
|
||||
LaraItem->Pose.Position.z + 8 * locust->escapeZrot - locust->pos.Position.z);
|
||||
|
||||
int distance = pow(LaraItem->Pose.Position.z - locust->pos.Position.z, 2) + pow(LaraItem->Pose.Position.x - locust->pos.Position.x, 2);
|
||||
int distance = SQUARE(LaraItem->Pose.Position.z - locust->pos.Position.z) + SQUARE(LaraItem->Pose.Position.x - locust->pos.Position.x);
|
||||
int square = int(sqrt(distance)) / 8;
|
||||
if (square <= 128)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Doors
|
||||
{
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ROOM_INFO;
|
||||
struct DOORPOS_DATA;
|
||||
struct DOOR_DATA;
|
||||
struct ItemInfo;
|
||||
struct ROOM_INFO;
|
||||
|
||||
namespace TEN::Entities::Doors
|
||||
{
|
||||
void InitialiseDoor(short itemNumber);
|
||||
void DoorCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
void DoorControl(short itemNumber);
|
||||
void OpenThatDoor(DOORPOS_DATA* doorPos, DOOR_DATA* dd);
|
||||
void ShutThatDoor(DOORPOS_DATA* doorPos, DOOR_DATA* dd);
|
||||
void OpenThatDoor(DOORPOS_DATA* doorPos, DOOR_DATA* door);
|
||||
void ShutThatDoor(DOORPOS_DATA* doorPos, DOOR_DATA* door);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Doors
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Doors
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Doors
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Doors
|
||||
{
|
||||
|
|
|
@ -1,122 +1,157 @@
|
|||
#include "framework.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Game/control/control.h"
|
||||
#include "Objects/Generic/Object/polerope.h"
|
||||
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/collision/sphere.h"
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/control/control.h"
|
||||
#include "Game/control/lot.h"
|
||||
#include "Specific/input.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/Lara/lara_helpers.h"
|
||||
#include "Game/Lara/lara_struct.h"
|
||||
#include "Game/Lara/lara_tests.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Specific/input.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/trmath.h"
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/collision/sphere.h"
|
||||
|
||||
using namespace TEN::Input;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::Generic
|
||||
{
|
||||
Vector3Int PolePos = { 0, 0, -208 };
|
||||
Vector3Int PolePosR = { 0, 0, 0 };
|
||||
|
||||
OBJECT_COLLISION_BOUNDS PoleBounds =
|
||||
const vector<LaraState> VPoleMountedStates =
|
||||
{
|
||||
-256, 256,
|
||||
LS_POLE_IDLE,
|
||||
LS_POLE_UP,
|
||||
LS_POLE_DOWN,
|
||||
LS_POLE_TURN_CLOCKWISE,
|
||||
LS_POLE_TURN_COUNTER_CLOCKWISE
|
||||
};
|
||||
const vector<LaraState> VPoleGroundedMountStates =
|
||||
{
|
||||
LS_IDLE,
|
||||
LS_TURN_LEFT_SLOW,
|
||||
LS_TURN_RIGHT_SLOW,
|
||||
LS_TURN_LEFT_FAST,
|
||||
LS_TURN_RIGHT_FAST,
|
||||
LS_WALK_FORWARD,
|
||||
LS_RUN_FORWARD
|
||||
};
|
||||
const vector<LaraState> VPoleAirborneMountStates =
|
||||
{
|
||||
LS_REACH,
|
||||
LS_JUMP_UP
|
||||
};
|
||||
|
||||
// TODO: These might be interfering with the SetPosition command. -- Sezz 2022.08.29
|
||||
auto VPolePos = Vector3Int(0, 0, -208);
|
||||
auto VPolePosR = Vector3Int::Zero;
|
||||
|
||||
OBJECT_COLLISION_BOUNDS VPoleBounds =
|
||||
{
|
||||
-CLICK(1), CLICK(1),
|
||||
0, 0,
|
||||
-512, 512,
|
||||
-ANGLE(10.0f), ANGLE(10.0f),
|
||||
-ANGLE(30.0f), ANGLE(30.0f),
|
||||
-ANGLE(10.0f), ANGLE(10.0f)
|
||||
-CLICK(2), CLICK(2),
|
||||
ANGLE(-10.0f), ANGLE(10.0f),
|
||||
ANGLE(-30.0f), ANGLE(30.0f),
|
||||
ANGLE(-10.0f), ANGLE(10.0f)
|
||||
};
|
||||
|
||||
void PoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
|
||||
{
|
||||
auto* laraInfo = GetLaraInfo(laraItem);
|
||||
auto* poleItem = &g_Level.Items[itemNumber];
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
|
||||
bool isLara = !poleItem->IsLara();
|
||||
bool isFacingPole = IsPointInFront(laraItem->Pose, poleItem->Pose.Position.ToVector3());
|
||||
|
||||
if (TrInput & IN_ACTION && isLara &&
|
||||
laraInfo->Control.HandStatus == HandStatus::Free &&
|
||||
laraItem->Animation.ActiveState == LS_IDLE &&
|
||||
laraItem->Animation.AnimNumber == LA_STAND_IDLE || laraInfo->Control.IsMoving &&
|
||||
laraInfo->InteractedItem == itemNumber)
|
||||
// Mount while grounded.
|
||||
if (TrInput & IN_ACTION && isFacingPole &&
|
||||
CheckLaraState((LaraState)laraItem->Animation.ActiveState, VPoleGroundedMountStates) &&
|
||||
lara->Control.HandStatus == HandStatus::Free ||
|
||||
(lara->Control.IsMoving && lara->InteractedItem == itemNumber))
|
||||
{
|
||||
short rot = poleItem->Pose.Orientation.y;
|
||||
// Temporarily reorient pole.
|
||||
short yOrient = poleItem->Pose.Orientation.y;
|
||||
poleItem->Pose.Orientation.y = laraItem->Pose.Orientation.y;
|
||||
|
||||
if (TestLaraPosition(&PoleBounds, poleItem, laraItem))
|
||||
if (TestLaraPosition(&VPoleBounds, poleItem, laraItem))
|
||||
{
|
||||
if (MoveLaraPosition(&PolePos, poleItem, laraItem))
|
||||
if (MoveLaraPosition(&VPolePos, poleItem, laraItem))
|
||||
{
|
||||
laraItem->Animation.AnimNumber = LA_STAND_TO_POLE;
|
||||
laraItem->Animation.ActiveState = LS_POLE_IDLE;
|
||||
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
|
||||
laraInfo->Control.IsMoving = false;
|
||||
laraInfo->Control.HandStatus = HandStatus::Busy;
|
||||
SetAnimation(laraItem, LA_STAND_TO_POLE);
|
||||
lara->Control.IsMoving = false;
|
||||
lara->Control.HandStatus = HandStatus::Busy;
|
||||
}
|
||||
else
|
||||
laraInfo->InteractedItem = itemNumber;
|
||||
lara->InteractedItem = itemNumber;
|
||||
|
||||
poleItem->Pose.Orientation.y = rot;
|
||||
poleItem->Pose.Orientation.y = yOrient;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber)
|
||||
if (lara->Control.IsMoving && lara->InteractedItem == itemNumber)
|
||||
{
|
||||
laraInfo->Control.IsMoving = false;
|
||||
laraInfo->Control.HandStatus = HandStatus::Free;
|
||||
lara->Control.IsMoving = false;
|
||||
lara->Control.HandStatus = HandStatus::Free;
|
||||
}
|
||||
|
||||
poleItem->Pose.Orientation.y = rot;
|
||||
poleItem->Pose.Orientation.y = yOrient;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (TrInput & IN_ACTION && isLara &&
|
||||
laraInfo->Control.HandStatus == HandStatus::Free &&
|
||||
laraItem->Animation.IsAirborne &&
|
||||
laraItem->Animation.Velocity.y > (int)laraInfo->Control.HandStatus && // ?????
|
||||
laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP)
|
||||
|
||||
// Mount while airborne.
|
||||
if (TrInput & IN_ACTION && isFacingPole &&
|
||||
CheckLaraState((LaraState)laraItem->Animation.ActiveState, VPoleAirborneMountStates) &&
|
||||
laraItem->Animation.IsAirborne &&
|
||||
laraItem->Animation.Velocity.y > 0.0f &&
|
||||
lara->Control.HandStatus == HandStatus::Free)
|
||||
{
|
||||
if (TestBoundsCollide(poleItem, laraItem, 100) &&
|
||||
TestLaraPoleCollision(laraItem, coll, true, -CLICK(1)) &&
|
||||
TestLaraPoleCollision(laraItem, coll, false))
|
||||
// Test sphere collision.
|
||||
if (!TestLaraPoleCollision(laraItem, coll, true, -CLICK(1)) || !TestLaraPoleCollision(laraItem, coll, false))
|
||||
return;
|
||||
|
||||
// Test bounds collision.
|
||||
if (TestBoundsCollide(poleItem, laraItem, LARA_RADIUS + (int)round(abs(laraItem->Animation.Velocity.z))))
|
||||
{
|
||||
if (TestCollision(poleItem, laraItem))
|
||||
{
|
||||
short rot = poleItem->Pose.Orientation.y;
|
||||
// Temporarily reorient pole.
|
||||
short yOrient = poleItem->Pose.Orientation.y;
|
||||
poleItem->Pose.Orientation.y = laraItem->Pose.Orientation.y;
|
||||
|
||||
// Reaching.
|
||||
if (laraItem->Animation.ActiveState == LS_REACH)
|
||||
{
|
||||
PolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 10;
|
||||
AlignLaraPosition(&PolePosR, poleItem, laraItem);
|
||||
laraItem->Animation.AnimNumber = LA_REACH_TO_POLE;
|
||||
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
|
||||
VPolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 10;
|
||||
AlignLaraPosition(&VPolePosR, poleItem, laraItem);
|
||||
SetAnimation(laraItem, LA_REACH_TO_POLE);
|
||||
}
|
||||
// Jumping up.
|
||||
else
|
||||
{
|
||||
PolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 66;
|
||||
AlignLaraPosition(&PolePosR, poleItem, laraItem);
|
||||
laraItem->Animation.AnimNumber = LA_JUMP_UP_TO_POLE;
|
||||
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
|
||||
VPolePosR.y = laraItem->Pose.Position.y - poleItem->Pose.Position.y + 66;
|
||||
AlignLaraPosition(&VPolePosR, poleItem, laraItem);
|
||||
SetAnimation(laraItem, LA_JUMP_UP_TO_POLE);
|
||||
}
|
||||
|
||||
laraItem->Animation.ActiveState = LS_POLE_IDLE;
|
||||
laraItem->Animation.Velocity.y = 0;
|
||||
laraItem->Animation.IsAirborne = false;
|
||||
laraInfo->Control.HandStatus = HandStatus::Busy;
|
||||
poleItem->Pose.Orientation.y = rot;
|
||||
laraItem->Animation.Velocity.y = 0.0f;
|
||||
lara->Control.HandStatus = HandStatus::Busy;
|
||||
poleItem->Pose.Orientation.y = yOrient;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
// Player is not interacting with vertical pole; do regular object collision.
|
||||
if (!CheckLaraState((LaraState)laraItem->Animation.ActiveState, VPoleMountedStates) &&
|
||||
laraItem->Animation.ActiveState != LS_JUMP_BACK)
|
||||
{
|
||||
if (!isLara || ((laraItem->Animation.ActiveState < LS_POLE_IDLE ||
|
||||
laraItem->Animation.ActiveState > LS_POLE_TURN_COUNTER_CLOCKWISE) &&
|
||||
laraItem->Animation.ActiveState != LS_JUMP_BACK))
|
||||
{
|
||||
ObjectCollision(itemNumber, laraItem, coll);
|
||||
}
|
||||
ObjectCollision(itemNumber, laraItem, coll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Generic
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
namespace TEN::Entities::Switches
|
||||
{
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/Generic/Traps/dart_emitter.h"
|
||||
#include "Specific/level.h"
|
||||
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/items.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Renderer/Renderer11Enums.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
|
||||
namespace TEN::Entities::Traps
|
||||
{
|
||||
|
@ -228,4 +229,4 @@ namespace TEN::Entities::Traps
|
|||
|
||||
spark->dSize = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,4 @@ namespace TEN::Entities::Traps
|
|||
void DartControl(short itemNumber);
|
||||
void DartEmitterControl(short itemNumber);
|
||||
void TriggerDartSmoke(int x, int y, int z, int xv, int zv, bool hit);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,32 @@
|
|||
#include "framework.h"
|
||||
#include "Game/effects/debris.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/camera.h"
|
||||
#include "Objects/Generic/Traps/falling_block.h"
|
||||
|
||||
#include "Game/animation.h"
|
||||
#include "Game/camera.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/collision/floordata.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
#include "Game/effects/debris.h"
|
||||
#include "Game/room.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Floordata;
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
constexpr auto FALLINGBLOCK_INITIAL_SPEED = 10;
|
||||
constexpr auto FALLINGBLOCK_MAX_SPEED = 100;
|
||||
constexpr auto FALLINGBLOCK_FALL_VELOCITY = 4;
|
||||
constexpr auto FALLINGBLOCK_INITIAL_SPEED = 10;
|
||||
constexpr auto FALLINGBLOCK_MAX_SPEED = 100;
|
||||
constexpr auto FALLINGBLOCK_FALL_VELOCITY = 4;
|
||||
constexpr auto FALLINGBLOCK_FALL_ROTATION_SPEED = 1;
|
||||
constexpr auto FALLINGBLOCK_DELAY = 52;
|
||||
constexpr auto FALLINGBLOCK_WIBBLE = 3;
|
||||
constexpr auto FALLINGBLOCK_HEIGHT_TOLERANCE = 8;
|
||||
constexpr auto FALLINGBLOCK_CRUMBLE_DELAY = 100;
|
||||
constexpr auto FALLINGBLOCK_DELAY = 52;
|
||||
constexpr auto FALLINGBLOCK_WIBBLE = 3;
|
||||
constexpr auto FALLINGBLOCK_HEIGHT_TOLERANCE = 8;
|
||||
constexpr auto FALLINGBLOCK_CRUMBLE_DELAY = 100;
|
||||
|
||||
void InitialiseFallingBlock(short itemNumber)
|
||||
{
|
||||
auto item = &g_Level.Items[itemNumber];
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
g_Level.Items[itemNumber].MeshBits = 1;
|
||||
TEN::Floordata::UpdateBridgeItem(itemNumber);
|
||||
|
@ -34,15 +36,17 @@ void InitialiseFallingBlock(short itemNumber)
|
|||
item->Animation.Mutator[i].Rotation = Vector3::Zero;
|
||||
}
|
||||
|
||||
void FallingBlockCollision(short itemNum, ItemInfo* l, CollisionInfo* coll)
|
||||
void FallingBlockCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
|
||||
{
|
||||
ItemInfo* item = &g_Level.Items[itemNum];
|
||||
if (!item->ItemFlags[0] && !item->TriggerFlags && abs(item->Pose.Position.y - l->Pose.Position.y) < FALLINGBLOCK_HEIGHT_TOLERANCE)
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
if (!item->ItemFlags[0] && !item->TriggerFlags &&
|
||||
abs(item->Pose.Position.y - laraItem->Pose.Position.y) < FALLINGBLOCK_HEIGHT_TOLERANCE)
|
||||
{
|
||||
if (!((item->Pose.Position.x ^ l->Pose.Position.x) & 0xFFFFFC00) && !((l->Pose.Position.z ^ item->Pose.Position.z) & 0xFFFFFC00))
|
||||
if (!((item->Pose.Position.x ^ laraItem->Pose.Position.x) & 0xFFFFFC00) && !((laraItem->Pose.Position.z ^ item->Pose.Position.z) & 0xFFFFFC00))
|
||||
{
|
||||
SoundEffect(SFX_TR4_ROCK_FALL_CRUMBLE, &item->Pose);
|
||||
AddActiveItem(itemNum);
|
||||
AddActiveItem(itemNumber);
|
||||
|
||||
item->ItemFlags[0] = 0;
|
||||
item->Status = ITEM_ACTIVE;
|
||||
|
@ -53,7 +57,7 @@ void FallingBlockCollision(short itemNum, ItemInfo* l, CollisionInfo* coll)
|
|||
|
||||
void FallingBlockControl(short itemNumber)
|
||||
{
|
||||
ItemInfo* item = &g_Level.Items[itemNumber];
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
if (item->TriggerFlags)
|
||||
{
|
||||
|
@ -65,7 +69,7 @@ void FallingBlockControl(short itemNumber)
|
|||
{
|
||||
if (item->ItemFlags[0] < FALLINGBLOCK_DELAY)
|
||||
{
|
||||
// Subtly shake all meshes separately
|
||||
// Subtly shake all meshes separately.
|
||||
for (int i = 0; i < item->Animation.Mutator.size(); i++)
|
||||
{
|
||||
item->Animation.Mutator[i].Rotation.x = RADIAN * GenerateFloat(-FALLINGBLOCK_WIBBLE, FALLINGBLOCK_WIBBLE);
|
||||
|
@ -75,7 +79,7 @@ void FallingBlockControl(short itemNumber)
|
|||
}
|
||||
else
|
||||
{
|
||||
// Make rotational falling movement with some random seed
|
||||
// Make rotational falling movement with some random seed.
|
||||
for (int i = 0; i < item->Animation.Mutator.size(); i++)
|
||||
{
|
||||
auto rotSpeed = i % 2 ? FALLINGBLOCK_FALL_ROTATION_SPEED : -FALLINGBLOCK_FALL_ROTATION_SPEED;
|
||||
|
@ -97,31 +101,31 @@ void FallingBlockControl(short itemNumber)
|
|||
|
||||
item->Pose.Position.y += item->ItemFlags[1];
|
||||
}
|
||||
|
||||
if (GetDistanceToFloor(itemNumber) >= 0)
|
||||
{
|
||||
// If crumbled before actual delay (e.g. too low position), force delay to be correct
|
||||
if (item->ItemFlags[0] < FALLINGBLOCK_DELAY)
|
||||
item->ItemFlags[0] = FALLINGBLOCK_DELAY;
|
||||
|
||||
// Convert object to shatter item
|
||||
ShatterItem.yRot = item->Pose.Orientation.y;
|
||||
ShatterItem.meshIndex = Objects[item->ObjectNumber].meshIndex;
|
||||
ShatterItem.color = item->Color;
|
||||
ShatterItem.sphere.x = item->Pose.Position.x;
|
||||
ShatterItem.sphere.y = item->Pose.Position.y - STEP_SIZE; // So debris won't spawn below floor
|
||||
ShatterItem.sphere.z = item->Pose.Position.z;
|
||||
ShatterItem.bit = 0;
|
||||
ShatterImpactData.impactDirection = Vector3(0, -(float)item->ItemFlags[1] / (float)FALLINGBLOCK_MAX_SPEED, 0);
|
||||
ShatterImpactData.impactLocation = { (float)ShatterItem.sphere.x, (float)ShatterItem.sphere.y, (float)ShatterItem.sphere.z };
|
||||
ShatterObject(&ShatterItem, nullptr, 0, item->RoomNumber, false);
|
||||
|
||||
SoundEffect(SFX_TR4_ROCK_FALL_LAND, &item->Pose);
|
||||
KillItem(itemNumber);
|
||||
}
|
||||
}
|
||||
|
||||
item->ItemFlags[0]++;
|
||||
|
||||
if (GetDistanceToFloor(itemNumber) >= 0)
|
||||
{
|
||||
// If crumbled before actual delay (e.g. too low position), force delay to be correct
|
||||
if (item->ItemFlags[0] < FALLINGBLOCK_DELAY)
|
||||
item->ItemFlags[0] = FALLINGBLOCK_DELAY;
|
||||
|
||||
// Convert object to shatter item
|
||||
ShatterItem.yRot = item->Pose.Orientation.y;
|
||||
ShatterItem.meshIndex = Objects[item->ObjectNumber].meshIndex;
|
||||
ShatterItem.color = item->Color;
|
||||
ShatterItem.sphere.x = item->Pose.Position.x;
|
||||
ShatterItem.sphere.y = item->Pose.Position.y - STEP_SIZE; // So debris won't spawn below floor
|
||||
ShatterItem.sphere.z = item->Pose.Position.z;
|
||||
ShatterItem.bit = 0;
|
||||
ShatterImpactData.impactDirection = Vector3(0, -(float)item->ItemFlags[1] / (float)FALLINGBLOCK_MAX_SPEED, 0);
|
||||
ShatterImpactData.impactLocation = { (float)ShatterItem.sphere.x, (float)ShatterItem.sphere.y, (float)ShatterItem.sphere.z };
|
||||
ShatterObject(&ShatterItem, nullptr, 0, item->RoomNumber, false);
|
||||
|
||||
SoundEffect(SFX_TR4_ROCK_FALL_LAND, &item->Pose);
|
||||
KillItem(itemNumber);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
void FallingBlockCollision(short itemNum, ItemInfo* l, CollisionInfo* coll);
|
||||
void FallingBlockCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
void FallingBlockControl(short itemNumber);
|
||||
std::optional<int> FallingBlockFloor(short itemNumber, int x, int y, int z);
|
||||
std::optional<int> FallingBlockCeiling(short itemNumber, int x, int y, int z);
|
||||
int FallingBlockFloorBorder(short itemNumber);
|
||||
int FallingBlockCeilingBorder(short itemNumber);
|
||||
int FallingBlockCeilingBorder(short itemNumber);
|
||||
|
|
|
@ -461,6 +461,7 @@ void StartTraps()
|
|||
object->saveFlags = true;
|
||||
object->savePosition = true;
|
||||
object->usingDrawAnimatingItem = true;
|
||||
object->isPickup = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
|
||||
// Puzzles
|
||||
void PuzzleHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
|
|
|
@ -5,33 +5,35 @@
|
|||
#include "Game/control/control.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/people.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
BITE_INFO ApeBite = { 0, -19, 75, 15 };
|
||||
const vector<int> ApeAttackJoints = { 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
|
||||
constexpr auto APE_ATTACK_DAMAGE = 200;
|
||||
|
||||
constexpr auto APE_ATTACK_RANGE = SECTOR(0.42f);
|
||||
constexpr auto APE_PANIC_RANGE = SECTOR(2);
|
||||
constexpr auto APE_ATTACK_RANGE = SQUARE(SECTOR(0.42f));
|
||||
constexpr auto APE_PANIC_RANGE = SQUARE(SECTOR(2));
|
||||
|
||||
constexpr auto APE_JUMP_CHANCE = 0xa0;
|
||||
constexpr auto APE_POUND_CHEST_CHANCE = APE_JUMP_CHANCE + 0xA0;
|
||||
constexpr auto APE_JUMP_CHANCE = 0xA0;
|
||||
constexpr auto APE_POUND_CHEST_CHANCE = APE_JUMP_CHANCE + 0xA0;
|
||||
constexpr auto APE_POUND_GROUND_CHANCE = APE_POUND_CHEST_CHANCE + 0xA0;
|
||||
constexpr auto APE_RUN_LEFT_CHANCE = APE_POUND_GROUND_CHANCE + 0xA0;
|
||||
constexpr auto APE_RUN_LEFT_CHANCE = APE_POUND_GROUND_CHANCE + 0xA0;
|
||||
|
||||
constexpr auto SHIFT = 75;
|
||||
|
||||
#define APE_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||
#define APE_DISPLAY_ANGLE ANGLE(45.0f)
|
||||
#define APE_DISPLAY_ANGLE ANGLE(45.0f)
|
||||
|
||||
const auto ApeBite = BiteInfo(Vector3(0.0f, -19.0f, 75.0f), 15);
|
||||
const vector<int> ApeAttackJoints = { 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
|
||||
enum ApeState
|
||||
{
|
||||
|
@ -73,10 +75,12 @@ namespace TEN::Entities::TR1
|
|||
APE_ANIM_VAULT = 19
|
||||
};
|
||||
|
||||
const std::array ApeDeathAnims = { APE_ANIM_DEATH_1, APE_ANIM_DEATH_2 };
|
||||
|
||||
enum ApeFlags
|
||||
{
|
||||
APE_FLAG_ATTACK = (1 << 0),
|
||||
APE_FLAG_TURN_LEFT = (1 << 1),
|
||||
APE_FLAG_ATTACK = (1 << 0),
|
||||
APE_FLAG_TURN_LEFT = (1 << 1),
|
||||
APE_FLAG_TURN_RIGHT = (1 << 2)
|
||||
};
|
||||
|
||||
|
@ -88,25 +92,25 @@ namespace TEN::Entities::TR1
|
|||
if (creature->Flags & APE_FLAG_TURN_LEFT)
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(90.0f);
|
||||
creature->Flags -= APE_FLAG_TURN_LEFT;
|
||||
creature->Flags &= ~APE_FLAG_TURN_LEFT;
|
||||
}
|
||||
else if (item->Flags & APE_FLAG_TURN_RIGHT)
|
||||
{
|
||||
item->Pose.Orientation.y += ANGLE(90.0f);
|
||||
creature->Flags -= APE_FLAG_TURN_RIGHT;
|
||||
creature->Flags &= ~APE_FLAG_TURN_RIGHT;
|
||||
}
|
||||
|
||||
long long xx = item->Pose.Position.z / SECTOR(1);
|
||||
long long yy = item->Pose.Position.x / SECTOR(1);
|
||||
long long y = item->Pose.Position.y;
|
||||
int xx = item->Pose.Position.z / SECTOR(1);
|
||||
int yy = item->Pose.Position.x / SECTOR(1);
|
||||
int y = item->Pose.Position.y;
|
||||
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
|
||||
if (item->Pose.Position.y > (y - CLICK(1.5f)))
|
||||
return;
|
||||
|
||||
long long xFloor = item->Pose.Position.z / SECTOR(1);
|
||||
long long yFloor = item->Pose.Position.x / SECTOR(1);
|
||||
int xFloor = item->Pose.Position.z / SECTOR(1);
|
||||
int yFloor = item->Pose.Position.x / SECTOR(1);
|
||||
if (xx == xFloor)
|
||||
{
|
||||
if (yy == yFloor)
|
||||
|
@ -141,17 +145,10 @@ namespace TEN::Entities::TR1
|
|||
// diagonal
|
||||
}
|
||||
|
||||
switch (CreatureVault(itemNumber, angle, 2, SHIFT))
|
||||
if (CreatureVault(itemNumber, angle, 2, SHIFT) == 2)
|
||||
{
|
||||
case 2:
|
||||
item->Pose.Position.y = y;
|
||||
item->Animation.AnimNumber = Objects[ID_APE].animIndex + APE_ANIM_VAULT;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = APE_STATE_VAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
SetAnimation(item, APE_ANIM_VAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,11 +166,7 @@ namespace TEN::Entities::TR1
|
|||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != APE_STATE_DEATH)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + APE_ANIM_DEATH_1 + (short)(GetRandomControl() / 0x4000);
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = APE_STATE_DEATH;
|
||||
}
|
||||
SetAnimation(item, ApeDeathAnims[GenerateInt(0, ApeDeathAnims.size() - 1)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -183,12 +176,12 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, TIMID);
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
GetCreatureMood(item, &AI, false);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
angle = CreatureTurn(item, creatureInfo->MaxTurn);
|
||||
|
||||
if (item->HitStatus || AI.distance < pow(APE_PANIC_RANGE, 2))
|
||||
if (item->HitStatus || AI.distance < APE_PANIC_RANGE)
|
||||
creatureInfo->Flags |= APE_FLAG_ATTACK;
|
||||
|
||||
short random;
|
||||
|
@ -203,13 +196,13 @@ namespace TEN::Entities::TR1
|
|||
}
|
||||
else if (item->Flags & APE_FLAG_TURN_RIGHT)
|
||||
{
|
||||
item->Pose.Orientation.y += ANGLE(90);
|
||||
item->Pose.Orientation.y += ANGLE(90.0f);
|
||||
creatureInfo->Flags -= APE_FLAG_TURN_RIGHT;
|
||||
}
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (AI.bite && AI.distance < pow(APE_ATTACK_RANGE, 2))
|
||||
else if (AI.bite && AI.distance < APE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = APE_STATE_ATTACK;
|
||||
else if (!(creatureInfo->Flags & APE_FLAG_ATTACK) &&
|
||||
AI.zoneNumber == AI.enemyZone && AI.ahead)
|
||||
|
@ -240,7 +233,7 @@ namespace TEN::Entities::TR1
|
|||
case APE_STATE_RUN_FORWARD:
|
||||
creatureInfo->MaxTurn = APE_RUN_TURN_RATE_MAX;
|
||||
|
||||
if (creatureInfo->Flags == 0 &&
|
||||
if (!creatureInfo->Flags &&
|
||||
AI.angle > -APE_DISPLAY_ANGLE &&
|
||||
AI.angle < APE_DISPLAY_ANGLE)
|
||||
{
|
||||
|
@ -297,9 +290,9 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, ApeAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, &ApeBite, DoBloodSplat);
|
||||
item->Animation.RequiredState = APE_STATE_IDLE;
|
||||
DoDamage(creatureInfo->Enemy, APE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, ApeBite, DoBloodSplat);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -15,25 +15,25 @@ using std::vector;
|
|||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
BITE_INFO BearBite = { 0, 96, 335, 14 };
|
||||
const vector<int> BearAttackJoints = { 2, 3, 5, 6, 14, 17 };
|
||||
|
||||
constexpr auto BEAR_RUN_DAMAGE = 3;
|
||||
constexpr auto BEAR_RUN_DAMAGE = 3;
|
||||
constexpr auto BEAR_ATTACK_DAMAGE = 200;
|
||||
constexpr auto BEAR_SLAM_DAMAGE = 200;
|
||||
constexpr auto BEAR_PAT_DAMAGE = 400;
|
||||
constexpr auto BEAR_SLAM_DAMAGE = 200;
|
||||
constexpr auto BEAR_PAT_DAMAGE = 400;
|
||||
|
||||
constexpr auto BEAR_ATTACK_RANGE = SECTOR(1);
|
||||
constexpr auto BEAR_REAR_RANGE = SECTOR(2);
|
||||
constexpr auto BEAR_ATTACK_RANGE = SECTOR(1);
|
||||
constexpr auto BEAR_REAR_RANGE = SECTOR(2);
|
||||
constexpr auto BEAR_REAR_SWIPE_ATTACK_RANGE = SECTOR(0.6f);
|
||||
constexpr auto BEAR_EAT_RANGE = CLICK(3);
|
||||
constexpr auto BEAR_EAT_RANGE = CLICK(3);
|
||||
|
||||
constexpr auto BEAR_ROAR_CHANCE = 0x50;
|
||||
constexpr auto BEAR_REAR_CHANCE = 0x300;
|
||||
constexpr auto BEAR_DROP_CHANCE = 0x600;
|
||||
|
||||
#define BEAR_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||
#define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||
#define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||
|
||||
const auto BearBite = BiteInfo(Vector3(0.0f, 96.0f, 335.0f), 14);
|
||||
const vector<int> BearAttackJoints = { 2, 3, 5, 6, 14, 17 };
|
||||
|
||||
enum BearState
|
||||
{
|
||||
|
@ -133,8 +133,8 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
|
@ -194,7 +194,9 @@ namespace TEN::Entities::TR1
|
|||
item->Animation.TargetState = BEAR_STATE_IDLE;
|
||||
else if (AI.ahead && !item->Animation.RequiredState)
|
||||
{
|
||||
if (!creature->Flags && AI.distance < pow(BEAR_REAR_RANGE, 2) && GetRandomControl() < BEAR_REAR_CHANCE)
|
||||
if (AI.distance < pow(BEAR_REAR_RANGE, 2) &&
|
||||
GetRandomControl() < BEAR_REAR_CHANCE &&
|
||||
!creature->Flags)
|
||||
{
|
||||
item->Animation.RequiredState = BEAR_STATE_REAR;
|
||||
item->Animation.TargetState = BEAR_STATE_IDLE;
|
||||
|
@ -262,7 +264,7 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, &BearBite, DoBloodSplat);
|
||||
CreatureEffect(item, BearBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, BEAR_ATTACK_DAMAGE);
|
||||
item->Animation.RequiredState = BEAR_STATE_IDLE;
|
||||
}
|
||||
|
|
|
@ -10,107 +10,100 @@
|
|||
#include "Game/misc.h"
|
||||
#include "Game/people.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
BITE_INFO BigRatBite = { 0, -11, 108, 3 };
|
||||
const vector<int> BigRatAttackJoints = { 0, 1, 2, 3, 7, 8, 24, 25 };
|
||||
constexpr auto BIG_RAT_BITE_ATTACK_DAMAGE = 20;
|
||||
constexpr auto BIG_RAT_POUNCE_ATTACK_DAMAGE = 25;
|
||||
|
||||
constexpr auto BIG_RAT_BITE_DAMAGE = 20;
|
||||
constexpr auto BIG_RAT_CHARGE_DAMAGE = 25;
|
||||
constexpr auto BIG_RAT_ALERT_RANGE = SQUARE(SECTOR(1.5f));
|
||||
constexpr auto BIG_RAT_VISIBILITY_RANGE = SQUARE(SECTOR(5));
|
||||
constexpr auto BIG_RAT_LAND_BITE_ATTACK_RANGE = SQUARE(SECTOR(0.34f));
|
||||
constexpr auto BIG_RAT_POUNCE_ATTACK_RANGE = SQUARE(SECTOR(0.5f));
|
||||
constexpr auto BIG_RAT_WATER_BITE_ATTACK_RANGE = SQUARE(SECTOR(0.3f));
|
||||
|
||||
constexpr auto DEFAULT_SWIM_UPDOWN_SPEED = 32;
|
||||
constexpr auto BIG_RAT_REAR_POSE_CHANCE = 0.008f;
|
||||
constexpr auto BIG_RAT_SWIM_UP_DOWN_SPEED = 32;
|
||||
constexpr auto BIG_RAT_WATER_SURFACE_OFFSET = 10;
|
||||
|
||||
constexpr auto BIG_RAT_ALERT_RANGE = SECTOR(1.5f);
|
||||
constexpr auto BIG_RAT_VISIBILITY_RANGE = SECTOR(5);
|
||||
constexpr auto BIG_RAT_BITE_RANGE = SECTOR(0.34f);
|
||||
constexpr auto BIG_RAT_CHARGE_RANGE = SECTOR(0.5f);
|
||||
constexpr auto BIG_RAT_WATER_BITE_RANGE = SECTOR(0.3f);
|
||||
#define BIG_RAT_RUN_TURN_RATE_MAX ANGLE(6.0f)
|
||||
#define BIG_RAT_SWIM_TURN_RATE_MAX ANGLE(3.0f)
|
||||
|
||||
constexpr auto BIG_RAT_POSE_CHANCE = 0x100;
|
||||
|
||||
#define BIG_RAT_RUN_TURN_ANGLE ANGLE(6.0f)
|
||||
#define BIG_RAT_SWIM_TURN_ANGLE ANGLE(3.0f)
|
||||
const auto BigRatBite = BiteInfo(Vector3(0.0f, -11.0f, 108.0f), 3);
|
||||
|
||||
enum BigRatState
|
||||
{
|
||||
BIG_RAT_STATE_EMPTY = 0,
|
||||
BIG_RAT_STATE_NONE = 0,
|
||||
BIG_RAT_STATE_IDLE = 1,
|
||||
BIG_RAT_STATE_CHARGE_ATTACK = 2,
|
||||
BIG_RAT_STATE_RUN = 3,
|
||||
BIG_RAT_STATE_BITE_ATTACK = 4,
|
||||
BIG_RAT_STATE_POUNCE_ATTACK = 2,
|
||||
BIG_RAT_STATE_RUN_FORWARD = 3,
|
||||
BIG_RAT_STATE_LAND_BITE_ATTACK = 4,
|
||||
BIG_RAT_STATE_LAND_DEATH = 5,
|
||||
BIG_RAT_STATE_POSE = 6,
|
||||
BIG_RAT_STATE_REAR_POSE = 6,
|
||||
BIG_RAT_STATE_SWIM = 7,
|
||||
BIG_RAT_STATE_SWIM_ATTACK = 8,
|
||||
BIG_RAT_STATE_SWIM_BITE_ATTACK = 8,
|
||||
BIG_RAT_STATE_WATER_DEATH = 9
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum BigRatAnim
|
||||
{
|
||||
BIG_RAT_ANIM_EMPTY = 0,
|
||||
BIG_RAT_ANIM_STOP_TO_RUN = 1,
|
||||
BIG_RAT_ANIM_RUN = 2,
|
||||
BIG_RAT_ANIM_RUN_TO_STOP = 3,
|
||||
BIG_RAT_ANIM_POSE = 4,
|
||||
BIG_RAT_ANIM_POSE_TO_STOP = 5,
|
||||
BIG_RAT_ANIM_IDLE = 0,
|
||||
BIG_RAT_ANIM_IDLE_TO_RUN_FORWARD = 1,
|
||||
BIG_RAT_ANIM_RUN_FORWARD = 2,
|
||||
BIG_RAT_ANIM_RUN_FORWARD_TO_IDLE = 3,
|
||||
BIG_RAT_ANIM_REAR_POSE = 4,
|
||||
BIG_RAT_ANIM_REAR_POSE_TO_IDLE = 5,
|
||||
BIG_RAT_ANIM_LAND_BITE_ATTACK = 6,
|
||||
BIG_RAT_ANIM_CHARGE_ATTACK = 7,
|
||||
BIG_RAT_ANIM_POUNCE_ATTACK = 7,
|
||||
BIG_RAT_ANIM_LAND_DEATH = 8,
|
||||
BIG_RAT_ANIM_SWIM = 9,
|
||||
BIG_RAT_ANIM_WATER_BITE = 10,
|
||||
BIG_RAT_ANIM_WATER_BITE_ATTACK = 10,
|
||||
BIG_RAT_ANIM_WATER_DEATH = 11,
|
||||
BIG_RAT_ANIM_RUN_TO_SWIM = 12,
|
||||
BIG_RAT_ANIM_SWIM_TO_RUN = 13
|
||||
|
||||
// NOTE: These animations don't exist for the TR2 rat. -- TokyoSU 2022.08.10
|
||||
BIG_RAT_ANIM_RUN_FORWARD_TO_SWIM = 12,
|
||||
BIG_RAT_ANIM_SWIM_TO_RUN_FORWARD = 13
|
||||
};
|
||||
|
||||
void InitialiseBigRat(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
InitialiseCreature(itemNumber);
|
||||
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item))
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + BIG_RAT_ANIM_SWIM;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = BIG_RAT_STATE_SWIM;
|
||||
item->Animation.TargetState = BIG_RAT_STATE_SWIM;
|
||||
}
|
||||
SetAnimation(item, BIG_RAT_ANIM_SWIM);
|
||||
else
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + BIG_RAT_ANIM_EMPTY;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = BIG_RAT_STATE_IDLE;
|
||||
item->Animation.TargetState = BIG_RAT_STATE_IDLE;
|
||||
}
|
||||
SetAnimation(item, BIG_RAT_ANIM_IDLE);
|
||||
}
|
||||
|
||||
static bool RatIsInWater(ItemInfo* item)
|
||||
int GetRatWaterHeight(ItemInfo* item)
|
||||
{
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short roomNumber = item->RoomNumber;
|
||||
GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
|
||||
int waterDepth = GetWaterSurface(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, roomNumber);
|
||||
auto probe = GetCollision(item);
|
||||
int waterDepth = GetWaterSurface(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, probe.RoomNumber);
|
||||
|
||||
if (waterDepth != NO_HEIGHT)
|
||||
{
|
||||
creature->LOT.Step = SECTOR(20);
|
||||
creature->LOT.Drop = -SECTOR(20);
|
||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED;
|
||||
return true;
|
||||
creature->LOT.Fly = BIG_RAT_SWIM_UP_DOWN_SPEED;
|
||||
return waterDepth;
|
||||
}
|
||||
else
|
||||
{
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
creature->LOT.Fly = NO_FLYING;
|
||||
return false;
|
||||
}
|
||||
|
||||
return NO_HEIGHT;
|
||||
}
|
||||
|
||||
void BigRatControl(short itemNumber)
|
||||
|
@ -120,34 +113,24 @@ namespace TEN::Entities::TR1
|
|||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
auto* objectInfo = &Objects[item->ObjectNumber];
|
||||
|
||||
int waterHeight = GetRatWaterHeight(item);
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
int waterHeight = GetWaterHeight(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
|
||||
bool isOnWater = waterHeight != NO_HEIGHT;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != BIG_RAT_STATE_LAND_DEATH &&
|
||||
item->Animation.ActiveState != BIG_RAT_STATE_WATER_DEATH)
|
||||
{
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item))
|
||||
{
|
||||
item->Animation.AnimNumber = objectInfo->animIndex + BIG_RAT_ANIM_WATER_DEATH;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = BIG_RAT_STATE_WATER_DEATH;
|
||||
item->Animation.TargetState = BIG_RAT_STATE_WATER_DEATH;
|
||||
}
|
||||
if (isOnWater)
|
||||
SetAnimation(item, BIG_RAT_ANIM_WATER_DEATH);
|
||||
else
|
||||
{
|
||||
item->Animation.AnimNumber = objectInfo->animIndex + BIG_RAT_ANIM_LAND_DEATH;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = BIG_RAT_STATE_LAND_DEATH;
|
||||
item->Animation.TargetState = BIG_RAT_STATE_LAND_DEATH;
|
||||
}
|
||||
SetAnimation(item, BIG_RAT_ANIM_LAND_DEATH);
|
||||
}
|
||||
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item))
|
||||
if (isOnWater)
|
||||
CreatureFloat(itemNumber);
|
||||
}
|
||||
else
|
||||
|
@ -158,110 +141,94 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, TIMID);
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
GetCreatureMood(item, &AI, false);
|
||||
CreatureMood(item, &AI, false);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
if (item->AIBits & ALL_AIOBJ)
|
||||
GetAITarget(creature);
|
||||
else if (creature->HurtByLara)
|
||||
creature->Enemy = LaraItem;
|
||||
|
||||
if ((item->HitStatus || AI.distance < pow(BIG_RAT_ALERT_RANGE, 2)) ||
|
||||
(TargetVisible(item, &AI) && AI.distance < pow(BIG_RAT_VISIBILITY_RANGE, 2)))
|
||||
{
|
||||
if (!creature->Alerted)
|
||||
creature->Alerted = true;
|
||||
|
||||
AlertAllGuards(itemNumber);
|
||||
}
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case BIG_RAT_STATE_IDLE:
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (AI.bite && AI.distance < pow(BIG_RAT_BITE_RANGE, 2))
|
||||
item->Animation.TargetState = BIG_RAT_STATE_BITE_ATTACK;
|
||||
else if (AI.bite && AI.distance < BIG_RAT_LAND_BITE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BIG_RAT_STATE_LAND_BITE_ATTACK;
|
||||
else
|
||||
item->Animation.TargetState = BIG_RAT_STATE_RUN;
|
||||
item->Animation.TargetState = BIG_RAT_STATE_RUN_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case BIG_RAT_STATE_RUN:
|
||||
creature->MaxTurn = BIG_RAT_RUN_TURN_ANGLE;
|
||||
case BIG_RAT_STATE_RUN_FORWARD:
|
||||
creature->MaxTurn = BIG_RAT_RUN_TURN_RATE_MAX;
|
||||
|
||||
if (RatIsInWater(item))
|
||||
TENLog("WaterHeight: " + std::to_string(waterHeight));
|
||||
if (isOnWater)
|
||||
{
|
||||
item->Animation.TargetState = BIG_RAT_STATE_SWIM;
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_SWIM;
|
||||
SetAnimation(item, BIG_RAT_ANIM_SWIM);
|
||||
break;
|
||||
}
|
||||
|
||||
if (AI.ahead && item->TestBits(JointBitType::Touch, BigRatAttackJoints))
|
||||
if (AI.ahead && item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||
item->Animation.TargetState = BIG_RAT_STATE_IDLE;
|
||||
else if (AI.bite && AI.distance < pow(BIG_RAT_CHARGE_RANGE, 2))
|
||||
item->Animation.TargetState = BIG_RAT_STATE_CHARGE_ATTACK;
|
||||
else if (AI.ahead && GetRandomControl() < BIG_RAT_POSE_CHANCE)
|
||||
else if (AI.bite && AI.distance < BIG_RAT_POUNCE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BIG_RAT_STATE_POUNCE_ATTACK;
|
||||
else if (AI.ahead && TestProbability(BIG_RAT_REAR_POSE_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = BIG_RAT_STATE_IDLE;
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_POSE;
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_REAR_POSE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BIG_RAT_STATE_BITE_ATTACK:
|
||||
case BIG_RAT_STATE_LAND_BITE_ATTACK:
|
||||
if (!item->Animation.RequiredState && AI.ahead &&
|
||||
item->TestBits(JointBitType::Touch, BigRatAttackJoints))
|
||||
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||
{
|
||||
CreatureEffect(item, &BigRatBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, BIG_RAT_BITE_DAMAGE);
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_IDLE;
|
||||
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BIG_RAT_STATE_CHARGE_ATTACK:
|
||||
case BIG_RAT_STATE_POUNCE_ATTACK:
|
||||
if (!item->Animation.RequiredState && AI.ahead &&
|
||||
item->TestBits(JointBitType::Touch, BigRatAttackJoints))
|
||||
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||
{
|
||||
CreatureEffect(item, &BigRatBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, BIG_RAT_CHARGE_DAMAGE);
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_RUN;
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_RUN_FORWARD;
|
||||
DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BIG_RAT_STATE_POSE:
|
||||
if (creature->Mood != MoodType::Bored || GetRandomControl() < BIG_RAT_POSE_CHANCE)
|
||||
case BIG_RAT_STATE_REAR_POSE:
|
||||
if (creature->Mood != MoodType::Bored || TestProbability(BIG_RAT_REAR_POSE_CHANCE))
|
||||
item->Animation.TargetState = BIG_RAT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case BIG_RAT_STATE_SWIM:
|
||||
creature->MaxTurn = BIG_RAT_SWIM_TURN_ANGLE;
|
||||
creature->MaxTurn = BIG_RAT_SWIM_TURN_RATE_MAX;
|
||||
|
||||
if (!RatIsInWater(item))
|
||||
if (!isOnWater)
|
||||
{
|
||||
item->Animation.TargetState = BIG_RAT_STATE_RUN;
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_RUN;
|
||||
SetAnimation(item, BIG_RAT_ANIM_RUN_FORWARD);
|
||||
break;
|
||||
}
|
||||
|
||||
if (AI.ahead && item->TestBits(JointBitType::Touch, BigRatAttackJoints))
|
||||
item->Animation.TargetState = BIG_RAT_STATE_SWIM_ATTACK;
|
||||
if (AI.ahead && item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||
item->Animation.TargetState = BIG_RAT_STATE_SWIM_BITE_ATTACK;
|
||||
|
||||
break;
|
||||
|
||||
case BIG_RAT_STATE_SWIM_ATTACK:
|
||||
case BIG_RAT_STATE_SWIM_BITE_ATTACK:
|
||||
if (!item->Animation.RequiredState && AI.ahead &&
|
||||
item->TestBits(JointBitType::Touch, BigRatAttackJoints))
|
||||
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||
{
|
||||
CreatureEffect(item, &BigRatBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, BIG_RAT_BITE_DAMAGE);
|
||||
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
||||
}
|
||||
|
||||
item->Animation.TargetState = BIG_RAT_STATE_SWIM;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -270,10 +237,10 @@ namespace TEN::Entities::TR1
|
|||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
|
||||
if (RatIsInWater(item))
|
||||
if (isOnWater)
|
||||
{
|
||||
CreatureUnderwater(item, 0);
|
||||
item->Pose.Position.y = waterHeight;
|
||||
item->Pose.Position.y = waterHeight - BIG_RAT_WATER_SURFACE_OFFSET;
|
||||
}
|
||||
else
|
||||
item->Pose.Position.y = item->Floor;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Game/Lara/lara.h"
|
||||
#include "Game/Lara/lara_one_gun.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/missile.h"
|
||||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
|
@ -20,20 +21,17 @@ using std::vector;
|
|||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
BITE_INFO CentaurRocketBite = { 11, 415, 41, 13 };
|
||||
BITE_INFO CentaurRearBite = { 50, 30, 0, 5 };
|
||||
constexpr auto CENTAUR_REAR_DAMAGE = 200;
|
||||
constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f);
|
||||
constexpr auto CENTAUR_REAR_CHANCE = 0x60;
|
||||
constexpr auto CENTAUR_BOMB_VELOCITY = 20;
|
||||
|
||||
#define CENTAUR_TURN_RATE_MAX ANGLE(4.0f)
|
||||
|
||||
const auto CentaurRocketBite = BiteInfo(Vector3(11.0f, 415.0f, 41.0f), 13);
|
||||
const auto CentaurRearBite = BiteInfo(Vector3(50.0f, 30.0f, 0.0f), 5);
|
||||
const vector<int> CentaurAttackJoints = { 0, 3, 4, 7, 8, 16, 17 };
|
||||
|
||||
constexpr auto CENTAUR_REAR_DAMAGE = 200;
|
||||
|
||||
constexpr auto CENTAUR_PROJECTILE_SPEED = CLICK(1);
|
||||
|
||||
constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f);
|
||||
|
||||
constexpr auto CENTAUR_REAR_CHANCE = 0x60;
|
||||
|
||||
#define CENTAUR_TURN_ANGLE ANGLE(4.0f)
|
||||
|
||||
enum CentaurState
|
||||
{
|
||||
CENTAUR_STATE_NONE = 0,
|
||||
|
@ -51,135 +49,13 @@ namespace TEN::Entities::TR1
|
|||
CENTAUR_ANIM_DEATH = 8,
|
||||
};
|
||||
|
||||
void ControlCentaurBomb(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
bool aboveWater = false;
|
||||
auto oldPos = item->Pose.Position;
|
||||
|
||||
item->Pose.Orientation.z += ANGLE(35.0f);
|
||||
if (!TestEnvironment(ENV_FLAG_WATER, item->RoomNumber))
|
||||
{
|
||||
item->Pose.Orientation.x -= ANGLE(1.0f);
|
||||
if (item->Pose.Orientation.x < -ANGLE(90.0f))
|
||||
item->Pose.Orientation.x = -ANGLE(90.0f);
|
||||
|
||||
item->Animation.Velocity.z = CENTAUR_PROJECTILE_SPEED * phd_cos(item->Pose.Orientation.x);
|
||||
item->Animation.Velocity.y = -CENTAUR_PROJECTILE_SPEED * phd_sin(item->Pose.Orientation.x);
|
||||
aboveWater = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Animation.Velocity.y += 3;
|
||||
aboveWater = true;
|
||||
|
||||
if (item->Animation.Velocity.z)
|
||||
{
|
||||
item->Pose.Orientation.z += ((item->Animation.Velocity.z / 4) + 7) * ANGLE(1.0f);
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Pose.Orientation.y += ((item->Animation.Velocity.z / 2) + 7) * ANGLE(1.0f);
|
||||
else
|
||||
item->Pose.Orientation.x += ((item->Animation.Velocity.z / 2) + 7) * ANGLE(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
TranslateItem(item, item->Pose.Orientation, item->Animation.Velocity.z);
|
||||
|
||||
auto probe = GetCollision(item);
|
||||
|
||||
if (probe.Position.Floor < item->Pose.Position.y ||
|
||||
probe.Position.Ceiling > item->Pose.Position.y)
|
||||
{
|
||||
item->Pose.Position = oldPos;
|
||||
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item->RoomNumber))
|
||||
TriggerUnderwaterExplosion(item, 0);
|
||||
else
|
||||
{
|
||||
item->Pose.Position.y -= CLICK(0.5f);
|
||||
TriggerShockwave(&item->Pose, 48, 304, 96, 0, 96, 128, 24, 0, 0);
|
||||
|
||||
TriggerExplosionSparks(oldPos.x, oldPos.y, oldPos.z, 3, -2, 0, item->RoomNumber);
|
||||
for (int x = 0; x < 2; x++)
|
||||
TriggerExplosionSparks(oldPos.x, oldPos.y, oldPos.z, 3, -1, 0, item->RoomNumber);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->RoomNumber != probe.RoomNumber)
|
||||
ItemNewRoom(itemNumber, probe.RoomNumber);
|
||||
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item->RoomNumber) && aboveWater)
|
||||
SetupRipple(item->Pose.Position.x, g_Level.Rooms[item->RoomNumber].minfloor, item->Pose.Position.z, (GetRandomControl() & 7) + 8, 0);
|
||||
|
||||
GetCollidedObjects(item, HARPOON_HIT_RADIUS, true, &CollidedItems[0], &CollidedMeshes[0], 0);
|
||||
|
||||
if (!CollidedItems[0] && !CollidedMeshes[0])
|
||||
return;
|
||||
|
||||
if (CollidedItems[0])
|
||||
{
|
||||
auto* currentItem = CollidedItems[0];
|
||||
|
||||
int k = 0;
|
||||
do
|
||||
{
|
||||
auto* currentObject = &Objects[currentItem->ObjectNumber];
|
||||
|
||||
if (currentItem->Status == ITEM_ACTIVE &&
|
||||
currentObject->intelligent && !currentObject->undead &&
|
||||
currentObject->collision)
|
||||
{
|
||||
DoExplosiveDamageOnBaddy(LaraItem, currentItem, item, LaraWeaponType::Crossbow);
|
||||
}
|
||||
|
||||
k++;
|
||||
currentItem = CollidedItems[k];
|
||||
|
||||
} while (currentItem);
|
||||
}
|
||||
}
|
||||
|
||||
static void RocketGun(ItemInfo* centaurItem)
|
||||
{
|
||||
short itemNumber;
|
||||
itemNumber = CreateItem();
|
||||
|
||||
if (itemNumber != NO_ITEM)
|
||||
{
|
||||
auto* projectileItem = &g_Level.Items[itemNumber];
|
||||
|
||||
projectileItem->ObjectNumber = ID_PROJ_BOMB;
|
||||
projectileItem->Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
projectileItem->RoomNumber = centaurItem->RoomNumber;
|
||||
|
||||
auto pos = Vector3Int(11, 415, 41);
|
||||
GetJointAbsPosition(centaurItem, &pos, 13);
|
||||
|
||||
projectileItem->Pose.Position = pos;
|
||||
InitialiseItem(itemNumber);
|
||||
|
||||
projectileItem->Pose.Orientation = Vector3Shrt(0, centaurItem->Pose.Orientation.y, 0 );
|
||||
|
||||
projectileItem->Animation.Velocity.z = CENTAUR_PROJECTILE_SPEED * phd_cos(projectileItem->Pose.Orientation.x);
|
||||
projectileItem->Animation.Velocity.y = -CENTAUR_PROJECTILE_SPEED * phd_cos(projectileItem->Pose.Orientation.x);
|
||||
projectileItem->ItemFlags[0] = 1;
|
||||
|
||||
AddActiveItem(itemNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void CentaurControl(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
|
||||
|
@ -200,9 +76,9 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, CENTAUR_TURN_ANGLE);
|
||||
angle = CreatureTurn(item, CENTAUR_TURN_RATE_MAX);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
|
@ -252,7 +128,7 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState)
|
||||
{
|
||||
item->Animation.RequiredState = CENTAUR_STATE_AIM;
|
||||
RocketGun(item);
|
||||
CreatureEffect2(item, CentaurRocketBite, CENTAUR_BOMB_VELOCITY, head, BombGun);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -261,7 +137,7 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, CentaurAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, &CentaurRearBite, DoBloodSplat);
|
||||
CreatureEffect(item, CentaurRearBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, CENTAUR_REAR_DAMAGE);
|
||||
item->Animation.RequiredState = CENTAUR_STATE_IDLE;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
constexpr auto SHARD_VELOCITY = 250;
|
||||
constexpr auto BOMB_VELOCITY = 220;
|
||||
|
||||
void CentaurControl(short itemNumber);
|
||||
void ControlCentaurBomb(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace TEN::Entities::TR1
|
|||
if (!found)
|
||||
itemNumber = NO_ITEM;
|
||||
|
||||
return (itemNumber == NO_ITEM ? NULL : &g_Level.Items[itemNumber]);
|
||||
return (itemNumber == NO_ITEM ? nullptr : &g_Level.Items[itemNumber]);
|
||||
}
|
||||
|
||||
static short GetWeaponDamage(LaraWeaponType weaponType)
|
||||
|
@ -59,7 +59,7 @@ namespace TEN::Entities::TR1
|
|||
|
||||
auto* reference = FindReference(item, ID_BACON_REFERENCE);
|
||||
|
||||
if (item->Data == NULL)
|
||||
if (!item->Data)
|
||||
{
|
||||
Vector3Int pos;
|
||||
if (reference == nullptr)
|
||||
|
|
|
@ -14,21 +14,27 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
const std::vector<int> MutantAttackJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||
const std::vector<int> MutantAttackLeftJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
|
||||
const std::vector<int> MutantAttackRightJoints = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||
constexpr auto MUTANT_ATTACK_DAMAGE = 500;
|
||||
constexpr auto MUTANT_CONTACT_DAMAGE = 6;
|
||||
|
||||
constexpr auto MUTANT_ATTACK_RANGE = SQUARE(SECTOR(2.5f));
|
||||
constexpr auto MUTANT_CLOSE_RANGE = SQUARE(SECTOR(2.2f));
|
||||
|
||||
constexpr auto MUTANT_ATTACK_1_CHANCE = 0x2AF8;
|
||||
constexpr auto MUTANT_ATTACK_2_CHANCE = 0x55F0;
|
||||
|
||||
#define MUTANT_NEED_TURN ANGLE(45.0f)
|
||||
#define MUTANT_TURN ANGLE(3.0f)
|
||||
#define MUTANT_ATTACK_RANGE pow(2600, 2)
|
||||
#define MUTANT_CLOSE_RANGE pow(2250, 2)
|
||||
#define MUTANT_ATTACK_1_CHANCE 11000
|
||||
#define MUTANT_ATTACK_2_CHANCE 22000
|
||||
#define MUTANT_ATTACK_DAMAGE 500
|
||||
#define MUTANT_TOUCH_DAMAGE 5
|
||||
#define LARA_GIANT_MUTANT_DEATH 6
|
||||
|
||||
#define LARA_GIANT_MUTANT_DEATH 6 // TODO: Not 13? Check this.
|
||||
|
||||
const vector<int> MutantAttackJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||
const vector<int> MutantAttackLeftJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
|
||||
const vector<int> MutantAttackRightJoints = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||
|
||||
enum GiantMutantState
|
||||
{
|
||||
|
@ -47,7 +53,7 @@ namespace TEN::Entities::TR1
|
|||
};
|
||||
|
||||
// TODO
|
||||
enum GianMutantAnim
|
||||
enum GiantMutantAnim
|
||||
{
|
||||
MUTANT_ANIM_DEATH = 13,
|
||||
};
|
||||
|
@ -80,14 +86,14 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = (short)phd_atan(creature->Target.z - item->Pose.Position.z, creature->Target.x - item->Pose.Position.x) - item->Pose.Orientation.y;
|
||||
|
||||
if (item->TouchBits)
|
||||
{
|
||||
DoDamage(creature->Enemy, MUTANT_TOUCH_DAMAGE);
|
||||
DoDamage(creature->Enemy, MUTANT_CONTACT_DAMAGE);
|
||||
}
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#include "Objects/TR1/Entity/tr1_natla.h"
|
||||
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/missile.h"
|
||||
|
@ -14,17 +14,21 @@
|
|||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
BITE_INFO NatlaGunBite = { 5, 220, 7, 4 };
|
||||
// TODO: Organise.
|
||||
constexpr auto NATLA_SHOT_DAMAGE = 100;
|
||||
constexpr auto NATLA_NEAR_DEATH = 200;
|
||||
constexpr auto NATLA_DEATH_TIME = (FPS * 16); // 16 seconds.
|
||||
constexpr auto NATLA_FLYMODE = 0x8000;
|
||||
constexpr auto NATLA_TIMER = 0x7FFF;
|
||||
constexpr auto NATLA_LAND_CHANCE = 0x100;
|
||||
constexpr auto NATLA_GUN_VELOCITY = 400;
|
||||
|
||||
#define NATLA_NEAR_DEATH 200
|
||||
#define NATLA_FLYMODE 0x8000
|
||||
#define NATLA_TIMER 0x7fff
|
||||
#define NATLA_FIRE_ARC ANGLE(30.0f)
|
||||
#define NATLA_FLY_TURN ANGLE(5.0f)
|
||||
#define NATLA_RUN_TURN ANGLE(6.0f)
|
||||
#define NATLA_LAND_CHANCE 0x100
|
||||
#define NATLA_DEATH_TIME (FPS * 16) // 16 seconds.
|
||||
#define NATLA_SHOT_DAMAGE 100
|
||||
#define NATLA_TURN_NEAR_DEATH_SPEED ANGLE(6.0f)
|
||||
#define NATLA_TURN_SPEED ANGLE(5.0f)
|
||||
#define NATLA_FLY_ANGLE_SPEED ANGLE(5.0f)
|
||||
#define NATLA_SHOOT_ANGLE ANGLE(30.0f)
|
||||
|
||||
const auto NatlaGunBite = BiteInfo(Vector3(5.0f, 220.0f, 7.0f), 4);
|
||||
|
||||
enum NatlaState
|
||||
{
|
||||
|
@ -71,11 +75,11 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, NATLA_RUN_TURN);
|
||||
shoot = (AI.angle > -NATLA_FIRE_ARC && AI.angle < NATLA_FIRE_ARC&& Targetable(item, &AI));
|
||||
angle = CreatureTurn(item, NATLA_TURN_NEAR_DEATH_SPEED);
|
||||
shoot = (AI.angle > -NATLA_SHOOT_ANGLE && AI.angle < NATLA_SHOOT_ANGLE&& Targetable(item, &AI));
|
||||
|
||||
if (facing)
|
||||
{
|
||||
|
@ -107,7 +111,7 @@ namespace TEN::Entities::TR1
|
|||
|
||||
if (timer >= 20)
|
||||
{
|
||||
short FXNumber = CreatureEffect(item, &NatlaGunBite, ShardGun);
|
||||
short FXNumber = CreatureEffect(item, NatlaGunBite, ShardGun);
|
||||
if (FXNumber != NO_ITEM)
|
||||
{
|
||||
auto* fx = &EffectList[FXNumber];
|
||||
|
@ -125,7 +129,7 @@ namespace TEN::Entities::TR1
|
|||
|
||||
if (timer >= 20)
|
||||
{
|
||||
short FXNumber = CreatureEffect(item, &NatlaGunBite, ShardGun);
|
||||
short FXNumber = CreatureEffect(item, NatlaGunBite, ShardGun);
|
||||
if (FXNumber != NO_ITEM)
|
||||
{
|
||||
auto* fx = &EffectList[FXNumber];
|
||||
|
@ -175,7 +179,7 @@ namespace TEN::Entities::TR1
|
|||
creature->LOT.Fly = NO_FLYING;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
shoot = (AI.angle > -NATLA_FIRE_ARC && AI.angle < NATLA_FIRE_ARC&& Targetable(item, &AI));
|
||||
shoot = (AI.angle > -NATLA_SHOOT_ANGLE && AI.angle < NATLA_SHOOT_ANGLE&& Targetable(item, &AI));
|
||||
|
||||
if (item->Animation.ActiveState == NATLA_STATE_FLY && (creature->Flags & NATLA_FLYMODE))
|
||||
{
|
||||
|
@ -183,7 +187,7 @@ namespace TEN::Entities::TR1
|
|||
creature->Flags -= NATLA_FLYMODE;
|
||||
|
||||
if (!(creature->Flags & NATLA_FLYMODE))
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
creature->LOT.Step = SECTOR(20);
|
||||
creature->LOT.Drop = -SECTOR(20);
|
||||
|
@ -198,17 +202,17 @@ namespace TEN::Entities::TR1
|
|||
head = AI.angle;
|
||||
|
||||
if (item->Animation.ActiveState != NATLA_STATE_FLY || (creature->Flags & NATLA_FLYMODE))
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
item->Pose.Orientation.y -= facing;
|
||||
angle = CreatureTurn(item, NATLA_FLY_TURN);
|
||||
angle = CreatureTurn(item, NATLA_TURN_SPEED);
|
||||
|
||||
if (item->Animation.ActiveState == NATLA_STATE_FLY)
|
||||
{
|
||||
if (AI.angle > NATLA_FLY_TURN)
|
||||
facing += NATLA_FLY_TURN;
|
||||
else if (AI.angle < -NATLA_FLY_TURN)
|
||||
facing -= NATLA_FLY_TURN;
|
||||
if (AI.angle > NATLA_FLY_ANGLE_SPEED)
|
||||
facing += NATLA_FLY_ANGLE_SPEED;
|
||||
else if (AI.angle < -NATLA_FLY_ANGLE_SPEED)
|
||||
facing -= NATLA_FLY_ANGLE_SPEED;
|
||||
else
|
||||
facing += AI.angle;
|
||||
|
||||
|
@ -238,7 +242,7 @@ namespace TEN::Entities::TR1
|
|||
|
||||
if (timer >= 30)
|
||||
{
|
||||
short FXNumber = CreatureEffect(item, &NatlaGunBite, BombGun);
|
||||
short FXNumber = CreatureEffect(item, NatlaGunBite, BombGun);
|
||||
if (FXNumber != NO_ITEM)
|
||||
{
|
||||
auto* fx = &EffectList[FXNumber];
|
||||
|
@ -264,15 +268,15 @@ namespace TEN::Entities::TR1
|
|||
case NATLA_STATE_SHOOT:
|
||||
if (!item->Animation.RequiredState)
|
||||
{
|
||||
short FXNumber = CreatureEffect(item, &NatlaGunBite, BombGun);
|
||||
short FXNumber = CreatureEffect(item, NatlaGunBite, BombGun);
|
||||
if (FXNumber != NO_ITEM)
|
||||
gun = EffectList[FXNumber].pos.Orientation.x;
|
||||
|
||||
FXNumber = CreatureEffect(item, &NatlaGunBite, BombGun);
|
||||
FXNumber = CreatureEffect(item, NatlaGunBite, BombGun);
|
||||
if (FXNumber != NO_ITEM)
|
||||
EffectList[FXNumber].pos.Orientation.y += (short)((GetRandomControl() - 0x4000) / 4);
|
||||
|
||||
FXNumber = CreatureEffect(item, &NatlaGunBite, BombGun);
|
||||
FXNumber = CreatureEffect(item, NatlaGunBite, BombGun);
|
||||
if (FXNumber != NO_ITEM)
|
||||
EffectList[FXNumber].pos.Orientation.y += (short)((GetRandomControl() - 0x4000) / 4);
|
||||
|
||||
|
|
486
TombEngine/Objects/TR1/Entity/tr1_winged_mutant.cpp
Normal file
486
TombEngine/Objects/TR1/Entity/tr1_winged_mutant.cpp
Normal file
|
@ -0,0 +1,486 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR1/Entity/tr1_winged_mutant.h"
|
||||
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/control/lot.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/effects/tomb4fx.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/missile.h"
|
||||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
constexpr auto WINGED_MUTANT_IDLE_JUMP_ATTACK_DAMAGE = 150;
|
||||
constexpr auto WINGED_MUTANT_RUN_JUMP_ATTACK_DAMAGE = 100;
|
||||
constexpr auto WINGED_MUTANT_SWIPE_ATTACK_DAMAGE = 200;
|
||||
|
||||
constexpr auto WINGED_MUTANT_WALK_RANGE = SQUARE(SECTOR(4.5f));
|
||||
constexpr auto WINGED_MUTANT_SWIPE_ATTACK_RANGE = SQUARE(CLICK(1.17f));
|
||||
constexpr auto WINGED_MUTANT_RUN_JUMP_ATTACK_RANGE = SQUARE(CLICK(2.34f));
|
||||
constexpr auto WINGED_MUTANT_IDLE_JUMP_ATTACK_RANGE = SQUARE(SECTOR(2.5f));
|
||||
constexpr auto WINGED_MUTANT_ATTACK_RANGE = SQUARE(SECTOR(3.75f));
|
||||
|
||||
constexpr auto WINGED_MUTANT_POSE_CHANCE = 85;
|
||||
constexpr auto WINGED_MUTANT_UNPOSE_CHANCE = 200;
|
||||
|
||||
constexpr auto WINGED_MUTANT_FLY_VELOCITY = CLICK(1) / 8;
|
||||
constexpr auto WINGED_MUTANT_SHARD_VELOCITY = 250;
|
||||
constexpr auto WINGED_MUTANT_BOMB_VELOCITY = 220;
|
||||
|
||||
#define WINGED_MUTANT_WALK_FORWARD_TURN_RATE_MAX ANGLE(2.0f)
|
||||
#define WINGED_MUTANT_RUN_FORWARD_TURN_RATE_MAX ANGLE(6.0f)
|
||||
|
||||
const auto WingedMutantBite = BiteInfo(Vector3(-27.0f, 98.0f, 0.0f), 10);
|
||||
const auto WingedMutantRocketBite = BiteInfo(Vector3(51.0f, 213.0f, 0.0f), 14);
|
||||
const auto WingedMutantShardBite = BiteInfo(Vector3(-35.0f, 269.0f, 0.0f), 9);
|
||||
const vector<int> WingedMutantJoints = { WingedMutantShardBite.meshNum, WingedMutantBite.meshNum, WingedMutantRocketBite.meshNum };
|
||||
|
||||
enum WingedMutantState
|
||||
{
|
||||
WMUTANT_STATE_NONE = 0,
|
||||
WMUTANT_STATE_IDLE = 1,
|
||||
WMUTANT_STATE_WALK_FORWARD = 2,
|
||||
WMUTANT_STATE_RUN_FORWARD = 3,
|
||||
WMUTANT_STATE_IDLE_JUMP_ATTACK = 4,
|
||||
WMUTANT_STATE_DEATH = 5,
|
||||
WMUTANT_STATE_POSE = 6,
|
||||
WMUTANT_STATE_RUN_JUMP_ATTACK = 7,
|
||||
WMUTANT_STATE_SWIPE_ATTACK = 8,
|
||||
WMUTANT_STATE_AIM_DART = 9,
|
||||
WMUTANT_STATE_AIM_BOMB = 10,
|
||||
WMUTANT_STATE_SHOOT = 11,
|
||||
WMUTANT_STATE_INACTIVE = 12,
|
||||
WMUTANT_STATE_FLY = 13,
|
||||
};
|
||||
|
||||
enum WingedMutantAnim
|
||||
{
|
||||
WMUTANT_ANIM_INACTIVE = 0,
|
||||
WMUTANT_ANIM_INACTIVE_TO_IDLE = 1,
|
||||
WMUTANT_ANIM_IDLE = 2,
|
||||
WMUTANT_ANIM_IDLE_TO_RUN = 3,
|
||||
WMUTANT_ANIM_RUN_FORWARD = 4,
|
||||
WMUTANT_ANIM_IDLE_JUMP_ATTACK_START = 5,
|
||||
WMUTANT_ANIM_IDLE_JUMP_ATTACK_END = 6,
|
||||
WMUTANT_ANIM_IDLE_TO_POSE = 7,
|
||||
WMUTANT_ANIM_POSE = 8,
|
||||
WMUTANT_ANIM_POSE_TO_IDLE = 9,
|
||||
WMUTANT_ANIM_POSE_TO_WALK_FORWARD = 10,
|
||||
WMUTANT_ANIM_WALK_FORWARD = 11,
|
||||
WMUTANT_ANIM_WALK_FORWARD_TO_IDLE = 12,
|
||||
WMUTANT_ANIM_WALK_FORWARD_TO_POSE = 13,
|
||||
WMUTANT_ANIM_RUN_JUMP_ATTACK = 14,
|
||||
WMUTANT_ANIM_IDLE_TO_AIM_1 = 15,
|
||||
WMUTANT_ANIM_AIM_DART = 16,
|
||||
WMUTANT_ANIM_SHOOT_DART = 17,
|
||||
WMUTANT_ANIM_AIM_DART_TO_IDLE = 18,
|
||||
WMUTANT_ANIM_IDLE_TO_AIM_BOMB = 19,
|
||||
WMUTANT_ANIM_SHOOT_BOMB = 20,
|
||||
WMUTANT_ANIM_RUN_FORWARD_TO_IDLE = 21,
|
||||
WMUTANT_ANIM_AIM_BOMB_TO_IDLE = 22,
|
||||
WMUTANT_ANIM_IDLE_TO_FLY = 23,
|
||||
WMUTANT_ANIM_FLY = 24,
|
||||
WMUTANT_ANIM_FLY_TO_IDLE = 25,
|
||||
WMUTANT_ANIM_SWIPE_ATTACK = 26
|
||||
};
|
||||
|
||||
enum WingedMutantPathFinding
|
||||
{
|
||||
WMUTANT_PATH_GROUND = 1,
|
||||
WMUTANT_PATH_AERIAL = 2
|
||||
};
|
||||
|
||||
// NOTE: Originally, winged mutants did not have OCBs. -- TokyoSU 5/8/2022
|
||||
enum WingedMutantOcb
|
||||
{
|
||||
WMUTANT_OCB_START_AERIAL = (1 << 0),
|
||||
WMUTANT_OCB_START_INACTIVE = (1 << 1),
|
||||
WMUTANT_OCB_START_POSE = (1 << 2),
|
||||
WMUTANT_OCB_NO_WINGS = (1 << 3),
|
||||
WMUTANT_OCB_DISABLE_DART_WEAPON = (1 << 4),
|
||||
WMUTANT_OCB_DISABLE_BOMB_WEAPON = (1 << 5)
|
||||
};
|
||||
|
||||
enum WingedMutantProjectileType
|
||||
{
|
||||
WMUTANT_PROJ_NONE,
|
||||
WMUTANT_PROJ_DART,
|
||||
WMUTANT_PROJ_BOMB
|
||||
};
|
||||
|
||||
enum WingedMutantConfig
|
||||
{
|
||||
WMUTANT_CONF_CAN_FLY,
|
||||
WMUTANT_CONF_PATHFINDING_MODE,
|
||||
WMUTANT_CONF_PROJECTILE_MODE,
|
||||
WMUTANT_CONF_NO_WINGS,
|
||||
WMUTANT_CONF_DISABLE_DART_WEAPON,
|
||||
WMUTANT_CONF_DISABLE_BOMB_WEAPON
|
||||
};
|
||||
|
||||
void SwitchPathfinding(CreatureInfo* creature, WingedMutantPathFinding path)
|
||||
{
|
||||
switch (path)
|
||||
{
|
||||
case WMUTANT_PATH_GROUND:
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
creature->LOT.Fly = NO_FLYING;
|
||||
break;
|
||||
|
||||
case WMUTANT_PATH_AERIAL:
|
||||
creature->LOT.Step = SECTOR(30);
|
||||
creature->LOT.Drop = -SECTOR(30);
|
||||
creature->LOT.Fly = WINGED_MUTANT_FLY_VELOCITY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WingedMutantProjectileType CanTargetLara(ItemInfo* item, CreatureInfo* creature, AI_INFO* AI)
|
||||
{
|
||||
if (Targetable(item, AI) && (AI->zoneNumber != AI->enemyZone || AI->distance > WINGED_MUTANT_ATTACK_RANGE))
|
||||
{
|
||||
if ((AI->angle > 0 && AI->angle < ANGLE(45.0f)) &&
|
||||
item->TestFlags(WMUTANT_OCB_DISABLE_DART_WEAPON, false))
|
||||
{
|
||||
return WMUTANT_PROJ_DART;
|
||||
}
|
||||
else if ((AI->angle < 0 && AI->angle > -ANGLE(45.0f)) &&
|
||||
item->TestFlags(WMUTANT_OCB_DISABLE_BOMB_WEAPON, false))
|
||||
{
|
||||
return WMUTANT_PROJ_BOMB;
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot be targeted.
|
||||
return WMUTANT_PROJ_NONE;
|
||||
}
|
||||
|
||||
void WingedInitOCB(ItemInfo* item, CreatureInfo* creature)
|
||||
{
|
||||
if (item->TestOcb(WMUTANT_OCB_START_AERIAL))
|
||||
{
|
||||
SwitchPathfinding(creature, WMUTANT_PATH_AERIAL);
|
||||
SetAnimation(item, WMUTANT_ANIM_FLY);
|
||||
item->SetFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PATH_AERIAL);
|
||||
}
|
||||
else if (item->TestOcb(WMUTANT_OCB_START_INACTIVE))
|
||||
{
|
||||
SwitchPathfinding(creature, WMUTANT_PATH_GROUND);
|
||||
SetAnimation(item, WMUTANT_ANIM_INACTIVE);
|
||||
item->SetFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PATH_GROUND);
|
||||
}
|
||||
else if (item->TestOcb(WMUTANT_OCB_START_POSE))
|
||||
{
|
||||
SwitchPathfinding(creature, WMUTANT_PATH_GROUND);
|
||||
SetAnimation(item, WMUTANT_ANIM_INACTIVE);
|
||||
item->SetFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PATH_GROUND);
|
||||
}
|
||||
|
||||
// Remove OCBs since we don't need them anymore.
|
||||
if (item->TestOcb(WMUTANT_OCB_START_AERIAL))
|
||||
item->RemoveOcb(WMUTANT_OCB_START_AERIAL);
|
||||
if (item->TestOcb(WMUTANT_OCB_START_INACTIVE))
|
||||
item->RemoveOcb(WMUTANT_OCB_START_INACTIVE);
|
||||
if (item->TestOcb(WMUTANT_OCB_START_POSE))
|
||||
item->RemoveOcb(WMUTANT_OCB_START_POSE);
|
||||
}
|
||||
|
||||
// NOTE: Doesn't exist in the original game. -- TokyoSU 5/8/2022
|
||||
void InitialiseWingedMutant(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
InitialiseCreature(itemNumber);
|
||||
item->SetFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PATH_GROUND);
|
||||
item->SetFlags(WMUTANT_CONF_PROJECTILE_MODE, WMUTANT_PROJ_NONE);
|
||||
|
||||
if (item->TestOcb(WMUTANT_OCB_NO_WINGS))
|
||||
{
|
||||
item->SetFlags(WMUTANT_CONF_CAN_FLY, false);
|
||||
item->MeshBits = 0xFFE07FFF;
|
||||
}
|
||||
else
|
||||
item->SetFlags(WMUTANT_CONF_CAN_FLY, true);
|
||||
|
||||
if (item->TestOcb(WMUTANT_OCB_DISABLE_BOMB_WEAPON))
|
||||
item->SetFlags(WMUTANT_CONF_DISABLE_BOMB_WEAPON, true);
|
||||
if (item->TestOcb(WMUTANT_OCB_DISABLE_DART_WEAPON))
|
||||
item->SetFlags(WMUTANT_CONF_DISABLE_DART_WEAPON, true);
|
||||
|
||||
if (item->TestOcb(WMUTANT_OCB_DISABLE_BOMB_WEAPON))
|
||||
item->RemoveOcb(WMUTANT_OCB_DISABLE_BOMB_WEAPON);
|
||||
if (item->TestOcb(WMUTANT_OCB_DISABLE_DART_WEAPON))
|
||||
item->RemoveOcb(WMUTANT_OCB_DISABLE_DART_WEAPON);
|
||||
if (item->TestOcb(WMUTANT_OCB_NO_WINGS))
|
||||
item->RemoveOcb(WMUTANT_OCB_NO_WINGS);
|
||||
}
|
||||
|
||||
void WingedMutantControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short torso = 0; // Only when shooting.
|
||||
short angle = 0;
|
||||
|
||||
bool flyEnabled = item->TestFlags(WMUTANT_CONF_CAN_FLY, true);
|
||||
bool flyStatus = item->TestFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PATH_AERIAL);
|
||||
|
||||
WingedInitOCB(item, creature);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
CreatureDie(itemNumber, true);
|
||||
SoundEffect(SFX_TR1_ATLANTEAN_EXPLODE, &item->Pose);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
SwitchPathfinding(creature, WMUTANT_PATH_GROUND);
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
else
|
||||
{
|
||||
head = 0;
|
||||
torso = 0;
|
||||
}
|
||||
|
||||
GetCreatureMood(item, &AI, flyStatus);
|
||||
CreatureMood(item, &AI, flyStatus);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
auto shootType = CanTargetLara(item, creature, &AI);
|
||||
if (flyEnabled)
|
||||
{
|
||||
if (item->Animation.ActiveState == WMUTANT_STATE_FLY)
|
||||
{
|
||||
if (flyStatus && creature->Mood != MoodType::Escape &&
|
||||
AI.zoneNumber == AI.enemyZone)
|
||||
{
|
||||
item->SetFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PATH_GROUND);
|
||||
}
|
||||
|
||||
SwitchPathfinding(creature, WMUTANT_PATH_AERIAL);
|
||||
CreatureAIInfo(item, &AI);
|
||||
}
|
||||
else if ((AI.zoneNumber != AI.enemyZone &&
|
||||
!flyStatus && shootType == WMUTANT_PROJ_NONE &&
|
||||
(!AI.ahead || creature->Mood == MoodType::Bored)) ||
|
||||
creature->Mood == MoodType::Escape)
|
||||
{
|
||||
item->SetFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PATH_AERIAL);
|
||||
}
|
||||
}
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case WMUTANT_STATE_INACTIVE:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (TargetVisible(item, &AI) || creature->HurtByLara)
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_IDLE:
|
||||
torso = 0;
|
||||
creature->MaxTurn = 0;
|
||||
item->SetFlags(WMUTANT_CONF_PATHFINDING_MODE, WMUTANT_PROJ_NONE);
|
||||
|
||||
if (flyStatus && flyEnabled)
|
||||
item->Animation.TargetState = WMUTANT_STATE_FLY;
|
||||
else if (item->TestBits(JointBitType::Touch, WingedMutantJoints[1]))
|
||||
item->Animation.TargetState = WMUTANT_STATE_SWIPE_ATTACK;
|
||||
else if (AI.bite && AI.distance < WINGED_MUTANT_IDLE_JUMP_ATTACK_RANGE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE_JUMP_ATTACK;
|
||||
else if (AI.bite && AI.distance < WINGED_MUTANT_SWIPE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_SWIPE_ATTACK;
|
||||
else if (shootType == WMUTANT_PROJ_DART)
|
||||
item->Animation.TargetState = WMUTANT_STATE_AIM_DART;
|
||||
else if (shootType == WMUTANT_PROJ_BOMB)
|
||||
item->Animation.TargetState = WMUTANT_STATE_AIM_BOMB;
|
||||
else if (creature->Mood == MoodType::Bored ||
|
||||
(creature->Mood == MoodType::Stalk && AI.distance < WINGED_MUTANT_WALK_RANGE))
|
||||
{
|
||||
item->Animation.TargetState = WMUTANT_STATE_POSE;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = WMUTANT_STATE_RUN_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_POSE:
|
||||
head = 0; // Pose has an animation for the head.
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (shootType != WMUTANT_PROJ_NONE || (flyStatus && flyEnabled))
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Stalk)
|
||||
{
|
||||
if (AI.distance < WINGED_MUTANT_WALK_RANGE)
|
||||
{
|
||||
if (AI.zoneNumber == AI.enemyZone ||
|
||||
GetRandomControl() < WINGED_MUTANT_UNPOSE_CHANCE)
|
||||
{
|
||||
item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD;
|
||||
}
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored && GetRandomControl() < WINGED_MUTANT_UNPOSE_CHANCE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD;
|
||||
else if (creature->Mood == MoodType::Attack ||
|
||||
creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = WINGED_MUTANT_WALK_FORWARD_TURN_RATE_MAX;
|
||||
|
||||
if (shootType != WMUTANT_PROJ_NONE || (flyStatus && flyEnabled))
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Attack || creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Bored ||
|
||||
(creature->Mood == MoodType::Stalk && AI.zoneNumber != AI.enemyZone))
|
||||
{
|
||||
if (GetRandomControl() < WINGED_MUTANT_POSE_CHANCE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_POSE;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Stalk &&
|
||||
AI.distance > WINGED_MUTANT_WALK_RANGE)
|
||||
{
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_RUN_FORWARD:
|
||||
creature->MaxTurn = WINGED_MUTANT_RUN_FORWARD_TURN_RATE_MAX;
|
||||
|
||||
if (flyStatus && flyEnabled)
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
else if (item->TestBits(JointBitType::Touch, WingedMutantJoints[1]))
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
else if (AI.bite && AI.distance < WINGED_MUTANT_RUN_JUMP_ATTACK_RANGE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_RUN_JUMP_ATTACK;
|
||||
else if (AI.bite && AI.distance < WINGED_MUTANT_SWIPE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_SWIPE_ATTACK;
|
||||
else if (AI.ahead && AI.distance < WINGED_MUTANT_SWIPE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_SWIPE_ATTACK;
|
||||
else if (shootType != WMUTANT_PROJ_NONE)
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Bored ||
|
||||
(creature->Mood == MoodType::Stalk && AI.distance < WINGED_MUTANT_WALK_RANGE))
|
||||
{
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_IDLE_JUMP_ATTACK:
|
||||
if (item->Animation.RequiredState == WMUTANT_STATE_NONE &&
|
||||
item->TestBits(JointBitType::Touch, WingedMutantJoints[1]))
|
||||
{
|
||||
DoDamage(creature->Enemy, WINGED_MUTANT_IDLE_JUMP_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, WingedMutantBite, DoBloodSplat);
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_RUN_JUMP_ATTACK:
|
||||
if (item->Animation.RequiredState == WMUTANT_STATE_NONE &&
|
||||
item->TestBits(JointBitType::Touch, WingedMutantJoints[1]))
|
||||
{
|
||||
DoDamage(creature->Enemy, WINGED_MUTANT_RUN_JUMP_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, WingedMutantBite, DoBloodSplat);
|
||||
item->Animation.TargetState = WMUTANT_STATE_RUN_FORWARD;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_SWIPE_ATTACK:
|
||||
if (item->Animation.RequiredState == WMUTANT_STATE_NONE &&
|
||||
item->TestBits(JointBitType::Touch, WingedMutantJoints[1]))
|
||||
{
|
||||
DoDamage(creature->Enemy, WINGED_MUTANT_SWIPE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, WingedMutantBite, DoBloodSplat);
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_AIM_DART:
|
||||
torso = AI.angle / 2;
|
||||
creature->MaxTurn = 0;
|
||||
item->SetFlags(WMUTANT_CONF_PROJECTILE_MODE, WMUTANT_PROJ_DART);
|
||||
|
||||
if (shootType == WMUTANT_PROJ_DART)
|
||||
item->Animation.TargetState = WMUTANT_STATE_SHOOT;
|
||||
else
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_AIM_BOMB:
|
||||
torso = AI.angle / 2;
|
||||
creature->MaxTurn = 0;
|
||||
item->SetFlags(WMUTANT_CONF_PROJECTILE_MODE, WMUTANT_PROJ_BOMB);
|
||||
|
||||
if (shootType == WMUTANT_PROJ_BOMB)
|
||||
item->Animation.TargetState = WMUTANT_STATE_SHOOT;
|
||||
else
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case WMUTANT_STATE_SHOOT:
|
||||
{
|
||||
torso = AI.angle / 2;
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
bool isDart = item->TestFlags(WMUTANT_CONF_PROJECTILE_MODE, WMUTANT_PROJ_DART);
|
||||
bool isBomb = item->TestFlags(WMUTANT_CONF_PROJECTILE_MODE, WMUTANT_PROJ_BOMB);
|
||||
|
||||
if (isDart)
|
||||
CreatureEffect2(item, WingedMutantShardBite, WINGED_MUTANT_SHARD_VELOCITY, torso, ShardGun);
|
||||
else if (isBomb)
|
||||
CreatureEffect2(item, WingedMutantRocketBite, WINGED_MUTANT_BOMB_VELOCITY, torso, BombGun);
|
||||
|
||||
item->SetFlags(WMUTANT_CONF_PROJECTILE_MODE, WMUTANT_PROJ_NONE);
|
||||
break;
|
||||
}
|
||||
|
||||
case WMUTANT_STATE_FLY:
|
||||
if (!flyStatus && item->Pose.Position.y == item->Floor)
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE; // Switch to ground mode.
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureJoint(item, 0, torso);
|
||||
CreatureJoint(item, 1, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
}
|
||||
}
|
8
TombEngine/Objects/TR1/Entity/tr1_winged_mutant.h
Normal file
8
TombEngine/Objects/TR1/Entity/tr1_winged_mutant.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
void InitialiseWingedMutant(short itemNumber);
|
||||
void WingedMutantControl(short itemNumber);
|
||||
}
|
||||
|
|
@ -15,24 +15,24 @@ using std::vector;
|
|||
|
||||
namespace TEN::Entities::TR1
|
||||
{
|
||||
BITE_INFO WolfBite = { 0, -14, 174, 6 };
|
||||
const vector<int> WolfAttackJoints = { 0, 1, 2, 3, 6, 8, 9, 10, 12, 13, 14 };
|
||||
|
||||
constexpr auto WOLF_BITE_DAMAGE = 100;
|
||||
constexpr auto WOLF_BITE_DAMAGE = 100;
|
||||
constexpr auto WOLF_LUNGE_DAMAGE = 50;
|
||||
|
||||
constexpr auto WOLF_ATTACK_RANGE = SECTOR(1.5f);
|
||||
constexpr auto WOLF_STALK_RANGE = SECTOR(2);
|
||||
constexpr auto WOLF_ATTACK_RANGE = SQUARE(SECTOR(1.5f));
|
||||
constexpr auto WOLF_STALK_RANGE = SQUARE(SECTOR(2));
|
||||
|
||||
constexpr auto WOLF_WAKE_CHANCE = 0x20;
|
||||
constexpr auto WOLF_WAKE_CHANCE = 0x20;
|
||||
constexpr auto WOLF_SLEEP_CHANCE = 0x20;
|
||||
constexpr auto WOLF_HOWL_CHANCE = 0x180;
|
||||
constexpr auto WOLF_HOWL_CHANCE = 0x180;
|
||||
|
||||
constexpr auto WOLF_SLEEP_FRAME = 96;
|
||||
|
||||
#define WOLF_WALK_TURN_ANGLE ANGLE(2.0f)
|
||||
#define WOLF_RUN_TURN_ANGLE ANGLE(5.0f)
|
||||
#define WOLF_STALK_TURN_ANGLE ANGLE(2.0f)
|
||||
#define WOLF_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||
#define WOLF_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||
#define WOLF_STALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||
|
||||
const auto WolfBite = BiteInfo(Vector3(0.0f, -14.0f, 174.0f), 6);
|
||||
const vector<int> WolfAttackJoints = { 0, 1, 2, 3, 6, 8, 9, 10, 12, 13, 14 };
|
||||
|
||||
enum WolfState
|
||||
{
|
||||
|
@ -94,8 +94,8 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, TIMID);
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
GetCreatureMood(item, &AI, false);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
if (item->Animation.ActiveState != WOLF_STATE_SLEEP)
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
@ -126,7 +126,7 @@ namespace TEN::Entities::TR1
|
|||
break;
|
||||
|
||||
case WOLF_STATE_WALK:
|
||||
creature->MaxTurn = WOLF_WALK_TURN_ANGLE;
|
||||
creature->MaxTurn = WOLF_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (creature->Mood != MoodType::Bored)
|
||||
{
|
||||
|
@ -158,7 +158,7 @@ namespace TEN::Entities::TR1
|
|||
break;
|
||||
|
||||
case WOLF_STATE_STALK:
|
||||
creature->MaxTurn = WOLF_STALK_TURN_ANGLE;
|
||||
creature->MaxTurn = WOLF_STALK_TURN_RATE_MAX;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = WOLF_STATE_RUN;
|
||||
|
@ -185,12 +185,12 @@ namespace TEN::Entities::TR1
|
|||
break;
|
||||
|
||||
case WOLF_STATE_RUN:
|
||||
creature->MaxTurn = WOLF_RUN_TURN_ANGLE;
|
||||
creature->MaxTurn = WOLF_RUN_TURN_RATE_MAX;
|
||||
tilt = angle;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(WOLF_ATTACK_RANGE, 2))
|
||||
if (AI.ahead && AI.distance < WOLF_ATTACK_RANGE)
|
||||
{
|
||||
if (AI.distance > (pow(WOLF_ATTACK_RANGE, 2) / 2) &&
|
||||
if (AI.distance > (WOLF_ATTACK_RANGE / 2) &&
|
||||
(AI.enemyFacing > FRONT_ARC || AI.enemyFacing < -FRONT_ARC))
|
||||
{
|
||||
item->Animation.RequiredState = WOLF_STATE_STALK;
|
||||
|
@ -202,7 +202,8 @@ namespace TEN::Entities::TR1
|
|||
item->Animation.RequiredState = WOLF_STATE_NONE;
|
||||
}
|
||||
}
|
||||
else if (creature->Mood == MoodType::Stalk && AI.distance < pow(WOLF_STALK_RANGE, 2))
|
||||
else if (creature->Mood == MoodType::Stalk &&
|
||||
AI.distance < WOLF_STALK_RANGE)
|
||||
{
|
||||
item->Animation.RequiredState = WOLF_STATE_STALK;
|
||||
item->Animation.TargetState = WOLF_STATE_CROUCH;
|
||||
|
@ -215,11 +216,12 @@ namespace TEN::Entities::TR1
|
|||
case WOLF_STATE_ATTACK:
|
||||
tilt = angle;
|
||||
|
||||
if (!item->Animation.RequiredState && item->TestBits(JointBitType::Touch, WolfAttackJoints))
|
||||
if (!item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, WolfAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, &WolfBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, WOLF_LUNGE_DAMAGE);
|
||||
item->Animation.RequiredState = WOLF_STATE_RUN;
|
||||
DoDamage(creature->Enemy, WOLF_LUNGE_DAMAGE);
|
||||
CreatureEffect(item, WolfBite, DoBloodSplat);
|
||||
}
|
||||
|
||||
item->Animation.TargetState = WOLF_STATE_RUN;
|
||||
|
@ -229,9 +231,9 @@ namespace TEN::Entities::TR1
|
|||
if (AI.ahead && !item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, WolfAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, &WolfBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, WOLF_BITE_DAMAGE);
|
||||
item->Animation.RequiredState = WOLF_STATE_CROUCH;
|
||||
DoDamage(creature->Enemy, WOLF_BITE_DAMAGE);
|
||||
CreatureEffect(item, WolfBite, DoBloodSplat);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Game/control/box.h"
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/missile.h"
|
||||
#include "Specific/setup.h"
|
||||
#include "Specific/level.h"
|
||||
|
||||
|
@ -17,6 +18,7 @@
|
|||
#include "Objects/TR1/Entity/tr1_wolf.h" // OK
|
||||
#include "Objects/TR1/Entity/tr1_big_rat.h" // OK
|
||||
#include "Objects/TR1/Entity/tr1_centaur.h"
|
||||
#include "Objects/TR1/Entity/tr1_winged_mutant.h"
|
||||
#include "Objects/Utils/object_helper.h"
|
||||
|
||||
using namespace TEN::Entities::TR1;
|
||||
|
@ -39,8 +41,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveHitpoints = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
|
||||
g_Level.Bones[obj->boneIndex + 2 * 4] |= ROT_Y;
|
||||
obj->SetBoneRotation(2, ROT_Y); // head
|
||||
}
|
||||
|
||||
obj = &Objects[ID_BEAR];
|
||||
|
@ -59,8 +60,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveHitpoints = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
|
||||
g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y;
|
||||
obj->SetBoneRotation(13, ROT_Y); // head
|
||||
}
|
||||
|
||||
obj = &Objects[ID_APE];
|
||||
|
@ -99,7 +99,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveFlags = true;
|
||||
obj->waterCreature = true;
|
||||
obj->zoneType = ZONE_WATER;
|
||||
g_Level.Bones[obj->boneIndex + 4] |= ROT_Y;
|
||||
obj->SetBoneRotation(1, ROT_Y); // head
|
||||
}
|
||||
|
||||
obj = &Objects[ID_NATLA];
|
||||
|
@ -107,17 +107,17 @@ static void StartEntity(ObjectInfo* obj)
|
|||
{
|
||||
obj->initialise = InitialiseCreature;
|
||||
obj->collision = CreatureCollision;
|
||||
obj->hitEffect = HIT_BLOOD;
|
||||
obj->control = NatlaControl;
|
||||
obj->shadowType = ShadowMode::All;
|
||||
obj->HitPoints = 400;
|
||||
obj->radius = 204;
|
||||
obj->hitEffect = HIT_BLOOD;
|
||||
obj->intelligent = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->savePosition = true;
|
||||
obj->saveHitpoints = true;
|
||||
g_Level.Bones[obj->boneIndex + 2 * 4] |= (ROT_Z | ROT_X);
|
||||
obj->SetBoneRotation(2, ROT_X | ROT_Z);
|
||||
}
|
||||
|
||||
obj = &Objects[ID_GIANT_MUTANT];
|
||||
|
@ -135,7 +135,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveFlags = true;
|
||||
obj->savePosition = true;
|
||||
obj->saveHitpoints = true;
|
||||
g_Level.Bones[obj->boneIndex + 1 * 4] |= ROT_Y;
|
||||
obj->SetBoneRotation(1, ROT_Y);
|
||||
}
|
||||
|
||||
obj = &Objects[ID_LARA_DOPPELGANGER];
|
||||
|
@ -169,10 +169,36 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->hitEffect = HIT_BLOOD;
|
||||
obj->pivotLength = 400;
|
||||
obj->radius = WALL_SIZE / 3;
|
||||
obj->intelligent = 1;
|
||||
obj->savePosition = obj->saveHitpoints = obj->saveAnim = obj->saveFlags = 1;
|
||||
g_Level.Bones[obj->boneIndex + 10 * 4] |= ROT_Y | ROT_X;
|
||||
obj->intelligent = true;
|
||||
obj->savePosition = true;
|
||||
obj->saveHitpoints = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->zoneType = ZONE_BLOCKABLE;
|
||||
obj->SetBoneRotation(10, ROT_X | ROT_Y);
|
||||
}
|
||||
|
||||
obj = &Objects[ID_WINGED_MUMMY];
|
||||
if (obj->loaded)
|
||||
{
|
||||
obj->initialise = InitialiseWingedMutant;
|
||||
obj->control = WingedMutantControl;
|
||||
obj->collision = CreatureCollision;
|
||||
obj->shadowType = ShadowMode::All;
|
||||
obj->hitEffect = HIT_BLOOD;
|
||||
obj->pivotLength = 150;
|
||||
obj->radius = WALL_SIZE / 3;
|
||||
obj->HitPoints = 50;
|
||||
obj->intelligent = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->saveHitpoints = true;
|
||||
obj->savePosition = true;
|
||||
obj->zoneType = ZONE_FLYER;
|
||||
obj->SetBoneRotation(1, ROT_Y); // torso
|
||||
obj->SetBoneRotation(2, ROT_Y); // head
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void StartObject(ObjectInfo* obj)
|
||||
|
@ -193,7 +219,9 @@ static void StartTrap(ObjectInfo* obj)
|
|||
|
||||
static void StartProjectiles(ObjectInfo* obj)
|
||||
{
|
||||
InitProjectile(obj, ControlCentaurBomb, ID_PROJ_BOMB);
|
||||
InitProjectile(obj, ControlMissile, ID_PROJ_SHARD);
|
||||
InitProjectile(obj, ControlMissile, ID_PROJ_NATLA);
|
||||
InitProjectile(obj, ControlMissile, ID_PROJ_BOMB);
|
||||
}
|
||||
|
||||
static ObjectInfo* objToInit;
|
||||
|
|
|
@ -10,113 +10,147 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
BITE_INFO BarracudaBite = { 2, -60, 121, 7 };
|
||||
using std::vector;
|
||||
|
||||
// TODO
|
||||
enum BarracudaState
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
constexpr auto BARRACUDA_ATTACK_DAMAGE = 100;
|
||||
constexpr auto BARRACUDA_IDLE_ATTACK_RANGE = SQUARE(SECTOR(0.67f));
|
||||
constexpr auto BARRACUDA_SWIM_FAST_ATTACK_RANGE = SQUARE(SECTOR(0.34f));
|
||||
|
||||
};
|
||||
const auto BarracudaBite = BiteInfo(Vector3(2.0f, -60.0f, 121.0f), 7);
|
||||
const vector<int> BarracudaAttackJoints = { 5, 6, 7 };
|
||||
|
||||
// TODO
|
||||
enum BarracudaAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void BarracudaControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
enum BarracudaState
|
||||
{
|
||||
if (item->Animation.ActiveState != 6)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[ID_BARRACUDA].animIndex + 6;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 6;
|
||||
}
|
||||
BARRACUDA_STATE_NONE = 0,
|
||||
BARRACUDA_STATE_IDLE = 1,
|
||||
BARRACUDA_STATE_SWIM_SLOW = 2,
|
||||
BARRACUDA_STATE_SWIM_FAST = 3,
|
||||
BARRACUDA_STATE_IDLE_ATTACK = 4,
|
||||
BARRACUDA_STATE_SWIM_FAST_ATTACK = 5,
|
||||
BARRACUDA_STATE_DEATH = 6,
|
||||
};
|
||||
|
||||
CreatureFloat(itemNumber);
|
||||
return;
|
||||
}
|
||||
else
|
||||
enum BarracudaAnim
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
BARRACUDA_ANIM_SWIM_FAST_ATTACK_LEFT_END = 0,
|
||||
BARRACUDA_ANIM_SWIM_FAST_ATTACK_RIGHT_END = 1,
|
||||
BARRACUDA_ANIM_IDLE_ATTACK_END = 2,
|
||||
BARRACUDA_ANIM_IDLE_ATTACK_CONTINUE = 3,
|
||||
BARRACUDA_ANIM_SWIM_FAST_ATTACK_LEFT_CONTINUE = 4,
|
||||
BARRACUDA_ANIM_SWIM_FAST_ATTACK_RIGHT_CONTINUE = 5,
|
||||
BARRACUDA_ANIM_DEATH_START = 6,
|
||||
BARRACUDA_ANIM_DEATH_END = 7,
|
||||
BARRACUDA_ANIM_IDLE_ATTACK_START = 8,
|
||||
BARRACUDA_ANIM_IDLE_TO_SWIM_SLOW = 9,
|
||||
BARRACUDA_ANIM_IDLE_TO_SWIM_FAST = 10,
|
||||
BARRACUDA_ANIM_IDLE = 11,
|
||||
BARRACUDA_ANIM_SWIM_SLOW = 12,
|
||||
BARRACUDA_ANIM_SWIM_FAST = 13,
|
||||
BARRACUDA_ANIM_SWIM_SLOW_TO_IDLE = 13,
|
||||
BARRACUDA_ANIM_SWIM_SLOW_TO_FAST = 14,
|
||||
BARRACUDA_ANIM_SWIM_FAST_ATTACK_LEFT_START = 16,
|
||||
BARRACUDA_ANIM_SWIM_FAST_ATTACK_RIGHT_START = 17,
|
||||
BARRACUDA_ANIM_SWIM_FAST_TO_IDLE = 18,
|
||||
BARRACUDA_ANIM_SWIM_FAST_TO_SLOW = 19
|
||||
};
|
||||
|
||||
GetCreatureMood(item, &AI, TIMID);
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
void BarracudaControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
case 1:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (AI.ahead && AI.distance < pow(680, 2))
|
||||
item->Animation.TargetState = 4;
|
||||
else if (creature->Mood == MoodType::Stalk)
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
creature->MaxTurn = ANGLE(2.0f);
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
break;
|
||||
else if (AI.ahead && (item->TouchBits & 0xE0))
|
||||
item->Animation.TargetState = 1;
|
||||
else if (creature->Mood != MoodType::Stalk)
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
creature->MaxTurn = ANGLE(4.0f);
|
||||
creature->Flags = 0;
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (AI.ahead && AI.distance < pow(340, 2))
|
||||
item->Animation.TargetState = 5;
|
||||
else if (AI.ahead && AI.distance < pow(680, 2))
|
||||
item->Animation.TargetState = 1;
|
||||
else if (creature->Mood == MoodType::Stalk)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (!creature->Flags && (item->TouchBits & 0xE0))
|
||||
if (item->Animation.ActiveState != BARRACUDA_STATE_DEATH)
|
||||
{
|
||||
CreatureEffect(item, &BarracudaBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, 100);
|
||||
creature->Flags = 1;
|
||||
item->Animation.AnimNumber = Objects[ID_BARRACUDA].animIndex + BARRACUDA_ANIM_DEATH_START;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = BARRACUDA_STATE_DEATH;
|
||||
}
|
||||
|
||||
break;
|
||||
CreatureFloat(itemNumber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
CreatureUnderwater(item, CLICK(1));
|
||||
GetCreatureMood(item, &AI, false);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case BARRACUDA_STATE_IDLE:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_SWIM_SLOW;
|
||||
else if (AI.ahead && AI.distance < BARRACUDA_IDLE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_IDLE_ATTACK;
|
||||
else if (creature->Mood == MoodType::Stalk)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_SWIM_SLOW;
|
||||
else
|
||||
item->Animation.TargetState = BARRACUDA_STATE_SWIM_FAST;
|
||||
|
||||
break;
|
||||
|
||||
case BARRACUDA_STATE_SWIM_SLOW:
|
||||
creature->MaxTurn = ANGLE(2.0f);
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
break;
|
||||
else if (AI.ahead && item->TestBits(JointBitType::Touch, BarracudaAttackJoints))
|
||||
item->Animation.TargetState = BARRACUDA_STATE_IDLE;
|
||||
else if (creature->Mood != MoodType::Stalk)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_SWIM_FAST;
|
||||
|
||||
break;
|
||||
|
||||
case BARRACUDA_STATE_SWIM_FAST:
|
||||
creature->MaxTurn = ANGLE(4.0f);
|
||||
creature->Flags = 0;
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_SWIM_SLOW;
|
||||
else if (AI.ahead && AI.distance < BARRACUDA_SWIM_FAST_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_SWIM_FAST_ATTACK;
|
||||
else if (AI.ahead && AI.distance < BARRACUDA_IDLE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Stalk)
|
||||
item->Animation.TargetState = BARRACUDA_STATE_SWIM_SLOW;
|
||||
|
||||
break;
|
||||
|
||||
case BARRACUDA_STATE_IDLE_ATTACK:
|
||||
case BARRACUDA_STATE_SWIM_FAST_ATTACK:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (item->TestBits(JointBitType::Touch, BarracudaAttackJoints) &&
|
||||
!creature->Flags)
|
||||
{
|
||||
DoDamage(creature->Enemy, BARRACUDA_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BarracudaBite, DoBloodSplat);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
CreatureUnderwater(item, CLICK(1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
void BarracudaControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void BarracudaControl(short itemNumber);
|
||||
}
|
||||
|
|
206
TombEngine/Objects/TR2/Entity/tr2_bird_monster.cpp
Normal file
206
TombEngine/Objects/TR2/Entity/tr2_bird_monster.cpp
Normal file
|
@ -0,0 +1,206 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR2/Entity/tr2_bird_monster.h"
|
||||
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/control/control.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
constexpr auto BIRD_MONSTER_ATTACK_DAMAGE = 200;
|
||||
constexpr auto BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE = SQUARE(SECTOR(1));
|
||||
constexpr auto BIRD_MONSTER_PUNCH_ATTACK_RANGE = SQUARE(SECTOR(2));
|
||||
|
||||
#define BIRD_MONSTER_WALK_TURN_RATE_MAX ANGLE(4.0f)
|
||||
|
||||
const auto BirdMonsterBiteLeft = BiteInfo(Vector3(0.0f, 224.0f, 0.0f), 19);
|
||||
const auto BirdMonsterBiteRight = BiteInfo(Vector3(0.0f, 224.0f, 0.0f), 22);
|
||||
const vector<int> BirdMonsterAttackLeftJoints = { 18, 19 };
|
||||
const vector<int> BirdMonsterAttackRightJoints = { 21, 22 };
|
||||
|
||||
enum BirdMonsterState
|
||||
{
|
||||
BMONSTER_STATE_NONE = 0,
|
||||
BMONSTER_STATE_IDLE = 1,
|
||||
BMONSTER_STATE_WALK_FORWARD = 2,
|
||||
BMONSTER_STATE_SLAM_ATTACK_START = 3,
|
||||
BMONSTER_STATE_SLAM_ATTACK_CONTINUE = 4,
|
||||
BMONSTER_STATE_PUNCH_ATTACK_START = 5,
|
||||
BMONSTER_STATE_PUNCH_ATTACK_RIGHT_CONTINUE = 6,
|
||||
BMONSTER_STATE_PUNCH_ATTACK_LEFT_CONTINUE = 7,
|
||||
BMONSTER_STATE_ROAR = 8,
|
||||
BMONSTER_STATE_DEATH = 9,
|
||||
BMONSTER_STATE_CRUSH_ATTACK_START = 10,
|
||||
BMONSTER_STATE_CRUSH_ATTACK_CONTINUE = 11
|
||||
};
|
||||
|
||||
enum BirdMonsterAnim
|
||||
{
|
||||
BMONSTER_ANIM_IDLE = 0,
|
||||
BMONSTER_ANIM_IDLE_TO_WALK_FORWARD = 1,
|
||||
BMONSTER_ANIM_WALK_FORWARD = 2,
|
||||
BMONSTER_ANIM_WALK_FORWARD_TO_IDLE_LEFT = 3,
|
||||
BMONSTER_ANIM_WALK_FORWARD_TO_IDLE_RIGHT = 4,
|
||||
BMONSTER_ANIM_SLAM_ATTACK_START = 5,
|
||||
BMONSTER_ANIM_SLAM_ATTACK_CANCEL = 6,
|
||||
BMONSTER_ANIM_SLAM_ATTACK_CONTINUE = 7,
|
||||
BMONSTER_ANIM_SLAM_ATTACK_END = 8,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_RIGHT_START = 9,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_RIGHT_CANCEL = 10,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_RIGHT_CONTINUE = 11,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_RIGHT_END = 12,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_LEFT_START = 13,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_LEFT_CANCEL = 14,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_LEFT_CONTINUE = 15,
|
||||
BMONSTER_ANIM_PUNCH_ATTACK_LEFT_END = 16,
|
||||
BMONSTER_ANIM_ROAR_START = 17,
|
||||
BMONSTER_ANIM_ROAR_CONTINUE = 18,
|
||||
BMONSTER_ANIM_ROAR_END = 19,
|
||||
BMONSTER_ANIM_DEATH = 20,
|
||||
BMONSTER_ANIM_CRUSH_ATTACK_START = 21,
|
||||
BMONSTER_ANIM_CRUSH_ATTACK_CANCEL = 22,
|
||||
BMONSTER_ANIM_CRUSH_ATTACK_CONTINUE = 23,
|
||||
BMONSTER_ANIM_CRUSH_ATTACK_END = 24
|
||||
};
|
||||
|
||||
void BirdMonsterControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != BMONSTER_STATE_DEATH)
|
||||
SetAnimation(item, BMONSTER_ANIM_DEATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case BMONSTER_STATE_IDLE:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE)
|
||||
{
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = BMONSTER_STATE_SLAM_ATTACK_START;
|
||||
else
|
||||
item->Animation.TargetState = BMONSTER_STATE_CRUSH_ATTACK_START;
|
||||
}
|
||||
else if (AI.ahead &&
|
||||
(creature->Mood == MoodType::Bored || creature->Mood == MoodType::Stalk))
|
||||
{
|
||||
if (AI.zoneNumber != AI.enemyZone)
|
||||
{
|
||||
item->Animation.TargetState = BMONSTER_STATE_WALK_FORWARD;
|
||||
creature->Mood = MoodType::Escape;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = BMONSTER_STATE_ROAR;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = BMONSTER_STATE_WALK_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case BMONSTER_STATE_ROAR:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (creature->Mood != MoodType::Bored || !AI.ahead)
|
||||
item->Animation.TargetState = BMONSTER_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case BMONSTER_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = BIRD_MONSTER_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (AI.ahead && AI.distance < BIRD_MONSTER_PUNCH_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BMONSTER_STATE_PUNCH_ATTACK_START;
|
||||
else if ((creature->Mood == MoodType::Bored || creature->Mood == MoodType::Stalk) && AI.ahead)
|
||||
item->Animation.TargetState = BMONSTER_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case BMONSTER_STATE_SLAM_ATTACK_START:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BMONSTER_STATE_SLAM_ATTACK_CONTINUE;
|
||||
else
|
||||
item->Animation.TargetState = BMONSTER_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case BMONSTER_STATE_PUNCH_ATTACK_START:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < BIRD_MONSTER_PUNCH_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BMONSTER_STATE_PUNCH_ATTACK_RIGHT_CONTINUE;
|
||||
else
|
||||
item->Animation.TargetState = BMONSTER_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case BMONSTER_STATE_CRUSH_ATTACK_START:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE)
|
||||
item->Animation.TargetState = BMONSTER_STATE_CRUSH_ATTACK_CONTINUE;
|
||||
else
|
||||
item->Animation.TargetState = BMONSTER_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case BMONSTER_STATE_SLAM_ATTACK_CONTINUE:
|
||||
case BMONSTER_STATE_PUNCH_ATTACK_RIGHT_CONTINUE:
|
||||
case BMONSTER_STATE_CRUSH_ATTACK_CONTINUE:
|
||||
case BMONSTER_STATE_PUNCH_ATTACK_LEFT_CONTINUE:
|
||||
if (!(creature->Flags & 1) &&
|
||||
item->TestBits(JointBitType::Touch, BirdMonsterAttackRightJoints))
|
||||
{
|
||||
DoDamage(creature->Enemy, BIRD_MONSTER_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BirdMonsterBiteRight, DoBloodSplat);
|
||||
creature->Flags |= 1;
|
||||
}
|
||||
|
||||
if (!(creature->Flags & 2) &&
|
||||
item->TestBits(JointBitType::Touch, BirdMonsterAttackLeftJoints))
|
||||
{
|
||||
DoDamage(creature->Enemy, BIRD_MONSTER_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BirdMonsterBiteLeft, DoBloodSplat);
|
||||
creature->Flags |= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
}
|
||||
}
|
6
TombEngine/Objects/TR2/Entity/tr2_bird_monster.h
Normal file
6
TombEngine/Objects/TR2/Entity/tr2_bird_monster.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void BirdMonsterControl(short itemNumber);
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR2/Entity/tr2_birdmonster.h"
|
||||
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/control/control.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
BITE_INFO BirdMonsterBiteLeft = { 0, 224, 0, 19 };
|
||||
BITE_INFO BirdMonsterBiteRight = { 0, 224, 0, 22 };
|
||||
|
||||
// TODO
|
||||
enum BirdMonsterState
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum BirdMonsterAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void BirdMonsterControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 9)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 20;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 9;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 1:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 3;
|
||||
else
|
||||
item->Animation.TargetState = 10;
|
||||
}
|
||||
else if (AI.ahead && (creature->Mood == MoodType::Bored || creature->Mood == MoodType::Stalk))
|
||||
{
|
||||
if (AI.zoneNumber != AI.enemyZone)
|
||||
{
|
||||
item->Animation.TargetState = 2;
|
||||
creature->Mood = MoodType::Escape;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 8;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 8:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (creature->Mood != MoodType::Bored || !AI.ahead)
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
creature->MaxTurn = ANGLE(4.0f);
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 5;
|
||||
else if ((creature->Mood == MoodType::Bored || creature->Mood == MoodType::Stalk) && AI.ahead)
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
item->Animation.TargetState = 4;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 6;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 10:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
item->Animation.TargetState = 11;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 6:
|
||||
case 11:
|
||||
case 7:
|
||||
if (!(creature->Flags & 1) && item->TouchBits & 0x600000)
|
||||
{
|
||||
CreatureEffect(item, &BirdMonsterBiteRight, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, 200);
|
||||
creature->Flags |= 1;
|
||||
}
|
||||
|
||||
if (!(creature->Flags & 2) && item->TouchBits & 0x0C0000)
|
||||
{
|
||||
CreatureEffect(item, &BirdMonsterBiteLeft, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, 200);
|
||||
creature->Flags |= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
void BirdMonsterControl(short itemNumber);
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,10 @@
|
|||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/items.h"
|
||||
|
||||
void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
void DragonControl(short backNumber);
|
||||
void InitialiseBartoli(short itemNumber);
|
||||
void BartoliControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
void DragonControl(short backNumber);
|
||||
void InitialiseBartoli(short itemNumber);
|
||||
void BartoliControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -10,155 +10,158 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
BITE_INFO EagleBite = { 15, 46, 21, 6 };
|
||||
BITE_INFO CrowBite = { 2, 10, 60, 14 };
|
||||
|
||||
// TODO
|
||||
enum EagleState
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
const auto EagleBite = BiteInfo(Vector3(15.0f, 46.0f, 21.0f), 6);
|
||||
const auto CrowBite = BiteInfo(Vector3(2.0f, 10.0f, 60.0f), 14);
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum EagleAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void InitialiseEagle(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
ClearItem(itemNumber);
|
||||
|
||||
if (item->ObjectNumber == ID_CROW)
|
||||
// TODO
|
||||
enum EagleState
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[ID_CROW].animIndex + 14;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = item->Animation.TargetState = 7;
|
||||
}
|
||||
else
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum EagleAnim
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[ID_EAGLE].animIndex + 5;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = item->Animation.TargetState = 2;
|
||||
}
|
||||
}
|
||||
|
||||
void EagleControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
};
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
void InitialiseEagle(short itemNumber)
|
||||
{
|
||||
switch (item->Animation.ActiveState)
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
ClearItem(itemNumber);
|
||||
|
||||
if (item->ObjectNumber == ID_CROW)
|
||||
{
|
||||
case 4:
|
||||
if (item->Pose.Position.y > item->Floor)
|
||||
{
|
||||
item->Pose.Position.y = item->Floor;
|
||||
item->Animation.Velocity.y = 0;
|
||||
item->Animation.IsAirborne = false;
|
||||
item->Animation.TargetState = 5;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
item->Pose.Position.y = item->Floor;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (item->ObjectNumber == ID_CROW)
|
||||
item->Animation.AnimNumber = Objects[ID_CROW].animIndex + 1;
|
||||
else
|
||||
item->Animation.AnimNumber = Objects[ID_EAGLE].animIndex + 8;
|
||||
|
||||
item->Animation.AnimNumber = Objects[ID_CROW].animIndex + 14;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 4;
|
||||
item->Animation.Velocity.z = 0;
|
||||
item->Animation.IsAirborne = true;
|
||||
break;
|
||||
item->Animation.ActiveState = item->Animation.TargetState = 7;
|
||||
}
|
||||
item->Pose.Orientation.x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
|
||||
angle = CreatureTurn(item, ANGLE(3.0f));
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
else
|
||||
{
|
||||
case 7:
|
||||
item->Pose.Position.y = item->Floor;
|
||||
|
||||
if (creature->Mood != MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
item->Pose.Position.y = item->Floor;
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
break;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
item->Animation.TargetState = 6;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
item->Animation.TargetState = 6;
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if (!creature->Flags && item->TouchBits)
|
||||
{
|
||||
DoDamage(creature->Enemy, 20);
|
||||
|
||||
if (item->ObjectNumber == ID_CROW)
|
||||
CreatureEffect(item, &CrowBite, DoBloodSplat);
|
||||
else
|
||||
CreatureEffect(item, &EagleBite, DoBloodSplat);
|
||||
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
item->Animation.AnimNumber = Objects[ID_EAGLE].animIndex + 5;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = item->Animation.TargetState = 2;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
void EagleControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 4:
|
||||
if (item->Pose.Position.y > item->Floor)
|
||||
{
|
||||
item->Pose.Position.y = item->Floor;
|
||||
item->Animation.Velocity.y = 0;
|
||||
item->Animation.IsAirborne = false;
|
||||
item->Animation.TargetState = 5;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
item->Pose.Position.y = item->Floor;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (item->ObjectNumber == ID_CROW)
|
||||
item->Animation.AnimNumber = Objects[ID_CROW].animIndex + 1;
|
||||
else
|
||||
item->Animation.AnimNumber = Objects[ID_EAGLE].animIndex + 8;
|
||||
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 4;
|
||||
item->Animation.Velocity.z = 0;
|
||||
item->Animation.IsAirborne = true;
|
||||
break;
|
||||
}
|
||||
item->Pose.Orientation.x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
angle = CreatureTurn(item, ANGLE(3.0f));
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 7:
|
||||
item->Pose.Position.y = item->Floor;
|
||||
|
||||
if (creature->Mood != MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
item->Pose.Position.y = item->Floor;
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
break;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
item->Animation.TargetState = 6;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
item->Animation.TargetState = 6;
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if (!creature->Flags && item->TouchBits)
|
||||
{
|
||||
DoDamage(creature->Enemy, 20);
|
||||
|
||||
if (item->ObjectNumber == ID_CROW)
|
||||
CreatureEffect(item, CrowBite, DoBloodSplat);
|
||||
else
|
||||
CreatureEffect(item, EagleBite, DoBloodSplat);
|
||||
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
void InitialiseEagle(short itemNumber);
|
||||
void EagleControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void InitialiseEagle(short itemNumber);
|
||||
void EagleControl(short itemNumber);
|
||||
}
|
||||
|
|
305
TombEngine/Objects/TR2/Entity/tr2_knife_thrower.cpp
Normal file
305
TombEngine/Objects/TR2/Entity/tr2_knife_thrower.cpp
Normal file
|
@ -0,0 +1,305 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR2/Entity/tr2_knife_thrower.h"
|
||||
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/collision/floordata.h"
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
constexpr auto KNIFE_PROJECTILE_DAMAGE = 50;
|
||||
|
||||
// TODO: Ranges.
|
||||
|
||||
const auto KnifeBiteLeft = BiteInfo(Vector3::Zero, 5);
|
||||
const auto KnifeBiteRight = BiteInfo(Vector3::Zero, 8);
|
||||
|
||||
enum KnifeThrowerState
|
||||
{
|
||||
KTHROWER_STATE_NONE = 0,
|
||||
KTHROWER_STATE_IDLE = 1,
|
||||
KTHROWER_STATE_WALK_FORWARD = 2,
|
||||
KTHROWER_STATE_RUN_FORWARD = 3,
|
||||
KTHROWER_STATE_WALK_KNIFE_ATTACK_LEFT_START = 4,
|
||||
KTHROWER_STATE_WALK_KNIFE_ATTACK_LEFT_CONTINUE = 5,
|
||||
KTHROWER_STATE_WALK_KNIFE_ATTACK_RIGHT_START = 6,
|
||||
KTHROWER_STATE_WALK_KNIFE_ATTACK_RIGHT_CONTINUE = 7,
|
||||
KTHROWER_STATE_IDLE_KNIFE_ATTACK_START = 8,
|
||||
KTHROWER_STATE_IDLE_KNIFE_ATTACK_CONTINUE = 9,
|
||||
KTHROWER_STATE_DEATH = 10
|
||||
};
|
||||
|
||||
enum KnifeThrowerAnim
|
||||
{
|
||||
KTHROWER_ANIM_IDLE = 0,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_LEFT_START = 1,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_LEFT_CANCEL = 2,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_RIGHT_START = 3,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_RIGHT_CANCEL = 4,
|
||||
KTHROWER_ANIM_IDLE_KNIFE_ATTACK_START = 5,
|
||||
KTHROWER_ANIM_RUN_FORWARD_TO_IDLE = 6,
|
||||
KTHROWER_ANIM_RUN_FORWARD_TO_WALK_FORWARD = 7,
|
||||
KTHROWER_ANIM_RUN_FORWARD = 8,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_LEFT_CONTINUE = 9,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_RIGHT_CONTINUE = 10,
|
||||
KTHROWER_ANIM_IDLE_KNIFE_ATTACK_CONTINUE = 11,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_LEFT_END = 12,
|
||||
KTHROWER_ANIM_IDLE_KNIFE_ATTACK_END_TO_IDLE = 13,
|
||||
KTHROWER_ANIM_IDLE_KNIFE_ATTACK_END_TO_START = 14,
|
||||
KTHROWER_ANIM_IDLE_TO_RUN_FORWARD = 15,
|
||||
KTHROWER_ANIM_IDLE_TO_WALK_FORWARD = 16,
|
||||
KTHROWER_ANIM_WALK_FORWARD = 17,
|
||||
KTHROWER_ANIM_WALK_FORWARD_TO_RUN_FORWARD = 18,
|
||||
KTHROWER_ANIM_WALK_FORWARD_TO_IDLE_START = 19,
|
||||
KTHROWER_ANIM_WALK_FORWARD_TO_IDLE_END = 20,
|
||||
KTHROWER_ANIM_WALK_KNIFE_ATTACK_RIGHT_END = 21,
|
||||
KTHROWER_ANIM_IDLE_KNIFE_ATTACK_CANCEL = 22,
|
||||
KTHROWER_ANIM_DEATH = 23
|
||||
};
|
||||
|
||||
void KnifeControl(short fxNumber)
|
||||
{
|
||||
auto* fx = &EffectList[fxNumber];
|
||||
|
||||
if (fx->counter <= 0)
|
||||
{
|
||||
KillEffect(fxNumber);
|
||||
return;
|
||||
}
|
||||
else
|
||||
fx->counter--;
|
||||
|
||||
int speed = fx->speed * phd_cos(fx->pos.Orientation.x);
|
||||
fx->pos.Position.z += speed * phd_cos(fx->pos.Orientation.y);
|
||||
fx->pos.Position.x += speed * phd_sin(fx->pos.Orientation.y);
|
||||
fx->pos.Position.y += fx->speed * phd_sin(-fx->pos.Orientation.x);
|
||||
|
||||
auto probe = GetCollision(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, fx->roomNumber);
|
||||
|
||||
if (fx->pos.Position.y >= probe.Position.Floor ||
|
||||
fx->pos.Position.y <= probe.Position.Ceiling)
|
||||
{
|
||||
KillEffect(fxNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if (probe.RoomNumber != fx->roomNumber)
|
||||
EffectNewRoom(fxNumber, probe.RoomNumber);
|
||||
|
||||
fx->pos.Orientation.z += ANGLE(30.0f);
|
||||
|
||||
if (ItemNearLara(&fx->pos, 200))
|
||||
{
|
||||
DoDamage(LaraItem, KNIFE_PROJECTILE_DAMAGE);
|
||||
|
||||
fx->pos.Orientation.y = LaraItem->Pose.Orientation.y;
|
||||
fx->speed = LaraItem->Animation.Velocity.z;
|
||||
fx->frameNumber = fx->counter = 0;
|
||||
|
||||
DoBloodSplat(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, 80, fx->pos.Orientation.y, fx->roomNumber);
|
||||
SoundEffect(SFX_TR2_CRUNCH2, &fx->pos);
|
||||
KillEffect(fxNumber);
|
||||
}
|
||||
}
|
||||
|
||||
short ThrowKnife(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
||||
{
|
||||
short fxNumber = 0;
|
||||
// TODO: add fx parameters
|
||||
return fxNumber;
|
||||
}
|
||||
|
||||
void KnifeThrowerControl(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short torso = 0;
|
||||
short head = 0;
|
||||
short tilt = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != KTHROWER_STATE_DEATH)
|
||||
SetAnimation(item, KTHROWER_ANIM_DEATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case KTHROWER_STATE_IDLE:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = KTHROWER_STATE_RUN_FORWARD;
|
||||
else if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = KTHROWER_STATE_IDLE_KNIFE_ATTACK_START;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (!AI.ahead || AI.distance > pow(SECTOR(6), 2))
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_FORWARD;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(4), 2))
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_FORWARD;
|
||||
else
|
||||
item->Animation.TargetState = KTHROWER_STATE_RUN_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = ANGLE(3.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = KTHROWER_STATE_RUN_FORWARD;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance < pow(SECTOR(2.5f), 2) || AI.zoneNumber != AI.enemyZone)
|
||||
item->Animation.TargetState = KTHROWER_STATE_IDLE;
|
||||
else if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_KNIFE_ATTACK_LEFT_START;
|
||||
else
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_KNIFE_ATTACK_RIGHT_START;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(6), 2))
|
||||
item->Animation.TargetState = KTHROWER_STATE_IDLE;
|
||||
}
|
||||
else if (!AI.ahead || AI.distance > pow(SECTOR(4), 2))
|
||||
item->Animation.TargetState = KTHROWER_STATE_RUN_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_RUN_FORWARD:
|
||||
tilt = angle / 3;
|
||||
creature->MaxTurn = ANGLE(6.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_FORWARD;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(6), 2))
|
||||
item->Animation.TargetState = KTHROWER_STATE_IDLE;
|
||||
else
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_FORWARD;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(4), 2))
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_WALK_KNIFE_ATTACK_LEFT_START:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_KNIFE_ATTACK_LEFT_CONTINUE;
|
||||
else
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_WALK_KNIFE_ATTACK_RIGHT_START:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_KNIFE_ATTACK_RIGHT_CONTINUE;
|
||||
else
|
||||
item->Animation.TargetState = KTHROWER_STATE_WALK_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_IDLE_KNIFE_ATTACK_START:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = KTHROWER_STATE_IDLE_KNIFE_ATTACK_CONTINUE;
|
||||
else
|
||||
item->Animation.TargetState = KTHROWER_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_WALK_KNIFE_ATTACK_LEFT_CONTINUE:
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
CreatureEffect(item, KnifeBiteLeft, ThrowKnife);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_WALK_KNIFE_ATTACK_RIGHT_CONTINUE:
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
CreatureEffect(item, KnifeBiteRight, ThrowKnife);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KTHROWER_STATE_IDLE_KNIFE_ATTACK_CONTINUE:
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
CreatureEffect(item, KnifeBiteLeft, ThrowKnife);
|
||||
CreatureEffect(item, KnifeBiteRight, ThrowKnife);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torso);
|
||||
CreatureJoint(item, 1, head);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
}
|
7
TombEngine/Objects/TR2/Entity/tr2_knife_thrower.h
Normal file
7
TombEngine/Objects/TR2/Entity/tr2_knife_thrower.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void KnifeControl(short fxNumber);
|
||||
void KnifeThrowerControl(short itemNumber);
|
||||
}
|
|
@ -1,270 +0,0 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR2/Entity/tr2_knifethrower.h"
|
||||
|
||||
#include "Game/collision/floordata.h"
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
#include "Game/items.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Game/people.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
#include "Sound/sound.h"
|
||||
|
||||
BITE_INFO KnifeBiteLeft = { 0, 0, 0, 5 };
|
||||
BITE_INFO KnifeBiteRight = { 0, 0, 0, 8 };
|
||||
|
||||
// TODO
|
||||
enum KnifeThrowerState
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum KnifeThrowerAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void KnifeControl(short fxNumber)
|
||||
{
|
||||
auto* fx = &EffectList[fxNumber];
|
||||
|
||||
if (fx->counter <= 0)
|
||||
{
|
||||
KillEffect(fxNumber);
|
||||
return;
|
||||
}
|
||||
else
|
||||
fx->counter--;
|
||||
|
||||
int speed = fx->speed * phd_cos(fx->pos.Orientation.x);
|
||||
fx->pos.Position.z += speed * phd_cos(fx->pos.Orientation.y);
|
||||
fx->pos.Position.x += speed * phd_sin(fx->pos.Orientation.y);
|
||||
fx->pos.Position.y += fx->speed * phd_sin(-fx->pos.Orientation.x);
|
||||
|
||||
auto probe = GetCollision(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, fx->roomNumber);
|
||||
|
||||
if (fx->pos.Position.y >= probe.Position.Floor ||
|
||||
fx->pos.Position.y <= probe.Position.Ceiling)
|
||||
{
|
||||
KillEffect(fxNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if (probe.RoomNumber != fx->roomNumber)
|
||||
EffectNewRoom(fxNumber, probe.RoomNumber);
|
||||
|
||||
fx->pos.Orientation.z += ANGLE(30.0f);
|
||||
|
||||
if (ItemNearLara(&fx->pos, 200))
|
||||
{
|
||||
DoDamage(LaraItem, 50);
|
||||
|
||||
fx->pos.Orientation.y = LaraItem->Pose.Orientation.y;
|
||||
fx->speed = LaraItem->Animation.Velocity.z;
|
||||
fx->frameNumber = fx->counter = 0;
|
||||
|
||||
SoundEffect(SFX_TR2_CRUNCH2, &fx->pos);
|
||||
DoBloodSplat(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, 80, fx->pos.Orientation.y, fx->roomNumber);
|
||||
KillEffect(fxNumber);
|
||||
}
|
||||
}
|
||||
|
||||
static short ThrowKnife(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
||||
{
|
||||
short fxNumber = 0;
|
||||
// TODO: add fx parameters
|
||||
return fxNumber;
|
||||
}
|
||||
|
||||
void KnifeThrowerControl(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short torso = 0;
|
||||
short head = 0;
|
||||
short tilt = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 10)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 23;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 10;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 1:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 8;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (!AI.ahead || AI.distance > pow(SECTOR(6), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(4), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
creature->MaxTurn = ANGLE(3.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance < pow(SECTOR(2.5f), 2) || AI.zoneNumber != AI.enemyZone)
|
||||
item->Animation.TargetState = 1;
|
||||
else if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 4;
|
||||
else
|
||||
item->Animation.TargetState = 6;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(6), 2))
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
else if (!AI.ahead || AI.distance > pow(SECTOR(4), 2))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
tilt = angle / 3;
|
||||
creature->MaxTurn = ANGLE(6.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.TargetState = 2;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(6), 2))
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(4), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 5;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 7;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 8:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 9;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
CreatureEffect(item, &KnifeBiteLeft, ThrowKnife);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 7:
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
CreatureEffect(item, &KnifeBiteRight, ThrowKnife);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 9:
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
CreatureEffect(item, &KnifeBiteLeft, ThrowKnife);
|
||||
CreatureEffect(item, &KnifeBiteRight, ThrowKnife);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torso);
|
||||
CreatureJoint(item, 1, head);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
void KnifeControl(short fxNumber);
|
||||
void KnifeThrowerControl(short itemNumber);
|
|
@ -11,407 +11,410 @@
|
|||
#include "Specific/setup.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
BITE_INFO MercenaryUziBite = { 0, 150, 19, 17 };
|
||||
BITE_INFO MercenaryAutoPistolBite = { 0, 230, 9, 17 };
|
||||
|
||||
// TODO
|
||||
enum MercenaryState
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
const auto MercenaryUziBite = BiteInfo(Vector3(0.0f, 150.0f, 19.0f), 17);
|
||||
const auto MercenaryAutoPistolBite = BiteInfo(Vector3(0.0f, 230.0f, 9.0f), 17);
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum MercenaryAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void MercenaryUziControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short headX = 0;
|
||||
short headY = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
// TODO
|
||||
enum MercenaryState
|
||||
{
|
||||
if (item->Animation.ActiveState != 13)
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum MercenaryAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void MercenaryUziControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short headX = 0;
|
||||
short headY = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 14;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 13;
|
||||
if (item->Animation.ActiveState != 13)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 14;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, TIMID);
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
else
|
||||
{
|
||||
case 1:
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance > pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
if (GetRandomControl() >= 0x2000)
|
||||
{
|
||||
if (GetRandomControl() >= 0x4000)
|
||||
item->Animation.TargetState = 11;
|
||||
else
|
||||
item->Animation.TargetState = 7;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (creature->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (!AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
creature->MaxTurn = ANGLE(7.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance <= pow(SECTOR(2), 2) || AI.zoneNumber != AI.enemyZone)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 12;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = 3;
|
||||
else
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, false);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 1:
|
||||
if (AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
tilt = angle / 3;
|
||||
creature->MaxTurn = ANGLE(10.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood != MoodType::Escape)
|
||||
{
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 1;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
|
||||
if (!ShotLara(item, &AI, &MercenaryUziBite, torsoY, 8))
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
if (AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 10:
|
||||
case 14:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
|
||||
if (!ShotLara(item, &AI, &MercenaryUziBite, torsoY, 8))
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
if (AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, headY);
|
||||
CreatureJoint(item, 3, headX);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
|
||||
void MercenaryAutoPistolControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short headX = 0;
|
||||
short headY = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 11)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 9;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 11;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 2:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 4;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance <= pow(SECTOR(2), 2))
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance > pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
if (GetRandomControl() >= 0x2000)
|
||||
{
|
||||
if (GetRandomControl() >= 0x4000)
|
||||
item->Animation.TargetState = 5;
|
||||
item->Animation.TargetState = 11;
|
||||
else
|
||||
item->Animation.TargetState = 8;
|
||||
item->Animation.TargetState = 7;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 7;
|
||||
item->Animation.TargetState = 5;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (creature->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = 4;
|
||||
if (!AI.ahead || GetRandomControl() < 0x100)
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
creature->MaxTurn = ANGLE(7.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 4;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance < pow(SECTOR(2), 2) || AI.zoneNumber == AI.enemyZone || GetRandomControl() < 1024)
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 4;
|
||||
else if (AI.ahead && GetRandomControl() < 1024)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
tilt = angle / 3;
|
||||
creature->MaxTurn = ANGLE(10.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood != MoodType::Escape && (creature->Mood == MoodType::Escape || Targetable(item, &AI)))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 5:
|
||||
case 6:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 8:
|
||||
case 13:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
if (GetRandomControl() < 0x2000)
|
||||
if (creature->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (!AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
ShotLara(item, &AI, &MercenaryAutoPistolBite, torsoY, 50);
|
||||
creature->Flags = 1;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 9:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
creature->MaxTurn = ANGLE(7.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance <= pow(SECTOR(2), 2) || AI.zoneNumber != AI.enemyZone)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 12;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = 3;
|
||||
else
|
||||
{
|
||||
if (AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
tilt = angle / 3;
|
||||
creature->MaxTurn = ANGLE(10.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood != MoodType::Escape)
|
||||
{
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 1;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
|
||||
if (!ShotLara(item, &AI, MercenaryUziBite, torsoY, 8))
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
if (AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
if (creature->Flags != 1)
|
||||
break;
|
||||
|
||||
case 10:
|
||||
case 14:
|
||||
if (AI.ahead)
|
||||
{
|
||||
if (!ShotLara(item, &AI, &MercenaryAutoPistolBite, torsoY, 50))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
creature->Flags = 1;
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 12:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 13;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 10:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
if (!ShotLara(item, &AI, MercenaryUziBite, torsoY, 8))
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
if (AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
if (creature->Flags != 2)
|
||||
{
|
||||
if (!ShotLara(item, &AI, &MercenaryAutoPistolBite, torsoY, 50))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
creature->Flags = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, headY);
|
||||
CreatureJoint(item, 3, headX);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, headY);
|
||||
CreatureJoint(item, 3, headX);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
void MercenaryAutoPistolControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short headX = 0;
|
||||
short headY = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 11)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 9;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 11;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 2:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 4;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance <= pow(SECTOR(2), 2))
|
||||
{
|
||||
if (GetRandomControl() >= 0x2000)
|
||||
{
|
||||
if (GetRandomControl() >= 0x4000)
|
||||
item->Animation.TargetState = 5;
|
||||
else
|
||||
item->Animation.TargetState = 8;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 7;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (creature->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = 4;
|
||||
if (!AI.ahead || GetRandomControl() < 0x100)
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
creature->MaxTurn = ANGLE(7.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 4;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance < pow(SECTOR(2), 2) || AI.zoneNumber == AI.enemyZone || GetRandomControl() < 1024)
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 4;
|
||||
else if (AI.ahead && GetRandomControl() < 1024)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
tilt = angle / 3;
|
||||
creature->MaxTurn = ANGLE(10.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Mood != MoodType::Escape && (creature->Mood == MoodType::Escape || Targetable(item, &AI)))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 5:
|
||||
case 6:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 8:
|
||||
case 13:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
|
||||
if (!creature->Flags)
|
||||
{
|
||||
if (GetRandomControl() < 0x2000)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
ShotLara(item, &AI, MercenaryAutoPistolBite, torsoY, 50);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 9:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
|
||||
if (AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
if (creature->Flags != 1)
|
||||
{
|
||||
if (!ShotLara(item, &AI, MercenaryAutoPistolBite, torsoY, 50))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 12:
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
}
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 13;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 10:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
|
||||
if (AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
if (creature->Flags != 2)
|
||||
{
|
||||
if (!ShotLara(item, &AI, MercenaryAutoPistolBite, torsoY, 50))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
creature->Flags = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, headY);
|
||||
CreatureJoint(item, 3, headX);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
void MercenaryUziControl(short itemNumber);
|
||||
void MercenaryAutoPistolControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void MercenaryUziControl(short itemNumber);
|
||||
void MercenaryAutoPistolControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -11,216 +11,219 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
bool MonksAttackLara;
|
||||
|
||||
BITE_INFO MonkBite = { -23,16,265, 14 };
|
||||
|
||||
// TODO
|
||||
enum MonkState
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
const auto MonkBite = BiteInfo(Vector3(-23.0f, 16.0f, 265.0f), 14);
|
||||
|
||||
};
|
||||
bool MonksAttackLara;
|
||||
|
||||
// TODO
|
||||
enum MonkAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void MonkControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short torso = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
// TODO
|
||||
enum MonkState
|
||||
{
|
||||
if (item->Animation.ActiveState != 9)
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum MonkAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void MonkControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short torso = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 20 + (GetRandomControl() / 0x4000);
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 9;
|
||||
if (item->Animation.ActiveState != 9)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 20 + (GetRandomControl() / 0x4000);
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MonksAttackLara)
|
||||
creature->Enemy = LaraItem;
|
||||
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (!MonksAttackLara && creature->Enemy == LaraItem)
|
||||
creature->Enemy = NULL;
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
else
|
||||
{
|
||||
case 1:
|
||||
creature->Flags &= 0x0FFF;
|
||||
if (MonksAttackLara)
|
||||
creature->Enemy = LaraItem;
|
||||
|
||||
if (!MonksAttackLara && AI.ahead && Lara.TargetEntity == item)
|
||||
break;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (!MonksAttackLara && creature->Enemy == LaraItem)
|
||||
creature->Enemy = nullptr;
|
||||
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
if (GetRandomControl() < 0x7000)
|
||||
item->Animation.TargetState = 4;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
item->Animation.TargetState = 7;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
case 1:
|
||||
creature->Flags &= 0x0FFF;
|
||||
|
||||
case 11:
|
||||
creature->Flags &= 0x0FFF;
|
||||
|
||||
if (!MonksAttackLara && AI.ahead && Lara.TargetEntity == item)
|
||||
break;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
{
|
||||
auto random = GetRandomControl();
|
||||
if (random < 0x3000)
|
||||
item->Animation.TargetState = 5;
|
||||
else if (random < 0x6000)
|
||||
item->Animation.TargetState = 8;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
creature->MaxTurn = ANGLE(3.0f);
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (!MonksAttackLara && AI.ahead && Lara.TargetEntity == item)
|
||||
break;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x7000)
|
||||
item->Animation.TargetState = 4;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
item->Animation.TargetState = 7;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 11:
|
||||
creature->Flags &= 0x0FFF;
|
||||
|
||||
if (!MonksAttackLara && AI.ahead && Lara.TargetEntity == item)
|
||||
break;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
{
|
||||
auto random = GetRandomControl();
|
||||
if (random < 0x3000)
|
||||
item->Animation.TargetState = 5;
|
||||
else if (random < 0x6000)
|
||||
item->Animation.TargetState = 8;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
creature->MaxTurn = ANGLE(3.0f);
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (!MonksAttackLara && AI.ahead && Lara.TargetEntity == item)
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
}
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
}
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
else if (!AI.ahead || AI.distance > pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
else if (!AI.ahead || AI.distance > pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
case 3:
|
||||
tilt = angle / 4;
|
||||
creature->MaxTurn = ANGLE(4.0f);
|
||||
creature->Flags &= 0x0FFF;
|
||||
|
||||
if (MonksAttackLara)
|
||||
creature->MaxTurn += ANGLE(1.0f);
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
|
||||
case 3:
|
||||
tilt = angle / 4;
|
||||
creature->MaxTurn = ANGLE(4.0f);
|
||||
creature->Flags &= 0x0FFF;
|
||||
|
||||
if (MonksAttackLara)
|
||||
creature->MaxTurn += ANGLE(1.0f);
|
||||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(3), 2))
|
||||
item->Animation.TargetState = 10;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 8:
|
||||
if (!AI.ahead || AI.distance > pow(SECTOR(0.5f), 2))
|
||||
item->Animation.TargetState = 11;
|
||||
else
|
||||
item->Animation.TargetState = 6;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 10:
|
||||
auto* enemy = creature->Enemy;
|
||||
if (enemy == LaraItem)
|
||||
{
|
||||
if (!(creature->Flags & 0xF000) && item->TouchBits & 0x4000)
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
{
|
||||
creature->Flags |= 0x1000;
|
||||
SoundEffect(SFX_TR2_CRUNCH1, &item->Pose);
|
||||
CreatureEffect(item, &MonkBite, DoBloodSplat);
|
||||
DoDamage(enemy, 150);
|
||||
if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(creature->Flags & 0xf000) && enemy)
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(3), 2))
|
||||
item->Animation.TargetState = 10;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(2) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) < CLICK(2) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(2))
|
||||
if (GetRandomControl() < 0x4000)
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 8:
|
||||
if (!AI.ahead || AI.distance > pow(SECTOR(0.5f), 2))
|
||||
item->Animation.TargetState = 11;
|
||||
else
|
||||
item->Animation.TargetState = 6;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 10:
|
||||
auto * enemy = creature->Enemy;
|
||||
if (enemy == LaraItem)
|
||||
{
|
||||
if (!(creature->Flags & 0xF000) && item->TouchBits & 0x4000)
|
||||
{
|
||||
creature->Flags |= 0x1000;
|
||||
DoDamage(enemy, 150);
|
||||
CreatureEffect(item, MonkBite, DoBloodSplat);
|
||||
SoundEffect(SFX_TR2_CRUNCH1, &item->Pose);
|
||||
DoDamage(enemy, 5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(creature->Flags & 0xf000) && enemy)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(2) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) < CLICK(2) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(2))
|
||||
{
|
||||
creature->Flags |= 0x1000;
|
||||
DoDamage(enemy, 5);
|
||||
SoundEffect(SFX_TR2_CRUNCH1, &item->Pose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torso);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torso);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
void MonkControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void MonkControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -9,122 +9,148 @@
|
|||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
BITE_INFO RatBite = { 0, 0, 57, 2 };
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
// TODO
|
||||
enum RatState
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
constexpr auto RAT_ATTACK_DAMAGE = 20;
|
||||
constexpr auto RAT_ATTACK_RANGE = SQUARE(CLICK(0.7f));
|
||||
|
||||
};
|
||||
constexpr auto RAT_SQUEAK_CHANCE = 0.04f;
|
||||
constexpr auto RAT_IDLE_CHANCE = 0.08f;
|
||||
constexpr auto RAT_WALK_CHANCE = 0.92f;
|
||||
|
||||
// TODO
|
||||
enum RatAnim
|
||||
{
|
||||
#define RAT_TURN_RATE_MAX ANGLE(6.0f)
|
||||
|
||||
};
|
||||
const auto RatBite = BiteInfo(Vector3(0.0f, 0.0f, 57.0f), 2);
|
||||
|
||||
void RatControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short random = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
enum RatState
|
||||
{
|
||||
if (item->Animation.ActiveState != 6)
|
||||
RAT_STATE_NONE = 0,
|
||||
RAT_STATE_WALK_FORWARD = 1,
|
||||
RAT_STATE_IDLE = 2,
|
||||
RAT_STATE_SQUEAK = 3,
|
||||
RAT_STATE_BITE_ATTACK = 4,
|
||||
RAT_STATE_POUNCE_ATTACK = 5,
|
||||
RAT_STATE_DEATH = 6
|
||||
};
|
||||
|
||||
enum RatAnim
|
||||
{
|
||||
RAT_ANIM_IDLE = 0,
|
||||
RAT_ANIM_IDLE_TO_WALK_FORWARD = 1,
|
||||
RAT_ANIM_WALK_FORWARD = 2,
|
||||
RAT_ANIM_WALK_FORWARD_TO_IDLE = 3,
|
||||
RAT_ANIM_SQUEAK = 4,
|
||||
RAT_ANIM_BITE_ATTACK_START = 5,
|
||||
RAT_ANIM_BITE_ATTACK_CONTINUE = 6,
|
||||
RAT_ANIM_BITE_ATTACK_END = 7,
|
||||
RAT_ANIM_POUNCE_ATTACK = 8,
|
||||
RAT_ANIM_DEATH = 9
|
||||
};
|
||||
|
||||
void RatControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 9;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 6;
|
||||
if (item->Animation.ActiveState != RAT_STATE_DEATH)
|
||||
SetAnimation(item, RAT_ANIM_DEATH);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, TIMID);
|
||||
CreatureMood(item, &AI, TIMID);
|
||||
|
||||
angle = CreatureTurn(item, ANGLE(6.0f));
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
else
|
||||
{
|
||||
case 4:
|
||||
if (info->Mood == MoodType::Bored || info->Mood == MoodType::Stalk)
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, false);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
angle = CreatureTurn(item, RAT_TURN_RATE_MAX);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
short random = (short)GetRandomControl();
|
||||
if (random < 0x500)
|
||||
item->Animation.RequiredState = 3;
|
||||
else if (random > 0xA00)
|
||||
item->Animation.RequiredState = 1;
|
||||
}
|
||||
else if (AI.distance < pow(340, 2))
|
||||
item->Animation.RequiredState = 5;
|
||||
else
|
||||
item->Animation.RequiredState = 1;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
info->MaxTurn = ANGLE(6.0f);
|
||||
|
||||
if (info->Mood == MoodType::Bored || info->Mood == MoodType::Stalk)
|
||||
{
|
||||
random = (short)GetRandomControl();
|
||||
if (random < 0x500)
|
||||
case RAT_STATE_BITE_ATTACK:
|
||||
if (creature->Mood == MoodType::Bored ||
|
||||
creature->Mood == MoodType::Stalk)
|
||||
{
|
||||
item->Animation.RequiredState = 3;
|
||||
item->Animation.TargetState = 2;
|
||||
if (TestProbability(RAT_SQUEAK_CHANCE))
|
||||
item->Animation.RequiredState = RAT_STATE_SQUEAK;
|
||||
else if (TestProbability(RAT_WALK_CHANCE))
|
||||
item->Animation.RequiredState = RAT_STATE_WALK_FORWARD;
|
||||
}
|
||||
else if (random < 0xA00)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (AI.distance < RAT_ATTACK_RANGE)
|
||||
item->Animation.RequiredState = RAT_STATE_POUNCE_ATTACK;
|
||||
else
|
||||
item->Animation.RequiredState = RAT_STATE_WALK_FORWARD;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = RAT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case RAT_STATE_IDLE:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
|
||||
break;
|
||||
|
||||
case RAT_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = RAT_TURN_RATE_MAX;
|
||||
|
||||
if (creature->Mood == MoodType::Bored ||
|
||||
creature->Mood == MoodType::Stalk)
|
||||
{
|
||||
if (TestProbability(RAT_SQUEAK_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = RAT_STATE_IDLE;
|
||||
item->Animation.RequiredState = RAT_STATE_SQUEAK;
|
||||
}
|
||||
else if (TestProbability(RAT_IDLE_CHANCE))
|
||||
item->Animation.TargetState = RAT_STATE_IDLE;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < RAT_ATTACK_RANGE)
|
||||
item->Animation.TargetState = RAT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case RAT_STATE_POUNCE_ATTACK:
|
||||
if (!item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, RatBite.meshNum))
|
||||
{
|
||||
item->Animation.RequiredState = RAT_STATE_IDLE;
|
||||
DoDamage(creature->Enemy, RAT_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, RatBite, DoBloodSplat);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RAT_STATE_SQUEAK:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (TestProbability(RAT_SQUEAK_CHANCE))
|
||||
item->Animation.TargetState = RAT_STATE_IDLE;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(340, 2))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (!item->Animation.RequiredState && item->TouchBits & 0x7F)
|
||||
{
|
||||
CreatureEffect(item, &RatBite, DoBloodSplat);
|
||||
DoDamage(info->Enemy, 20);
|
||||
item->Animation.RequiredState = 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (GetRandomControl() < 0x500)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
void RatControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void RatControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -11,105 +11,108 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
BITE_INFO SharkBite = { 17, -22, 344, 12 };
|
||||
|
||||
void SharkControl(short itemNumber)
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
const auto SharkBite = BiteInfo(Vector3(17.0f, -22.0f, 344.0f), 12);
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
void SharkControl(short itemNumber)
|
||||
{
|
||||
if (item->Animation.ActiveState != 5)
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[ID_SHARK].animIndex + 4;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 5;
|
||||
}
|
||||
|
||||
CreatureFloat(itemNumber);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 0:
|
||||
info->Flags = 0;
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2) && AI.zoneNumber == AI.enemyZone)
|
||||
item->Animation.TargetState = 3;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
info->MaxTurn = ANGLE(0.5f);
|
||||
|
||||
if (info->Mood == MoodType::Bored)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2))
|
||||
item->Animation.TargetState = 0;
|
||||
else if (info->Mood == MoodType::Escape || AI.distance > pow(SECTOR(3), 2) || !AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info->MaxTurn = ANGLE(2.0f);
|
||||
info->Flags = 0;
|
||||
|
||||
if (info->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
else if (info->Mood == MoodType::Escape)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(1365, 2) && AI.zoneNumber == AI.enemyZone)
|
||||
if (item->Animation.ActiveState != 5)
|
||||
{
|
||||
if (GetRandomControl() < 0x800)
|
||||
item->Animation.AnimNumber = Objects[ID_SHARK].animIndex + 4;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 5;
|
||||
}
|
||||
|
||||
CreatureFloat(itemNumber);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 0:
|
||||
info->Flags = 0;
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2) && AI.zoneNumber == AI.enemyZone)
|
||||
item->Animation.TargetState = 3;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
info->MaxTurn = ANGLE(0.5f);
|
||||
|
||||
if (info->Mood == MoodType::Bored)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2))
|
||||
item->Animation.TargetState = 0;
|
||||
else if (AI.distance < pow(SECTOR(0.75f), 2))
|
||||
item->Animation.TargetState = 4;
|
||||
else if (info->Mood == MoodType::Escape || AI.distance > pow(SECTOR(3), 2) || !AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info->MaxTurn = ANGLE(2.0f);
|
||||
info->Flags = 0;
|
||||
|
||||
if (info->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
else if (info->Mood == MoodType::Escape)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(1365, 2) && AI.zoneNumber == AI.enemyZone)
|
||||
{
|
||||
if (GetRandomControl() < 0x800)
|
||||
item->Animation.TargetState = 0;
|
||||
else if (AI.distance < pow(SECTOR(0.75f), 2))
|
||||
item->Animation.TargetState = 4;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (!info->Flags && item->TouchBits & 0x3400)
|
||||
{
|
||||
CreatureEffect(item, SharkBite, DoBloodSplat);
|
||||
DoDamage(info->Enemy, 400);
|
||||
info->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (!info->Flags && item->TouchBits & 0x3400)
|
||||
{
|
||||
CreatureEffect(item, &SharkBite, DoBloodSplat);
|
||||
DoDamage(info->Enemy, 400);
|
||||
info->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->Animation.ActiveState != 6)
|
||||
{
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
CreatureUnderwater(item, 340);
|
||||
if (item->Animation.ActiveState != 6)
|
||||
{
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
CreatureUnderwater(item, 340);
|
||||
}
|
||||
else
|
||||
AnimateItem(item);
|
||||
}
|
||||
else
|
||||
AnimateItem(item);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
void SharkControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void SharkControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -10,101 +10,93 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
BITE_INFO SilencerGunBite = { 3, 331, 56, 10 };
|
||||
|
||||
// TODO
|
||||
enum SilencerState
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
const auto SilencerGunBite = BiteInfo(Vector3(3.0f, 331.0f, 56.0f), 10);
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum SilencerAnim
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void SilencerControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
short head = 0;
|
||||
short tilt = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
// TODO
|
||||
enum SilencerState
|
||||
{
|
||||
if (item->Animation.ActiveState != 12 && item->Animation.ActiveState != 13)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 20;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 13;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum SilencerAnim
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
GetCreatureMood(item, &AI, VIOLENT);
|
||||
CreatureMood(item, &AI, VIOLENT);
|
||||
};
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
void SilencerControl(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
short head = 0;
|
||||
short tilt = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
case 3:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
if (item->Animation.ActiveState != 12 && item->Animation.ActiveState != 13)
|
||||
{
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 20;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 13;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (info->Mood == MoodType::Attack || !AI.ahead)
|
||||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 3:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
{
|
||||
if (AI.distance >= pow(SECTOR(2), 2))
|
||||
{
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Animation.RequiredState = 1;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetRandomControl() >= 1280)
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
if (GetRandomControl() < 2560)
|
||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
|
||||
if (info->Mood == MoodType::Attack || !AI.ahead)
|
||||
{
|
||||
if (AI.distance >= pow(SECTOR(2), 2))
|
||||
{
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Animation.RequiredState = 1;
|
||||
item->Animation.TargetState = 3;
|
||||
|
@ -112,157 +104,168 @@ void SilencerControl(short itemNumber)
|
|||
}
|
||||
else
|
||||
{
|
||||
item->Animation.RequiredState = 5;
|
||||
item->Animation.TargetState = 3;
|
||||
if (GetRandomControl() >= 1280)
|
||||
{
|
||||
if (GetRandomControl() < 2560)
|
||||
{
|
||||
item->Animation.RequiredState = 1;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Animation.RequiredState = 5;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
case 1:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
info->MaxTurn = 910;
|
||||
info->MaxTurn = 910;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AI.distance > pow(SECTOR(2), 2) || !AI.ahead)
|
||||
if (info->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 2;
|
||||
if (info->Mood == MoodType::Bored && GetRandomControl() < 0x300)
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AI.distance > pow(SECTOR(2), 2) || !AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
if (info->Mood == MoodType::Bored && GetRandomControl() < 0x300)
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
case 2:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
info->MaxTurn = ANGLE(5.0f);
|
||||
info->Flags = 0;
|
||||
tilt = angle / 4;
|
||||
info->MaxTurn = ANGLE(5.0f);
|
||||
info->Flags = 0;
|
||||
tilt = angle / 4;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
{
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 9;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
{
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 9;
|
||||
{
|
||||
if (AI.distance >= pow(SECTOR(2), 2) && AI.zoneNumber == AI.enemyZone)
|
||||
item->Animation.TargetState = 9;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (info->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = (GetRandomControl() >= 0x4000) ? 3 : 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
case 5:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance >= pow(SECTOR(2), 2) && AI.zoneNumber == AI.enemyZone)
|
||||
item->Animation.TargetState = 9;
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = 6;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info->Mood == MoodType::Attack || GetRandomControl() < 0x100)
|
||||
item->Animation.TargetState = 3;
|
||||
if (!AI.ahead)
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 10:
|
||||
info->MaxTurn = 0;
|
||||
info->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = item->Animation.ActiveState != 6 ? 11 : 7;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 11:
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
|
||||
if (!info->Flags)
|
||||
{
|
||||
ShotLara(item, &AI, SilencerGunBite, torsoY, 50);
|
||||
info->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 9:
|
||||
info->MaxTurn = ANGLE(5.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
|
||||
if (!item->Animation.RequiredState)
|
||||
{
|
||||
if (!ShotLara(item, &AI, SilencerGunBite, torsoY, 50))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
item->Animation.RequiredState = 9;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (info->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = (GetRandomControl() >= 0x4000) ? 3 : 2;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = 6;
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info->Mood == MoodType::Attack || GetRandomControl() < 0x100)
|
||||
item->Animation.TargetState = 3;
|
||||
if (!AI.ahead)
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 10:
|
||||
info->MaxTurn = 0;
|
||||
info->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = item->Animation.ActiveState != 6 ? 11 : 7;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 11:
|
||||
info->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
|
||||
if (!info->Flags)
|
||||
{
|
||||
ShotLara(item, &AI, &SilencerGunBite, torsoY, 50);
|
||||
info->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 9:
|
||||
info->MaxTurn = ANGLE(5.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
|
||||
if (!item->Animation.RequiredState)
|
||||
{
|
||||
if (!ShotLara(item, &AI, &SilencerGunBite, torsoY, 50))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
item->Animation.RequiredState = 9;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, head);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, head);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
void SilencerControl(short itemNumber);
|
||||
namespace TEN::Entities::TR2
|
||||
{
|
||||
void SilencerControl(short itemNumber);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue