Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

This commit is contained in:
MontyTRC89 2023-04-29 05:42:48 +02:00
commit cdcb4ae37b
136 changed files with 1037 additions and 1458 deletions

View file

@ -1,16 +1,26 @@
Version 1.0.9
=============
- Fix Cold bar triggered when room is dry (only trigger if and only if the room is water).
- Fix spiky wall speed value.
- Change speed by giving an OCB number to it or lua (Moveable::SetItemFlags[0]).
- Fix crash if little beetle does not exist in wad to load bats emitter.
- Fix gunflash rendering and position for entities.
- Fix snowmobile driver crashing the game.
- Fix knifethrower not throwing knife.
- Add TR1 Cowboy.
- Add TR3 Wall mounted blade.
- Add TR3 Claw mutant.
- Add removable puzzles from puzzle holes and puzzle dones.
- employed by setting the trigger type as "Switch" for either puzzle hole or puzzle done.
- Employed by setting the trigger type as "Switch" for either puzzle hole or puzzle done.
- Can be mixed with puzzle done and puzzle holes of the same or different type.
- Fix Cold bar triggered when room is dry (only trigger if and only if the room is water).
- Fix spiky wall speed value.
- Change speed by giving an OCB number to it or lua (Moveable::SetItemFlags[0]).
- Fix spiky wall being too slow when triggered.
- Add missing gunflash for some entities, also include dynamic light and smoke to all gunflashes.
- Add reusable keys for key holes
- Employed by setting the trigger type as "Switch" for key hole.
- Allow key hole Animation to be played via OCB number.
- Default 0 OCB will play Lara use key animation.
- Any positive OCB number will play the animation according to the OCB number.
- Add log reports if title level or other levels don't exist.
Lua API changes:
* Add function String::SetTranslated()

View file

@ -277,7 +277,7 @@ Timer = {
--- Get the total time for a timer.
-- This is the amount of time the timer will start with, as well as when starting a new loop
-- @function myTimer:GetRemainingTime
-- @function myTimer:GetTotalTime
-- @treturn float the timer's total time
GetTotalTime = function(t)
return LevelVars.Engine.Timer.timers[t.name].totalTime

View file

@ -21,7 +21,8 @@ namespace TEN::Hud
{
void StatusBar::Initialize(float value)
{
Value = std::clamp(value, 0.0f, 1.0f);
Value =
TargetValue = std::clamp(value, 0.0f, 1.0f);
}
void StatusBar::Update(float value)

View file

@ -280,7 +280,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
BLOCK(8),
3,
0,
0,
2,
0,
SFX_TR4_UZI_FIRE,
0
@ -781,6 +781,7 @@ void AimWeapon(ItemInfo& laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo)
arm.Orientation.InterpolateConstant(targetArmOrient, weaponInfo.AimSpeed);
}
// TODO: include snowmobile gun in GetAmmo(), else it won't be able to shoot if lara control it. TokyoSU: 21/04/2023
FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo& targetEntity, ItemInfo& laraItem, const EulerAngles& armOrient)
{
auto& player = *GetLaraInfo(&laraItem);

View file

@ -664,6 +664,16 @@ Vector3i GetJointPosition(ItemInfo* item, int jointIndex, const Vector3i& relOff
return GetJointPosition(*item, jointIndex, relOffset);
}
Vector3i GetJointPosition(ItemInfo* item, const CreatureBiteInfo& bite)
{
return GetJointPosition(item, bite.BoneID, bite.Position);
}
Vector3i GetJointPosition(const ItemInfo& item, const CreatureBiteInfo& bite)
{
return GetJointPosition(item, bite.BoneID, bite.Position);
}
Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex)
{
const auto& object = Objects[objectID];

View file

@ -10,6 +10,7 @@ class Pose;
class Vector3i;
struct ItemInfo;
struct ObjectInfo;
struct CreatureBiteInfo;
// NOTES:
// animNumber: Relative animation number.
@ -145,6 +146,9 @@ void DrawAnimatingItem(ItemInfo* item);
Vector3i GetJointPosition(const ItemInfo& item, int jointIndex, const Vector3i& relOffset = Vector3i::Zero);
Vector3i GetJointPosition(ItemInfo* item, int jointIndex, const Vector3i& relOffset = Vector3i::Zero);
Vector3i GetJointPosition(ItemInfo* item, const CreatureBiteInfo& bite);
Vector3i GetJointPosition(const ItemInfo& item, const CreatureBiteInfo& bite);
Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex);
Quaternion GetBoneOrientation(const ItemInfo& item, int boneIndex);
float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex);

View file

@ -7,6 +7,7 @@
#include "Game/collision/collide_room.h"
#include "Game/control/control.h"
#include "Game/control/lot.h"
#include "Game/effects/smoke.h"
#include "Game/effects/tomb4fx.h"
#include "Game/itemdata/creature_info.h"
#include "Game/Lara/lara.h"
@ -21,6 +22,8 @@
#include "Objects/TR5/Object/tr5_pushableblock.h"
#include "Renderer/Renderer11.h"
using namespace TEN::Effects::Smoke;
constexpr auto ESCAPE_DIST = SECTOR(5);
constexpr auto STALK_DIST = SECTOR(3);
constexpr auto REACHED_GOAL_RADIUS = 640;
@ -32,6 +35,7 @@ constexpr auto FEELER_DISTANCE = CLICK(2);
constexpr auto FEELER_ANGLE = ANGLE(45.0f);
constexpr auto CREATURE_AI_ROTATION_MAX = ANGLE(90.0f);
constexpr auto CREATURE_JOINT_ROTATION_MAX = ANGLE(70.0f);
constexpr auto GUN_EFFECT_CREATURE_YSHIFT = 75;
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
constexpr int HIGH_PRIO_RANGE = 8;
@ -577,15 +581,15 @@ void CreatureKill(ItemInfo* creatureItem, int creatureAnimNumber, int playerAnim
Camera.targetElevation = -ANGLE(25.0f);
}
short CreatureEffect2(ItemInfo* item, BiteInfo bite, short velocity, short angle, std::function<CreatureEffectFunction> func)
short CreatureEffect2(ItemInfo* item, const CreatureBiteInfo& bite, short velocity, short angle, std::function<CreatureEffectFunction> func)
{
auto pos = GetJointPosition(item, bite.meshNum, Vector3i(bite.Position));
auto pos = GetJointPosition(item, bite);
return func(pos.x, pos.y, pos.z, velocity, angle, item->RoomNumber);
}
short CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func)
short CreatureEffect(ItemInfo* item, const CreatureBiteInfo& bite, std::function<CreatureEffectFunction> func)
{
auto pos = GetJointPosition(item, bite.meshNum, Vector3i(bite.Position));
auto pos = GetJointPosition(item, bite);
return func(pos.x, pos.y, pos.z, item->Animation.Velocity.z, item->Pose.Orientation.y, item->RoomNumber);
}
@ -722,12 +726,30 @@ short CreatureTurn(ItemInfo* item, short maxTurn)
return angle;
}
static void PlayGunEffectForCreature(ItemInfo* item, const CreatureMuzzleflashInfo& muzzleFlash)
{
if (muzzleFlash.Delay == 0) return;
auto muzzleNewpos = muzzleFlash.Bite;
auto pos = GetJointPosition(item, muzzleNewpos);
TriggerDynamicLight(pos.x, pos.y, pos.z, 15, 128, 64, 16);
if (muzzleFlash.UseSmoke)
{
// NOTE: Fix the smoke position,
// avoiding creating new variable in CreatureMuzzleflashInfo for a smoke biteInfo
muzzleNewpos.Position.y -= GUN_EFFECT_CREATURE_YSHIFT;
auto smokePos = GetJointPosition(item, muzzleNewpos);
TriggerGunSmokeParticles(smokePos.x, smokePos.y, smokePos.z, 0, 0, 0, 1, LaraWeaponType::Pistol, 12, item->RoomNumber);
}
}
bool CreatureAnimation(short itemNumber, short angle, short tilt)
{
auto* item = &g_Level.Items[itemNumber];
if (!item->IsCreature())
return false;
auto* creature = GetCreatureInfo(item);
PlayGunEffectForCreature(item, creature->MuzzleFlash[0]);
PlayGunEffectForCreature(item, creature->MuzzleFlash[1]);
auto prevPos = item->Pose.Position;

View file

@ -2,10 +2,10 @@
#include "Specific/level.h"
#include "Math/Math.h"
struct BiteInfo;
struct CreatureInfo;
struct ItemInfo;
struct LOTInfo;
struct CreatureBiteInfo;
enum class JumpDistance
{
@ -20,55 +20,6 @@ enum TARGET_TYPE
SECONDARY_TARGET
};
struct OBJECT_BONES
{
short bone0;
short bone1;
short bone2;
short bone3;
OBJECT_BONES()
{
this->bone0 = 0;
this->bone1 = 0;
this->bone2 = 0;
this->bone3 = 0;
}
OBJECT_BONES(short all)
{
this->bone0 = all;
this->bone1 = all;
this->bone2 = all;
this->bone3 = all;
}
OBJECT_BONES(short angleY, short angleX)
{
this->bone0 = angleY;
this->bone1 = angleX;
this->bone2 = angleY;
this->bone3 = angleX;
}
OBJECT_BONES(short angleY, short angleX, bool total)
{
this->bone0 = angleY;
this->bone1 = angleX;
if (total)
{
this->bone2 = angleY;
this->bone3 = angleX;
}
else
{
this->bone2 = 0;
this->bone3 = 0;
}
}
};
struct AI_INFO
{
int zoneNumber;
@ -99,30 +50,6 @@ struct OVERLAP
int flags;
};
struct BiteInfo
{
Vector3 Position = Vector3::Zero;
int meshNum = 0;
BiteInfo()
{
this->Position = Vector3::Zero;
this->meshNum = 0;
}
BiteInfo(const Vector3& pos, int meshNumber)
{
this->Position = pos;
this->meshNum = meshNumber;
}
BiteInfo(float xPos, float yPos, float zPos, int meshNumber)
{
this->Position = Vector3(xPos, yPos, zPos);
this->meshNum = meshNumber;
}
};
#define CreatureEffectFunction short(int x, int y, int z, short speed, short yRot, short roomNumber)
constexpr auto BOX_BLOCKED = (1 << 14); // unpassable for other enemies, always set for movable blocks & closed doors
@ -164,8 +91,8 @@ short AIGuard(CreatureInfo* creature);
void AlertNearbyGuards(ItemInfo* item);
void AlertAllGuards(short itemNumber);
void CreatureKill(ItemInfo* item, int entityKillAnim, int laraExtraKillAnim, int entityKillState, int laraKillState);
short CreatureEffect2(ItemInfo* item, BiteInfo bite, short velocity, short angle, std::function<CreatureEffectFunction> func);
short CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func);
short CreatureEffect2(ItemInfo* item, const CreatureBiteInfo& bite, short velocity, short angle, std::function<CreatureEffectFunction> func);
short CreatureEffect(ItemInfo* item, const CreatureBiteInfo& bite, std::function<CreatureEffectFunction> func);
void CreatureUnderwater(ItemInfo* item, int depth);
void CreatureFloat(short itemNumber);
void CreatureJoint(ItemInfo* item, short joint, short required, short maxAngle = ANGLE(70.0f));

View file

@ -271,7 +271,6 @@ unsigned CALLBACK GameMain(void *)
else
g_Renderer.RenderTitleImage();
// Execute the Lua gameflow and play the game.
g_GameFlow->DoFlow();

View file

@ -146,6 +146,12 @@ void InitializeSlot(short itemNumber, bool makeTarget)
break;
case LotType::SnowmobileGun:
creature->LOT.Step = CLICK(1);
creature->LOT.Drop = -BLOCK(1);
creature->LOT.Zone = ZoneType::Human;
break;
// Can climb.
case LotType::Human:
creature->LOT.Step = BLOCK(1);

View file

@ -17,6 +17,7 @@
#include "Game/savegame.h"
#include "Game/spotcam.h"
#include "Objects/Generic/Switches/generic_switch.h"
#include "Objects/Generic/puzzles_keys.h"
#include "Objects/objectslist.h"
#include "Objects/TR3/Vehicles/kayak.h"
#include "Sound/sound.h"
@ -82,7 +83,8 @@ bool GetKeyTrigger(ItemInfo* item)
return true;
}
int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch)
// NOTE: attatchedToSwitch parameter unused.
int GetSwitchTrigger(ItemInfo* item, short* itemNumbersPtr, int attatchedToSwitch)
{
auto triggerIndex = GetTriggerIndex(item);
@ -95,14 +97,14 @@ int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch)
return 0;
trigger += 2;
short* current = itemNos;
short* currentPtr = itemNumbersPtr;
int k = 0;
do
{
if (TRIG_BITS(*trigger) == TO_OBJECT && item != &g_Level.Items[*trigger & VALUE_BITS])
{
current[k] = *trigger & VALUE_BITS;
currentPtr[k] = *trigger & VALUE_BITS;
++k;
}
@ -121,8 +123,11 @@ int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch)
int SwitchTrigger(short itemNumber, short timer)
{
auto& item = g_Level.Items[itemNumber];
const auto& player = Lara;
if ((item.ObjectNumber >= ID_PUZZLE_DONE1 && item.ObjectNumber <= ID_PUZZLE_DONE16) && item.ItemFlags[1])
// Handle reusable receptacles.
if (item.ObjectNumber >= ID_PUZZLE_DONE1 && item.ObjectNumber <= ID_PUZZLE_DONE16 &&
item.ItemFlags[1] != 0)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_ACTIVE;
@ -131,7 +136,8 @@ int SwitchTrigger(short itemNumber, short timer)
return 1;
}
if ((item.ObjectNumber >= ID_PUZZLE_HOLE1 && item.ObjectNumber <= ID_PUZZLE_HOLE16) && item.ItemFlags[1])
if (item.ObjectNumber >= ID_PUZZLE_HOLE1 && item.ObjectNumber <= ID_PUZZLE_HOLE16 &&
item.ItemFlags[1] != 0)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_DEACTIVATED;
@ -142,11 +148,42 @@ int SwitchTrigger(short itemNumber, short timer)
if ((item.ObjectNumber >= ID_PUZZLE_DONE1 && item.ObjectNumber <= ID_PUZZLE_DONE16) ||
(item.ObjectNumber >= ID_PUZZLE_HOLE1 && item.ObjectNumber <= ID_PUZZLE_HOLE16))
{
return 0;
}
// Handle reusable receptacles.
if (item.ObjectNumber >= ID_KEY_HOLE1 && item.ObjectNumber <= ID_KEY_HOLE16 &&
item.ItemFlags[1] != 0 &&
(item.ItemFlags[5] == (int)ReusableReceptacleState::Empty || item.ItemFlags[5] == (int)ReusableReceptacleState::None) &&
player.Control.HandStatus != HandStatus::Busy)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_ACTIVE;
item.ItemFlags[5] = (int)ReusableReceptacleState::Done;
item.ItemFlags[1] = false;
return 1;
}
if (item.ObjectNumber >= ID_KEY_HOLE1 && item.ObjectNumber <= ID_KEY_HOLE16 &&
item.ItemFlags[1] != 0 && item.ItemFlags[5] == (int)ReusableReceptacleState::Done &&
player.Control.HandStatus != HandStatus::Busy)
{
item.Flags |= IFLAG_ACTIVATION_MASK;
item.Status = ITEM_DEACTIVATED;
item.ItemFlags[5] = (int)ReusableReceptacleState::Empty;
item.ItemFlags[1] = false;
return 1;
}
if (item.ObjectNumber >= ID_KEY_HOLE1 && item.ObjectNumber <= ID_KEY_HOLE16)
return 0;
// Handle switches.
if (item.Status == ITEM_DEACTIVATED)
{
if ((!item.Animation.ActiveState && item.ObjectNumber != ID_JUMP_SWITCH || item.Animation.ActiveState == 1 && item.ObjectNumber == ID_JUMP_SWITCH) &&
if (((item.Animation.ActiveState == 0 && item.ObjectNumber != ID_JUMP_SWITCH) ||
(item.Animation.ActiveState == 1 && item.ObjectNumber == ID_JUMP_SWITCH)) &&
timer > 0)
{
item.Timer = timer;
@ -158,23 +195,23 @@ int SwitchTrigger(short itemNumber, short timer)
return 1;
}
if (item.TriggerFlags >= 0 || item.Animation.ActiveState)
if (item.TriggerFlags >= 0 || item.Animation.ActiveState != 0)
{
RemoveActiveItem(itemNumber);
item.Status = ITEM_NOT_ACTIVE;
if (!item.ItemFlags[0] == 0)
item.Flags |= ONESHOT;
return 1;
}
else //if ((item.ObjectNumber >= ID_PUZZLE_DONE1 && item.ObjectNumber <= ID_PUZZLE_DONE16) && item.ItemFlags[1] == 0 ||
//(item.ObjectNumber >= ID_PUZZLE_HOLE1 && item.ObjectNumber <= ID_PUZZLE_HOLE16) && item.ItemFlags[1] == 0)
else
{
item.Status = ITEM_ACTIVE;
return 1;
}
}
else if (item.Status)
else if (item.Status != 0)
{
if (item.ObjectNumber == ID_AIRLOCK_SWITCH &&
item.Animation.AnimNumber == GetAnimIndex(item, 2) &&
@ -193,15 +230,18 @@ int SwitchTrigger(short itemNumber, short timer)
return 0;
}
int KeyTrigger(short itemNum)
int KeyTrigger(short itemNumber)
{
ItemInfo* item = &g_Level.Items[itemNum];
int oldkey;
auto* item = &g_Level.Items[itemNumber];
const auto& player = Lara;
if ((item->Status != ITEM_ACTIVE || Lara.Control.HandStatus == HandStatus::Busy) && (!KeyTriggerActive || Lara.Control.HandStatus != HandStatus::Busy))
if ((item->Status != ITEM_ACTIVE || player.Control.HandStatus == HandStatus::Busy) &&
(!KeyTriggerActive || player.Control.HandStatus != HandStatus::Busy))
{
return -1;
}
oldkey = KeyTriggerActive;
int oldkey = KeyTriggerActive;
if (!oldkey)
item->Status = ITEM_DEACTIVATED;
@ -211,9 +251,9 @@ int KeyTrigger(short itemNum)
return oldkey;
}
bool PickupTrigger(short itemNum)
bool PickupTrigger(short itemNumber)
{
ItemInfo* item = &g_Level.Items[itemNum];
auto* item = &g_Level.Items[itemNumber];
if (((item->Flags & IFLAG_CLEAR_BODY) && (item->Flags & IFLAG_KILLED)) ||
item->Status != ITEM_INVISIBLE ||
@ -223,7 +263,7 @@ bool PickupTrigger(short itemNum)
return false;
}
KillItem(itemNum);
KillItem(itemNumber);
item->Flags |= IFLAG_CLEAR_BODY;
return true;
@ -260,6 +300,7 @@ void RefreshCamera(short type, short* data)
}
else
targetOk = 0;
break;
case TO_TARGET:
@ -273,7 +314,7 @@ void RefreshCamera(short type, short* data)
if (Camera.item)
if (!targetOk || (targetOk == 2 && Camera.item->LookedAt && Camera.item != Camera.lastItem))
Camera.item = NULL;
Camera.item = nullptr;
if (Camera.number == -1 && Camera.timer > 0)
Camera.timer = -1;
@ -521,8 +562,8 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
short targetType = 0;
short trigger = 0;
ItemInfo* item = NULL;
ItemInfo* cameraItem = NULL;
ItemInfo* item = nullptr;
ItemInfo* cameraItem = nullptr;
do
{
@ -784,12 +825,12 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
FlipEffect = newEffect;
}
void TestTriggers(ItemInfo* item, bool heavy, int heavyFlags)
void TestTriggers(ItemInfo* item, bool isHeavy, int heavyFlags)
{
auto roomNum = item->RoomNumber;
auto floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNum);
auto roomNumber = item->RoomNumber;
auto floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, floor, item->Index, heavy, heavyFlags);
TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, floor, item->Index, isHeavy, heavyFlags);
}
void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags)

View file

@ -67,16 +67,16 @@ extern int TriggerTimer;
extern int KeyTriggerActive;
bool GetKeyTrigger(ItemInfo* item);
int GetSwitchTrigger(ItemInfo* item, short* itemNos, int attatchedToSwitch);
int GetSwitchTrigger(ItemInfo* item, short* itemNumbersPtr, int attatchedToSwitch);
int SwitchTrigger(short itemNumber, short timer);
int KeyTrigger(short itemNum);
bool PickupTrigger(short itemNum);
int KeyTrigger(short itemNumber);
bool PickupTrigger(short itemNumber);
void RefreshCamera(short type, short* data);
int TriggerActive(ItemInfo* item);
short* GetTriggerIndex(FloorInfo* floor, int x, int y, int z);
short* GetTriggerIndex(ItemInfo* item);
void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags = 0);
void TestTriggers(ItemInfo* item, bool heavy, int heavyFlags = 0);
void TestTriggers(ItemInfo* item, bool isHeavy, int heavyFlags = 0);
void ProcessSectorFlags(ItemInfo* item);
void Antitrigger(short const value, short const flags = 0);

View file

@ -52,7 +52,7 @@ int SplashCount = 0;
Vector3i NodeVectors[ParticleNodeOffsetIDs::NodeMax];
NODEOFFSET_INFO NodeOffsets[ParticleNodeOffsetIDs::NodeMax] =
{
{ -16, 40, 160, -LM_LHAND, false }, // TR5 offset 0, TODO: This mesh is invalid as it can't be negative. -- TokyoSU 23.02.20
{ -16, 40, 160, 13, false }, // TR5 offset 0
{ -16, -8, 160, 0, false }, // TR5 offset 1
{ 0, 0, 256, 8, false }, // TR5 offset 2
{ 0, 0, 256, 17, false }, // TR5 offset 3

View file

@ -113,6 +113,11 @@ namespace TEN::Effects::Smoke
//TODO: add additional "Weapon Special" param or something. Currently initial == 2 means Rocket Launcher backwards smoke.
//TODO: Refactor different weapon types out of it
void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count)
{
TriggerGunSmokeParticles(x, y, z, xv, yv, zv, initial, weaponType, count, LaraItem->RoomNumber);
}
void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count, short roomNumber)
{
auto& s = GetFreeSmokeParticle();
s = {};
@ -124,7 +129,7 @@ namespace TEN::Effects::Smoke
s.velocity = direction;
s.gravity = -.1f;
s.affectedByWind = TestEnvironment(ENV_FLAG_WIND, LaraItem);
s.affectedByWind = TestEnvironment(ENV_FLAG_WIND, x, y, z, roomNumber);
s.sourceColor = Vector4(.4f, .4f, .4f, 1);
s.destinationColor = Vector4(0, 0, 0, 0);
@ -135,7 +140,7 @@ namespace TEN::Effects::Smoke
float size = Random::GenerateFloat(48, 80);
s.sourceSize = size * 2;
s.destinationSize = size * 8;
s.sourceColor = {0.75,0.75,1,1};
s.sourceColor = { 0.75,0.75,1,1 };
s.terminalVelocity = 0;
s.friction = 0.82f;
s.life = Random::GenerateFloat(60, 90);
@ -188,7 +193,7 @@ namespace TEN::Effects::Smoke
s.position += Vector3(Random::GenerateFloat(-8, 8), Random::GenerateFloat(-8, 8), Random::GenerateFloat(-8, 8));
s.angularVelocity = Random::GenerateFloat(-PI_DIV_4, PI_DIV_4);
s.angularDrag = 0.95f;
s.room = LaraItem->RoomNumber;
s.room = roomNumber;
}
void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int velocity, int moving)

View file

@ -34,6 +34,7 @@ namespace TEN::Effects::Smoke
void DisableSmokeParticles();
void TriggerFlareSmoke(const Vector3& pos, const Vector3& direction, int life, int room);
void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count);
void TriggerGunSmokeParticles(int x, int y, int z, int xv, int yv, int zv, byte initial, LaraWeaponType weaponType, byte count, short roomNumber);
void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int velocity, int moving);
void TriggerRocketSmoke(int x, int y, int z);
void TriggerBreathSmoke(long x, long y, long z, short angle);

View file

@ -335,6 +335,11 @@ void ThrowFire(int itemNum, int meshIndex, Vector3i offset, Vector3i speed)
}
}
void ThrowFire(int itemNum, const CreatureBiteInfo& bite, Vector3i speed)
{
ThrowFire(itemNum, bite.BoneID, bite.Position, speed);
}
void ThrowPoison(int itemNum, int meshIndex, Vector3i offset, Vector3i speed, Vector3 color)
{
auto* item = &g_Level.Items[itemNum];
@ -343,11 +348,15 @@ void ThrowPoison(int itemNum, int meshIndex, Vector3i offset, Vector3i speed, Ve
{
auto* spark = SetupPoisonSpark(color);
AttachAndCreateSpark(spark, item, meshIndex, offset, speed);
spark->flags = SP_POISON | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
}
}
void ThrowPoison(int itemNum, const CreatureBiteInfo& bite, Vector3i speed, Vector3 color)
{
ThrowPoison(itemNum, bite.BoneID, bite.Position, speed, color);
}
void UpdateFireProgress()
{
TriggerGlobalStaticFlame();

View file

@ -6,6 +6,7 @@
enum class LaraWeaponType;
struct ItemInfo;
struct CreatureBiteInfo;
enum BodyPartFlags
{
@ -234,7 +235,9 @@ void TriggerGlobalFireSmoke();
void TriggerGlobalFireFlame();
void TriggerPilotFlame(int itemNum, int nodeIndex);
void ThrowFire(int itemNum, int meshIndex, Vector3i offset, Vector3i speed);
void ThrowFire(int itemNum, const CreatureBiteInfo& bite, Vector3i speed);
void ThrowPoison(int itemNum, int meshIndex, Vector3i offset, Vector3i speed, Vector3 color);
void ThrowPoison(int itemNum, const CreatureBiteInfo& bite, Vector3i speed, Vector3 color);
void UpdateFireProgress();
void ClearFires();
void AddFire(int x, int y, int z, short roomNum, float size, short fade);

View file

@ -0,0 +1,9 @@
#include "framework.h"
#include "Game/itemdata/creature_info.h"
#include "Game/items.h"
bool CreatureInfo::IsTargetAlive()
{
return ((Enemy != nullptr) && (Enemy->HitPoints > 0));
}

View file

@ -71,6 +71,62 @@ struct LOTInfo
bool CanMonkey = false;
};
struct CreatureBiteInfo
{
Vector3i Position = Vector3i::Zero;
int BoneID = -1;
CreatureBiteInfo() {}
CreatureBiteInfo(const Vector3i& pos, int boneID)
{
Position = pos;
BoneID = boneID;
}
CreatureBiteInfo(int x, int y, int z, int boneID)
{
Position = Vector3i(x, y, z);
BoneID = boneID;
}
};
struct CreatureMuzzleflashInfo
{
CreatureBiteInfo Bite = {};
int Delay = 0;
bool SwitchToMuzzle2 = false; // Changes muzzle object to ID_GUNFLASH2.
bool ApplyXRotation = true; // Applies X axis rotation for muzzleflash (required for creatures).
bool ApplyZRotation = true; // Applies Y axis rotation for muzzleflash (required for creatures).
bool UseSmoke = true; // Determines if CreatureAnimation calls TriggerGunSmokeParticles().
CreatureMuzzleflashInfo() {}
CreatureMuzzleflashInfo(const Vector3i& pos, int boneID, int delay, bool changeToMuzzle2 = false)
{
Bite = CreatureBiteInfo(pos, boneID);
Delay = delay;
SwitchToMuzzle2 = changeToMuzzle2;
}
CreatureMuzzleflashInfo(const CreatureBiteInfo& bite, int delay, bool changeToMuzzle2 = false)
{
Bite = bite;
Delay = delay;
SwitchToMuzzle2 = changeToMuzzle2;
}
CreatureMuzzleflashInfo(const CreatureBiteInfo& bite, int delay, bool changeToMuzzle2 = false, bool applyXRot = true, bool applyZRot = true)
{
Bite = bite;
Delay = delay;
SwitchToMuzzle2 = changeToMuzzle2;
ApplyXRotation = applyXRot;
ApplyZRotation = applyZRot;
}
};
struct CreatureInfo
{
int ItemNumber = -1;
@ -96,11 +152,13 @@ struct CreatureInfo
bool MonkeySwingAhead = false;
bool ReachedGoal = false;
short FiredWeapon = 0;
CreatureMuzzleflashInfo MuzzleFlash[2];
short Tosspad = 0;
short LocationAI = 0;
short Flags = 0;
bool IsTargetAlive();
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
CreatureAIPriority Priority = CreatureAIPriority::None;
size_t FramesSinceLOTUpdate = 0;

View file

@ -115,7 +115,7 @@ void ControlMissile(short fxNumber)
EffectNewRoom(fxNumber, pointColl.RoomNumber);
if (fx.objectNumber == ID_KNIFETHROWER_KNIFE)
fx.pos.Orientation.z += ANGLE(3.0f); // Update knife rotation over time.
fx.pos.Orientation.z += ANGLE(30.0f); // Update knife rotation over time.
switch (fx.objectNumber)
{

View file

@ -12,7 +12,7 @@
#include "Game/misc.h"
#include "Sound/sound.h"
bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, int damage)
bool ShotLara(ItemInfo* item, AI_INFO* AI, const CreatureBiteInfo& gun, short extraRotation, int damage)
{
auto* creature = GetCreatureInfo(item);
auto* enemy = creature->Enemy;
@ -79,9 +79,10 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, in
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber)
{
// TODO: Remove the -128 and fix the Ricochet effect going on the floor ! TokyoSU: 28/04/2023
auto pos = GameVector(
LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->Floor,
LaraItem->Floor - 128,
LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->RoomNumber);

View file

@ -1,9 +1,10 @@
#pragma once
#include "Game/control/box.h"
struct CreatureBiteInfo;
constexpr auto MAX_VISIBILITY_DISTANCE = SECTOR(8);
bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, int damage);
bool ShotLara(ItemInfo* item, AI_INFO* AI, const CreatureBiteInfo& 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);

View file

@ -20,7 +20,6 @@
#include "Game/room.h"
#include "Objects/Generic/Object/rope.h"
#include "Objects/Generic/Switches/fullblock_switch.h"
#include "Objects/Generic/Traps/traps.h"
#include "Objects/Generic/puzzles_keys.h"
#include "Objects/Sink.h"
#include "Objects/TR4/Entity/tr4_beetle_swarm.h"
@ -527,8 +526,7 @@ bool SaveGame::Save(int slot)
flatbuffers::Offset<Save::Short> shortOffset;
flatbuffers::Offset<Save::Int> intOffset;
if (Objects[itemToSerialize.ObjectNumber].intelligent
&& itemToSerialize.Data.is<CreatureInfo>())
if (Objects[itemToSerialize.ObjectNumber].intelligent && itemToSerialize.IsCreature())
{
auto creature = GetCreatureInfo(&itemToSerialize);
@ -538,12 +536,10 @@ bool SaveGame::Save(int slot)
auto jointRotationsOffset = fbb.CreateVector(jointRotations);
Save::CreatureBuilder creatureBuilder{ fbb };
creatureBuilder.add_alerted(creature->Alerted);
creatureBuilder.add_can_jump(creature->LOT.CanJump);
creatureBuilder.add_can_monkey(creature->LOT.CanMonkey);
creatureBuilder.add_enemy(creature->Enemy - g_Level.Items.data());
creatureBuilder.add_fired_weapon(creature->FiredWeapon);
creatureBuilder.add_flags(creature->Flags);
creatureBuilder.add_friendly(creature->Friendly);
creatureBuilder.add_head_left(creature->HeadLeft);
@ -555,6 +551,8 @@ bool SaveGame::Save(int slot)
creatureBuilder.add_joint_rotation(jointRotationsOffset);
creatureBuilder.add_jump_ahead(creature->JumpAhead);
creatureBuilder.add_location_ai(creature->LocationAI);
creatureBuilder.add_weapon_delay1(creature->MuzzleFlash[0].Delay);
creatureBuilder.add_weapon_delay2(creature->MuzzleFlash[1].Delay);
creatureBuilder.add_maximum_turn(creature->MaxTurn);
creatureBuilder.add_monkey_swing_ahead(creature->MonkeySwingAhead);
creatureBuilder.add_mood((int)creature->Mood);
@ -1545,13 +1543,14 @@ bool SaveGame::Load(int slot)
creature->LOT.CanMonkey = savedCreature->can_monkey();
if (savedCreature->enemy() >= 0)
creature->Enemy = &g_Level.Items[savedCreature->enemy()];
creature->FiredWeapon = savedCreature->fired_weapon();
creature->Flags = savedCreature->flags();
creature->Friendly = savedCreature->friendly();
creature->HeadLeft = savedCreature->head_left();
creature->HeadRight = savedCreature->head_right();
creature->HurtByLara = savedCreature->hurt_by_lara();
creature->LocationAI = savedCreature->location_ai();
creature->MuzzleFlash[0].Delay = savedCreature->weapon_delay1();
creature->MuzzleFlash[1].Delay = savedCreature->weapon_delay2();
creature->LOT.IsAmphibious = savedCreature->is_amphibious();
creature->LOT.IsJumping = savedCreature->is_jumping();
creature->LOT.IsMonkeying = savedCreature->is_monkeying();

View file

@ -12,7 +12,6 @@
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_collide.h"
#include "Objects/Generic/Traps/traps.h"
#include "Sound/sound.h"
#include "Specific/clock.h"
#include "Specific/level.h"

View file

@ -1,299 +0,0 @@
#include "framework.h"
#include "traps.h"
#include "Game/items.h"
#include "Game/effects/effects.h"
#include "Game/effects/tomb4fx.h"
#include "Game/effects/weather.h"
#include "Game/Lara/lara.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/sphere.h"
#include "Game/camera.h"
#include "tr5_light.h"
#include "Game/animation.h"
#include "Specific/level.h"
#include "Specific/Input/Input.h"
#include "Game/room.h"
#include "Sound/sound.h"
using namespace TEN::Effects::Environment;
static short WreckingBallData[2] = {0, 0};
void InitialiseWreckingBall(short itemNumber)
{
ItemInfo* item;
short room;
item = &g_Level.Items[itemNumber];
item->ItemFlags[3] = FindAllItems(ID_ANIMATING16)[0];
room = item->RoomNumber;
item->Pose.Position.y = GetCeiling(GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &room), item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z) + 1644;
GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &room);
if (room != item->RoomNumber)
ItemNewRoom(itemNumber, room);
}
void WreckingBallCollision(short itemNumber, ItemInfo* l, CollisionInfo* coll)
{
ItemInfo* item;
int x, y, z, test;
short damage;
item = &g_Level.Items[itemNumber];
if (TestBoundsCollide(item, l, coll->Setup.Radius))
{
x = l->Pose.Position.x;
y = l->Pose.Position.y;
z = l->Pose.Position.z;
test = (x & 1023) > 256 && (x & 1023) < 768 && (z & 1023) > 256 && (z & 1023) < 768;
damage = item->Animation.Velocity.y > 0 ? 96 : 0;
if (ItemPushItem(item, l, coll, coll->Setup.EnableSpasm, 1))
{
if (test)
DoDamage(l, INT_MAX);
else
DoDamage(l, damage);
x -= l->Pose.Position.x;
y -= l->Pose.Position.y;
z -= l->Pose.Position.z;
if (damage)
{
for (int i = 14 + (GetRandomControl() & 3); i > 0; --i)
{
TriggerBlood(l->Pose.Position.x + (GetRandomControl() & 63) - 32, l->Pose.Position.y - (GetRandomControl() & 511) - 256,
l->Pose.Position.z + (GetRandomControl() & 63) - 32, -1, 1);
}
}
if (!coll->Setup.EnableObjectPush || test)
{
l->Pose.Position.x += x;
l->Pose.Position.y += y;
l->Pose.Position.z += z;
}
}
}
}
void WreckingBallControl(short itemNumber)
{
ItemInfo* item, *item2;
int test, x, z, oldX, oldZ, wx, wz, flagX, flagZ, height, dx, dz, ceilingX, ceilingZ, adx, adz;
short room;
item = &g_Level.Items[itemNumber];
test = 1;
item2 = &g_Level.Items[item->ItemFlags[3]];
if (LaraItem->Pose.Position.x >= 45056 && LaraItem->Pose.Position.x <= 57344 && LaraItem->Pose.Position.z >= 26624 && LaraItem->Pose.Position.z <= 43008
|| item->ItemFlags[2] < 900)
{
if (item->ItemFlags[2] < 900)
{
if (!item->ItemFlags[2] || !(GlobalCounter & 0x3F))
{
WreckingBallData[0] = GetRandomControl() % 7 - 3;
WreckingBallData[1] = GetRandomControl() % 7 - 3;
}
x = (WreckingBallData[0] << 10) + 51712;
z = (WreckingBallData[1] << 10) + 34304;
test = 0;
}
else
{
x = LaraItem->Pose.Position.x;
z = LaraItem->Pose.Position.z;
}
}
else
{
x = 51200;
z = 33792;
test = 0;
}
if (item->ItemFlags[2] < 900)
++item->ItemFlags[2];
if (item->ItemFlags[1] <= 0)
{
oldX = item->Pose.Position.x;
oldZ = item->Pose.Position.z;
x = x & 0xFFFFFE00 | 0x200;
z = z & 0xFFFFFE00 | 0x200;
dx = x - item->Pose.Position.x;
dz = z - item->Pose.Position.z;
wx = 0;
if (dx < 0)
wx = -1024;
else if (dx > 0)
wx = 1024;
wz = 0;
if (dz < 0)
wz = -1024;
else if (dz > 0)
wz = 1024;
room = item->RoomNumber;
ceilingX = GetCeiling(GetFloor(item->Pose.Position.x + wx, item2->Pose.Position.y, item->Pose.Position.z, &room), item->Pose.Position.x + wx, item2->Pose.Position.y, item->Pose.Position.z);
room = item->RoomNumber;
ceilingZ = GetCeiling(GetFloor(item->Pose.Position.x, item2->Pose.Position.y, item->Pose.Position.z + wz, &room), item->Pose.Position.x, item2->Pose.Position.y, item->Pose.Position.z + wz);
if (ceilingX <= item2->Pose.Position.y && ceilingX != NO_HEIGHT)
flagX = 1;
else
flagX = 0;
if (ceilingZ <= item2->Pose.Position.y && ceilingZ != NO_HEIGHT)
flagZ = 1;
else
flagZ = 0;
if (!item->ItemFlags[0])
{
if (flagX && dx && (abs(dx) > abs(dz) || !flagZ || GetRandomControl() & 1))
{
item->ItemFlags[0] = 1;
}
else if (flagZ && dz)
{
item->ItemFlags[0] = 2;
}
}
if (item->ItemFlags[0] == 1)
{
SoundEffect(SFX_TR5_BASE_CLAW_MOTOR_B_LOOP, &item->Pose);
adx = abs(dx);
if (adx >= 32)
adx = 32;
if (dx > 0)
{
item->Pose.Position.x += adx;
}
else if (dx < 0)
{
item->Pose.Position.x -= adx;
}
else
{
item->ItemFlags[0] = 0;
}
}
if (item->ItemFlags[0] == 2)
{
SoundEffect(SFX_TR5_BASE_CLAW_MOTOR_B_LOOP, &item->Pose);
adz = abs(dz);
if (adz >= 32)
adz = 32;
if (dz > 0)
{
item->Pose.Position.z += adz;
}
else if (dz < 0)
{
item->Pose.Position.z -= adz;
}
else
{
item->ItemFlags[0] = 0;
}
}
if (item->ItemFlags[1] == -1 && (oldX != item->Pose.Position.x || oldZ != item->Pose.Position.z))
{
item->ItemFlags[1] = 0;
SoundEffect(SFX_TR5_BASE_CLAW_MOTOR_A, &item->Pose);
}
if ((item->Pose.Position.x & 0x3FF) == 512 && (item->Pose.Position.z & 0x3FF) == 512)
item->ItemFlags[0] = 0;
if (x == item->Pose.Position.x && z == item->Pose.Position.z && test)
{
if (item->ItemFlags[1] != -1)
{
StopSoundEffect(SFX_TR5_BASE_CLAW_MOTOR_B_LOOP);
SoundEffect(SFX_TR5_BASE_CLAW_MOTOR_C, &item->Pose);
}
item->ItemFlags[1] = 1;
item->TriggerFlags = 30;
}
}
else if (item->ItemFlags[1] == 1)
{
if (!item->TriggerFlags)
{
--item->TriggerFlags;
}
else if (!item->Animation.ActiveState)
{
item->Animation.TargetState = 1;
}
else if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameEnd)
{
SoundEffect(SFX_TR5_BASE_CLAW_DROP, &item->Pose);
++item->ItemFlags[1];
item->Animation.Velocity.y = 6;
item->Pose.Position.y += item->Animation.Velocity.y;
}
}
else if (item->ItemFlags[1] == 2)
{
item->Animation.Velocity.y += 24;
item->Pose.Position.y += item->Animation.Velocity.y;
room = item->RoomNumber;
height = GetFloorHeight(GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &room), item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z);
if (height < item->Pose.Position.y)
{
item->Pose.Position.y = height;
if (item->Animation.Velocity.y > 48)
{
BounceCamera(item, 64, 8192);
item->Animation.Velocity.y = -item->Animation.Velocity.y / 8;
}
else
{
++item->ItemFlags[1];
item->Animation.Velocity.y = 0;
}
}
else if (height - item->Pose.Position.y < 1536 && item->Animation.ActiveState)
{
item->Animation.TargetState = 0;
}
}
else if (item->ItemFlags[1] == 3)
{
item->Animation.Velocity.y -= 3;
item->Pose.Position.y += item->Animation.Velocity.y;
if (item->Pose.Position.y < item2->Pose.Position.y + 1644)
{
StopSoundEffect(SFX_TR5_BASE_CLAW_WINCH_UP_LOOP);
item->ItemFlags[0] = 1;
item->Pose.Position.y = item2->Pose.Position.y + 1644;
if (item->Animation.Velocity.y < -32)
{
SoundEffect(SFX_TR5_BASE_CLAW_TOP_IMPACT, &item->Pose, SoundEnvironment::Land, 1.0f, 0.5f);
item->Animation.Velocity.y = -item->Animation.Velocity.y / 8;
BounceCamera(item, 16, 8192);
}
else
{
item->ItemFlags[1] = -1;
item->Animation.Velocity.y = 0;
item->ItemFlags[0] = 0;
}
}
else if (!item->ItemFlags[0])
{
SoundEffect(SFX_TR5_BASE_CLAW_WINCH_UP_LOOP, &item->Pose);
}
}
item2->Pose.Position.x = item->Pose.Position.x;
item2->Pose.Position.z = item->Pose.Position.z;
room = item->RoomNumber;
item2->Pose.Position.y = GetCeiling(GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &room), item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z);
GetFloor(item2->Pose.Position.x, item2->Pose.Position.y, item2->Pose.Position.z, &room);
if (room != item2->RoomNumber)
ItemNewRoom(item->ItemFlags[3], room);
TriggerAlertLight(item2->Pose.Position.x, item2->Pose.Position.y + 64, item2->Pose.Position.z, 255, 64, 0, 64 * (GlobalCounter & 0x3F), item2->RoomNumber, 24);
TriggerAlertLight(item2->Pose.Position.x, item2->Pose.Position.y + 64, item2->Pose.Position.z, 255, 64, 0, 64 * (GlobalCounter - 32) & 0xFFF, item2->RoomNumber, 24);
room = item->RoomNumber;
GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &room);
if (room != item->RoomNumber)
ItemNewRoom(itemNumber, room);
AnimateItem(item);
}

View file

@ -1,9 +0,0 @@
#pragma once
struct CollisionInfo;
struct ItemInfo;
void InitializeFallingBlock(short itemNumber);
void InitializeWreckingBall(short itemNumber);
void WreckingBallCollision(short itemNumber, ItemInfo* l, CollisionInfo* coll);
void WreckingBallControl(short itemNumber);

View file

@ -33,13 +33,6 @@ enum class PuzzleType
AnimAfter
};
enum PuzzleHoleType
{
None = 0,
Done = 1,
Hole = 2
};
ObjectCollisionBounds PuzzleBounds =
{
GameBoundingBox(
@ -67,7 +60,7 @@ const ObjectCollisionBounds KeyHoleBounds =
void InitializePuzzleHole(short itemNumber)
{
auto& receptacleItem = g_Level.Items[itemNumber];
receptacleItem.ItemFlags[5] = PuzzleHoleType::Hole;
receptacleItem.ItemFlags[5] = (int)ReusableReceptacleState::Empty;
}
void InitializePuzzleDone(short itemNumber)
@ -83,8 +76,8 @@ void PuzzleHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co
auto& receptacleItem = g_Level.Items[itemNumber];
auto& player = GetLaraInfo(*laraItem);
//Start level with the right object when loading the game.
if (receptacleItem.ItemFlags[5] == PuzzleHoleType::Done)
// Start level with correct object when loading game.
if (receptacleItem.ItemFlags[5] == (int)ReusableReceptacleState::Done)
{
receptacleItem.ObjectNumber += GAME_OBJECT_ID{ ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1 };
SetAnimation(receptacleItem, 0);
@ -163,7 +156,7 @@ void PuzzleHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co
}
}
RemoveObjectFromInventory(static_cast<GAME_OBJECT_ID>(receptacleItem.ObjectNumber - (ID_PUZZLE_HOLE1 - ID_PUZZLE_ITEM1)), 1);
RemoveObjectFromInventory(GAME_OBJECT_ID(receptacleItem.ObjectNumber - (ID_PUZZLE_HOLE1 - ID_PUZZLE_ITEM1)), 1);
if (puzzleType == PuzzleType::Specfic)
{
@ -244,8 +237,8 @@ void PuzzleDoneCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co
return;
AnimateItem(&receptacleItem);
//Start level with the right object when loading the game.
if (receptacleItem.ItemFlags[5] == PuzzleHoleType::Hole)
// Start level with correct object when loading game.
if (receptacleItem.ItemFlags[5] == (int)ReusableReceptacleState::Empty)
{
receptacleItem.ObjectNumber = GAME_OBJECT_ID(receptacleItem.ObjectNumber - (ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1));
SetAnimation(receptacleItem, 0);
@ -253,12 +246,12 @@ void PuzzleDoneCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co
return;
}
//Activate triggers when startig the level for the first time
if (receptacleItem.ItemFlags[5] == PuzzleHoleType::None)
// Activate triggers when startig level for first time.
if (receptacleItem.ItemFlags[5] == (int)ReusableReceptacleState::None)
{
receptacleItem.ItemFlags[1] = true;
TestTriggers(receptacleItem.Pose.Position.x, receptacleItem.Pose.Position.y, receptacleItem.Pose.Position.z, receptacleItem.RoomNumber, false, 0);
receptacleItem.ItemFlags[5] = PuzzleHoleType::Done;
receptacleItem.ItemFlags[5] = (int)ReusableReceptacleState::Done;
}
auto puzzleType = PuzzleType::Normal;
@ -341,7 +334,7 @@ void PuzzleDone(ItemInfo* item, short itemNumber)
item->ItemFlags[1] = true;
item->ObjectNumber += GAME_OBJECT_ID{ ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1 };
item->ItemFlags[5] = PuzzleHoleType::Done;
item->ItemFlags[5] = (int)ReusableReceptacleState::Done;
SetAnimation(item, 0);
item->ResetModelToDefault();
}
@ -371,7 +364,7 @@ void PuzzleHole(ItemInfo* item, short itemNumber)
item->ItemFlags[1] = true;
item->ObjectNumber = GAME_OBJECT_ID(item->ObjectNumber - (ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1));
item->ItemFlags[5] = PuzzleHoleType::Hole;
item->ItemFlags[5] = (int)ReusableReceptacleState::Empty;
SetAnimation(item, 0);
item->ResetModelToDefault();
}
@ -441,35 +434,27 @@ void DoPuzzle()
// Keys
void KeyHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{
auto* laraInfo = GetLaraInfo(laraItem);
auto* keyHoleItem = &g_Level.Items[itemNumber];
auto* player = GetLaraInfo(laraItem);
if (g_Level.Items[itemNumber].TriggerFlags == 1 &&
keyHoleItem->ObjectNumber == ID_KEY_HOLE8)
{
if (keyHoleItem->ItemFlags[3])
{
keyHoleItem->ItemFlags[3]--;
if (!keyHoleItem->ItemFlags[3])
keyHoleItem->MeshBits = 2;
}
}
short* triggerIndexPtr = GetTriggerIndex(keyHoleItem);
short triggerType = (*(triggerIndexPtr++) >> 8) & 0x3F;
bool actionReady = (TrInput & IN_ACTION || g_Gui.GetInventoryItemChosen() != NO_ITEM);
bool isActionReady = (IsHeld(In::Action) || g_Gui.GetInventoryItemChosen() != NO_ITEM);
bool laraAvailable = !BinocularRange &&
bool isPlayerAvailable = !BinocularRange &&
laraItem->Animation.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_IDLE;
bool actionActive = laraInfo->Control.IsMoving && laraInfo->Context.InteractedItem == itemNumber;
bool actionActive = player->Control.IsMoving && player->Context.InteractedItem == itemNumber;
if (actionActive || (actionReady && laraAvailable))
if (actionActive || (isActionReady && isPlayerAvailable))
{
if (TestLaraPosition(KeyHoleBounds, keyHoleItem, laraItem))
{
if (!laraInfo->Control.IsMoving) //TROYE INVENTORY FIX ME
if (!player->Control.IsMoving)
{
if (keyHoleItem->Status != ITEM_NOT_ACTIVE)
if (keyHoleItem->Status != ITEM_NOT_ACTIVE && triggerType != TRIGGER_TYPES::SWITCH)
return;
if (g_Gui.GetInventoryItemChosen() == NO_ITEM)
@ -483,46 +468,42 @@ void KeyHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
if (g_Gui.GetInventoryItemChosen() != keyHoleItem->ObjectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1))
return;
laraInfo->Context.InteractedItem = itemNumber;
player->Context.InteractedItem = itemNumber;
}
if (laraInfo->Context.InteractedItem != itemNumber)
if (player->Context.InteractedItem != itemNumber)
return;
if (MoveLaraPosition(KeyHolePosition, keyHoleItem, laraItem))
{
if (keyHoleItem->ObjectNumber == ID_KEY_HOLE8)
laraItem->Animation.AnimNumber = LA_KEYCARD_USE;
if (triggerType != TRIGGER_TYPES::SWITCH)
{
RemoveObjectFromInventory(GAME_OBJECT_ID(keyHoleItem->ObjectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1)), 1);
}
else
{
RemoveObjectFromInventory(static_cast<GAME_OBJECT_ID>(keyHoleItem->ObjectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1)), 1);
laraItem->Animation.AnimNumber = LA_USE_KEY;
keyHoleItem->ItemFlags[1] = true;
}
laraItem->Animation.AnimNumber = keyHoleItem->TriggerFlags;
laraItem->Animation.ActiveState = LS_INSERT_KEY;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraInfo->Control.IsMoving = false;
player->Control.IsMoving = false;
ResetPlayerFlex(laraItem);
laraInfo->Control.HandStatus = HandStatus::Busy;
player->Control.HandStatus = HandStatus::Busy;
keyHoleItem->Flags |= TRIGGERED;
keyHoleItem->Status = ITEM_ACTIVE;
if (keyHoleItem->TriggerFlags == 1 && keyHoleItem->ObjectNumber == ID_KEY_HOLE8)
{
keyHoleItem->ItemFlags[3] = 92;
g_Gui.SetInventoryItemChosen(NO_ITEM);
return;
}
}
g_Gui.SetInventoryItemChosen(NO_ITEM);
return;
}
if (laraInfo->Control.IsMoving && laraInfo->Context.InteractedItem == itemNumber)
if (player->Control.IsMoving && player->Context.InteractedItem == itemNumber)
{
laraInfo->Control.IsMoving = false;
laraInfo->Control.HandStatus = HandStatus::Free;
player->Control.IsMoving = false;
player->Control.HandStatus = HandStatus::Free;
}
}
else

View file

@ -3,6 +3,13 @@
struct CollisionInfo;
struct ItemInfo;
enum class ReusableReceptacleState
{
None = 0,
Done = 1,
Empty = 2
};
// Puzzles
void PuzzleHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void PuzzleDoneCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);

View file

@ -18,8 +18,8 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto COWBOY_WALK_TURN_RATE_MAX = ANGLE(3.0f);
constexpr auto COWBOY_RUN_TURN_RATE_MAX = ANGLE(6.0f);
const auto CowboyGunLeft = BiteInfo(Vector3(1.0f, 200.0f, 40.0f), 5);
const auto CowboyGunRight = BiteInfo(Vector3(-1.0f, 200.0f, 40.0f), 8);
const auto CowboyGunLeft = CreatureBiteInfo(Vector3(1.0f, 200.0f, 40.0f), 5);
const auto CowboyGunRight = CreatureBiteInfo(Vector3(-1.0f, 200.0f, 40.0f), 8);
enum CowboyState
{

View file

@ -35,7 +35,7 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto APE_RUN_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto APE_DISPLAY_ANGLE = ANGLE(45.0f);
const auto ApeBite = BiteInfo(Vector3(0.0f, -19.0f, 75.0f), 15);
const auto ApeBite = CreatureBiteInfo(Vector3i(0, -19, 75), 15);
const auto ApeAttackJoints = std::vector<unsigned int>{ 8, 9, 10, 11, 12, 13, 14, 15 };
enum ApeState

View file

@ -33,7 +33,7 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto BEAR_WALK_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto BEAR_RUN_TURN_RATE_MAX = ANGLE(5.0f);
const auto BearBite = BiteInfo(Vector3(0.0f, 96.0f, 335.0f), 14);
const auto BearBite = CreatureBiteInfo(Vector3i(0, 96, 335), 14);
const auto BearAttackJoints = std::vector<unsigned int>{ 2, 3, 5, 6, 14, 17 };
enum BearState

View file

@ -33,7 +33,7 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto BIG_RAT_RUN_TURN_RATE_MAX = ANGLE(6.0f);
constexpr auto BIG_RAT_SWIM_TURN_RATE_MAX = ANGLE(3.0f);
const auto BigRatBite = BiteInfo(Vector3(0.0f, -11.0f, 108.0f), 3);
const auto BigRatBite = CreatureBiteInfo(Vector3i(0, -11, 108), 3);
enum BigRatState
{
@ -163,7 +163,7 @@ namespace TEN::Entities::Creatures::TR1
break;
}
if (ai.ahead && item->TouchBits.Test(BigRatBite.meshNum))
if (ai.ahead && item->TouchBits.Test(BigRatBite.BoneID))
{
item->Animation.TargetState = BIG_RAT_STATE_IDLE;
}
@ -181,7 +181,7 @@ namespace TEN::Entities::Creatures::TR1
case BIG_RAT_STATE_LAND_BITE_ATTACK:
if (item->Animation.RequiredState == NO_STATE && ai.ahead &&
item->TouchBits.Test(BigRatBite.meshNum))
item->TouchBits.Test(BigRatBite.BoneID))
{
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
CreatureEffect(item, BigRatBite, DoBloodSplat);
@ -192,7 +192,7 @@ namespace TEN::Entities::Creatures::TR1
case BIG_RAT_STATE_POUNCE_ATTACK:
if (item->Animation.RequiredState == NO_STATE && ai.ahead &&
item->TouchBits.Test(BigRatBite.meshNum))
item->TouchBits.Test(BigRatBite.BoneID))
{
DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE);
CreatureEffect(item, BigRatBite, DoBloodSplat);
@ -216,14 +216,14 @@ namespace TEN::Entities::Creatures::TR1
break;
}
if (ai.ahead && item->TouchBits.Test(BigRatBite.meshNum))
if (ai.ahead && item->TouchBits.Test(BigRatBite.BoneID))
item->Animation.TargetState = BIG_RAT_STATE_SWIM_BITE_ATTACK;
break;
case BIG_RAT_STATE_SWIM_BITE_ATTACK:
if (item->Animation.RequiredState == NO_STATE && ai.ahead &&
item->TouchBits.Test(BigRatBite.meshNum))
item->TouchBits.Test(BigRatBite.BoneID))
{
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
CreatureEffect(item, BigRatBite, DoBloodSplat);

View file

@ -29,8 +29,8 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto 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 auto CentaurRocketBite = CreatureBiteInfo(Vector3(11, 415, 41), 13);
const auto CentaurRearBite = CreatureBiteInfo(Vector3(50, 30, 0), 5);
const auto CentaurAttackJoints = std::vector<unsigned int>{ 0, 3, 4, 7, 8, 16, 17 };
enum CentaurState

View file

@ -33,7 +33,7 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto NATLA_FLY_ANGLE_SPEED = ANGLE(5.0f);
constexpr auto NATLA_SHOOT_ANGLE = ANGLE(30.0f);
const auto NatlaGunBite = BiteInfo(Vector3(5.0f, 220.0f, 7.0f), 4);
const auto NatlaGunBite = CreatureBiteInfo(Vector3i(5, 220, 7), 4);
enum NatlaState
{

View file

@ -39,9 +39,9 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto WINGED_MUTANT_WALK_FORWARD_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto 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 auto WingedMutantBite = CreatureBiteInfo(Vector3i(-27, 98, 0), 10);
const auto WingedMutantRocketBite = CreatureBiteInfo(Vector3i(51, 213, 0), 14);
const auto WingedMutantShardBite = CreatureBiteInfo(Vector3i(-35, 269, 0), 9);
const auto WingedMutantJoints = std::vector<unsigned int>{ 9, 10, 14 };
enum WingedMutantState

View file

@ -32,7 +32,7 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto WOLF_RUN_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto WOLF_STALK_TURN_RATE_MAX = ANGLE(2.0f);
const auto WolfBite = BiteInfo(Vector3(0.0f, -14.0f, 174.0f), 6);
const auto WolfBite = CreatureBiteInfo(Vector3i(0, -14, 174), 6);
const auto WolfAttackJoints = std::vector<unsigned int>{ 0, 1, 2, 3, 6, 8, 9, 10, 12, 13, 14 };
enum WolfState

View file

@ -17,8 +17,8 @@
#include "Objects/TR1/Entity/tr1_giant_mutant.h" // OK
#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/TR1/Entity/tr1_centaur.h" // OK
#include "Objects/TR1/Entity/tr1_winged_mutant.h" // OK
#include "Objects/Utils/object_helper.h"
// Traps
@ -157,7 +157,6 @@ static void StartEntity(ObjectInfo* obj)
obj->control = WingedMutantControl;
obj->collision = CreatureCollision;
obj->shadowType = ShadowMode::All;
obj->hitEffect = HitEffect::Blood;
obj->pivotLength = 150;
obj->radius = BLOCK(1 / 3.0f);
obj->HitPoints = 50;

View file

@ -16,7 +16,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto BARRACUDA_IDLE_ATTACK_RANGE = SQUARE(BLOCK(0.67f));
constexpr auto BARRACUDA_SWIM_FAST_ATTACK_RANGE = SQUARE(BLOCK(0.34f));
const auto BarracudaBite = BiteInfo(Vector3(2.0f, -60.0f, 121.0f), 7);
const auto BarracudaBite = CreatureBiteInfo(Vector3i(2, -60, 121), 7);
const auto BarracudaAttackJoints = std::vector<unsigned int>{ 5, 6, 7 };
enum BarracudaState

View file

@ -21,8 +21,8 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto 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 auto BirdMonsterBiteLeft = CreatureBiteInfo(Vector3i(0, 224, 0), 19);
const auto BirdMonsterBiteRight = CreatureBiteInfo(Vector3i(0, 224, 0), 22);
const auto BirdMonsterAttackLeftJoints = std::vector<unsigned int>{ 18, 19 };
const auto BirdMonsterAttackRightJoints = std::vector<unsigned int>{ 21, 22 };

View file

@ -24,7 +24,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto DRAGON_SWIPE_ATTACK_DAMAGE = 250;
constexpr auto DRAGON_CONTACT_DAMAGE = 10;
const auto DragonMouthBite = BiteInfo(Vector3(35.0f, 171.0f, 1168.0f), 12);
const auto DragonMouthBite = CreatureBiteInfo(Vector3i(35, 171, 1168), 12);
const auto DragonSwipeAttackJointsLeft = std::vector<unsigned int>{ 24, 25, 26, 27, 28, 29, 30 };
const auto DragonSwipeAttackJointsRight = std::vector<unsigned int>{ 1, 2, 3, 4, 5, 6, 7 };

View file

@ -13,8 +13,8 @@
namespace TEN::Entities::Creatures::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);
const auto EagleBite = CreatureBiteInfo(Vector3i(15, 46, 21), 6);
const auto CrowBite = CreatureBiteInfo(Vector3i(2, 10, 60), 14);
enum EagleOrCrowState
{

View file

@ -11,6 +11,7 @@
#include "Game/Lara/lara.h"
#include "Game/misc.h"
#include "Game/people.h"
#include "Game/missile.h"
#include "Math/Math.h"
#include "Sound/sound.h"
#include "Specific/level.h"
@ -24,8 +25,8 @@ namespace TEN::Entities::Creatures::TR2
// TODO: Ranges.
const auto KnifeBiteLeft = BiteInfo(Vector3::Zero, 5);
const auto KnifeBiteRight = BiteInfo(Vector3::Zero, 8);
const auto KnifeBiteLeft = CreatureBiteInfo(Vector3i::Zero, 5);
const auto KnifeBiteRight = CreatureBiteInfo(Vector3i::Zero, 8);
enum KnifeThrowerState
{
@ -70,55 +71,23 @@ namespace TEN::Entities::Creatures::TR2
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.Position, 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
short fxNumber = CreateNewEffect(roomNumber);
if (fxNumber != NO_ITEM)
{
auto* fx = &EffectList[fxNumber];
fx->objectNumber = ID_KNIFETHROWER_KNIFE;
fx->pos.Position.x = x;
fx->pos.Position.y = y;
fx->pos.Position.z = z;
fx->speed = velocity;
fx->pos.Orientation.y = yRot;
fx->fallspeed = 0;
fx->flag2 = KNIFE_PROJECTILE_DAMAGE;
fx->color = Vector4::One;
ShootAtLara(*fx);
}
return fxNumber;
}
@ -264,7 +233,7 @@ namespace TEN::Entities::Creatures::TR2
if (!creature->Flags)
{
CreatureEffect(item, KnifeBiteLeft, ThrowKnife);
CreatureEffect2(item, KnifeBiteLeft, 100, torso, ThrowKnife);
creature->Flags = 1;
}
@ -276,7 +245,7 @@ namespace TEN::Entities::Creatures::TR2
if (!creature->Flags)
{
CreatureEffect(item, KnifeBiteRight, ThrowKnife);
CreatureEffect2(item, KnifeBiteRight, 100, torso, ThrowKnife);
creature->Flags = 1;
}
@ -288,8 +257,8 @@ namespace TEN::Entities::Creatures::TR2
if (!creature->Flags)
{
CreatureEffect(item, KnifeBiteLeft, ThrowKnife);
CreatureEffect(item, KnifeBiteRight, ThrowKnife);
CreatureEffect2(item, KnifeBiteLeft, 100, torso, ThrowKnife);
CreatureEffect2(item, KnifeBiteRight, 100, torso, ThrowKnife);
creature->Flags = 1;
}

View file

@ -2,6 +2,5 @@
namespace TEN::Entities::Creatures::TR2
{
void KnifeControl(short fxNumber);
void KnifeThrowerControl(short itemNumber);
}

View file

@ -13,8 +13,8 @@
namespace TEN::Entities::Creatures::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);
const auto MercenaryUziBite = CreatureBiteInfo(Vector3i(0, 200, 19), 17);
const auto MercenaryAutoPistolBite = CreatureBiteInfo(Vector3i(0, 230, 9), 17);
// TODO
enum MercenaryState
@ -41,6 +41,9 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->HitPoints <= 0)
{
if (item->Animation.ActiveState != 13)
@ -163,8 +166,13 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (item->Animation.FrameNumber == GetFrameIndex(item, 0))
{
if (!ShotLara(item, &AI, MercenaryUziBite, extraTorsoRot.y, 8))
item->Animation.TargetState = 1;
creature->MuzzleFlash[0].Bite = MercenaryUziBite;
creature->MuzzleFlash[0].Delay = 2;
}
if (AI.distance < pow(SECTOR(2), 2))
item->Animation.TargetState = 1;
@ -179,8 +187,13 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (item->Animation.FrameNumber == GetFrameIndex(item, 0))
{
if (!ShotLara(item, &AI, MercenaryUziBite, extraTorsoRot.y, 8))
item->Animation.TargetState = 1;
creature->MuzzleFlash[0].Bite = MercenaryUziBite;
creature->MuzzleFlash[0].Delay = 2;
}
if (AI.distance < pow(SECTOR(2), 2))
item->Animation.TargetState = 2;
@ -210,6 +223,9 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->HitPoints <= 0)
{
if (item->Animation.ActiveState != 11)
@ -330,12 +346,13 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.x = AI.xAngle;
extraTorsoRot.y = AI.angle;
if (!creature->Flags)
if (creature->Flags == 0)
{
if (GetRandomControl() < 0x2000)
item->Animation.TargetState = 2;
ShotLara(item, &AI, MercenaryAutoPistolBite, extraTorsoRot.y, 50);
creature->MuzzleFlash[0].Bite = MercenaryAutoPistolBite;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1;
}
}
@ -353,11 +370,12 @@ namespace TEN::Entities::Creatures::TR2
if (AI.distance < pow(SECTOR(2), 2))
item->Animation.TargetState = 3;
if (creature->Flags != 1)
if (creature->Flags == 0)
{
if (!ShotLara(item, &AI, MercenaryAutoPistolBite, extraTorsoRot.y, 50))
item->Animation.TargetState = 3;
creature->MuzzleFlash[0].Bite = MercenaryAutoPistolBite;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1;
}
}
@ -395,7 +413,8 @@ namespace TEN::Entities::Creatures::TR2
{
if (!ShotLara(item, &AI, MercenaryAutoPistolBite, extraTorsoRot.y, 50))
item->Animation.TargetState = 3;
creature->MuzzleFlash[0].Bite = MercenaryAutoPistolBite;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 2;
}
}

View file

@ -13,7 +13,7 @@
namespace TEN::Entities::Creatures::TR2
{
const auto MonkBite = BiteInfo(Vector3(-23.0f, 16.0f, 265.0f), 14);
const auto MonkBite = CreatureBiteInfo(Vector3i(-23, 16, 265), 14);
bool MonksAttackLara;

View file

@ -25,7 +25,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto RAT_TURN_RATE_MAX = ANGLE(6.0f);
const auto RatBite = BiteInfo(Vector3(0.0f, 0.0f, 57.0f), 2);
const auto RatBite = CreatureBiteInfo(Vector3i(0, 0, 57), 2);
enum RatState
{
@ -131,7 +131,7 @@ namespace TEN::Entities::Creatures::TR2
case RAT_STATE_POUNCE_ATTACK:
if (item->Animation.RequiredState == NO_STATE &&
item->TouchBits.Test(RatBite.meshNum))
item->TouchBits.Test(RatBite.BoneID))
{
item->Animation.RequiredState = RAT_STATE_IDLE;
DoDamage(creature->Enemy, RAT_ATTACK_DAMAGE);

View file

@ -15,7 +15,7 @@ namespace TEN::Entities::Creatures::TR2
{
constexpr auto SHARK_BITE_ATTACK_DAMAGE = 400;
const auto SharkBite = BiteInfo(Vector3(17.0f, -22.0f, 344.0f), 12);
const auto SharkBite = CreatureBiteInfo(Vector3i(17, -22, 344), 12);
const auto SharkBiteAttackJoints = std::vector<unsigned int>{ 10, 12, 13 };
void SharkControl(short itemNumber)

View file

@ -21,7 +21,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto SILENCER_WALK_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto SILENCER_RUN_TURN_RATE_MAX = ANGLE(5.0f);
const auto SilencerGunBite = BiteInfo(Vector3(3.0f, 331.0f, 56.0f), 10);
const auto SilencerGunBite = CreatureBiteInfo(Vector3(-10, 360, 60), 10);
enum SilencerState
{
@ -87,6 +87,9 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->HitPoints <= 0)
{
if (item->Animation.ActiveState != SILENCER_STATE_DEATH_1 &&
@ -279,9 +282,11 @@ namespace TEN::Entities::Creatures::TR2
else
extraHeadRot.y = AI.angle;
if (!creature->Flags)
if (creature->Flags == 0 && item->Animation.FrameNumber == GetFrameIndex(item, 0))
{
ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE);
creature->MuzzleFlash[0].Bite = SilencerGunBite;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1;
}
@ -298,11 +303,14 @@ namespace TEN::Entities::Creatures::TR2
else
extraHeadRot.y = AI.angle;
if (item->Animation.RequiredState == NO_STATE)
if (item->Animation.RequiredState == NO_STATE &&
(item->Animation.AnimNumber == GetAnimIndex(*item, SILENCER_ANIM_RUN_FORWARD_SHOOT_LEFT) && item->Animation.FrameNumber == GetFrameIndex(item, 1) ||
item->Animation.AnimNumber == GetAnimIndex(*item, SILENCER_ANIM_RUN_FORWARD_SHOOT_RIGHT) && item->Animation.FrameNumber == GetFrameIndex(item, 3)))
{
if (!ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE))
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
creature->MuzzleFlash[0].Bite = SilencerGunBite;
creature->MuzzleFlash[0].Delay = 2;
item->Animation.RequiredState = SILENCER_STATE_RUN_SHOOT;
}

View file

@ -7,8 +7,10 @@
#include "Game/collision/sphere.h"
#include "Game/control/box.h"
#include "Game/control/lot.h"
#include "Game/effects/smoke.h"
#include "Game/itemdata/creature_info.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/misc.h"
#include "Game/people.h"
#include "Objects/TR2/Vehicles/skidoo.h"
@ -17,15 +19,19 @@
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Smoke;
namespace TEN::Entities::Creatures::TR2
{
constexpr auto SMAN_WAIT_RANGE = SQUARE(BLOCK(4));
const auto SkidooBiteLeft = BiteInfo(Vector3(240.0f, -190.0f, 540.0f), 0);
const auto SkidooBiteRight = BiteInfo(Vector3(-240.0f, -190.0f, 540.0f), 0);
const auto SkidooBiteLeft = CreatureBiteInfo(Vector3i(240, -80, 540), 0);
const auto SkidooBiteRight = CreatureBiteInfo(Vector3i(-240, -80, 540), 0);
const auto SkidooBiteSmokeLeft = CreatureBiteInfo(Vector3i(240, -80, 450), 0);
const auto SkidooBiteSmokeRight = CreatureBiteInfo(Vector3i(-240, -80, 450), 0);
constexpr auto SMAN_MIN_TURN = ANGLE(2.0f);
constexpr auto SMAN_TARGET_ANGLE = ANGLE(15.0f);
constexpr auto SMAN_TARGET_ANGLE = ANGLE(30.0f);
enum SnowmobileManState
{
@ -45,30 +51,34 @@ namespace TEN::Entities::Creatures::TR2
SMAN_ANIM_DEATH = 10
};
void InitializeSkidooMan(short itemNumber)
static void CreateSnowmobileGun(ItemInfo* driver)
{
short skidooItemNumber = CreateItem();
if (skidooItemNumber != NO_ITEM)
short snowmobileNumber = CreateItem();
if (snowmobileNumber != NO_ITEM)
{
auto* riderItem = &g_Level.Items[itemNumber];
auto* skidooItem = &g_Level.Items[skidooItemNumber];
skidooItem->ObjectNumber = ID_SNOWMOBILE_GUN;
skidooItem->Pose.Position = riderItem->Pose.Position;
skidooItem->Pose.Orientation.y = riderItem->Pose.Orientation.y;
skidooItem->RoomNumber = riderItem->RoomNumber;
skidooItem->Flags = ITEM_INVISIBLE;
skidooItem->Model.Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f);
InitializeItem(skidooItemNumber);
// The rider remembers his skidoo.
riderItem->Data = skidooItemNumber;
g_Level.NumItems++;
auto* snowmobileGunItem = &g_Level.Items[snowmobileNumber];
snowmobileGunItem->Pose.Position = driver->Pose.Position;
snowmobileGunItem->Pose.Orientation.y = driver->Pose.Orientation.y;
snowmobileGunItem->RoomNumber = driver->RoomNumber;
snowmobileGunItem->ObjectNumber = ID_SNOWMOBILE_GUN;
snowmobileGunItem->Model.Color = driver->Model.Color;
snowmobileGunItem->Flags = IFLAG_ACTIVATION_MASK;
InitializeItem(snowmobileNumber); g_Level.NumItems++;
driver->Data = snowmobileNumber; // Register the snowmobile gun for the driver to control it.
}
else
TENLog("Can't create skidoo for rider!", LogLevel::Error);
{
TENLog("Failed to create the ID_SNOWMOBILE_GUN from ID_SNOWMOBILE_DRIVER.", LogLevel::Warning);
}
}
void InitializeSkidooMan(short itemNumber)
{
auto* item = &g_Level.Items[itemNumber];
if (item->Flags & IFLAG_REVERSE)
item->Status &= ~ITEM_INVISIBLE;
else
item->Status = ITEM_INVISIBLE;
}
void SkidooManCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
@ -95,60 +105,67 @@ namespace TEN::Entities::Creatures::TR2
void SkidooManControl(short riderItemNumber)
{
auto* riderItem = &g_Level.Items[riderItemNumber];
if (!riderItem->Data)
auto* rider = &g_Level.Items[riderItemNumber];
if (!rider->Data)
{
TENLog("Rider data does not contain the skidoo itemNumber!", LogLevel::Error);
// Create the snowmobile.
CreateSnowmobileGun(rider);
if (!rider->Data)
TENLog("Rider data does not contain the skidoo itemNumber !", LogLevel::Error);
return;
}
short itemNumber = (short)riderItem->Data;
auto* item = &g_Level.Items[itemNumber];
short itemNumber = (short)rider->Data;
auto* skidoo = &g_Level.Items[itemNumber];
if (!item->Data)
if (!skidoo->Data)
{
EnableEntityAI(itemNumber, true);
item->Status = ITEM_ACTIVE;
skidoo->Status = ITEM_ACTIVE;
}
auto* creatureInfo = GetCreatureInfo(item);
auto* creatureInfo = GetCreatureInfo(skidoo);
short angle = 0;
int damage;
if (creatureInfo->MuzzleFlash[0].Delay != 0)
creatureInfo->MuzzleFlash[0].Delay--;
if (creatureInfo->MuzzleFlash[1].Delay != 0)
creatureInfo->MuzzleFlash[1].Delay--;
AI_INFO AI;
if (item->HitPoints <= 0)
if (skidoo->HitPoints <= 0)
{
if (riderItem->Animation.ActiveState != SMAN_STATE_DEATH)
if (rider->Animation.ActiveState != SMAN_STATE_DEATH)
{
riderItem->Pose.Position = item->Pose.Position;
riderItem->Pose.Orientation.y = item->Pose.Orientation.y;
riderItem->RoomNumber = item->RoomNumber;
rider->Pose.Position = skidoo->Pose.Position;
rider->Pose.Orientation.y = skidoo->Pose.Orientation.y;
rider->RoomNumber = skidoo->RoomNumber;
riderItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_DRIVER].animIndex + SMAN_ANIM_DEATH;
riderItem->Animation.FrameNumber = g_Level.Anims[riderItem->Animation.AnimNumber].frameBase;
riderItem->Animation.ActiveState = SMAN_STATE_DEATH;
rider->Animation.AnimNumber = Objects[ID_SNOWMOBILE_DRIVER].animIndex + SMAN_ANIM_DEATH;
rider->Animation.FrameNumber = g_Level.Anims[rider->Animation.AnimNumber].frameBase;
rider->Animation.ActiveState = SMAN_STATE_DEATH;
if (Lara.TargetEntity == item)
if (Lara.TargetEntity == skidoo)
Lara.TargetEntity = nullptr;
}
else
AnimateItem(riderItem);
AnimateItem(rider);
if (item->Animation.ActiveState == SMAN_STATE_MOVING || item->Animation.ActiveState == SMAN_STATE_WAIT)
item->Animation.TargetState = SMAN_STATE_WAIT;
if (skidoo->Animation.ActiveState == SMAN_STATE_MOVING || skidoo->Animation.ActiveState == SMAN_STATE_WAIT)
skidoo->Animation.TargetState = SMAN_STATE_WAIT;
else
item->Animation.TargetState = SMAN_STATE_MOVING;
skidoo->Animation.TargetState = SMAN_STATE_MOVING;
}
else
{
CreatureAIInfo(item, &AI);
CreatureAIInfo(skidoo, &AI);
GetCreatureMood(item, &AI, true);
CreatureMood(item, &AI, true);
GetCreatureMood(skidoo, &AI, true);
CreatureMood(skidoo, &AI, true);
angle = CreatureTurn(item, ANGLE(3.0f));
angle = CreatureTurn(skidoo, ANGLE(3.0f));
switch (item->Animation.ActiveState)
switch (skidoo->Animation.ActiveState)
{
case SMAN_STATE_WAIT:
if (creatureInfo->Mood == MoodType::Bored)
@ -156,98 +173,119 @@ namespace TEN::Entities::Creatures::TR2
else if (abs(AI.angle) < SMAN_TARGET_ANGLE && AI.distance < SMAN_WAIT_RANGE)
break;
item->Animation.TargetState = SMAN_STATE_MOVING;
skidoo->Animation.TargetState = SMAN_STATE_MOVING;
break;
case SMAN_STATE_MOVING:
if (creatureInfo->Mood == MoodType::Bored)
item->Animation.TargetState = SMAN_STATE_WAIT;
skidoo->Animation.TargetState = SMAN_STATE_WAIT;
else if (abs(AI.angle) < SMAN_TARGET_ANGLE && AI.distance < SMAN_WAIT_RANGE)
item->Animation.TargetState = SMAN_STATE_WAIT;
skidoo->Animation.TargetState = SMAN_STATE_WAIT;
else if (angle < -SMAN_MIN_TURN)
item->Animation.TargetState = SMAN_STATE_START_LEFT;
skidoo->Animation.TargetState = SMAN_STATE_START_LEFT;
else if (angle > SMAN_MIN_TURN)
item->Animation.TargetState = SMAN_STATE_START_RIGHT;
skidoo->Animation.TargetState = SMAN_STATE_START_RIGHT;
break;
case SMAN_STATE_START_LEFT:
case SMAN_STATE_LEFT:
if (angle < -SMAN_MIN_TURN)
item->Animation.TargetState = SMAN_STATE_LEFT;
skidoo->Animation.TargetState = SMAN_STATE_LEFT;
else
item->Animation.TargetState = SMAN_STATE_MOVING;
skidoo->Animation.TargetState = SMAN_STATE_MOVING;
break;
case SMAN_STATE_START_RIGHT:
case SMAN_STATE_RIGHT:
if (angle < -SMAN_MIN_TURN)
item->Animation.TargetState = SMAN_STATE_LEFT;
skidoo->Animation.TargetState = SMAN_STATE_LEFT;
else
item->Animation.TargetState = SMAN_STATE_MOVING;
skidoo->Animation.TargetState = SMAN_STATE_MOVING;
break;
}
}
if (riderItem->Animation.ActiveState != SMAN_STATE_DEATH)
if (rider->Animation.ActiveState != SMAN_STATE_DEATH)
{
if (!creatureInfo->Flags && abs(AI.angle) < SMAN_TARGET_ANGLE && LaraItem->HitPoints > 0)
if (creatureInfo->Flags == 0 && abs(AI.angle) < SMAN_TARGET_ANGLE && creatureInfo->Enemy->HitPoints > 0)
{
damage = (Lara.Context.Vehicle != NO_ITEM) ? 10 : 50;
int damage = (creatureInfo->Enemy->IsLara() && GetLaraInfo(creatureInfo->Enemy)->Context.Vehicle != NO_ITEM) ? 10 : 50;
if (ShotLara(item, &AI, SkidooBiteLeft, 0, damage) + ShotLara(item, &AI, SkidooBiteRight, 0, damage))
creatureInfo->Flags = 5;
ShotLara(skidoo, &AI, SkidooBiteLeft, 0, damage);
auto jointPos = GetJointPosition(skidoo, SkidooBiteSmokeLeft);
TriggerGunSmokeParticles(jointPos.x, jointPos.y, jointPos.z, 0, 0, 0, 1, LaraWeaponType::Snowmobile, 16, skidoo->RoomNumber);
creatureInfo->MuzzleFlash[0].Bite = SkidooBiteLeft;
creatureInfo->MuzzleFlash[0].Delay = 1;
creatureInfo->MuzzleFlash[0].SwitchToMuzzle2 = true;
creatureInfo->MuzzleFlash[0].ApplyXRotation = false;
creatureInfo->MuzzleFlash[0].UseSmoke = false;
ShotLara(skidoo, &AI, SkidooBiteRight, 0, damage);
jointPos = GetJointPosition(skidoo, SkidooBiteSmokeRight);
TriggerGunSmokeParticles(jointPos.x, jointPos.y, jointPos.z, 0, 0, 0, 1, LaraWeaponType::Snowmobile, 16, skidoo->RoomNumber);
creatureInfo->MuzzleFlash[1].Bite = SkidooBiteRight;
creatureInfo->MuzzleFlash[1].Delay = 1;
creatureInfo->MuzzleFlash[1].SwitchToMuzzle2 = true;
creatureInfo->MuzzleFlash[1].ApplyXRotation = false;
creatureInfo->MuzzleFlash[1].UseSmoke = false;
creatureInfo->Flags = 4;
}
if (creatureInfo->Flags)
if (creatureInfo->Flags != 0)
{
SoundEffect(SFX_TR4_BADDY_UZI, &item->Pose);
SoundEffect(SFX_TR4_BADDY_UZI, &skidoo->Pose);
creatureInfo->Flags--;
}
}
if (item->Animation.ActiveState == SMAN_STATE_WAIT)
if (skidoo->Animation.ActiveState == SMAN_STATE_WAIT)
{
SoundEffect(SFX_TR2_VEHICLE_SNOWMOBILE_IDLE, &item->Pose);
SoundEffect(SFX_TR2_VEHICLE_SNOWMOBILE_IDLE, &skidoo->Pose);
creatureInfo->JointRotation[0] = 0;
}
else
{
creatureInfo->JointRotation[0] = (creatureInfo->JointRotation[0] == 1) ? 2 : 1;
DoSnowEffect(item);
SoundEffect(SFX_TR2_VEHICLE_SNOWMOBILE_IDLE, &item->Pose, SoundEnvironment::Land, 0.5f + item->Animation.Velocity.z / 100.0f); // SKIDOO_MAX_VELOCITY. TODO: Check actual sound!
DoSnowEffect(skidoo);
SoundEffect(SFX_TR2_VEHICLE_SNOWMOBILE_IDLE, &skidoo->Pose, SoundEnvironment::Land, 0.5f + skidoo->Animation.Velocity.z / 100.0f); // SKIDOO_MAX_VELOCITY. TODO: Check actual sound!
}
CreatureAnimation(itemNumber, angle, 0);
if (riderItem->Animation.ActiveState != SMAN_STATE_DEATH)
if (rider->Animation.ActiveState != SMAN_STATE_DEATH)
{
riderItem->Pose.Position = item->Pose.Position;
riderItem->Pose.Orientation.y = item->Pose.Orientation.y;
rider->Pose.Position = skidoo->Pose.Position;
rider->Pose.Orientation.y = skidoo->Pose.Orientation.y;
if (item->RoomNumber != riderItem->RoomNumber)
ItemNewRoom(riderItemNumber, item->RoomNumber);
if (skidoo->RoomNumber != rider->RoomNumber)
ItemNewRoom(riderItemNumber, skidoo->RoomNumber);
riderItem->Animation.AnimNumber = item->Animation.AnimNumber + (Objects[ID_SNOWMOBILE_DRIVER].animIndex - Objects[ID_SNOWMOBILE_GUN].animIndex);
riderItem->Animation.FrameNumber = item->Animation.FrameNumber + (g_Level.Anims[riderItem->Animation.AnimNumber].frameBase - g_Level.Anims[item->Animation.AnimNumber].frameBase);
rider->Animation.AnimNumber = skidoo->Animation.AnimNumber + (Objects[ID_SNOWMOBILE_DRIVER].animIndex - Objects[ID_SNOWMOBILE_GUN].animIndex);
rider->Animation.FrameNumber = skidoo->Animation.FrameNumber + (g_Level.Anims[rider->Animation.AnimNumber].frameBase - g_Level.Anims[skidoo->Animation.AnimNumber].frameBase);
}
else if (riderItem->Status == ITEM_DEACTIVATED &&
item->Animation.Velocity.z == 0 &&
item->Animation.Velocity.y == 0)
else if (rider->Status == ITEM_DEACTIVATED &&
skidoo->Animation.Velocity.z == 0 &&
skidoo->Animation.Velocity.y == 0)
{
RemoveActiveItem(riderItemNumber);
riderItem->Collidable = false;
riderItem->HitPoints = NOT_TARGETABLE;
riderItem->Flags |= IFLAG_INVISIBLE;
rider->Collidable = false;
rider->HitPoints = NOT_TARGETABLE;
rider->Flags |= IFLAG_INVISIBLE;
DisableEntityAI(itemNumber);
item->ObjectNumber = ID_SNOWMOBILE;
item->Status = ITEM_DEACTIVATED;
InitializeSkidoo(itemNumber);
skidoo->ObjectNumber = ID_SNOWMOBILE;
skidoo->Status = ITEM_DEACTIVATED;
((SkidooInfo*)item->Data)->Armed = true;
InitializeSkidoo(itemNumber);
if (skidoo->Data.is<SkidooInfo>())
{
auto* skidooData = (SkidooInfo*)skidoo->Data;
skidooData->Armed = true;
}
}
}
}

View file

@ -34,8 +34,8 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto SPEAR_GUARDIAN_SWAPMESH_TIME = 3;
const auto SpearGuardianBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 11);
const auto SpearGuardianBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 18);
const auto SpearGuardianBiteLeft = CreatureBiteInfo(Vector3i(0, 0, 920), 11);
const auto SpearGuardianBiteRight = CreatureBiteInfo(Vector3i(0, 0, 920), 18);
enum SpearGuardianState
{
@ -108,7 +108,7 @@ namespace TEN::Entities::Creatures::TR2
{
auto& creature = *GetCreatureInfo(&item);
if (!(creature.Flags & 1) && item.TouchBits.Test(SpearGuardianBiteRight.meshNum))
if (!(creature.Flags & 1) && item.TouchBits.Test(SpearGuardianBiteRight.BoneID))
{
DoDamage(creature.Enemy, damage);
CreatureEffect(&item, SpearGuardianBiteRight, DoBloodSplat);
@ -116,7 +116,7 @@ namespace TEN::Entities::Creatures::TR2
SoundEffect(SFX_TR2_CRUNCH2, &item.Pose);
}
if (!(creature.Flags & 2) && item.TouchBits.Test(SpearGuardianBiteLeft.meshNum))
if (!(creature.Flags & 2) && item.TouchBits.Test(SpearGuardianBiteLeft.BoneID))
{
DoDamage(creature.Enemy, damage);
CreatureEffect(&item, SpearGuardianBiteLeft, DoBloodSplat);

View file

@ -30,7 +30,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto SMALL_SPIDER_TURN_RATE_MAX = ANGLE(8.0f);
constexpr auto BIG_SPIDER_TURN_RATE_MAX = ANGLE(4.0f);
const auto SpiderBite = BiteInfo(Vector3(0.0f, 0.0f, 41.0f), 1);
const auto SpiderBite = CreatureBiteInfo(Vector3i(0, 0, 41), 1);
enum SpiderState
{
@ -72,7 +72,7 @@ namespace TEN::Entities::Creatures::TR2
void DoSpiderBloodEffect(ItemInfo& item)
{
auto pos = GetJointPosition(&item, SpiderBite.meshNum, SpiderBite.Position);
auto pos = GetJointPosition(&item, SpiderBite);
DoBloodSplat(pos.x, pos.y, pos.z, 10, item.Pose.Position.y, item.RoomNumber);
}

View file

@ -29,7 +29,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto SWORD_GUARDIAN_MESH_SWAP_TIME = 3;
const auto SwordBite = BiteInfo(Vector3(0.0f, 37.0f, 550.0f), 15);
const auto SwordBite = CreatureBiteInfo(Vector3i(0, 37, 550), 15);
enum SwordGuardianState
{
@ -352,7 +352,7 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = ai.angle;
}
if (!creature->Flags && item->TouchBits.Test(SwordBite.meshNum))
if (!creature->Flags && item->TouchBits.Test(SwordBite.BoneID))
{
DoDamage(creature->Enemy, SWORD_GUARDIAN_ATTACK_DAMAGE);
CreatureEffect(item, SwordBite, DoBloodSplat);

View file

@ -12,8 +12,8 @@
namespace TEN::Entities::Creatures::TR2
{
const auto WorkerDualGunBiteLeft = BiteInfo(Vector3(-2.0f, 275.0f, 23.0f), 6);
const auto WorkerDualGunBiteRight = BiteInfo(Vector3(2.0f, 275.0f, 23.0f), 10);
const auto WorkerDualGunBiteLeft = CreatureBiteInfo(Vector3i(-2, 340, 23), 6);
const auto WorkerDualGunBiteRight = CreatureBiteInfo(Vector3i(2, 340, 23), 10);
// TODO
enum WorkerDualGunState
@ -40,6 +40,11 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (creature->MuzzleFlash[1].Delay != 0)
creature->MuzzleFlash[1].Delay--;
if (item->HitPoints <= 0)
{
if (item->Animation.ActiveState != 11)
@ -223,9 +228,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (!creature->Flags)
if (creature->Flags == 0 && TestAnimFrame(*item, 0))
{
ShotLara(item, &AI, WorkerDualGunBiteLeft, extraTorsoRot.y, 50);
creature->MuzzleFlash[0].Bite = WorkerDualGunBiteLeft;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1;
}
@ -238,9 +245,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (!creature->Flags)
if (creature->Flags == 0 && TestAnimFrame(*item, 0))
{
ShotLara(item, &AI, WorkerDualGunBiteRight, extraTorsoRot.y, 50);
creature->MuzzleFlash[0].Bite = WorkerDualGunBiteRight;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1;
}
@ -269,10 +278,14 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (!creature->Flags)
if (creature->Flags == 0 && item->Animation.FrameNumber == GetFrameIndex(item, 0))
{
ShotLara(item, &AI, WorkerDualGunBiteLeft, extraTorsoRot.y, 50);
ShotLara(item, &AI, WorkerDualGunBiteRight, extraTorsoRot.y, 50);
creature->MuzzleFlash[0].Bite = WorkerDualGunBiteLeft;
creature->MuzzleFlash[0].Delay = 1;
creature->MuzzleFlash[1].Bite = WorkerDualGunBiteRight;
creature->MuzzleFlash[1].Delay = 1;
creature->Flags = 1;
}

View file

@ -26,7 +26,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto WORKER_FLAME_RUN_TURN_RATE_MAX = ANGLE(10.0f);
const auto WorkerFlamethrowerOffset = Vector3i(0, 140, 0);
const auto WorkerFlamethrowerBite = BiteInfo(Vector3(0.0f, 250.0f, 32.0f), 9);
const auto WorkerFlamethrowerBite = CreatureBiteInfo(Vector3(0.0f, 250.0f, 32.0f), 9);
enum WorkerFlamethrowerState
{
@ -70,8 +70,7 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
auto pos = GetJointPosition(item, WorkerFlamethrowerBite.meshNum, Vector3i(WorkerFlamethrowerBite.Position));
auto pos = GetJointPosition(item, WorkerFlamethrowerBite.BoneID, WorkerFlamethrowerBite.Position);
if (item->HitPoints <= 0)
{
if (item->Animation.ActiveState != WORKER_FLAME_STATE_DEATH)
@ -82,12 +81,12 @@ namespace TEN::Entities::Creatures::TR2
if (item->Animation.ActiveState != WORKER_FLAME_STATE_ATTACK && item->Animation.ActiveState != WORKER_FLAME_STATE_WALK_FORWARD_ATTACK)
{
TriggerDynamicLight(pos.x, pos.y, pos.z, (GetRandomControl() & 4) + 10, (GetRandomControl() & 7) + 128, (GetRandomControl() & 7) + 64, GetRandomControl() & 7);
TriggerPilotFlame(itemNumber, WorkerFlamethrowerBite.meshNum);
TriggerPilotFlame(itemNumber, WorkerFlamethrowerBite.BoneID);
}
else
{
TriggerDynamicLight(pos.x, pos.y, pos.z, (GetRandomControl() & 4) + 14, (GetRandomControl() & 7) + 128, (GetRandomControl() & 7) + 64, GetRandomControl() & 7);
ThrowFire(itemNumber, WorkerFlamethrowerBite.meshNum, WorkerFlamethrowerOffset, WorkerFlamethrowerOffset);
ThrowFire(itemNumber, WorkerFlamethrowerBite.BoneID, WorkerFlamethrowerOffset, WorkerFlamethrowerOffset);
}
AI_INFO AI;

View file

@ -13,16 +13,34 @@
namespace TEN::Entities::Creatures::TR2
{
const auto WorkerMachineGunBite = BiteInfo(Vector3(0.0f, 308.0f, 32.0f), 9);
const auto WorkerMachineGunBite = CreatureBiteInfo(Vector3i(0, 380, 37), 9);
// TODO
enum WorkerMachineGunState
{
};
// TODO
enum WorkerMachineGunAnim
{
};
void InitializeWorkerMachineGun(short itemNumber)
{
auto* item = &g_Level.Items[itemNumber];
InitializeCreature(itemNumber);
SetAnimation(item, 12);
}
static void Shoot(ItemInfo* item, CreatureInfo* creature, AI_INFO* ai, const EulerAngles& extraTorsoRot)
{
ShotLara(item, ai, WorkerMachineGunBite, extraTorsoRot.y, 30);
creature->MuzzleFlash[0].Bite = WorkerMachineGunBite;
creature->MuzzleFlash[0].Delay = 2;
}
void WorkerMachineGunControl(short itemNumber)
{
if (!CreatureActive(itemNumber))
@ -36,6 +54,9 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->HitPoints <= 0)
{
if (item->Animation.ActiveState != 7)
@ -191,13 +212,19 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (creature->Flags)
creature->Flags--;
else
if (item->Animation.AnimNumber == GetAnimIndex(*item, 2))
{
ShotLara(item, &AI, WorkerMachineGunBite, extraTorsoRot.y, 30);
creature->FiredWeapon = 1;
creature->Flags = 5;
if (item->Animation.FrameNumber == GetFrameIndex(item, 0))
Shoot(item, creature, &AI, extraTorsoRot);
else if (item->Animation.FrameNumber == GetFrameIndex(item, 6))
Shoot(item, creature, &AI, extraTorsoRot);
else if (item->Animation.FrameNumber == GetFrameIndex(item, 12))
Shoot(item, creature, &AI, extraTorsoRot);
}
else if (item->Animation.AnimNumber == GetAnimIndex(*item, 21) &&
item->Animation.FrameNumber == GetFrameIndex(item, 0))
{
Shoot(item, creature, &AI, extraTorsoRot);
}
if (item->Animation.TargetState != 1 &&
@ -215,14 +242,14 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (creature->Flags)
creature->Flags--;
else
{
ShotLara(item, &AI, WorkerMachineGunBite, extraTorsoRot.y, 30);
creature->FiredWeapon = 1;
creature->Flags = 5;
}
if (item->Animation.FrameNumber == GetFrameIndex(item, 0))
Shoot(item, creature, &AI, extraTorsoRot);
else if (item->Animation.FrameNumber == GetFrameIndex(item, 2))
Shoot(item, creature, &AI, extraTorsoRot);
else if (item->Animation.FrameNumber == GetFrameIndex(item, 6))
Shoot(item, creature, &AI, extraTorsoRot);
else if (item->Animation.FrameNumber == GetFrameIndex(item, 12))
Shoot(item, creature, &AI, extraTorsoRot);
break;
}

View file

@ -16,7 +16,7 @@ namespace TEN::Entities::Creatures::TR2
{
constexpr auto WORKER_SHOTGUN_NUM_SHOTS = 6;
const auto WorkerShotgunBite = BiteInfo(Vector3(0.0f, 281.0f, 40.0f), 9);
const auto WorkerShotgunBite = CreatureBiteInfo(Vector3i(0, 350, 40), 9);
// TODO
enum ShotgunWorkerState
@ -30,7 +30,7 @@ namespace TEN::Entities::Creatures::TR2
};
void ShotLaraWithShotgun(ItemInfo* item, AI_INFO* info, BiteInfo bite, short angleY, int damage)
void ShotLaraWithShotgun(ItemInfo* item, AI_INFO* info, const CreatureBiteInfo& bite, short angleY, int damage)
{
for (int i = 0; i < WORKER_SHOTGUN_NUM_SHOTS; i++)
ShotLara(item, info, bite, angleY, damage);
@ -57,12 +57,8 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
if (creature->FiredWeapon)
{
auto pos = GetJointPosition(item, WorkerShotgunBite.meshNum, Vector3i(WorkerShotgunBite.Position));
TriggerDynamicLight(pos.x, pos.y, pos.z, (creature->FiredWeapon * 2) + 4, 24, 16, 4);
creature->FiredWeapon--;
}
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->HitPoints <= 0)
{
@ -202,10 +198,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (!creature->Flags)
if (creature->Flags == 0)
{
ShotLaraWithShotgun(item, &AI, WorkerShotgunBite, extraTorsoRot.y, 25);
creature->FiredWeapon = 2;
creature->MuzzleFlash[0].Bite = WorkerShotgunBite;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1;
}
@ -224,10 +221,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle;
}
if (!creature->Flags)
if (creature->Flags == 0)
{
creature->MuzzleFlash[0].Bite = WorkerShotgunBite;
creature->MuzzleFlash[0].Delay = 1;
ShotLaraWithShotgun(item, &AI, WorkerShotgunBite, extraTorsoRot.y, 25);
creature->FiredWeapon = 2;
creature->Flags = 1;
}

View file

@ -16,8 +16,8 @@ using namespace TEN::Math;
namespace TEN::Entities::Creatures::TR2
{
const auto YetiBiteLeft = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 13);
const auto YetiBiteRight = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 10);
const auto YetiBiteLeft = CreatureBiteInfo(Vector3i(12, 101, 19), 13);
const auto YetiBiteRight = CreatureBiteInfo(Vector3i(12, 101, 19), 10);
const auto YetiAttackJoints1 = std::vector<unsigned int>{ 10, 12 }; // TODO: Rename.
const auto YetiAttackJoints2 = std::vector<unsigned int>{ 8, 9, 10 };

View file

@ -129,9 +129,6 @@ namespace TEN::Entities::Vehicles
skidooItem->Status = ITEM_ACTIVE;
}
if (skidooItem->ObjectNumber == ID_SNOWMOBILE_GUN)
skidoo->Armed = true;
skidoo->MomentumAngle = skidooItem->Pose.Orientation.y;
}
@ -204,6 +201,7 @@ namespace TEN::Entities::Vehicles
bool TestSkidooDismount(ItemInfo* skidooItem, ItemInfo* laraItem)
{
auto* lara = GetLaraInfo(laraItem);
auto* skidoo = GetSkidooInfo(skidooItem);
if (lara->Context.Vehicle != NO_ITEM)
{
@ -220,6 +218,8 @@ namespace TEN::Entities::Vehicles
laraItem->Pose.Orientation.x = 0;
laraItem->Pose.Orientation.z = 0;
lara->Control.HandStatus = HandStatus::Free;
if (skidoo->Armed)
lara->Control.Weapon.GunType = lara->Control.Weapon.LastGunType;
SetLaraVehicle(laraItem, nullptr);
}
else if (laraItem->Animation.ActiveState == SKIDOO_STATE_JUMP_OFF &&
@ -248,6 +248,9 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.IsAirborne = true;
lara->Control.MoveAngle = skidooItem->Pose.Orientation.y;
lara->Control.HandStatus = HandStatus::Free;
lara->Control.Weapon.GunType = lara->Control.Weapon.LastGunType;
if (skidoo->Armed)
lara->Control.Weapon.GunType = lara->Control.Weapon.LastGunType;
skidooItem->Collidable = false;
skidooItem->Flags |= IFLAG_INVISIBLE;
@ -663,7 +666,7 @@ namespace TEN::Entities::Vehicles
FindNewTarget(*laraItem, weapon);
AimWeapon(*laraItem, lara->RightArm, weapon);
if (TrInput & VEHICLE_IN_FIRE && !skidooItem->ItemFlags[0])
if (IsHeld(In::DrawWeapon) && !skidooItem->ItemFlags[0])
{
auto angles = EulerAngles(
lara->RightArm.Orientation.x,
@ -671,14 +674,13 @@ namespace TEN::Entities::Vehicles
0
);
if ((int)FireWeapon(LaraWeaponType::Pistol, *lara->TargetEntity, *laraItem, angles) +
(int)FireWeapon(LaraWeaponType::Pistol, *lara->TargetEntity, *laraItem, angles))
{
skidoo->FlashTimer = 2;
FireWeapon(LaraWeaponType::Snowmobile, *lara->TargetEntity, *laraItem, angles);
FireWeapon(LaraWeaponType::Snowmobile, *lara->TargetEntity, *laraItem, angles);
//lara->LeftArm.GunFlash = 1;
//lara->RightArm.GunFlash = 1;
SoundEffect(weapon.SampleNum, &laraItem->Pose);
skidooItem->ItemFlags[0] = 4;
}
}
if (skidooItem->ItemFlags[0])
skidooItem->ItemFlags[0]--;

View file

@ -8,6 +8,8 @@
#include "Specific/level.h"
#include "Specific/setup.h"
#include "Game/missile.h"
// Creatures
#include "Objects/TR2/Entity/tr2_barracuda.h" // OK
#include "Objects/TR2/Entity/tr2_bird_monster.h" // OK
@ -145,7 +147,6 @@ static void StartEntity(ObjectInfo* obj)
obj->control = SilencerControl;
obj->shadowType = ShadowMode::All;
obj->HitPoints = 25;
obj->biteOffset = 0;
obj->radius = 102;
obj->pivotLength = 50;
obj->intelligent = true;
@ -163,7 +164,6 @@ static void StartEntity(ObjectInfo* obj)
obj->control = SilencerControl;
obj->shadowType = ShadowMode::All;
obj->HitPoints = 25;
obj->biteOffset = 0;
obj->radius = 102;
obj->pivotLength = 50;
obj->intelligent = true;
@ -181,7 +181,6 @@ static void StartEntity(ObjectInfo* obj)
obj->control = SilencerControl;
obj->shadowType = ShadowMode::All;
obj->HitPoints = 25;
obj->biteOffset = 0;
obj->radius = 102;
obj->pivotLength = 50;
obj->intelligent = true;
@ -197,7 +196,6 @@ static void StartEntity(ObjectInfo* obj)
obj->collision = CreatureCollision;
obj->control = WorkerShotgunControl;
obj->shadowType = ShadowMode::All;
obj->biteOffset = 0;
obj->HitPoints = 25;
obj->pivotLength = 50;
obj->radius = 102;
@ -315,10 +313,6 @@ static void StartEntity(ObjectInfo* obj)
obj->SetupHitEffect();
}
obj = &Objects[ID_KNIFETHROWER_KNIFE];
if (obj->loaded)
obj->control = KnifeControl;
obj = &Objects[ID_MERCENARY_UZI];
if (obj->loaded)
{
@ -478,22 +472,23 @@ static void StartEntity(ObjectInfo* obj)
obj->pivotLength = 0;
obj->radius = 256;
obj->intelligent = true;
obj->LotType = LotType::SnowmobileGun;
obj->SetupHitEffect();
}
obj = &Objects[ID_SNOWMOBILE_DRIVER];
if (obj->loaded)
{
CheckIfSlotExists(ID_SNOWMOBILE_GUN, "ID_SNOWMOBILE_DRIVER", "ID_SNOWMOBILE_GUN");
obj->Initialize = InitializeSkidooMan;
obj->control = SkidooManControl;
obj->HitPoints = 1;
obj->SetupHitEffect(true);
}
}
static void StartObject(ObjectInfo* obj)
{
InitProjectile(obj, ControlMissile, ID_KNIFETHROWER_KNIFE);
}
static void StartTrap(ObjectInfo* obj)

View file

@ -35,7 +35,7 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto COMPY_PLAYER_ALERT_VELOCITY = 15;
constexpr auto COMPY_HIT_FLAG = 1;
const auto CompyBite = BiteInfo(Vector3::Zero, 2);
const auto CompyBite = CreatureBiteInfo(Vector3i::Zero, 2);
const auto CompyAttackJoints = std::vector<unsigned int>{ 1, 2 };
enum CompyState

View file

@ -27,9 +27,9 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto LIZARD_VAULT_SHIFT = 260;
const auto LizardBiteAttackBite = BiteInfo(Vector3(0.0f, -120.0f, 120.0f), 10);
const auto LizardSwipeAttackBite = BiteInfo(Vector3::Zero, 5);
const auto LizardGasBite = BiteInfo(Vector3(0.0f, -64.0f, 56.0f), 9);
const auto LizardBiteAttackBite = CreatureBiteInfo(Vector3i(0, -120, 120), 10);
const auto LizardSwipeAttackBite = CreatureBiteInfo(Vector3i::Zero, 5);
const auto LizardGasBite = CreatureBiteInfo(Vector3i(0, -64, 56), 9);
const auto LizardSwipeAttackJoints = std::vector<unsigned int>{ 5 };
const auto LizardBiteAttackJoints = std::vector<unsigned int>{ 10 };
@ -72,14 +72,13 @@ namespace TEN::Entities::Creatures::TR3
(g_Level.Boxes[creature.Enemy->BoxNumber].flags & BLOCKABLE));
}
static void SpawnLizardGas(int itemNumber, const BiteInfo& bite, int speed)
static void SpawnLizardGas(int itemNumber, const CreatureBiteInfo& bite, int speed)
{
static constexpr auto numPoisonThrows = 2;
for (int i = 0; i < numPoisonThrows; i++)
ThrowPoison(itemNumber, bite.meshNum, Vector3i(bite.Position), Vector3i(0.0f, -100.0f, speed << 2), Vector3(0.0f, 1.0f, 0.0f));
ThrowPoison(itemNumber, bite.meshNum, Vector3i(bite.Position), Vector3i(0.0f, -100.0f, speed << 1), Vector3(0.0f, 1.0f, 0.0f));
ThrowPoison(itemNumber, bite, Vector3i(0.0f, -100.0f, speed << 2), Vector3(0.0f, 1.0f, 0.0f));
ThrowPoison(itemNumber, bite, Vector3i(0.0f, -100.0f, speed << 1), Vector3(0.0f, 1.0f, 0.0f));
}
void LizardControl(short itemNumber)

View file

@ -36,8 +36,8 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto PUNA_EXPLOSION_MAIN_COLOR = Vector4(0.0f, 0.7f, 0.3f, 0.5f);
constexpr auto PUNA_EXPLOSION_SECOND_COLOR = Vector4(0.1f, 0.3f, 0.7f, 0.5f);
const auto PunaBossHeadBite = BiteInfo(Vector3::Zero, 8);
const auto PunaBossHandBite = BiteInfo(Vector3::Zero, 14);
const auto PunaBossHeadBite = CreatureBiteInfo(Vector3i::Zero, 8);
const auto PunaBossHandBite = CreatureBiteInfo(Vector3i::Zero, 14);
enum PunaState
{
@ -68,7 +68,7 @@ namespace TEN::Entities::Creatures::TR3
if (!item.TestFlags((int)BossItemFlags::Object, (short)BossFlagValue::Lizard))
return NO_ITEM;
auto pos = GetJointPosition(&item, PunaBossHeadBite.meshNum).ToVector3();
auto pos = GetJointPosition(&item, PunaBossHeadBite).ToVector3();
auto orient = Geometry::GetOrientToPoint(pos, target);
return (orient.y - item.Pose.Orientation.y);
}
@ -213,11 +213,11 @@ namespace TEN::Entities::Creatures::TR3
}
}
static void SpawnPunaLightning(ItemInfo& item, const Vector3& pos, const BiteInfo& bite, bool isSummon)
static void SpawnPunaLightning(ItemInfo& item, const Vector3& pos, const CreatureBiteInfo& bite, bool isSummon)
{
const auto& creature = *GetCreatureInfo(&item);
auto origin = GameVector(GetJointPosition(&item, bite.meshNum, bite.Position), item.RoomNumber);
auto origin = GameVector(GetJointPosition(&item, bite), item.RoomNumber);
if (isSummon)
{

View file

@ -33,8 +33,8 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto SHIVA_SWAPMESH_TIME = 3;
constexpr auto PLAYER_ANIM_SHIVA_DEATH = 7; // TODO: Move to LaraExtraAnims enum.
const auto ShivaBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 13);
const auto ShivaBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 22);
const auto ShivaBiteLeft = CreatureBiteInfo(Vector3i(0, 0, 920), 13);
const auto ShivaBiteRight = CreatureBiteInfo(Vector3i(0, 0, 920), 22);
const auto ShivaAttackLeftJoints = std::vector<unsigned int>{ 10, 13 };
const auto ShivaAttackRightJoints = std::vector<unsigned int>{ 22, 25 };

View file

@ -63,9 +63,9 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto SOPHIALEIGH_VAULT_SHIFT = 96;
const auto SophiaLeighStaffBite = BiteInfo(Vector3(-28.0f, 56.0f, 356.0f), 10);
const auto SophiaLeighLeftBite = BiteInfo(Vector3(-72.0f, 48.0f, 356.0f), 10);
const auto SophiaLeighRightBite = BiteInfo(Vector3(16.0f, 48.0f, 304.0f), 10);
const auto SophiaLeighStaffBite = CreatureBiteInfo(Vector3i(-28, 56, 356), 10);
const auto SophiaLeighLeftBite = CreatureBiteInfo(Vector3i(-72, 48, 356), 10);
const auto SophiaLeighRightBite = CreatureBiteInfo(Vector3i(16, 48, 304), 10);
struct SophiaData
{
@ -243,7 +243,7 @@ namespace TEN::Entities::Creatures::TR3
}
}
static void SpawnSophiaLeighProjectileBolt(ItemInfo& item, ItemInfo* enemy, const BiteInfo& bite, SophiaData* data, bool isBoltLarge, short angleAdd)
static void SpawnSophiaLeighProjectileBolt(ItemInfo& item, ItemInfo* enemy, const CreatureBiteInfo& bite, SophiaData* data, bool isBoltLarge, short angleAdd)
{
int fxNumber = CreateNewEffect(item.RoomNumber);
if (fxNumber == NO_ITEM)
@ -253,7 +253,7 @@ namespace TEN::Entities::Creatures::TR3
auto boltType = isBoltLarge ? (short)MissileType::SophiaLeighLarge : (short)MissileType::SophiaLeighNormal;
fx.pos.Position = GetJointPosition(&item, bite.meshNum, bite.Position);
fx.pos.Position = GetJointPosition(&item, bite);
fx.pos.Orientation.x = item.Pose.Orientation.x + data->torsoXAngle;
if (enemy->IsLara())

View file

@ -28,8 +28,8 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto WaspVenomSackLightColor = Vector4(0.0f, 0.35f, 0.0f, 1.0f);
const auto WaspBite = BiteInfo(Vector3(0.0f, 0.0f, -260.0f), 12);
const auto WaspVenomSackBite = BiteInfo(Vector3::Zero, 10);
const auto WaspBite = CreatureBiteInfo(Vector3i(0, 0, -260), 12);
const auto WaspVenomSackBite = CreatureBiteInfo(Vector3i::Zero, 10);
enum WaspMutantState
{
@ -112,7 +112,7 @@ namespace TEN::Entities::Creatures::TR3
item.ItemFlags[0] = 0;
// Spawn light.
auto pos = GetJointPosition(&item, WaspVenomSackBite.meshNum, WaspVenomSackBite.Position);
auto pos = GetJointPosition(&item, WaspVenomSackBite);
TriggerDynamicLight(
pos.x, pos.y, pos.z,
item.ItemFlags[0],
@ -276,7 +276,7 @@ namespace TEN::Entities::Creatures::TR3
item.Animation.RequiredState = WASP_STATE_FLY_FORWARD;
}
if (!creature.Flags && item.TouchBits.Test(WaspBite.meshNum))
if (!creature.Flags && item.TouchBits.Test(WaspBite.BoneID))
{
DoDamage(creature.Enemy, WASP_DAMAGE);
CreatureEffect2(&item, WaspBite, 10, item.Pose.Orientation.y, DoBloodSplat);

View file

@ -36,9 +36,9 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto CIVVY_TARGET_ALERT_VELOCITY = 10.0f;
constexpr auto CIVVY_VAULT_SHIFT = 260;
const auto CivvyBiteRight = BiteInfo(Vector3::Zero, 13);
const auto CivvyBiteLeft = BiteInfo(Vector3::Zero, 10);
const auto CivvyAttackJoints = std::vector<unsigned int>{ (unsigned int)CivvyBiteLeft.meshNum, (unsigned int)CivvyBiteRight.meshNum };
const auto CivvyBiteLeft = CreatureBiteInfo(Vector3i::Zero, 10);
const auto CivvyBiteRight = CreatureBiteInfo(Vector3i::Zero, 13);
const auto CivvyAttackJoints = std::vector<unsigned int>{ 10, 13 };
const auto CivvyExcludedTargets = std::vector<GAME_OBJECT_ID>
{

View file

@ -35,9 +35,9 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto CLAW_MUTANT_WALK_TURN_RATE_MAX = ANGLE(3.0f);
constexpr auto CLAW_MUTANT_RUN_TURN_RATE_MAX = ANGLE(4.0f);
const auto ClawMutantLeftBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 7);
const auto ClawMutantRightBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 4);
const auto ClawMutantTailBite = BiteInfo(Vector3(-32.0f, -16.0f, -119.0f), 13);
const auto ClawMutantLeftBite = CreatureBiteInfo(Vector3i(19, -13, 3), 7);
const auto ClawMutantRightBite = CreatureBiteInfo(Vector3i(19, -13, 3), 4);
const auto ClawMutantTailBite = CreatureBiteInfo(Vector3i(-32, -16, -119), 13);
enum ClawMutantState
{
@ -164,7 +164,7 @@ namespace TEN::Entities::Creatures::TR3
auto& fx = EffectList[plasmaBall];
auto jointPos = GetJointPosition(item, ClawMutantTailBite.meshNum, ClawMutantTailBite.Position);
auto jointPos = GetJointPosition(item, ClawMutantTailBite.BoneID, ClawMutantTailBite.Position);
auto orient = Geometry::GetOrientToPoint(jointPos.ToVector3(), enemyPos.ToVector3());
fx.pos.Position = jointPos;
@ -207,14 +207,14 @@ namespace TEN::Entities::Creatures::TR3
static void DamageTargetWithClaw(ItemInfo& source, ItemInfo& target)
{
if (source.ItemFlags[5] == 0 && source.TouchBits.Test(ClawMutantLeftBite.meshNum))
if (source.ItemFlags[5] == 0 && source.TouchBits.Test(ClawMutantLeftBite.BoneID))
{
DoDamage(&target, CLAW_MUTANT_CLAW_ATTACK_DAMAGE / 2);
CreatureEffect2(&source, ClawMutantLeftBite, 10, source.Pose.Orientation.y, DoBloodSplat);
source.ItemFlags[5] = 1;
}
if (source.ItemFlags[6] == 0 && source.TouchBits.Test(ClawMutantRightBite.meshNum))
if (source.ItemFlags[6] == 0 && source.TouchBits.Test(ClawMutantRightBite.BoneID))
{
DoDamage(&target, CLAW_MUTANT_CLAW_ATTACK_DAMAGE / 2);
CreatureEffect2(&source, ClawMutantRightBite, 10, source.Pose.Orientation.y, DoBloodSplat);

View file

@ -24,7 +24,7 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto COBRA_DISTURBANCE_VELOCITY = 15.0f;
constexpr auto COBRA_SLEEP_FRAME = 45;
const auto CobraBite = BiteInfo(Vector3::Zero, 13);
const auto CobraBite = CreatureBiteInfo(Vector3i::Zero, 13);
const auto CobraAttackJoints = std::vector<unsigned int>{ 13 };
enum CobraState

View file

@ -27,8 +27,7 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto FLAMETHROWER_WALK_TURN_RATE_MAX = ANGLE(5.0f);
const auto FlamethrowerOffset = Vector3i(0, 340, 0);
const auto FlamethrowerBite = BiteInfo(Vector3(0.0f, 340.0f, 64.0f), 7);
const auto FlamethrowerBite = CreatureBiteInfo(Vector3i(0, 340, 64), 7);
// TODO
enum FlamethrowerState
@ -64,10 +63,9 @@ namespace TEN::Entities::Creatures::TR3
short tilt = 0;
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
auto pos = GetJointPosition(item, FlamethrowerBite.meshNum, Vector3i(FlamethrowerBite.Position));
auto pos = GetJointPosition(item, FlamethrowerBite);
int randomInt = GetRandomControl();
if (item->Animation.ActiveState != 6 && item->Animation.ActiveState != 11)
{
TriggerDynamicLight(pos.x, pos.y, pos.z, (randomInt & 3) + 6, 24 - ((randomInt / 16) & 3), 16 - ((randomInt / 64) & 3), randomInt & 3);
@ -308,10 +306,10 @@ namespace TEN::Entities::Creatures::TR3
item->Animation.TargetState = FLAMETHROWER_STATE_IDLE;
if (creature->Flags < 40)
ThrowFire(itemNumber, FlamethrowerBite.meshNum, FlamethrowerOffset, Vector3i(0, creature->Flags * 1.5f, 0));
ThrowFire(itemNumber, FlamethrowerBite, Vector3i(0, creature->Flags * 1.5f, 0));
else
{
ThrowFire(itemNumber, FlamethrowerBite.meshNum, FlamethrowerOffset, Vector3i(0, (Random::GenerateInt() & 63) + 12, 0));
ThrowFire(itemNumber, FlamethrowerBite, Vector3i(0, (Random::GenerateInt() & 63) + 12, 0));
if (realEnemy)
{
/*code*/
@ -342,10 +340,10 @@ namespace TEN::Entities::Creatures::TR3
item->Animation.TargetState = FLAMETHROWER_STATE_WALK_FORWARD;
if (creature->Flags < 40)
ThrowFire(itemNumber, FlamethrowerBite.meshNum, FlamethrowerOffset, Vector3i(0, creature->Flags * 1.5f, 0));
ThrowFire(itemNumber, FlamethrowerBite, Vector3i(0, creature->Flags * 1.5f, 0));
else
{
ThrowFire(itemNumber, FlamethrowerBite.meshNum, FlamethrowerOffset, Vector3i(0, (GetRandomControl() & 63) + 12, 0));
ThrowFire(itemNumber, FlamethrowerBite, Vector3i(0, (GetRandomControl() & 63) + 12, 0));
if (realEnemy)
{
/*code*/

View file

@ -23,7 +23,7 @@ namespace TEN::Entities::Creatures::TR3
// TODO: Range constants.
const auto MonkeyBite = BiteInfo(Vector3(10.0f, 10.0f, 11.0f), 13);
const auto MonkeyBite = CreatureBiteInfo(Vector3i(10, 10, 11), 13);
const auto MonkeyAttackJoints = std::vector<unsigned int>{ 10, 13 };
enum MonkeyState

View file

@ -21,7 +21,7 @@ using namespace TEN::Math;
namespace TEN::Entities::Creatures::TR3
{
const auto MPGunBite = BiteInfo(Vector3(0.0f, 160.0f, 40.0f), 13);
const auto MPGunBite = CreatureBiteInfo(Vector3i(0, 225, 50), 13);
enum MPGunState
{
@ -67,13 +67,8 @@ namespace TEN::Entities::Creatures::TR3
short head = 0;
auto extraTorsoRot = EulerAngles::Zero;
if (creature->FiredWeapon)
{
auto pos = GetJointPosition(item, MPGunBite.meshNum, Vector3i(MPGunBite.Position));
TriggerDynamicLight(pos.x, pos.y, pos.z, (creature->FiredWeapon * 2) + 4, 24, 16, 4);
creature->FiredWeapon--;
}
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
{
@ -98,14 +93,14 @@ namespace TEN::Entities::Creatures::TR3
if (Targetable(item, &AI))
{
if (AI.angle > -ANGLE(45.0f) &&
AI.angle < ANGLE(45.0f))
if (AI.angle > -ANGLE(45.0f) && AI.angle < ANGLE(45.0f))
{
head = AI.angle;
extraTorsoRot.y = AI.angle;
ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32);
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
SoundEffect(SFX_TR3_OIL_SMG_FIRE, &item->Pose, SoundEnvironment::Land, 1.0f, 0.7f);
creature->FiredWeapon = 1;
}
}
}
@ -330,6 +325,12 @@ namespace TEN::Entities::Creatures::TR3
extraTorsoRot.y = AI.angle;
}
if (item->Animation.FrameNumber == GetFrameIndex(item, 0))
{
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 1;
}
if (item->Animation.RequiredState == MPGUN_STATE_WAIT)
item->Animation.TargetState = MPGUN_STATE_WAIT;
@ -346,6 +347,8 @@ namespace TEN::Entities::Creatures::TR3
{
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.TargetState = MPGUN_STATE_WAIT;
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
}
else if (item->HitStatus && Random::TestProbability(0.25f) && cover)
{
@ -368,6 +371,8 @@ namespace TEN::Entities::Creatures::TR3
{
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.TargetState = MPGUN_STATE_WAIT;
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
}
else if (item->HitStatus && Random::TestProbability(0.25f) && cover)
{
@ -391,6 +396,8 @@ namespace TEN::Entities::Creatures::TR3
{
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.RequiredState = MPGUN_STATE_WALK;
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
}
else if (item->HitStatus && Random::TestProbability(0.25f) && cover)
{
@ -418,6 +425,8 @@ namespace TEN::Entities::Creatures::TR3
{
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.TargetState = MPGUN_STATE_WALK;
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
}
if (AI.distance < pow(SECTOR(1.5f), 2))
@ -461,6 +470,8 @@ namespace TEN::Entities::Creatures::TR3
{
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32) || Random::TestProbability(1 / 8.0f))
item->Animation.TargetState = MPGUN_STATE_CROUCHED;
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
}
break;

View file

@ -18,8 +18,8 @@ using namespace TEN::Math;
namespace TEN::Entities::Creatures::TR3
{
const auto MPStickBite1 = BiteInfo(Vector3(247.0f, 10.0f, 11.0f), 13);
const auto MPStickBite2 = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 6);
const auto MPStickBite1 = CreatureBiteInfo(Vector3i(247, 10, 11), 13);
const auto MPStickBite2 = CreatureBiteInfo(Vector3i(0, 0, 100), 6);
const auto MPStickPunchAttackJoints = std::vector<unsigned int>{ 10, 13 };
const auto MPStickKickAttackJoints = std::vector<unsigned int>{ 5, 6 };

View file

@ -30,7 +30,7 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto RAPTOR_RUN_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto RAPTOR_ATTACK_TURN_RATE_MAX = ANGLE(2.0f);
const auto RaptorBite = BiteInfo(Vector3(0.0f, 66.0f, 318.0f), 22);
const auto RaptorBite = CreatureBiteInfo(Vector3i(0, 66, 318), 22);
const auto RaptorAttackJoints = std::vector<unsigned int>{ 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23 };
enum RaptorState

View file

@ -16,10 +16,9 @@
namespace TEN::Entities::Creatures::TR3
{
constexpr auto SCUBA_DIVER_ATTACK_DAMAGE = 50;
constexpr auto SCUBA_DIVER_SWIM_TURN_RATE_MAX = ANGLE(3.0f);
const auto ScubaGunBite = BiteInfo(Vector3(17.0f, 164.0f, 44.0f), 18);
const auto ScubaGunBite = CreatureBiteInfo(Vector3i(17, 164, 44), 18);
enum ScubaDiverState
{

View file

@ -34,7 +34,7 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto TIGER_PLAYER_ALERT_VELOCITY = 10.0f;
const auto TigerBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 26);
const auto TigerBite = CreatureBiteInfo(Vector3i(19, -13, 3), 26);
const auto TigerSwipeAttackJoints = std::vector<unsigned int>{ 14, 15, 16 };
const auto TigerBiteAttackJoints = std::vector<unsigned int>{ 22, 25, 26 };

View file

@ -29,8 +29,8 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto TONY_EXPLOSION_COUNT_MAX = 60;
constexpr auto TONY_EFFECT_COLOR = Vector4(0.8f, 0.4f, 0.0f, 0.5f);
const auto TonyLeftHandBite = BiteInfo(Vector3::Zero, 10);
const auto TonyRightHandBite = BiteInfo(Vector3::Zero, 13);
const auto TonyLeftHandBite = CreatureBiteInfo(Vector3i::Zero, 10);
const auto TonyRightHandBite = CreatureBiteInfo(Vector3i::Zero, 13);
// I can't set it to the TonyFlame struct since the control of the
// flame use fxNumber as argument or that FX_INFO have no void* to hold custom data.
@ -654,14 +654,14 @@ namespace TEN::Entities::Creatures::TR3
g = (g * bright) / 16;
b = (b * bright) / 16;
auto handPos = GetJointPosition(item, TonyLeftHandBite.meshNum);
auto handPos = GetJointPosition(item, TonyLeftHandBite);
TriggerTonyFlame(itemNumber, 13);
TriggerDynamicLight(handPos.x, handPos.y, handPos.z, 12, r, g, b);
if (item->Animation.ActiveState == TONY_STATE_SHOOT_CEILING ||
item->Animation.ActiveState == TONY_STATE_FLIPMAP)
{
handPos = GetJointPosition(item, TonyRightHandBite.meshNum);
handPos = GetJointPosition(item, TonyRightHandBite);
TriggerTonyFlame(itemNumber, 14);
TriggerDynamicLight(handPos.x, handPos.y, handPos.z, 12, r, g, b);
}

View file

@ -24,9 +24,9 @@ namespace TEN::Entities::Creatures::TR3
{
constexpr auto TRIBESMAN_DART_DAMAGE = -25; // NOTE: Negative value gives poison.
const auto TribesmanAxeBite = BiteInfo(Vector3(0.0f, 56.0f, 265.0f), 13);
const auto TribesmanDartBite1 = BiteInfo(Vector3(0.0f, 0.0f, -200.0f), 13);
const auto TribesmanDartBite2 = BiteInfo(Vector3(8.0f, 40.0f, -248.0f), 13);
const auto TribesmanAxeBite = CreatureBiteInfo(Vector3i(0, 56, 265), 13);
const auto TribesmanDartBite1 = CreatureBiteInfo(Vector3i(0, 0, -200), 13);
const auto TribesmanDartBite2 = CreatureBiteInfo(Vector3i(8, 40, -248), 13);
const auto TribesmanAxeAttackJoints = std::vector<unsigned int>{ 13 };
const auto TribesmanDartAttackJoints = std::vector<unsigned int>{ 10, 13 }; // TODO: Check.
@ -371,7 +371,7 @@ namespace TEN::Entities::Creatures::TR3
dartItem->ObjectNumber = ID_DARTS;
dartItem->RoomNumber = item->RoomNumber;
auto pos1 = GetJointPosition(item, TribesmanDartBite1.meshNum, Vector3i(TribesmanDartBite1.Position));
auto pos1 = GetJointPosition(item, TribesmanDartBite1);
auto pos2 = GetJointPosition(LaraItem, LM_TORSO);
auto orient = Geometry::GetOrientToPoint(pos1.ToVector3(), pos2.ToVector3());
@ -389,7 +389,7 @@ namespace TEN::Entities::Creatures::TR3
pos1 = Vector3i(TribesmanDartBite2.Position);
pos1.z += 96;
pos1 = GetJointPosition(item, TribesmanDartBite2.meshNum, pos1);
pos1 = GetJointPosition(item, TribesmanDartBite2.BoneID, pos1);
TriggerDartSmoke(pos1.x, pos1.y, pos1.z, 0, 0, true);
TriggerDartSmoke(pos1.x, pos1.y, pos1.z, 0, 0, true);

View file

@ -32,8 +32,8 @@ namespace TEN::Entities::Traps
SetAnimation(item, WALL_MOUNTED_BLADE_ANIM_IDLE);
// Used for GenericSphereBoxCollision.
item.ItemFlags[0] = WALL_MOUNTED_BLADE_JOINT;
item.ItemFlags[3] = WALL_MOUNTED_BLADE_HARM_DAMAGE;
item.ItemFlags[0] = WALL_MOUNTED_BLADE_JOINT; // Damaging mesh joint.
item.ItemFlags[3] = 0; // Set the Damage initially to 0 to avoid player being damaged when the blade is disabled.
item.ItemFlags[4] = 1; // NOTE: avoid the blade pushing lara in GenericSphereBoxCollision() !
}

View file

@ -47,7 +47,7 @@ namespace TEN::Entities::Vehicles
VehicleMountType::Back
};
const auto BigGunBite = BiteInfo(Vector3(0, 0, BGUN_ROCKET_SPAWN_DISTANCE), 2);
const auto BigGunBite = CreatureBiteInfo(Vector3i(0, 0, BGUN_ROCKET_SPAWN_DISTANCE), 2);
enum BigGunState
{
@ -125,7 +125,7 @@ namespace TEN::Entities::Vehicles
auto* projectileItem = &g_Level.Items[itemNumber];
projectileItem->ObjectNumber = ID_ROCKET;
auto pos = GetJointPosition(bigGunItem, BigGunBite.meshNum, BigGunBite.Position);
auto pos = GetJointPosition(bigGunItem, BigGunBite);
auto probe = GetCollision(pos.x, pos.y, pos.z, bigGunItem->RoomNumber);
projectileItem->RoomNumber = probe.RoomNumber;
projectileItem->Pose.Position = pos;

View file

@ -27,14 +27,14 @@ using namespace TEN::Math;
namespace TEN::Entities::Vehicles
{
BiteInfo QuadBikeEffectsPositions[6] =
const CreatureBiteInfo QuadBikeEffectsPositions[6] =
{
{ -56, -32, -380, 0 },
{ 56, -32, -380, 0 },
{ -8, 180, -48, 3 },
{ 8, 180, -48, 4 },
{ 90, 180, -32, 6 },
{ -90, 180, -32, 7 }
CreatureBiteInfo(Vector3i(-56, -32, -380), 0),
CreatureBiteInfo(Vector3i(56, -32, -380), 0),
CreatureBiteInfo(Vector3i(-8, 180, -48), 3),
CreatureBiteInfo(Vector3i(8, 180, -48), 4),
CreatureBiteInfo(Vector3i(90, 180, -32), 6),
CreatureBiteInfo(Vector3i(-90, 180, -32), 7)
};
const vector<VehicleMountType> QuadBikeMountTypes =
{
@ -1207,8 +1207,7 @@ namespace TEN::Entities::Vehicles
for (int i = 0; i < 2; i++)
{
auto pos = GetJointPosition(quadBikeItem, QuadBikeEffectsPositions[i].meshNum, Vector3i(QuadBikeEffectsPositions[i].Position));
auto pos = GetJointPosition(quadBikeItem, QuadBikeEffectsPositions[i]);
angle = quadBikeItem->Pose.Orientation.y + ((i == 0) ? 0x9000 : 0x7000);
if (quadBikeItem->Animation.Velocity.z > 32)
{

View file

@ -84,15 +84,16 @@ namespace TEN::Entities::Vehicles
#define UPV_LEAN_RATE ANGLE(0.6f)
#define UPV_LEAN_MAX ANGLE(10.0f)
BiteInfo UPVBites[6] =
const CreatureBiteInfo UPVBites[6] =
{
{ 0, 0, 0, 3 },
{ 0, 96, 256, 0 },
{ -128, 0, 64, 1 },
{ 0, 0, -64, 1 },
{ 128, 0, 64, 2 },
{ 0, 0, -64, 2 }
CreatureBiteInfo(Vector3i(0, 0, 0), 3),
CreatureBiteInfo(Vector3i(0, 96, 256), 0),
CreatureBiteInfo(Vector3i(-128, 0, 64), 1),
CreatureBiteInfo(Vector3i(0, 0, -64), 1),
CreatureBiteInfo(Vector3i(128, 0, 64), 2),
CreatureBiteInfo(Vector3i(0, 0, -64), 2)
};
const std::vector<VehicleMountType> UPVMountTypes =
{
VehicleMountType::LevelStart,
@ -304,7 +305,7 @@ namespace TEN::Entities::Vehicles
if (UPV->Velocity)
{
auto pos = GetJointPosition(UPVItem, UPVBites[UPV_BITE_TURBINE].meshNum, Vector3i(UPVBites[UPV_BITE_TURBINE].Position)).ToVector3();
auto pos = GetJointPosition(UPVItem, UPVBites[UPV_BITE_TURBINE]).ToVector3();
TriggerUPVMist(pos.x, pos.y + UPV_SHIFT, pos.z, abs(UPV->Velocity) / VEHICLE_VELOCITY_SCALE, UPVItem->Pose.Orientation.y + ANGLE(180.0f));
auto sphere = BoundingSphere(pos, BLOCK(1 / 32.0f));
@ -322,7 +323,7 @@ namespace TEN::Entities::Vehicles
for (int lp = 0; lp < 2; lp++)
{
int random = 31 - (GetRandomControl() & 3);
auto pos = GetJointPosition(UPVItem, UPVBites[UPV_BITE_FRONT_LIGHT].meshNum, Vector3i(
auto pos = GetJointPosition(UPVItem, UPVBites[UPV_BITE_FRONT_LIGHT].BoneID, Vector3i(
UPVBites[UPV_BITE_FRONT_LIGHT].Position.x,
UPVBites[UPV_BITE_FRONT_LIGHT].Position.y,
(int)UPVBites[UPV_BITE_FRONT_LIGHT].Position.z << (lp * 6)

View file

@ -242,7 +242,6 @@ static void StartEntity(ObjectInfo* obj)
obj->radius = 102;
obj->intelligent = true;
obj->pivotLength = 0;
obj->biteOffset = 0;
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
obj->SetBoneRotationFlags(13, ROT_Y);
obj->SetupHitEffect();

View file

@ -13,7 +13,6 @@
#include "Game/people.h"
#include "Game/room.h"
#include "Math/Math.h"
#include "Objects/Generic/Traps/traps.h"
#include "Objects/TR4/Entity/WraithInfo.h"
#include "Objects/objectslist.h"
#include "Sound/sound.h"

View file

@ -35,9 +35,9 @@ namespace TEN::Entities::TR4
constexpr auto AHMET_VIEW_ANGLE = ANGLE(45.0f);
constexpr auto AHMET_ENEMY_ANGLE = ANGLE(90.0f);
const auto AhmetBiteLeft = BiteInfo(Vector3::Zero, 16);
const auto AhmetBiteRight = BiteInfo(Vector3::Zero, 22);
const auto AhmetBiteJaw = BiteInfo(Vector3::Zero, 11);
const auto AhmetBiteLeft = CreatureBiteInfo(Vector3i::Zero, 16);
const auto AhmetBiteRight = CreatureBiteInfo(Vector3i::Zero, 22);
const auto AhmetBiteJaw = CreatureBiteInfo(Vector3i::Zero, 11);
const auto AhmetSwipeAttackLeftJoints = std::vector<unsigned int>{ 14, 15, 16, 17 };
const auto AhmetSwipeAttackRightJoints = std::vector<unsigned int>{ 20, 21, 22, 23 };

View file

@ -39,7 +39,7 @@ namespace TEN::Entities::TR4
constexpr auto NO_BABOON_COUNT = -2;
constexpr auto NO_CROWBAR_SWITCH_FOUND = -1;
const auto BaboonBite = BiteInfo(Vector3(10.0f, 10.0f, 11.0f), 4);
const auto BaboonBite = CreatureBiteInfo(Vector3i(10, 10, 11), 4);
const auto BaboonAttackJoints = std::vector<unsigned int>{ 11, 12 };
const auto BaboonAttackRightJoints = std::vector<unsigned int>{ 1, 2, 3, 5, 8, 9 };
const auto BaboonJumpAttackJoints = std::vector<unsigned int>{ 3, 4, 8 };

View file

@ -54,8 +54,8 @@ namespace TEN::Entities::TR4
{
constexpr auto BADDY_UZI_AMMO = 24;
const auto BaddyGunBite = BiteInfo(Vector3(0.0f, -16.0f, 200.0f), 11);
const auto BaddySwordBite = BiteInfo(Vector3::Zero, 15);
const auto BaddyGunBite = CreatureBiteInfo(Vector3i(-5, 200, 50), 11);
const auto BaddySwordBite = CreatureBiteInfo(Vector3i::Zero, 15);
const auto BaddySwordAttackJoints = std::vector<unsigned int>{ 14, 15, 16 };
enum BaddyState
@ -330,6 +330,9 @@ namespace TEN::Entities::TR4
// TODO: better add a second control routine for baddy 2 instead of mixing them?
short objectNumber = (Objects[ID_BADDY2].loaded ? ID_BADDY2 : ID_BADDY1);
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
bool roll = false;
bool jump = false;
@ -419,14 +422,6 @@ namespace TEN::Entities::TR4
item->ItemFlags[1] = item->RoomNumber;
// Handle baddy firing.
if (creature->FiredWeapon)
{
auto pos = GetJointPosition(item, BaddyGunBite.meshNum, Vector3i(BaddyGunBite.Position));
TriggerDynamicLight(pos.x, pos.y, pos.z, 4 * creature->FiredWeapon + 8, 24, 16, 4);
creature->FiredWeapon--;
}
CollisionResult probe;
if (item->HitPoints <= 0)
@ -1158,13 +1153,13 @@ namespace TEN::Entities::TR4
break;
}
creature->FiredWeapon = 1;
if (!item->HitStatus)
item->ItemFlags[2]--;
if (!ShotLara(item, &AI, BaddyGunBite, joint1, 15))
item->Animation.TargetState = BADDY_STATE_IDLE;
creature->MuzzleFlash[0].Bite = BaddyGunBite;
creature->MuzzleFlash[0].Delay = 2;
break;

View file

@ -24,7 +24,7 @@ namespace TEN::Entities::TR4
constexpr auto BAT_ANGLE = ANGLE(20.0f);
const auto BatBite = BiteInfo(Vector3(0.0f, 16.0f, 45.0f), 4);
const auto BatBite = CreatureBiteInfo(Vector3i(0, 16, 45), 4);
enum BatState
{

View file

@ -21,7 +21,7 @@ namespace TEN::Entities::TR4
constexpr auto BIG_BEETLE_ATTACK_RANGE = SQUARE(CLICK(1));
constexpr auto BIG_BEETLE_AWARE_RANGE = SQUARE(CLICK(12));
const auto BigBeetleBite = BiteInfo(Vector3::Zero, 12);
const auto BigBeetleBite = CreatureBiteInfo(Vector3i::Zero, 12);
const auto BigBeetleAttackJoints = std::vector<unsigned int>{ 5, 6 };
enum BigBeetleState

View file

@ -26,12 +26,10 @@ namespace TEN::Entities::TR4
constexpr auto BIG_SCORPION_ATTACK_RANGE = SQUARE(BLOCK(1.35));
constexpr auto BIG_SCORPION_RUN_RANGE = SQUARE(BLOCK(2));
const auto BigScorpionBite1 = BiteInfo(Vector3::Zero, 8);
const auto BigScorpionBite2 = BiteInfo(Vector3::Zero, 23);
const auto BigScorpionBite1 = CreatureBiteInfo(Vector3i::Zero, 8);
const auto BigScorpionBite2 = CreatureBiteInfo(Vector3i::Zero, 23);
const auto BigScorpionAttackJoints = std::vector<unsigned int>{ 8, 20, 21, 23, 24 };
int CutSeqNum;
enum BigScorpionState
{
// No state 0.
@ -87,7 +85,6 @@ namespace TEN::Entities::TR4
{
if (item->TriggerFlags > 0 && item->TriggerFlags < 7)
{
CutSeqNum = 4;
SetAnimation(item, BSCORPION_ANIM_DEATH);
item->Status = ITEM_INVISIBLE;
creature->MaxTurn = 0;
@ -114,11 +111,6 @@ namespace TEN::Entities::TR4
SetAnimation(item, BSCORPION_ANIM_DEATH);
}
}
else if (CutSeqNum == 4)
{
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameEnd - 1;
item->Status = ITEM_INVISIBLE;
}
else if (item->Animation.ActiveState == BSCORPION_STATE_DEATH && item->Status == ITEM_INVISIBLE)
item->Status = ITEM_ACTIVE;
}
@ -287,10 +279,7 @@ namespace TEN::Entities::TR4
}
}
if (!CutSeqNum)
CreatureAnimation(itemNumber, angle, 0);
auto radius = Vector2(object->radius, object->radius * 1.33f);
AlignEntityToSurface(item, radius);
AlignEntityToSurface(item, Vector2(object->radius, object->radius * 1.33f));
}
}

View file

@ -33,7 +33,7 @@ namespace TEN::Entities::TR4
constexpr auto CROC_STATE_RUN_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto CROC_STATE_SWIM_TURN_RATE_MAX = ANGLE(3.0f);
const auto CrocodileBite = BiteInfo(Vector3(0.0f, -100.0f, 500.0f), 9);
const auto CrocodileBite = CreatureBiteInfo(Vector3i(0, -100, 500), 9);
const auto CrocodileBiteAttackJoints = std::vector<unsigned int>{ 8, 9 };
enum CrocodileState
@ -115,7 +115,7 @@ namespace TEN::Entities::TR4
auto* item = &g_Level.Items[itemNumber];
auto* object = &Objects[item->ObjectNumber];
auto* creature = GetCreatureInfo(item);
auto head = EulerAngles::Zero, torso = EulerAngles::Zero;
short angle = 0;
short boneAngle = 0;
AI_INFO AI;
@ -298,28 +298,26 @@ namespace TEN::Entities::TR4
}
}
OBJECT_BONES boneRot;
if (item->Animation.ActiveState == CROC_STATE_IDLE || item->Animation.ActiveState == CROC_STATE_BITE_ATTACK || item->Animation.ActiveState == CROC_STATE_WATER_BITE_ATTACK)
{
boneRot.bone0 = AI.angle / 3;
boneRot.bone1 = AI.angle / 2;
boneRot.bone2 = 0;
boneRot.bone3 = 0;
head.y = AI.angle / 3;
head.x = AI.angle / 2;
torso.y = 0;
torso.x = 0;
}
else
{
boneRot.bone0 = boneAngle;
boneRot.bone1 = boneAngle;
boneRot.bone2 = -boneAngle;
boneRot.bone3 = -boneAngle;
head.y = boneAngle;
head.x = boneAngle;
torso.y = -boneAngle;
torso.x = -boneAngle;
}
CreatureTilt(item, 0);
CreatureJoint(item, 0, boneRot.bone0);
CreatureJoint(item, 1, boneRot.bone1);
CreatureJoint(item, 2, boneRot.bone2);
CreatureJoint(item, 3, boneRot.bone3);
CreatureJoint(item, 0, head.y);
CreatureJoint(item, 1, head.x);
CreatureJoint(item, 2, torso.y);
CreatureJoint(item, 3, torso.x);
CreatureAnimation(itemNumber, angle, 0);
if (item->Animation.ActiveState < CROC_STATE_SWIM_FORWARD)

View file

@ -22,7 +22,7 @@ namespace TEN::Entities::TR4
constexpr auto DOG_BITE_ATTACK_RANGE = SQUARE(BLOCK(0.55));
constexpr auto DOG_JUMP_ATTACK_RANGE = SQUARE(BLOCK(1));
const auto DogBite = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 3.0f);
const auto DogBite = CreatureBiteInfo(Vector3i(0, 0, 100), 3);
const auto DogJumpAttackJoints = std::vector<unsigned int>{ 3, 6, 9, 10, 13, 14 };
const auto DogBiteAttackJoints = std::vector<unsigned int>{ 3, 6 };

View file

@ -18,8 +18,8 @@ namespace TEN::Entities::TR4
{
constexpr auto GUIDE_ATTACK_DAMAGE = 20;
const auto GuideBite1 = BiteInfo(Vector3(0.0f, 20.0f, 180.0f), 18);
const auto GuideBite2 = BiteInfo(Vector3(30.0f, 80.0f, 50.0f), 15);
const auto GuideBite1 = CreatureBiteInfo(Vector3i(0, 20, 180), 18);
const auto GuideBite2 = CreatureBiteInfo(Vector3i(30, 80, 50), 15);
const auto GuideLeftFingerSwapJoints = std::vector<unsigned int>{ 15 };
const auto GuideRightHandSwapJoints = std::vector<unsigned int>{ 18 };
const auto GuideHeadSwapJoints = std::vector<unsigned int>{ 21 };
@ -119,7 +119,7 @@ namespace TEN::Entities::TR4
// Ignite torch.
if (item->ItemFlags[1] == 2)
{
auto pos = GetJointPosition(item, GuideBite1.meshNum, Vector3i(GuideBite1.Position));
auto pos = GetJointPosition(item, GuideBite1);
TriggerFireFlame(pos.x, pos.y - 20, pos.z, FlameType::Trail);
SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->Pose);
@ -479,7 +479,7 @@ namespace TEN::Entities::TR4
break;
case GUIDE_STATE_IGNITE_TORCH:
pos1 = GetJointPosition(item, GuideBite2.meshNum, Vector3i(GuideBite2.Position));
pos1 = GetJointPosition(item, GuideBite2);
frameNumber = item->Animation.FrameNumber - g_Level.Anims[item->Animation.AnimNumber].frameBase;
random = GetRandomControl();

View file

@ -16,7 +16,7 @@ namespace TEN::Entities::TR4
constexpr auto HAMMERHEAD_BITE_ATTACK_DAMAGE = 120;
constexpr auto HAMMERHEAD_ATTACK_RANGE = SQUARE(BLOCK(0.66f));
const auto HammerheadBite = BiteInfo(Vector3::Zero, 12);
const auto HammerheadBite = CreatureBiteInfo(Vector3i::Zero, 12);
const auto HammerheadBiteAttackJoints = std::vector<unsigned int>{ 10, 12, 13 };
enum HammerheadState

View file

@ -30,11 +30,11 @@ namespace TEN::Entities::TR4
constexpr auto HARPY_SWOOP_ATTACK_DAMAGE = 10;
constexpr auto HARPY_STINGER_POISON_POTENCY = 8;
const auto HarpyBite1 = BiteInfo(Vector3::Zero, 4);
const auto HarpyBite2 = BiteInfo(Vector3::Zero, 2);
const auto HarpyBite3 = BiteInfo(Vector3::Zero, 15);
const auto HarpyAttack1 = BiteInfo(Vector3(0.0f, 128.0f, 0.0f), 2);
const auto HarpyAttack2 = BiteInfo(Vector3(0.0f, 128.0f, 0.0f), 4);
const auto HarpyBite1 = CreatureBiteInfo(Vector3i::Zero, 4);
const auto HarpyBite2 = CreatureBiteInfo(Vector3i::Zero, 2);
const auto HarpyBite3 = CreatureBiteInfo(Vector3i::Zero, 15);
const auto HarpyAttack1 = CreatureBiteInfo(Vector3i(0, 128, 0), 2);
const auto HarpyAttack2 = CreatureBiteInfo(Vector3i(0, 128, 0), 4);
const auto HarpySwoopAttackJoints = std::vector<unsigned int>{ 2, 4, 15 };
const auto HarpyStingerAttackJoints = std::vector<unsigned int>{ 2, 4 };
@ -105,8 +105,8 @@ namespace TEN::Entities::TR4
{
item->ItemFlags[0]++;
auto rh = GetJointPosition(item, HarpyAttack1.meshNum, Vector3i(HarpyAttack1.Position));
auto lr = GetJointPosition(item, HarpyAttack2.meshNum, Vector3i(HarpyAttack2.Position));
auto rh = GetJointPosition(item, HarpyAttack1);
auto lr = GetJointPosition(item, HarpyAttack2);
int sG = (GetRandomControl() & 0x7F) + 32;
int sR = sG;
@ -149,7 +149,7 @@ namespace TEN::Entities::TR4
{
if (item->ItemFlags[0] <= 65 && GlobalCounter & 1)
{
auto pos3 = GetJointPosition(item, HarpyAttack1.meshNum, Vector3i(HarpyAttack1.Position.x, HarpyAttack1.Position.y * 2, HarpyAttack1.Position.z));
auto pos3 = GetJointPosition(item, HarpyAttack1.BoneID, Vector3i(HarpyAttack1.Position.x, HarpyAttack1.Position.y * 2, HarpyAttack1.Position.z));
auto orient = Geometry::GetOrientToPoint(lr.ToVector3(), rh.ToVector3());
auto pose = Pose(rh, orient);
TriggerHarpyMissile(&pose, item->RoomNumber, 2);
@ -157,7 +157,7 @@ namespace TEN::Entities::TR4
if (item->ItemFlags[0] >= 61 && item->ItemFlags[0] <= 65 && !(GlobalCounter & 1))
{
auto pos3 = GetJointPosition(item, HarpyAttack2.meshNum, Vector3i(HarpyAttack2.Position.x, HarpyAttack2.Position.y * 2, HarpyAttack2.Position.z));
auto pos3 = GetJointPosition(item, HarpyAttack2.BoneID, Vector3i(HarpyAttack2.Position.x, HarpyAttack2.Position.y * 2, HarpyAttack2.Position.z));
auto orient = Geometry::GetOrientToPoint(lr.ToVector3(), rh.ToVector3());
auto pose = Pose(rh, orient);
TriggerHarpyMissile(&pose, item->RoomNumber, 2);

View file

@ -20,17 +20,16 @@ using namespace TEN::Math;
namespace TEN::Entities::TR4
{
const auto HorsemanBite1 = BiteInfo(Vector3::Zero, 6);
const auto HorsemanBite2 = BiteInfo(Vector3::Zero, 14);
const auto HorsemanBite3 = BiteInfo(Vector3::Zero, 10);
const auto HorsemanBite1 = CreatureBiteInfo(Vector3i::Zero, 6);
const auto HorsemanBite2 = CreatureBiteInfo(Vector3i::Zero, 14);
const auto HorsemanBite3 = CreatureBiteInfo(Vector3i::Zero, 10);
const auto HorsemanAxeAttackJoints = std::vector<unsigned int>{ 5, 6 };
const auto HorsemanKickAttackJoints = std::vector<unsigned int>{ 14 };
const auto HorsemanMountedAttackJoints = std::vector<unsigned int>{ 5, 6, 10 };
const auto HorsemanShieldAttackJoints = std::vector<unsigned int>{ 10 };
const auto HorseBite1 = BiteInfo(Vector3::Zero, 13);
const auto HorseBite2 = BiteInfo(Vector3::Zero, 17);
const auto HorseBite3 = BiteInfo(Vector3::Zero, 19);
const auto HorseBite1 = CreatureBiteInfo(Vector3i::Zero, 13);
const auto HorseBite2 = CreatureBiteInfo(Vector3i::Zero, 17);
const auto HorseBite3 = CreatureBiteInfo(Vector3i::Zero, 19);
enum HorsemanState
{

View file

@ -24,7 +24,7 @@ namespace TEN::Entities::TR4
constexpr auto KTEMPLAR_IDLE_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto KTEMPLAR_WALK_TURN_RATE_MAX = ANGLE(7.0f);
const auto KnightTemplarBite = BiteInfo(Vector3::Zero, 11);
const auto KnightTemplarBite = CreatureBiteInfo(Vector3i::Zero, 11);
const auto KnightTemplarSwordAttackJoints = std::vector<unsigned int>{ 10, 11 };
enum KnightTemplarState

View file

@ -28,8 +28,8 @@ namespace TEN::Entities::TR4
constexpr auto MUMMY_WALK_TURN_RATE_MAX = ANGLE(7.0f);
constexpr auto MUMMY_ATTACK_TURN_RATE_MAX = ANGLE(7.0f);
const auto MummyBite1 = BiteInfo(Vector3::Zero, 11);
const auto MummyBite2 = BiteInfo(Vector3::Zero, 14);
const auto MummyBite1 = CreatureBiteInfo(Vector3i::Zero, 11);
const auto MummyBite2 = CreatureBiteInfo(Vector3i::Zero, 14);
const auto MummySwipeAttackJoints = std::vector<unsigned int>{ 11, 14 };
enum MummyState

View file

@ -271,7 +271,7 @@ namespace TEN::Entities::TR4
auto* item = &g_Level.Items[itemNumber];
auto* creature = GetCreatureInfo(item);
OBJECT_BONES mutantJoint;
auto head = EulerAngles::Zero, torso = EulerAngles::Zero;
int frameNumber;
short angle = 0;
short headY = 0;
@ -286,7 +286,7 @@ namespace TEN::Entities::TR4
AI_INFO AI;
MutantAIFix(item, &AI);
RotateHeadToTarget(item, creature, 9, headY);
RotateHeadToTarget(item, creature, 9, head.y);
GetCreatureMood(item, &AI, true);
CreatureMood(item, &AI, true);
@ -350,14 +350,16 @@ namespace TEN::Entities::TR4
}
if (item->Animation.ActiveState != MUTANT_STATE_LOCUST_ATTACK_1)
mutantJoint = OBJECT_BONES(headY, AI.xAngle, true);
else
mutantJoint = OBJECT_BONES(0);
{
head.x = AI.xAngle;
torso.x = AI.xAngle;
torso.y = AI.angle;
}
CreatureJoint(item, 0, mutantJoint.bone0);
CreatureJoint(item, 1, mutantJoint.bone1);
CreatureJoint(item, 2, mutantJoint.bone2);
CreatureJoint(item, 3, mutantJoint.bone3);
CreatureJoint(item, 0, head.y);
CreatureJoint(item, 1, head.x);
CreatureJoint(item, 2, torso.y);
CreatureJoint(item, 3, torso.x);
CreatureAnimation(itemNumber, angle, 0);
}
}

View file

@ -34,7 +34,7 @@ namespace TEN::Entities::TR4
constexpr auto SAS_WALK_RANGE = SQUARE(BLOCK(2));
constexpr auto SAS_SHOOT_RANGE = SQUARE(BLOCK(3));
const auto SasGunBite = BiteInfo(Vector3(0.0f, 550.0f, 84.0f), 7);
const auto SasGunBite = CreatureBiteInfo(Vector3i(0, 420, 80), 7);
const auto SasDragBodyPosition = Vector3i(0, 0, -460);
const auto SasDragBounds = ObjectCollisionBounds
@ -145,13 +145,8 @@ namespace TEN::Entities::TR4
short joint1 = 0;
short joint2 = 0;
// Handle SAS firing.
if (creature.FiredWeapon)
{
auto pos = GetJointPosition(&item, SasGunBite.meshNum, Vector3i(SasGunBite.Position));
TriggerDynamicLight(pos.x, pos.y, pos.z, 10, 24, 16, 4);
creature.FiredWeapon--;
}
if (creature.MuzzleFlash[0].Delay != 0)
creature.MuzzleFlash[0].Delay--;
if (item.HitPoints > 0)
{
@ -522,14 +517,15 @@ namespace TEN::Entities::TR4
joint1 = AI.xAngle;
}
if (creature.Flags)
if (creature.Flags != 0)
{
creature.Flags -= 1;
}
else
{
ShotLara(&item, &AI, SasGunBite, joint0, SAS_SHOT_DAMAGE);
creature.FiredWeapon = 3;
creature.MuzzleFlash[0].Bite = SasGunBite;
creature.MuzzleFlash[0].Delay = 2;
creature.Flags = 5;
}
@ -540,9 +536,6 @@ namespace TEN::Entities::TR4
item.Animation.TargetState = SAS_STATE_WAIT;
break;
default:
break;
}
if (FlashGrenadeAftershockTimer > 100 &&
@ -659,7 +652,7 @@ namespace TEN::Entities::TR4
grenadeItem->ObjectNumber = ID_GRENADE;
grenadeItem->RoomNumber = item.RoomNumber;
auto pos = GetJointPosition(&item, SasGunBite.meshNum, Vector3i(SasGunBite.Position));
auto pos = GetJointPosition(&item, SasGunBite);
grenadeItem->Pose.Position = pos;
auto floorHeight = GetCollision(pos.x, pos.y, pos.z, grenadeItem->RoomNumber).Position.Floor;

View file

@ -21,7 +21,7 @@ using namespace TEN::Gui;
namespace TEN::Entities::TR4
{
const auto SentryGunFlameOffset = Vector3i(-140, 0, 0);
const auto SentryGunBite = BiteInfo(Vector3::Zero, 8);
const auto SentryGunBite = CreatureBiteInfo(Vector3i::Zero, 8);
void InitializeSentryGun(short itemNumber)
{
@ -51,7 +51,7 @@ namespace TEN::Entities::TR4
{
if (item->ItemFlags[0])
{
auto pos = GetJointPosition(item, SentryGunBite.meshNum, Vector3i(SentryGunBite.Position));
auto pos = GetJointPosition(item, SentryGunBite);
TriggerDynamicLight(pos.x, pos.y, pos.z, 4 * item->ItemFlags[0] + 12, 24, 16, 4);
item->ItemFlags[0]--;
}

Some files were not shown because too many files have changed in this diff Show more