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 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 TR1 Cowboy.
- Add TR3 Wall mounted blade. - Add TR3 Wall mounted blade.
- Add TR3 Claw mutant. - Add TR3 Claw mutant.
- Add removable puzzles from puzzle holes and puzzle dones. - 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. - 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). - Add missing gunflash for some entities, also include dynamic light and smoke to all gunflashes.
- Fix spiky wall speed value. - Add reusable keys for key holes
- Change speed by giving an OCB number to it or lua (Moveable::SetItemFlags[0]). - Employed by setting the trigger type as "Switch" for key hole.
- Fix spiky wall being too slow when triggered. - 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: Lua API changes:
* Add function String::SetTranslated() * Add function String::SetTranslated()

View file

@ -277,7 +277,7 @@ Timer = {
--- Get the total time for a 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 -- 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 -- @treturn float the timer's total time
GetTotalTime = function(t) GetTotalTime = function(t)
return LevelVars.Engine.Timer.timers[t.name].totalTime return LevelVars.Engine.Timer.timers[t.name].totalTime

View file

@ -21,7 +21,8 @@ namespace TEN::Hud
{ {
void StatusBar::Initialize(float value) 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) void StatusBar::Update(float value)

View file

@ -280,7 +280,7 @@ WeaponInfo Weapons[(int)LaraWeaponType::NumWeapons] =
BLOCK(8), BLOCK(8),
3, 3,
0, 0,
0, 2,
0, 0,
SFX_TR4_UZI_FIRE, SFX_TR4_UZI_FIRE,
0 0
@ -781,6 +781,7 @@ void AimWeapon(ItemInfo& laraItem, ArmInfo& arm, const WeaponInfo& weaponInfo)
arm.Orientation.InterpolateConstant(targetArmOrient, weaponInfo.AimSpeed); 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) FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo& targetEntity, ItemInfo& laraItem, const EulerAngles& armOrient)
{ {
auto& player = *GetLaraInfo(&laraItem); auto& player = *GetLaraInfo(&laraItem);

View file

@ -664,6 +664,16 @@ Vector3i GetJointPosition(ItemInfo* item, int jointIndex, const Vector3i& relOff
return GetJointPosition(*item, jointIndex, relOffset); 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) Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex)
{ {
const auto& object = Objects[objectID]; const auto& object = Objects[objectID];

View file

@ -10,6 +10,7 @@ class Pose;
class Vector3i; class Vector3i;
struct ItemInfo; struct ItemInfo;
struct ObjectInfo; struct ObjectInfo;
struct CreatureBiteInfo;
// NOTES: // NOTES:
// animNumber: Relative animation number. // 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(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, 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); Vector3 GetJointOffset(GAME_OBJECT_ID objectID, int jointIndex);
Quaternion GetBoneOrientation(const ItemInfo& item, int boneIndex); Quaternion GetBoneOrientation(const ItemInfo& item, int boneIndex);
float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex); float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex);

View file

@ -7,6 +7,7 @@
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/control/control.h" #include "Game/control/control.h"
#include "Game/control/lot.h" #include "Game/control/lot.h"
#include "Game/effects/smoke.h"
#include "Game/effects/tomb4fx.h" #include "Game/effects/tomb4fx.h"
#include "Game/itemdata/creature_info.h" #include "Game/itemdata/creature_info.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
@ -21,6 +22,8 @@
#include "Objects/TR5/Object/tr5_pushableblock.h" #include "Objects/TR5/Object/tr5_pushableblock.h"
#include "Renderer/Renderer11.h" #include "Renderer/Renderer11.h"
using namespace TEN::Effects::Smoke;
constexpr auto ESCAPE_DIST = SECTOR(5); constexpr auto ESCAPE_DIST = SECTOR(5);
constexpr auto STALK_DIST = SECTOR(3); constexpr auto STALK_DIST = SECTOR(3);
constexpr auto REACHED_GOAL_RADIUS = 640; 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 FEELER_ANGLE = ANGLE(45.0f);
constexpr auto CREATURE_AI_ROTATION_MAX = ANGLE(90.0f); constexpr auto CREATURE_AI_ROTATION_MAX = ANGLE(90.0f);
constexpr auto CREATURE_JOINT_ROTATION_MAX = ANGLE(70.0f); constexpr auto CREATURE_JOINT_ROTATION_MAX = ANGLE(70.0f);
constexpr auto GUN_EFFECT_CREATURE_YSHIFT = 75;
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION #ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
constexpr int HIGH_PRIO_RANGE = 8; constexpr int HIGH_PRIO_RANGE = 8;
@ -577,15 +581,15 @@ void CreatureKill(ItemInfo* creatureItem, int creatureAnimNumber, int playerAnim
Camera.targetElevation = -ANGLE(25.0f); 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); 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); 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; 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) bool CreatureAnimation(short itemNumber, short angle, short tilt)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
if (!item->IsCreature()) if (!item->IsCreature())
return false; return false;
auto* creature = GetCreatureInfo(item);
PlayGunEffectForCreature(item, creature->MuzzleFlash[0]);
PlayGunEffectForCreature(item, creature->MuzzleFlash[1]);
auto prevPos = item->Pose.Position; auto prevPos = item->Pose.Position;

View file

@ -2,10 +2,10 @@
#include "Specific/level.h" #include "Specific/level.h"
#include "Math/Math.h" #include "Math/Math.h"
struct BiteInfo;
struct CreatureInfo; struct CreatureInfo;
struct ItemInfo; struct ItemInfo;
struct LOTInfo; struct LOTInfo;
struct CreatureBiteInfo;
enum class JumpDistance enum class JumpDistance
{ {
@ -20,55 +20,6 @@ enum TARGET_TYPE
SECONDARY_TARGET 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 struct AI_INFO
{ {
int zoneNumber; int zoneNumber;
@ -99,30 +50,6 @@ struct OVERLAP
int flags; 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) #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 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 AlertNearbyGuards(ItemInfo* item);
void AlertAllGuards(short itemNumber); void AlertAllGuards(short itemNumber);
void CreatureKill(ItemInfo* item, int entityKillAnim, int laraExtraKillAnim, int entityKillState, int laraKillState); 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 CreatureEffect2(ItemInfo* item, const CreatureBiteInfo& bite, short velocity, short angle, std::function<CreatureEffectFunction> func);
short CreatureEffect(ItemInfo* item, BiteInfo bite, std::function<CreatureEffectFunction> func); short CreatureEffect(ItemInfo* item, const CreatureBiteInfo& bite, std::function<CreatureEffectFunction> func);
void CreatureUnderwater(ItemInfo* item, int depth); void CreatureUnderwater(ItemInfo* item, int depth);
void CreatureFloat(short itemNumber); void CreatureFloat(short itemNumber);
void CreatureJoint(ItemInfo* item, short joint, short required, short maxAngle = ANGLE(70.0f)); void CreatureJoint(ItemInfo* item, short joint, short required, short maxAngle = ANGLE(70.0f));

View file

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

View file

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

View file

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

View file

@ -52,7 +52,7 @@ int SplashCount = 0;
Vector3i NodeVectors[ParticleNodeOffsetIDs::NodeMax]; Vector3i NodeVectors[ParticleNodeOffsetIDs::NodeMax];
NODEOFFSET_INFO NodeOffsets[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 { -16, -8, 160, 0, false }, // TR5 offset 1
{ 0, 0, 256, 8, false }, // TR5 offset 2 { 0, 0, 256, 8, false }, // TR5 offset 2
{ 0, 0, 256, 17, false }, // TR5 offset 3 { 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: add additional "Weapon Special" param or something. Currently initial == 2 means Rocket Launcher backwards smoke.
//TODO: Refactor different weapon types out of it //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) 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(); auto& s = GetFreeSmokeParticle();
s = {}; s = {};
@ -124,7 +129,7 @@ namespace TEN::Effects::Smoke
s.velocity = direction; s.velocity = direction;
s.gravity = -.1f; 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.sourceColor = Vector4(.4f, .4f, .4f, 1);
s.destinationColor = Vector4(0, 0, 0, 0); s.destinationColor = Vector4(0, 0, 0, 0);
@ -135,7 +140,7 @@ namespace TEN::Effects::Smoke
float size = Random::GenerateFloat(48, 80); float size = Random::GenerateFloat(48, 80);
s.sourceSize = size * 2; s.sourceSize = size * 2;
s.destinationSize = size * 8; s.destinationSize = size * 8;
s.sourceColor = {0.75,0.75,1,1}; s.sourceColor = { 0.75,0.75,1,1 };
s.terminalVelocity = 0; s.terminalVelocity = 0;
s.friction = 0.82f; s.friction = 0.82f;
s.life = Random::GenerateFloat(60, 90); 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.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.angularVelocity = Random::GenerateFloat(-PI_DIV_4, PI_DIV_4);
s.angularDrag = 0.95f; 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) 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 DisableSmokeParticles();
void TriggerFlareSmoke(const Vector3& pos, const Vector3& direction, int life, int room); 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);
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 TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int velocity, int moving);
void TriggerRocketSmoke(int x, int y, int z); void TriggerRocketSmoke(int x, int y, int z);
void TriggerBreathSmoke(long x, long y, long z, short angle); 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) void ThrowPoison(int itemNum, int meshIndex, Vector3i offset, Vector3i speed, Vector3 color)
{ {
auto* item = &g_Level.Items[itemNum]; 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); auto* spark = SetupPoisonSpark(color);
AttachAndCreateSpark(spark, item, meshIndex, offset, speed); AttachAndCreateSpark(spark, item, meshIndex, offset, speed);
spark->flags = SP_POISON | SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; 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() void UpdateFireProgress()
{ {
TriggerGlobalStaticFlame(); TriggerGlobalStaticFlame();

View file

@ -6,6 +6,7 @@
enum class LaraWeaponType; enum class LaraWeaponType;
struct ItemInfo; struct ItemInfo;
struct CreatureBiteInfo;
enum BodyPartFlags enum BodyPartFlags
{ {
@ -234,7 +235,9 @@ void TriggerGlobalFireSmoke();
void TriggerGlobalFireFlame(); void TriggerGlobalFireFlame();
void TriggerPilotFlame(int itemNum, int nodeIndex); void TriggerPilotFlame(int itemNum, int nodeIndex);
void ThrowFire(int itemNum, int meshIndex, Vector3i offset, Vector3i speed); 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, int meshIndex, Vector3i offset, Vector3i speed, Vector3 color);
void ThrowPoison(int itemNum, const CreatureBiteInfo& bite, Vector3i speed, Vector3 color);
void UpdateFireProgress(); void UpdateFireProgress();
void ClearFires(); void ClearFires();
void AddFire(int x, int y, int z, short roomNum, float size, short fade); 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; 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 struct CreatureInfo
{ {
int ItemNumber = -1; int ItemNumber = -1;
@ -96,11 +152,13 @@ struct CreatureInfo
bool MonkeySwingAhead = false; bool MonkeySwingAhead = false;
bool ReachedGoal = false; bool ReachedGoal = false;
short FiredWeapon = 0; CreatureMuzzleflashInfo MuzzleFlash[2];
short Tosspad = 0; short Tosspad = 0;
short LocationAI = 0; short LocationAI = 0;
short Flags = 0; short Flags = 0;
bool IsTargetAlive();
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION #ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
CreatureAIPriority Priority = CreatureAIPriority::None; CreatureAIPriority Priority = CreatureAIPriority::None;
size_t FramesSinceLOTUpdate = 0; size_t FramesSinceLOTUpdate = 0;

View file

@ -115,7 +115,7 @@ void ControlMissile(short fxNumber)
EffectNewRoom(fxNumber, pointColl.RoomNumber); EffectNewRoom(fxNumber, pointColl.RoomNumber);
if (fx.objectNumber == ID_KNIFETHROWER_KNIFE) 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) switch (fx.objectNumber)
{ {

View file

@ -12,7 +12,7 @@
#include "Game/misc.h" #include "Game/misc.h"
#include "Sound/sound.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* creature = GetCreatureInfo(item);
auto* enemy = creature->Enemy; 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) 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( auto pos = GameVector(
LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF, LaraItem->Pose.Position.x + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->Floor, LaraItem->Floor - 128,
LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF, LaraItem->Pose.Position.z + ((GetRandomControl() - 0x4000) << 9) / 0x7FFF,
LaraItem->RoomNumber); LaraItem->RoomNumber);

View file

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

View file

@ -12,7 +12,6 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_collide.h" #include "Game/Lara/lara_collide.h"
#include "Objects/Generic/Traps/traps.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/clock.h" #include "Specific/clock.h"
#include "Specific/level.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 AnimAfter
}; };
enum PuzzleHoleType
{
None = 0,
Done = 1,
Hole = 2
};
ObjectCollisionBounds PuzzleBounds = ObjectCollisionBounds PuzzleBounds =
{ {
GameBoundingBox( GameBoundingBox(
@ -67,7 +60,7 @@ const ObjectCollisionBounds KeyHoleBounds =
void InitializePuzzleHole(short itemNumber) void InitializePuzzleHole(short itemNumber)
{ {
auto& receptacleItem = g_Level.Items[itemNumber]; auto& receptacleItem = g_Level.Items[itemNumber];
receptacleItem.ItemFlags[5] = PuzzleHoleType::Hole; receptacleItem.ItemFlags[5] = (int)ReusableReceptacleState::Empty;
} }
void InitializePuzzleDone(short itemNumber) void InitializePuzzleDone(short itemNumber)
@ -83,8 +76,8 @@ void PuzzleHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co
auto& receptacleItem = g_Level.Items[itemNumber]; auto& receptacleItem = g_Level.Items[itemNumber];
auto& player = GetLaraInfo(*laraItem); auto& player = GetLaraInfo(*laraItem);
//Start level with the right object when loading the game. // Start level with correct object when loading game.
if (receptacleItem.ItemFlags[5] == PuzzleHoleType::Done) if (receptacleItem.ItemFlags[5] == (int)ReusableReceptacleState::Done)
{ {
receptacleItem.ObjectNumber += GAME_OBJECT_ID{ ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1 }; receptacleItem.ObjectNumber += GAME_OBJECT_ID{ ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1 };
SetAnimation(receptacleItem, 0); 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) if (puzzleType == PuzzleType::Specfic)
{ {
@ -244,8 +237,8 @@ void PuzzleDoneCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co
return; return;
AnimateItem(&receptacleItem); AnimateItem(&receptacleItem);
//Start level with the right object when loading the game. // Start level with correct object when loading game.
if (receptacleItem.ItemFlags[5] == PuzzleHoleType::Hole) if (receptacleItem.ItemFlags[5] == (int)ReusableReceptacleState::Empty)
{ {
receptacleItem.ObjectNumber = GAME_OBJECT_ID(receptacleItem.ObjectNumber - (ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1)); receptacleItem.ObjectNumber = GAME_OBJECT_ID(receptacleItem.ObjectNumber - (ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1));
SetAnimation(receptacleItem, 0); SetAnimation(receptacleItem, 0);
@ -253,12 +246,12 @@ void PuzzleDoneCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co
return; return;
} }
//Activate triggers when startig the level for the first time // Activate triggers when startig level for first time.
if (receptacleItem.ItemFlags[5] == PuzzleHoleType::None) if (receptacleItem.ItemFlags[5] == (int)ReusableReceptacleState::None)
{ {
receptacleItem.ItemFlags[1] = true; receptacleItem.ItemFlags[1] = true;
TestTriggers(receptacleItem.Pose.Position.x, receptacleItem.Pose.Position.y, receptacleItem.Pose.Position.z, receptacleItem.RoomNumber, false, 0); 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; auto puzzleType = PuzzleType::Normal;
@ -341,7 +334,7 @@ void PuzzleDone(ItemInfo* item, short itemNumber)
item->ItemFlags[1] = true; item->ItemFlags[1] = true;
item->ObjectNumber += GAME_OBJECT_ID{ ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1 }; 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); SetAnimation(item, 0);
item->ResetModelToDefault(); item->ResetModelToDefault();
} }
@ -371,7 +364,7 @@ void PuzzleHole(ItemInfo* item, short itemNumber)
item->ItemFlags[1] = true; item->ItemFlags[1] = true;
item->ObjectNumber = GAME_OBJECT_ID(item->ObjectNumber - (ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1)); 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); SetAnimation(item, 0);
item->ResetModelToDefault(); item->ResetModelToDefault();
} }
@ -441,35 +434,27 @@ void DoPuzzle()
// Keys // Keys
void KeyHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) void KeyHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
{ {
auto* laraInfo = GetLaraInfo(laraItem);
auto* keyHoleItem = &g_Level.Items[itemNumber]; auto* keyHoleItem = &g_Level.Items[itemNumber];
auto* player = GetLaraInfo(laraItem);
if (g_Level.Items[itemNumber].TriggerFlags == 1 && short* triggerIndexPtr = GetTriggerIndex(keyHoleItem);
keyHoleItem->ObjectNumber == ID_KEY_HOLE8) short triggerType = (*(triggerIndexPtr++) >> 8) & 0x3F;
{
if (keyHoleItem->ItemFlags[3])
{
keyHoleItem->ItemFlags[3]--;
if (!keyHoleItem->ItemFlags[3])
keyHoleItem->MeshBits = 2;
}
}
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.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_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 (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; return;
if (g_Gui.GetInventoryItemChosen() == NO_ITEM) 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)) if (g_Gui.GetInventoryItemChosen() != keyHoleItem->ObjectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1))
return; return;
laraInfo->Context.InteractedItem = itemNumber; player->Context.InteractedItem = itemNumber;
} }
if (laraInfo->Context.InteractedItem != itemNumber) if (player->Context.InteractedItem != itemNumber)
return; return;
if (MoveLaraPosition(KeyHolePosition, keyHoleItem, laraItem)) if (MoveLaraPosition(KeyHolePosition, keyHoleItem, laraItem))
{ {
if (keyHoleItem->ObjectNumber == ID_KEY_HOLE8) if (triggerType != TRIGGER_TYPES::SWITCH)
laraItem->Animation.AnimNumber = LA_KEYCARD_USE; {
RemoveObjectFromInventory(GAME_OBJECT_ID(keyHoleItem->ObjectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1)), 1);
}
else else
{ {
RemoveObjectFromInventory(static_cast<GAME_OBJECT_ID>(keyHoleItem->ObjectNumber - (ID_KEY_HOLE1 - ID_KEY_ITEM1)), 1); keyHoleItem->ItemFlags[1] = true;
laraItem->Animation.AnimNumber = LA_USE_KEY;
} }
laraItem->Animation.AnimNumber = keyHoleItem->TriggerFlags;
laraItem->Animation.ActiveState = LS_INSERT_KEY; laraItem->Animation.ActiveState = LS_INSERT_KEY;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraInfo->Control.IsMoving = false; player->Control.IsMoving = false;
ResetPlayerFlex(laraItem); ResetPlayerFlex(laraItem);
laraInfo->Control.HandStatus = HandStatus::Busy; player->Control.HandStatus = HandStatus::Busy;
keyHoleItem->Flags |= TRIGGERED; keyHoleItem->Flags |= TRIGGERED;
keyHoleItem->Status = ITEM_ACTIVE; 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); g_Gui.SetInventoryItemChosen(NO_ITEM);
return; return;
} }
if (laraInfo->Control.IsMoving && laraInfo->Context.InteractedItem == itemNumber) if (player->Control.IsMoving && player->Context.InteractedItem == itemNumber)
{ {
laraInfo->Control.IsMoving = false; player->Control.IsMoving = false;
laraInfo->Control.HandStatus = HandStatus::Free; player->Control.HandStatus = HandStatus::Free;
} }
} }
else else

View file

@ -3,6 +3,13 @@
struct CollisionInfo; struct CollisionInfo;
struct ItemInfo; struct ItemInfo;
enum class ReusableReceptacleState
{
None = 0,
Done = 1,
Empty = 2
};
// Puzzles // Puzzles
void PuzzleHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void PuzzleHoleCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
void PuzzleDoneCollision(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_WALK_TURN_RATE_MAX = ANGLE(3.0f);
constexpr auto COWBOY_RUN_TURN_RATE_MAX = ANGLE(6.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 CowboyGunLeft = CreatureBiteInfo(Vector3(1.0f, 200.0f, 40.0f), 5);
const auto CowboyGunRight = BiteInfo(Vector3(-1.0f, 200.0f, 40.0f), 8); const auto CowboyGunRight = CreatureBiteInfo(Vector3(-1.0f, 200.0f, 40.0f), 8);
enum CowboyState 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_RUN_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto APE_DISPLAY_ANGLE = ANGLE(45.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 }; const auto ApeAttackJoints = std::vector<unsigned int>{ 8, 9, 10, 11, 12, 13, 14, 15 };
enum ApeState 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_WALK_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto BEAR_RUN_TURN_RATE_MAX = ANGLE(5.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 }; const auto BearAttackJoints = std::vector<unsigned int>{ 2, 3, 5, 6, 14, 17 };
enum BearState 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_RUN_TURN_RATE_MAX = ANGLE(6.0f);
constexpr auto BIG_RAT_SWIM_TURN_RATE_MAX = ANGLE(3.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 enum BigRatState
{ {
@ -163,7 +163,7 @@ namespace TEN::Entities::Creatures::TR1
break; break;
} }
if (ai.ahead && item->TouchBits.Test(BigRatBite.meshNum)) if (ai.ahead && item->TouchBits.Test(BigRatBite.BoneID))
{ {
item->Animation.TargetState = BIG_RAT_STATE_IDLE; item->Animation.TargetState = BIG_RAT_STATE_IDLE;
} }
@ -181,7 +181,7 @@ namespace TEN::Entities::Creatures::TR1
case BIG_RAT_STATE_LAND_BITE_ATTACK: case BIG_RAT_STATE_LAND_BITE_ATTACK:
if (item->Animation.RequiredState == NO_STATE && ai.ahead && 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); DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
CreatureEffect(item, BigRatBite, DoBloodSplat); CreatureEffect(item, BigRatBite, DoBloodSplat);
@ -192,7 +192,7 @@ namespace TEN::Entities::Creatures::TR1
case BIG_RAT_STATE_POUNCE_ATTACK: case BIG_RAT_STATE_POUNCE_ATTACK:
if (item->Animation.RequiredState == NO_STATE && ai.ahead && 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); DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE);
CreatureEffect(item, BigRatBite, DoBloodSplat); CreatureEffect(item, BigRatBite, DoBloodSplat);
@ -216,14 +216,14 @@ namespace TEN::Entities::Creatures::TR1
break; 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; item->Animation.TargetState = BIG_RAT_STATE_SWIM_BITE_ATTACK;
break; break;
case BIG_RAT_STATE_SWIM_BITE_ATTACK: case BIG_RAT_STATE_SWIM_BITE_ATTACK:
if (item->Animation.RequiredState == NO_STATE && ai.ahead && 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); DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
CreatureEffect(item, BigRatBite, DoBloodSplat); CreatureEffect(item, BigRatBite, DoBloodSplat);

View file

@ -29,8 +29,8 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto CENTAUR_TURN_RATE_MAX = ANGLE(4.0f); constexpr auto CENTAUR_TURN_RATE_MAX = ANGLE(4.0f);
const auto CentaurRocketBite = BiteInfo(Vector3(11.0f, 415.0f, 41.0f), 13); const auto CentaurRocketBite = CreatureBiteInfo(Vector3(11, 415, 41), 13);
const auto CentaurRearBite = BiteInfo(Vector3(50.0f, 30.0f, 0.0f), 5); const auto CentaurRearBite = CreatureBiteInfo(Vector3(50, 30, 0), 5);
const auto CentaurAttackJoints = std::vector<unsigned int>{ 0, 3, 4, 7, 8, 16, 17 }; const auto CentaurAttackJoints = std::vector<unsigned int>{ 0, 3, 4, 7, 8, 16, 17 };
enum CentaurState 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_FLY_ANGLE_SPEED = ANGLE(5.0f);
constexpr auto NATLA_SHOOT_ANGLE = ANGLE(30.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 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_WALK_FORWARD_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto WINGED_MUTANT_RUN_FORWARD_TURN_RATE_MAX = ANGLE(6.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 WingedMutantBite = CreatureBiteInfo(Vector3i(-27, 98, 0), 10);
const auto WingedMutantRocketBite = BiteInfo(Vector3(51.0f, 213.0f, 0.0f), 14); const auto WingedMutantRocketBite = CreatureBiteInfo(Vector3i(51, 213, 0), 14);
const auto WingedMutantShardBite = BiteInfo(Vector3(-35.0f, 269.0f, 0.0f), 9); const auto WingedMutantShardBite = CreatureBiteInfo(Vector3i(-35, 269, 0), 9);
const auto WingedMutantJoints = std::vector<unsigned int>{ 9, 10, 14 }; const auto WingedMutantJoints = std::vector<unsigned int>{ 9, 10, 14 };
enum WingedMutantState enum WingedMutantState

View file

@ -31,8 +31,8 @@ namespace TEN::Entities::Creatures::TR1
constexpr auto WOLF_WALK_TURN_RATE_MAX = ANGLE(2.0f); constexpr auto WOLF_WALK_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto WOLF_RUN_TURN_RATE_MAX = ANGLE(5.0f); constexpr auto WOLF_RUN_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto WOLF_STALK_TURN_RATE_MAX = ANGLE(2.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 }; const auto WolfAttackJoints = std::vector<unsigned int>{ 0, 1, 2, 3, 6, 8, 9, 10, 12, 13, 14 };
enum WolfState enum WolfState

View file

@ -17,8 +17,8 @@
#include "Objects/TR1/Entity/tr1_giant_mutant.h" // OK #include "Objects/TR1/Entity/tr1_giant_mutant.h" // OK
#include "Objects/TR1/Entity/tr1_wolf.h" // OK #include "Objects/TR1/Entity/tr1_wolf.h" // OK
#include "Objects/TR1/Entity/tr1_big_rat.h" // OK #include "Objects/TR1/Entity/tr1_big_rat.h" // OK
#include "Objects/TR1/Entity/tr1_centaur.h" #include "Objects/TR1/Entity/tr1_centaur.h" // OK
#include "Objects/TR1/Entity/tr1_winged_mutant.h" #include "Objects/TR1/Entity/tr1_winged_mutant.h" // OK
#include "Objects/Utils/object_helper.h" #include "Objects/Utils/object_helper.h"
// Traps // Traps
@ -157,7 +157,6 @@ static void StartEntity(ObjectInfo* obj)
obj->control = WingedMutantControl; obj->control = WingedMutantControl;
obj->collision = CreatureCollision; obj->collision = CreatureCollision;
obj->shadowType = ShadowMode::All; obj->shadowType = ShadowMode::All;
obj->hitEffect = HitEffect::Blood;
obj->pivotLength = 150; obj->pivotLength = 150;
obj->radius = BLOCK(1 / 3.0f); obj->radius = BLOCK(1 / 3.0f);
obj->HitPoints = 50; 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_IDLE_ATTACK_RANGE = SQUARE(BLOCK(0.67f));
constexpr auto BARRACUDA_SWIM_FAST_ATTACK_RANGE = SQUARE(BLOCK(0.34f)); 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 }; const auto BarracudaAttackJoints = std::vector<unsigned int>{ 5, 6, 7 };
enum BarracudaState enum BarracudaState

View file

@ -21,8 +21,8 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto BIRD_MONSTER_WALK_TURN_RATE_MAX = ANGLE(4.0f); 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 BirdMonsterBiteLeft = CreatureBiteInfo(Vector3i(0, 224, 0), 19);
const auto BirdMonsterBiteRight = BiteInfo(Vector3(0.0f, 224.0f, 0.0f), 22); const auto BirdMonsterBiteRight = CreatureBiteInfo(Vector3i(0, 224, 0), 22);
const auto BirdMonsterAttackLeftJoints = std::vector<unsigned int>{ 18, 19 }; const auto BirdMonsterAttackLeftJoints = std::vector<unsigned int>{ 18, 19 };
const auto BirdMonsterAttackRightJoints = std::vector<unsigned int>{ 21, 22 }; 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_SWIPE_ATTACK_DAMAGE = 250;
constexpr auto DRAGON_CONTACT_DAMAGE = 10; 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 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 }; 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 namespace TEN::Entities::Creatures::TR2
{ {
const auto EagleBite = BiteInfo(Vector3(15.0f, 46.0f, 21.0f), 6); const auto EagleBite = CreatureBiteInfo(Vector3i(15, 46, 21), 6);
const auto CrowBite = BiteInfo(Vector3(2.0f, 10.0f, 60.0f), 14); const auto CrowBite = CreatureBiteInfo(Vector3i(2, 10, 60), 14);
enum EagleOrCrowState enum EagleOrCrowState
{ {

View file

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

View file

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

View file

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

View file

@ -13,7 +13,7 @@
namespace TEN::Entities::Creatures::TR2 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; bool MonksAttackLara;

View file

@ -25,7 +25,7 @@ namespace TEN::Entities::Creatures::TR2
constexpr auto RAT_TURN_RATE_MAX = ANGLE(6.0f); 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 enum RatState
{ {
@ -131,7 +131,7 @@ namespace TEN::Entities::Creatures::TR2
case RAT_STATE_POUNCE_ATTACK: case RAT_STATE_POUNCE_ATTACK:
if (item->Animation.RequiredState == NO_STATE && if (item->Animation.RequiredState == NO_STATE &&
item->TouchBits.Test(RatBite.meshNum)) item->TouchBits.Test(RatBite.BoneID))
{ {
item->Animation.RequiredState = RAT_STATE_IDLE; item->Animation.RequiredState = RAT_STATE_IDLE;
DoDamage(creature->Enemy, RAT_ATTACK_DAMAGE); DoDamage(creature->Enemy, RAT_ATTACK_DAMAGE);

View file

@ -15,7 +15,7 @@ namespace TEN::Entities::Creatures::TR2
{ {
constexpr auto SHARK_BITE_ATTACK_DAMAGE = 400; 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 }; const auto SharkBiteAttackJoints = std::vector<unsigned int>{ 10, 12, 13 };
void SharkControl(short itemNumber) 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_WALK_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto SILENCER_RUN_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 enum SilencerState
{ {
@ -87,6 +87,9 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero; auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero; auto extraTorsoRot = EulerAngles::Zero;
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
{ {
if (item->Animation.ActiveState != SILENCER_STATE_DEATH_1 && if (item->Animation.ActiveState != SILENCER_STATE_DEATH_1 &&
@ -279,9 +282,11 @@ namespace TEN::Entities::Creatures::TR2
else else
extraHeadRot.y = AI.angle; 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); ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE);
creature->MuzzleFlash[0].Bite = SilencerGunBite;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1; creature->Flags = 1;
} }
@ -298,11 +303,14 @@ namespace TEN::Entities::Creatures::TR2
else else
extraHeadRot.y = AI.angle; 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)) if (!ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE))
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD; item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
creature->MuzzleFlash[0].Bite = SilencerGunBite;
creature->MuzzleFlash[0].Delay = 2;
item->Animation.RequiredState = SILENCER_STATE_RUN_SHOOT; item->Animation.RequiredState = SILENCER_STATE_RUN_SHOOT;
} }

View file

@ -7,8 +7,10 @@
#include "Game/collision/sphere.h" #include "Game/collision/sphere.h"
#include "Game/control/box.h" #include "Game/control/box.h"
#include "Game/control/lot.h" #include "Game/control/lot.h"
#include "Game/effects/smoke.h"
#include "Game/itemdata/creature_info.h" #include "Game/itemdata/creature_info.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/misc.h" #include "Game/misc.h"
#include "Game/people.h" #include "Game/people.h"
#include "Objects/TR2/Vehicles/skidoo.h" #include "Objects/TR2/Vehicles/skidoo.h"
@ -17,15 +19,19 @@
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h" #include "Specific/setup.h"
using namespace TEN::Effects::Smoke;
namespace TEN::Entities::Creatures::TR2 namespace TEN::Entities::Creatures::TR2
{ {
constexpr auto SMAN_WAIT_RANGE = SQUARE(BLOCK(4)); constexpr auto SMAN_WAIT_RANGE = SQUARE(BLOCK(4));
const auto SkidooBiteLeft = BiteInfo(Vector3(240.0f, -190.0f, 540.0f), 0); const auto SkidooBiteLeft = CreatureBiteInfo(Vector3i(240, -80, 540), 0);
const auto SkidooBiteRight = BiteInfo(Vector3(-240.0f, -190.0f, 540.0f), 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_MIN_TURN = ANGLE(2.0f);
constexpr auto SMAN_TARGET_ANGLE = ANGLE(15.0f); constexpr auto SMAN_TARGET_ANGLE = ANGLE(30.0f);
enum SnowmobileManState enum SnowmobileManState
{ {
@ -45,30 +51,34 @@ namespace TEN::Entities::Creatures::TR2
SMAN_ANIM_DEATH = 10 SMAN_ANIM_DEATH = 10
}; };
void InitializeSkidooMan(short itemNumber) static void CreateSnowmobileGun(ItemInfo* driver)
{ {
short skidooItemNumber = CreateItem(); short snowmobileNumber = CreateItem();
if (skidooItemNumber != NO_ITEM) if (snowmobileNumber != NO_ITEM)
{ {
auto* riderItem = &g_Level.Items[itemNumber]; auto* snowmobileGunItem = &g_Level.Items[snowmobileNumber];
auto* skidooItem = &g_Level.Items[skidooItemNumber]; snowmobileGunItem->Pose.Position = driver->Pose.Position;
snowmobileGunItem->Pose.Orientation.y = driver->Pose.Orientation.y;
skidooItem->ObjectNumber = ID_SNOWMOBILE_GUN; snowmobileGunItem->RoomNumber = driver->RoomNumber;
skidooItem->Pose.Position = riderItem->Pose.Position; snowmobileGunItem->ObjectNumber = ID_SNOWMOBILE_GUN;
skidooItem->Pose.Orientation.y = riderItem->Pose.Orientation.y; snowmobileGunItem->Model.Color = driver->Model.Color;
skidooItem->RoomNumber = riderItem->RoomNumber; snowmobileGunItem->Flags = IFLAG_ACTIVATION_MASK;
skidooItem->Flags = ITEM_INVISIBLE; InitializeItem(snowmobileNumber); g_Level.NumItems++;
skidooItem->Model.Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f); driver->Data = snowmobileNumber; // Register the snowmobile gun for the driver to control it.
InitializeItem(skidooItemNumber);
// The rider remembers his skidoo.
riderItem->Data = skidooItemNumber;
g_Level.NumItems++;
} }
else 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) void SkidooManCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
@ -95,60 +105,67 @@ namespace TEN::Entities::Creatures::TR2
void SkidooManControl(short riderItemNumber) void SkidooManControl(short riderItemNumber)
{ {
auto* riderItem = &g_Level.Items[riderItemNumber]; auto* rider = &g_Level.Items[riderItemNumber];
if (!riderItem->Data) 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; return;
} }
short itemNumber = (short)riderItem->Data; short itemNumber = (short)rider->Data;
auto* item = &g_Level.Items[itemNumber]; auto* skidoo = &g_Level.Items[itemNumber];
if (!item->Data) if (!skidoo->Data)
{ {
EnableEntityAI(itemNumber, true); EnableEntityAI(itemNumber, true);
item->Status = ITEM_ACTIVE; skidoo->Status = ITEM_ACTIVE;
} }
auto* creatureInfo = GetCreatureInfo(item); auto* creatureInfo = GetCreatureInfo(skidoo);
short angle = 0; 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; 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; rider->Pose.Position = skidoo->Pose.Position;
riderItem->Pose.Orientation.y = item->Pose.Orientation.y; rider->Pose.Orientation.y = skidoo->Pose.Orientation.y;
riderItem->RoomNumber = item->RoomNumber; rider->RoomNumber = skidoo->RoomNumber;
riderItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_DRIVER].animIndex + SMAN_ANIM_DEATH; rider->Animation.AnimNumber = Objects[ID_SNOWMOBILE_DRIVER].animIndex + SMAN_ANIM_DEATH;
riderItem->Animation.FrameNumber = g_Level.Anims[riderItem->Animation.AnimNumber].frameBase; rider->Animation.FrameNumber = g_Level.Anims[rider->Animation.AnimNumber].frameBase;
riderItem->Animation.ActiveState = SMAN_STATE_DEATH; rider->Animation.ActiveState = SMAN_STATE_DEATH;
if (Lara.TargetEntity == item) if (Lara.TargetEntity == skidoo)
Lara.TargetEntity = nullptr; Lara.TargetEntity = nullptr;
} }
else else
AnimateItem(riderItem); AnimateItem(rider);
if (item->Animation.ActiveState == SMAN_STATE_MOVING || item->Animation.ActiveState == SMAN_STATE_WAIT) if (skidoo->Animation.ActiveState == SMAN_STATE_MOVING || skidoo->Animation.ActiveState == SMAN_STATE_WAIT)
item->Animation.TargetState = SMAN_STATE_WAIT; skidoo->Animation.TargetState = SMAN_STATE_WAIT;
else else
item->Animation.TargetState = SMAN_STATE_MOVING; skidoo->Animation.TargetState = SMAN_STATE_MOVING;
} }
else else
{ {
CreatureAIInfo(item, &AI); CreatureAIInfo(skidoo, &AI);
GetCreatureMood(item, &AI, true); GetCreatureMood(skidoo, &AI, true);
CreatureMood(item, &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: case SMAN_STATE_WAIT:
if (creatureInfo->Mood == MoodType::Bored) 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) else if (abs(AI.angle) < SMAN_TARGET_ANGLE && AI.distance < SMAN_WAIT_RANGE)
break; break;
item->Animation.TargetState = SMAN_STATE_MOVING; skidoo->Animation.TargetState = SMAN_STATE_MOVING;
break; break;
case SMAN_STATE_MOVING: case SMAN_STATE_MOVING:
if (creatureInfo->Mood == MoodType::Bored) 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) 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) 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) else if (angle > SMAN_MIN_TURN)
item->Animation.TargetState = SMAN_STATE_START_RIGHT; skidoo->Animation.TargetState = SMAN_STATE_START_RIGHT;
break; break;
case SMAN_STATE_START_LEFT: case SMAN_STATE_START_LEFT:
case SMAN_STATE_LEFT: case SMAN_STATE_LEFT:
if (angle < -SMAN_MIN_TURN) if (angle < -SMAN_MIN_TURN)
item->Animation.TargetState = SMAN_STATE_LEFT; skidoo->Animation.TargetState = SMAN_STATE_LEFT;
else else
item->Animation.TargetState = SMAN_STATE_MOVING; skidoo->Animation.TargetState = SMAN_STATE_MOVING;
break; break;
case SMAN_STATE_START_RIGHT: case SMAN_STATE_START_RIGHT:
case SMAN_STATE_RIGHT: case SMAN_STATE_RIGHT:
if (angle < -SMAN_MIN_TURN) if (angle < -SMAN_MIN_TURN)
item->Animation.TargetState = SMAN_STATE_LEFT; skidoo->Animation.TargetState = SMAN_STATE_LEFT;
else else
item->Animation.TargetState = SMAN_STATE_MOVING; skidoo->Animation.TargetState = SMAN_STATE_MOVING;
break; 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;
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;
if (ShotLara(item, &AI, SkidooBiteLeft, 0, damage) + ShotLara(item, &AI, SkidooBiteRight, 0, damage)) ShotLara(skidoo, &AI, SkidooBiteRight, 0, damage);
creatureInfo->Flags = 5; 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--; 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; creatureInfo->JointRotation[0] = 0;
} }
else else
{ {
creatureInfo->JointRotation[0] = (creatureInfo->JointRotation[0] == 1) ? 2 : 1; creatureInfo->JointRotation[0] = (creatureInfo->JointRotation[0] == 1) ? 2 : 1;
DoSnowEffect(item); DoSnowEffect(skidoo);
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! 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); CreatureAnimation(itemNumber, angle, 0);
if (riderItem->Animation.ActiveState != SMAN_STATE_DEATH) if (rider->Animation.ActiveState != SMAN_STATE_DEATH)
{ {
riderItem->Pose.Position = item->Pose.Position; rider->Pose.Position = skidoo->Pose.Position;
riderItem->Pose.Orientation.y = item->Pose.Orientation.y; rider->Pose.Orientation.y = skidoo->Pose.Orientation.y;
if (item->RoomNumber != riderItem->RoomNumber) if (skidoo->RoomNumber != rider->RoomNumber)
ItemNewRoom(riderItemNumber, item->RoomNumber); ItemNewRoom(riderItemNumber, skidoo->RoomNumber);
riderItem->Animation.AnimNumber = item->Animation.AnimNumber + (Objects[ID_SNOWMOBILE_DRIVER].animIndex - Objects[ID_SNOWMOBILE_GUN].animIndex); rider->Animation.AnimNumber = skidoo->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.FrameNumber = skidoo->Animation.FrameNumber + (g_Level.Anims[rider->Animation.AnimNumber].frameBase - g_Level.Anims[skidoo->Animation.AnimNumber].frameBase);
} }
else if (riderItem->Status == ITEM_DEACTIVATED && else if (rider->Status == ITEM_DEACTIVATED &&
item->Animation.Velocity.z == 0 && skidoo->Animation.Velocity.z == 0 &&
item->Animation.Velocity.y == 0) skidoo->Animation.Velocity.y == 0)
{ {
RemoveActiveItem(riderItemNumber); RemoveActiveItem(riderItemNumber);
riderItem->Collidable = false; rider->Collidable = false;
riderItem->HitPoints = NOT_TARGETABLE; rider->HitPoints = NOT_TARGETABLE;
riderItem->Flags |= IFLAG_INVISIBLE; rider->Flags |= IFLAG_INVISIBLE;
DisableEntityAI(itemNumber); DisableEntityAI(itemNumber);
item->ObjectNumber = ID_SNOWMOBILE; skidoo->ObjectNumber = ID_SNOWMOBILE;
item->Status = ITEM_DEACTIVATED; skidoo->Status = ITEM_DEACTIVATED;
InitializeSkidoo(itemNumber);
((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; constexpr auto SPEAR_GUARDIAN_SWAPMESH_TIME = 3;
const auto SpearGuardianBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 11); const auto SpearGuardianBiteLeft = CreatureBiteInfo(Vector3i(0, 0, 920), 11);
const auto SpearGuardianBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 18); const auto SpearGuardianBiteRight = CreatureBiteInfo(Vector3i(0, 0, 920), 18);
enum SpearGuardianState enum SpearGuardianState
{ {
@ -108,7 +108,7 @@ namespace TEN::Entities::Creatures::TR2
{ {
auto& creature = *GetCreatureInfo(&item); 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); DoDamage(creature.Enemy, damage);
CreatureEffect(&item, SpearGuardianBiteRight, DoBloodSplat); CreatureEffect(&item, SpearGuardianBiteRight, DoBloodSplat);
@ -116,7 +116,7 @@ namespace TEN::Entities::Creatures::TR2
SoundEffect(SFX_TR2_CRUNCH2, &item.Pose); 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); DoDamage(creature.Enemy, damage);
CreatureEffect(&item, SpearGuardianBiteLeft, DoBloodSplat); 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 SMALL_SPIDER_TURN_RATE_MAX = ANGLE(8.0f);
constexpr auto BIG_SPIDER_TURN_RATE_MAX = ANGLE(4.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 enum SpiderState
{ {
@ -72,7 +72,7 @@ namespace TEN::Entities::Creatures::TR2
void DoSpiderBloodEffect(ItemInfo& item) 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); 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; 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 enum SwordGuardianState
{ {
@ -352,7 +352,7 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = ai.angle; 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); DoDamage(creature->Enemy, SWORD_GUARDIAN_ATTACK_DAMAGE);
CreatureEffect(item, SwordBite, DoBloodSplat); CreatureEffect(item, SwordBite, DoBloodSplat);

View file

@ -12,8 +12,8 @@
namespace TEN::Entities::Creatures::TR2 namespace TEN::Entities::Creatures::TR2
{ {
const auto WorkerDualGunBiteLeft = BiteInfo(Vector3(-2.0f, 275.0f, 23.0f), 6); const auto WorkerDualGunBiteLeft = CreatureBiteInfo(Vector3i(-2, 340, 23), 6);
const auto WorkerDualGunBiteRight = BiteInfo(Vector3(2.0f, 275.0f, 23.0f), 10); const auto WorkerDualGunBiteRight = CreatureBiteInfo(Vector3i(2, 340, 23), 10);
// TODO // TODO
enum WorkerDualGunState enum WorkerDualGunState
@ -40,6 +40,11 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero; auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = 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->HitPoints <= 0)
{ {
if (item->Animation.ActiveState != 11) if (item->Animation.ActiveState != 11)
@ -223,9 +228,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle; extraTorsoRot.y = AI.angle;
} }
if (!creature->Flags) if (creature->Flags == 0 && TestAnimFrame(*item, 0))
{ {
ShotLara(item, &AI, WorkerDualGunBiteLeft, extraTorsoRot.y, 50); ShotLara(item, &AI, WorkerDualGunBiteLeft, extraTorsoRot.y, 50);
creature->MuzzleFlash[0].Bite = WorkerDualGunBiteLeft;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1; creature->Flags = 1;
} }
@ -238,9 +245,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle; extraTorsoRot.y = AI.angle;
} }
if (!creature->Flags) if (creature->Flags == 0 && TestAnimFrame(*item, 0))
{ {
ShotLara(item, &AI, WorkerDualGunBiteRight, extraTorsoRot.y, 50); ShotLara(item, &AI, WorkerDualGunBiteRight, extraTorsoRot.y, 50);
creature->MuzzleFlash[0].Bite = WorkerDualGunBiteRight;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1; creature->Flags = 1;
} }
@ -269,10 +278,14 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle; 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, WorkerDualGunBiteLeft, extraTorsoRot.y, 50);
ShotLara(item, &AI, WorkerDualGunBiteRight, 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; 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); constexpr auto WORKER_FLAME_RUN_TURN_RATE_MAX = ANGLE(10.0f);
const auto WorkerFlamethrowerOffset = Vector3i(0, 140, 0); 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 enum WorkerFlamethrowerState
{ {
@ -70,8 +70,7 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero; auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = 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->HitPoints <= 0)
{ {
if (item->Animation.ActiveState != WORKER_FLAME_STATE_DEATH) 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) 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); 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 else
{ {
TriggerDynamicLight(pos.x, pos.y, pos.z, (GetRandomControl() & 4) + 14, (GetRandomControl() & 7) + 128, (GetRandomControl() & 7) + 64, GetRandomControl() & 7); 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; AI_INFO AI;

View file

@ -13,16 +13,34 @@
namespace TEN::Entities::Creatures::TR2 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) void InitializeWorkerMachineGun(short itemNumber)
{ {
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
InitializeCreature(itemNumber); InitializeCreature(itemNumber);
SetAnimation(item, 12); 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) void WorkerMachineGunControl(short itemNumber)
{ {
if (!CreatureActive(itemNumber)) if (!CreatureActive(itemNumber))
@ -36,6 +54,9 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero; auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero; auto extraTorsoRot = EulerAngles::Zero;
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
{ {
if (item->Animation.ActiveState != 7) if (item->Animation.ActiveState != 7)
@ -191,13 +212,19 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle; extraTorsoRot.y = AI.angle;
} }
if (creature->Flags) if (item->Animation.AnimNumber == GetAnimIndex(*item, 2))
creature->Flags--;
else
{ {
ShotLara(item, &AI, WorkerMachineGunBite, extraTorsoRot.y, 30); if (item->Animation.FrameNumber == GetFrameIndex(item, 0))
creature->FiredWeapon = 1; Shoot(item, creature, &AI, extraTorsoRot);
creature->Flags = 5; 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 && if (item->Animation.TargetState != 1 &&
@ -215,14 +242,14 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle; extraTorsoRot.y = AI.angle;
} }
if (creature->Flags) if (item->Animation.FrameNumber == GetFrameIndex(item, 0))
creature->Flags--; Shoot(item, creature, &AI, extraTorsoRot);
else else if (item->Animation.FrameNumber == GetFrameIndex(item, 2))
{ Shoot(item, creature, &AI, extraTorsoRot);
ShotLara(item, &AI, WorkerMachineGunBite, extraTorsoRot.y, 30); else if (item->Animation.FrameNumber == GetFrameIndex(item, 6))
creature->FiredWeapon = 1; Shoot(item, creature, &AI, extraTorsoRot);
creature->Flags = 5; else if (item->Animation.FrameNumber == GetFrameIndex(item, 12))
} Shoot(item, creature, &AI, extraTorsoRot);
break; break;
} }

View file

@ -16,7 +16,7 @@ namespace TEN::Entities::Creatures::TR2
{ {
constexpr auto WORKER_SHOTGUN_NUM_SHOTS = 6; 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 // TODO
enum ShotgunWorkerState 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++) for (int i = 0; i < WORKER_SHOTGUN_NUM_SHOTS; i++)
ShotLara(item, info, bite, angleY, damage); ShotLara(item, info, bite, angleY, damage);
@ -57,12 +57,8 @@ namespace TEN::Entities::Creatures::TR2
auto extraHeadRot = EulerAngles::Zero; auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero; auto extraTorsoRot = EulerAngles::Zero;
if (creature->FiredWeapon) if (creature->MuzzleFlash[0].Delay != 0)
{ creature->MuzzleFlash[0].Delay--;
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 (item->HitPoints <= 0) if (item->HitPoints <= 0)
{ {
@ -202,10 +198,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle; extraTorsoRot.y = AI.angle;
} }
if (!creature->Flags) if (creature->Flags == 0)
{ {
ShotLaraWithShotgun(item, &AI, WorkerShotgunBite, extraTorsoRot.y, 25); ShotLaraWithShotgun(item, &AI, WorkerShotgunBite, extraTorsoRot.y, 25);
creature->FiredWeapon = 2; creature->MuzzleFlash[0].Bite = WorkerShotgunBite;
creature->MuzzleFlash[0].Delay = 2;
creature->Flags = 1; creature->Flags = 1;
} }
@ -224,10 +221,11 @@ namespace TEN::Entities::Creatures::TR2
extraTorsoRot.y = AI.angle; 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); ShotLaraWithShotgun(item, &AI, WorkerShotgunBite, extraTorsoRot.y, 25);
creature->FiredWeapon = 2;
creature->Flags = 1; creature->Flags = 1;
} }

View file

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

View file

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

View file

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

View file

@ -27,9 +27,9 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto LIZARD_VAULT_SHIFT = 260; constexpr auto LIZARD_VAULT_SHIFT = 260;
const auto LizardBiteAttackBite = BiteInfo(Vector3(0.0f, -120.0f, 120.0f), 10); const auto LizardBiteAttackBite = CreatureBiteInfo(Vector3i(0, -120, 120), 10);
const auto LizardSwipeAttackBite = BiteInfo(Vector3::Zero, 5); const auto LizardSwipeAttackBite = CreatureBiteInfo(Vector3i::Zero, 5);
const auto LizardGasBite = BiteInfo(Vector3(0.0f, -64.0f, 56.0f), 9); const auto LizardGasBite = CreatureBiteInfo(Vector3i(0, -64, 56), 9);
const auto LizardSwipeAttackJoints = std::vector<unsigned int>{ 5 }; const auto LizardSwipeAttackJoints = std::vector<unsigned int>{ 5 };
const auto LizardBiteAttackJoints = std::vector<unsigned int>{ 10 }; 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)); (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; static constexpr auto numPoisonThrows = 2;
for (int i = 0; i < numPoisonThrows; i++) 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, 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));
ThrowPoison(itemNumber, bite.meshNum, Vector3i(bite.Position), Vector3i(0.0f, -100.0f, speed << 1), Vector3(0.0f, 1.0f, 0.0f));
} }
void LizardControl(short itemNumber) 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_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); constexpr auto PUNA_EXPLOSION_SECOND_COLOR = Vector4(0.1f, 0.3f, 0.7f, 0.5f);
const auto PunaBossHeadBite = BiteInfo(Vector3::Zero, 8); const auto PunaBossHeadBite = CreatureBiteInfo(Vector3i::Zero, 8);
const auto PunaBossHandBite = BiteInfo(Vector3::Zero, 14); const auto PunaBossHandBite = CreatureBiteInfo(Vector3i::Zero, 14);
enum PunaState enum PunaState
{ {
@ -68,7 +68,7 @@ namespace TEN::Entities::Creatures::TR3
if (!item.TestFlags((int)BossItemFlags::Object, (short)BossFlagValue::Lizard)) if (!item.TestFlags((int)BossItemFlags::Object, (short)BossFlagValue::Lizard))
return NO_ITEM; return NO_ITEM;
auto pos = GetJointPosition(&item, PunaBossHeadBite.meshNum).ToVector3(); auto pos = GetJointPosition(&item, PunaBossHeadBite).ToVector3();
auto orient = Geometry::GetOrientToPoint(pos, target); auto orient = Geometry::GetOrientToPoint(pos, target);
return (orient.y - item.Pose.Orientation.y); 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); 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) if (isSummon)
{ {

View file

@ -33,8 +33,8 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto SHIVA_SWAPMESH_TIME = 3; constexpr auto SHIVA_SWAPMESH_TIME = 3;
constexpr auto PLAYER_ANIM_SHIVA_DEATH = 7; // TODO: Move to LaraExtraAnims enum. 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 ShivaBiteLeft = CreatureBiteInfo(Vector3i(0, 0, 920), 13);
const auto ShivaBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 22); const auto ShivaBiteRight = CreatureBiteInfo(Vector3i(0, 0, 920), 22);
const auto ShivaAttackLeftJoints = std::vector<unsigned int>{ 10, 13 }; const auto ShivaAttackLeftJoints = std::vector<unsigned int>{ 10, 13 };
const auto ShivaAttackRightJoints = std::vector<unsigned int>{ 22, 25 }; 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; constexpr auto SOPHIALEIGH_VAULT_SHIFT = 96;
const auto SophiaLeighStaffBite = BiteInfo(Vector3(-28.0f, 56.0f, 356.0f), 10); const auto SophiaLeighStaffBite = CreatureBiteInfo(Vector3i(-28, 56, 356), 10);
const auto SophiaLeighLeftBite = BiteInfo(Vector3(-72.0f, 48.0f, 356.0f), 10); const auto SophiaLeighLeftBite = CreatureBiteInfo(Vector3i(-72, 48, 356), 10);
const auto SophiaLeighRightBite = BiteInfo(Vector3(16.0f, 48.0f, 304.0f), 10); const auto SophiaLeighRightBite = CreatureBiteInfo(Vector3i(16, 48, 304), 10);
struct SophiaData 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); int fxNumber = CreateNewEffect(item.RoomNumber);
if (fxNumber == NO_ITEM) if (fxNumber == NO_ITEM)
@ -253,7 +253,7 @@ namespace TEN::Entities::Creatures::TR3
auto boltType = isBoltLarge ? (short)MissileType::SophiaLeighLarge : (short)MissileType::SophiaLeighNormal; 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; fx.pos.Orientation.x = item.Pose.Orientation.x + data->torsoXAngle;
if (enemy->IsLara()) 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); 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 WaspBite = CreatureBiteInfo(Vector3i(0, 0, -260), 12);
const auto WaspVenomSackBite = BiteInfo(Vector3::Zero, 10); const auto WaspVenomSackBite = CreatureBiteInfo(Vector3i::Zero, 10);
enum WaspMutantState enum WaspMutantState
{ {
@ -112,7 +112,7 @@ namespace TEN::Entities::Creatures::TR3
item.ItemFlags[0] = 0; item.ItemFlags[0] = 0;
// Spawn light. // Spawn light.
auto pos = GetJointPosition(&item, WaspVenomSackBite.meshNum, WaspVenomSackBite.Position); auto pos = GetJointPosition(&item, WaspVenomSackBite);
TriggerDynamicLight( TriggerDynamicLight(
pos.x, pos.y, pos.z, pos.x, pos.y, pos.z,
item.ItemFlags[0], item.ItemFlags[0],
@ -276,7 +276,7 @@ namespace TEN::Entities::Creatures::TR3
item.Animation.RequiredState = WASP_STATE_FLY_FORWARD; 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); DoDamage(creature.Enemy, WASP_DAMAGE);
CreatureEffect2(&item, WaspBite, 10, item.Pose.Orientation.y, DoBloodSplat); 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_TARGET_ALERT_VELOCITY = 10.0f;
constexpr auto CIVVY_VAULT_SHIFT = 260; constexpr auto CIVVY_VAULT_SHIFT = 260;
const auto CivvyBiteRight = BiteInfo(Vector3::Zero, 13); const auto CivvyBiteLeft = CreatureBiteInfo(Vector3i::Zero, 10);
const auto CivvyBiteLeft = BiteInfo(Vector3::Zero, 10); const auto CivvyBiteRight = CreatureBiteInfo(Vector3i::Zero, 13);
const auto CivvyAttackJoints = std::vector<unsigned int>{ (unsigned int)CivvyBiteLeft.meshNum, (unsigned int)CivvyBiteRight.meshNum }; const auto CivvyAttackJoints = std::vector<unsigned int>{ 10, 13 };
const auto CivvyExcludedTargets = std::vector<GAME_OBJECT_ID> 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_WALK_TURN_RATE_MAX = ANGLE(3.0f);
constexpr auto CLAW_MUTANT_RUN_TURN_RATE_MAX = ANGLE(4.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 ClawMutantLeftBite = CreatureBiteInfo(Vector3i(19, -13, 3), 7);
const auto ClawMutantRightBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 4); const auto ClawMutantRightBite = CreatureBiteInfo(Vector3i(19, -13, 3), 4);
const auto ClawMutantTailBite = BiteInfo(Vector3(-32.0f, -16.0f, -119.0f), 13); const auto ClawMutantTailBite = CreatureBiteInfo(Vector3i(-32, -16, -119), 13);
enum ClawMutantState enum ClawMutantState
{ {
@ -164,7 +164,7 @@ namespace TEN::Entities::Creatures::TR3
auto& fx = EffectList[plasmaBall]; 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()); auto orient = Geometry::GetOrientToPoint(jointPos.ToVector3(), enemyPos.ToVector3());
fx.pos.Position = jointPos; fx.pos.Position = jointPos;
@ -207,14 +207,14 @@ namespace TEN::Entities::Creatures::TR3
static void DamageTargetWithClaw(ItemInfo& source, ItemInfo& target) 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); DoDamage(&target, CLAW_MUTANT_CLAW_ATTACK_DAMAGE / 2);
CreatureEffect2(&source, ClawMutantLeftBite, 10, source.Pose.Orientation.y, DoBloodSplat); CreatureEffect2(&source, ClawMutantLeftBite, 10, source.Pose.Orientation.y, DoBloodSplat);
source.ItemFlags[5] = 1; 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); DoDamage(&target, CLAW_MUTANT_CLAW_ATTACK_DAMAGE / 2);
CreatureEffect2(&source, ClawMutantRightBite, 10, source.Pose.Orientation.y, DoBloodSplat); 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_DISTURBANCE_VELOCITY = 15.0f;
constexpr auto COBRA_SLEEP_FRAME = 45; 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 }; const auto CobraAttackJoints = std::vector<unsigned int>{ 13 };
enum CobraState enum CobraState

View file

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

View file

@ -23,7 +23,7 @@ namespace TEN::Entities::Creatures::TR3
// TODO: Range constants. // 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 }; const auto MonkeyAttackJoints = std::vector<unsigned int>{ 10, 13 };
enum MonkeyState enum MonkeyState

View file

@ -21,7 +21,7 @@ using namespace TEN::Math;
namespace TEN::Entities::Creatures::TR3 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 enum MPGunState
{ {
@ -67,13 +67,8 @@ namespace TEN::Entities::Creatures::TR3
short head = 0; short head = 0;
auto extraTorsoRot = EulerAngles::Zero; auto extraTorsoRot = EulerAngles::Zero;
if (creature->FiredWeapon) if (creature->MuzzleFlash[0].Delay != 0)
{ creature->MuzzleFlash[0].Delay--;
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 (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED)) 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 (Targetable(item, &AI))
{ {
if (AI.angle > -ANGLE(45.0f) && if (AI.angle > -ANGLE(45.0f) && AI.angle < ANGLE(45.0f))
AI.angle < ANGLE(45.0f))
{ {
head = AI.angle; head = AI.angle;
extraTorsoRot.y = AI.angle; extraTorsoRot.y = AI.angle;
ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32); 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); 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; 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) if (item->Animation.RequiredState == MPGUN_STATE_WAIT)
item->Animation.TargetState = 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)) if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.TargetState = MPGUN_STATE_WAIT; 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) 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)) if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.TargetState = MPGUN_STATE_WAIT; 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) 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)) if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.RequiredState = MPGUN_STATE_WALK; 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) 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)) if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
item->Animation.TargetState = MPGUN_STATE_WALK; item->Animation.TargetState = MPGUN_STATE_WALK;
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
} }
if (AI.distance < pow(SECTOR(1.5f), 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)) if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32) || Random::TestProbability(1 / 8.0f))
item->Animation.TargetState = MPGUN_STATE_CROUCHED; item->Animation.TargetState = MPGUN_STATE_CROUCHED;
creature->MuzzleFlash[0].Bite = MPGunBite;
creature->MuzzleFlash[0].Delay = 2;
} }
break; break;

View file

@ -18,8 +18,8 @@ using namespace TEN::Math;
namespace TEN::Entities::Creatures::TR3 namespace TEN::Entities::Creatures::TR3
{ {
const auto MPStickBite1 = BiteInfo(Vector3(247.0f, 10.0f, 11.0f), 13); const auto MPStickBite1 = CreatureBiteInfo(Vector3i(247, 10, 11), 13);
const auto MPStickBite2 = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 6); const auto MPStickBite2 = CreatureBiteInfo(Vector3i(0, 0, 100), 6);
const auto MPStickPunchAttackJoints = std::vector<unsigned int>{ 10, 13 }; const auto MPStickPunchAttackJoints = std::vector<unsigned int>{ 10, 13 };
const auto MPStickKickAttackJoints = std::vector<unsigned int>{ 5, 6 }; 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_RUN_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto RAPTOR_ATTACK_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 }; const auto RaptorAttackJoints = std::vector<unsigned int>{ 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23 };
enum RaptorState enum RaptorState

View file

@ -16,10 +16,9 @@
namespace TEN::Entities::Creatures::TR3 namespace TEN::Entities::Creatures::TR3
{ {
constexpr auto SCUBA_DIVER_ATTACK_DAMAGE = 50; constexpr auto SCUBA_DIVER_ATTACK_DAMAGE = 50;
constexpr auto SCUBA_DIVER_SWIM_TURN_RATE_MAX = ANGLE(3.0f); 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 enum ScubaDiverState
{ {

View file

@ -34,7 +34,7 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto TIGER_PLAYER_ALERT_VELOCITY = 10.0f; 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 TigerSwipeAttackJoints = std::vector<unsigned int>{ 14, 15, 16 };
const auto TigerBiteAttackJoints = std::vector<unsigned int>{ 22, 25, 26 }; const auto TigerBiteAttackJoints = std::vector<unsigned int>{ 22, 25, 26 };

View file

@ -27,10 +27,10 @@ namespace TEN::Entities::Creatures::TR3
constexpr auto TONY_TURN_RATE_MAX = ANGLE(2.0f); constexpr auto TONY_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto TONY_EXPLOSION_COUNT_MAX = 60; constexpr auto TONY_EXPLOSION_COUNT_MAX = 60;
constexpr auto TONY_EFFECT_COLOR = Vector4(0.8f, 0.4f, 0.0f, 0.5f); constexpr auto TONY_EFFECT_COLOR = Vector4(0.8f, 0.4f, 0.0f, 0.5f);
const auto TonyLeftHandBite = BiteInfo(Vector3::Zero, 10); const auto TonyLeftHandBite = CreatureBiteInfo(Vector3i::Zero, 10);
const auto TonyRightHandBite = BiteInfo(Vector3::Zero, 13); const auto TonyRightHandBite = CreatureBiteInfo(Vector3i::Zero, 13);
// I can't set it to the TonyFlame struct since the control of the // 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. // 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; g = (g * bright) / 16;
b = (b * bright) / 16; b = (b * bright) / 16;
auto handPos = GetJointPosition(item, TonyLeftHandBite.meshNum); auto handPos = GetJointPosition(item, TonyLeftHandBite);
TriggerTonyFlame(itemNumber, 13); TriggerTonyFlame(itemNumber, 13);
TriggerDynamicLight(handPos.x, handPos.y, handPos.z, 12, r, g, b); TriggerDynamicLight(handPos.x, handPos.y, handPos.z, 12, r, g, b);
if (item->Animation.ActiveState == TONY_STATE_SHOOT_CEILING || if (item->Animation.ActiveState == TONY_STATE_SHOOT_CEILING ||
item->Animation.ActiveState == TONY_STATE_FLIPMAP) item->Animation.ActiveState == TONY_STATE_FLIPMAP)
{ {
handPos = GetJointPosition(item, TonyRightHandBite.meshNum); handPos = GetJointPosition(item, TonyRightHandBite);
TriggerTonyFlame(itemNumber, 14); TriggerTonyFlame(itemNumber, 14);
TriggerDynamicLight(handPos.x, handPos.y, handPos.z, 12, r, g, b); 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. 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 TribesmanAxeBite = CreatureBiteInfo(Vector3i(0, 56, 265), 13);
const auto TribesmanDartBite1 = BiteInfo(Vector3(0.0f, 0.0f, -200.0f), 13); const auto TribesmanDartBite1 = CreatureBiteInfo(Vector3i(0, 0, -200), 13);
const auto TribesmanDartBite2 = BiteInfo(Vector3(8.0f, 40.0f, -248.0f), 13); const auto TribesmanDartBite2 = CreatureBiteInfo(Vector3i(8, 40, -248), 13);
const auto TribesmanAxeAttackJoints = std::vector<unsigned int>{ 13 }; const auto TribesmanAxeAttackJoints = std::vector<unsigned int>{ 13 };
const auto TribesmanDartAttackJoints = std::vector<unsigned int>{ 10, 13 }; // TODO: Check. 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->ObjectNumber = ID_DARTS;
dartItem->RoomNumber = item->RoomNumber; 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 pos2 = GetJointPosition(LaraItem, LM_TORSO);
auto orient = Geometry::GetOrientToPoint(pos1.ToVector3(), pos2.ToVector3()); auto orient = Geometry::GetOrientToPoint(pos1.ToVector3(), pos2.ToVector3());
@ -389,7 +389,7 @@ namespace TEN::Entities::Creatures::TR3
pos1 = Vector3i(TribesmanDartBite2.Position); pos1 = Vector3i(TribesmanDartBite2.Position);
pos1.z += 96; 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);
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); SetAnimation(item, WALL_MOUNTED_BLADE_ANIM_IDLE);
// Used for GenericSphereBoxCollision. // Used for GenericSphereBoxCollision.
item.ItemFlags[0] = WALL_MOUNTED_BLADE_JOINT; item.ItemFlags[0] = WALL_MOUNTED_BLADE_JOINT; // Damaging mesh joint.
item.ItemFlags[3] = WALL_MOUNTED_BLADE_HARM_DAMAGE; 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() ! item.ItemFlags[4] = 1; // NOTE: avoid the blade pushing lara in GenericSphereBoxCollision() !
} }

View file

@ -47,7 +47,7 @@ namespace TEN::Entities::Vehicles
VehicleMountType::Back 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 enum BigGunState
{ {
@ -125,7 +125,7 @@ namespace TEN::Entities::Vehicles
auto* projectileItem = &g_Level.Items[itemNumber]; auto* projectileItem = &g_Level.Items[itemNumber];
projectileItem->ObjectNumber = ID_ROCKET; 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); auto probe = GetCollision(pos.x, pos.y, pos.z, bigGunItem->RoomNumber);
projectileItem->RoomNumber = probe.RoomNumber; projectileItem->RoomNumber = probe.RoomNumber;
projectileItem->Pose.Position = pos; projectileItem->Pose.Position = pos;

View file

@ -27,14 +27,14 @@ using namespace TEN::Math;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
BiteInfo QuadBikeEffectsPositions[6] = const CreatureBiteInfo QuadBikeEffectsPositions[6] =
{ {
{ -56, -32, -380, 0 }, CreatureBiteInfo(Vector3i(-56, -32, -380), 0),
{ 56, -32, -380, 0 }, CreatureBiteInfo(Vector3i(56, -32, -380), 0),
{ -8, 180, -48, 3 }, CreatureBiteInfo(Vector3i(-8, 180, -48), 3),
{ 8, 180, -48, 4 }, CreatureBiteInfo(Vector3i(8, 180, -48), 4),
{ 90, 180, -32, 6 }, CreatureBiteInfo(Vector3i(90, 180, -32), 6),
{ -90, 180, -32, 7 } CreatureBiteInfo(Vector3i(-90, 180, -32), 7)
}; };
const vector<VehicleMountType> QuadBikeMountTypes = const vector<VehicleMountType> QuadBikeMountTypes =
{ {
@ -1207,8 +1207,7 @@ namespace TEN::Entities::Vehicles
for (int i = 0; i < 2; i++) 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); angle = quadBikeItem->Pose.Orientation.y + ((i == 0) ? 0x9000 : 0x7000);
if (quadBikeItem->Animation.Velocity.z > 32) 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_RATE ANGLE(0.6f)
#define UPV_LEAN_MAX ANGLE(10.0f) #define UPV_LEAN_MAX ANGLE(10.0f)
BiteInfo UPVBites[6] = const CreatureBiteInfo UPVBites[6] =
{ {
{ 0, 0, 0, 3 }, CreatureBiteInfo(Vector3i(0, 0, 0), 3),
{ 0, 96, 256, 0 }, CreatureBiteInfo(Vector3i(0, 96, 256), 0),
{ -128, 0, 64, 1 }, CreatureBiteInfo(Vector3i(-128, 0, 64), 1),
{ 0, 0, -64, 1 }, CreatureBiteInfo(Vector3i(0, 0, -64), 1),
{ 128, 0, 64, 2 }, CreatureBiteInfo(Vector3i(128, 0, 64), 2),
{ 0, 0, -64, 2 } CreatureBiteInfo(Vector3i(0, 0, -64), 2)
}; };
const std::vector<VehicleMountType> UPVMountTypes = const std::vector<VehicleMountType> UPVMountTypes =
{ {
VehicleMountType::LevelStart, VehicleMountType::LevelStart,
@ -304,7 +305,7 @@ namespace TEN::Entities::Vehicles
if (UPV->Velocity) 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)); 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)); auto sphere = BoundingSphere(pos, BLOCK(1 / 32.0f));
@ -322,7 +323,7 @@ namespace TEN::Entities::Vehicles
for (int lp = 0; lp < 2; lp++) for (int lp = 0; lp < 2; lp++)
{ {
int random = 31 - (GetRandomControl() & 3); 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.x,
UPVBites[UPV_BITE_FRONT_LIGHT].Position.y, UPVBites[UPV_BITE_FRONT_LIGHT].Position.y,
(int)UPVBites[UPV_BITE_FRONT_LIGHT].Position.z << (lp * 6) (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->radius = 102;
obj->intelligent = true; obj->intelligent = true;
obj->pivotLength = 0; obj->pivotLength = 0;
obj->biteOffset = 0;
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y); obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
obj->SetBoneRotationFlags(13, ROT_Y); obj->SetBoneRotationFlags(13, ROT_Y);
obj->SetupHitEffect(); obj->SetupHitEffect();

View file

@ -13,7 +13,6 @@
#include "Game/people.h" #include "Game/people.h"
#include "Game/room.h" #include "Game/room.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/Generic/Traps/traps.h"
#include "Objects/TR4/Entity/WraithInfo.h" #include "Objects/TR4/Entity/WraithInfo.h"
#include "Objects/objectslist.h" #include "Objects/objectslist.h"
#include "Sound/sound.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_VIEW_ANGLE = ANGLE(45.0f);
constexpr auto AHMET_ENEMY_ANGLE = ANGLE(90.0f); constexpr auto AHMET_ENEMY_ANGLE = ANGLE(90.0f);
const auto AhmetBiteLeft = BiteInfo(Vector3::Zero, 16); const auto AhmetBiteLeft = CreatureBiteInfo(Vector3i::Zero, 16);
const auto AhmetBiteRight = BiteInfo(Vector3::Zero, 22); const auto AhmetBiteRight = CreatureBiteInfo(Vector3i::Zero, 22);
const auto AhmetBiteJaw = BiteInfo(Vector3::Zero, 11); const auto AhmetBiteJaw = CreatureBiteInfo(Vector3i::Zero, 11);
const auto AhmetSwipeAttackLeftJoints = std::vector<unsigned int>{ 14, 15, 16, 17 }; const auto AhmetSwipeAttackLeftJoints = std::vector<unsigned int>{ 14, 15, 16, 17 };
const auto AhmetSwipeAttackRightJoints = std::vector<unsigned int>{ 20, 21, 22, 23 }; 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_BABOON_COUNT = -2;
constexpr auto NO_CROWBAR_SWITCH_FOUND = -1; 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 BaboonAttackJoints = std::vector<unsigned int>{ 11, 12 };
const auto BaboonAttackRightJoints = std::vector<unsigned int>{ 1, 2, 3, 5, 8, 9 }; const auto BaboonAttackRightJoints = std::vector<unsigned int>{ 1, 2, 3, 5, 8, 9 };
const auto BaboonJumpAttackJoints = std::vector<unsigned int>{ 3, 4, 8 }; 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; constexpr auto BADDY_UZI_AMMO = 24;
const auto BaddyGunBite = BiteInfo(Vector3(0.0f, -16.0f, 200.0f), 11); const auto BaddyGunBite = CreatureBiteInfo(Vector3i(-5, 200, 50), 11);
const auto BaddySwordBite = BiteInfo(Vector3::Zero, 15); const auto BaddySwordBite = CreatureBiteInfo(Vector3i::Zero, 15);
const auto BaddySwordAttackJoints = std::vector<unsigned int>{ 14, 15, 16 }; const auto BaddySwordAttackJoints = std::vector<unsigned int>{ 14, 15, 16 };
enum BaddyState enum BaddyState
@ -330,6 +330,9 @@ namespace TEN::Entities::TR4
// TODO: better add a second control routine for baddy 2 instead of mixing them? // TODO: better add a second control routine for baddy 2 instead of mixing them?
short objectNumber = (Objects[ID_BADDY2].loaded ? ID_BADDY2 : ID_BADDY1); short objectNumber = (Objects[ID_BADDY2].loaded ? ID_BADDY2 : ID_BADDY1);
if (creature->MuzzleFlash[0].Delay != 0)
creature->MuzzleFlash[0].Delay--;
bool roll = false; bool roll = false;
bool jump = false; bool jump = false;
@ -419,14 +422,6 @@ namespace TEN::Entities::TR4
item->ItemFlags[1] = item->RoomNumber; 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; CollisionResult probe;
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
@ -1158,13 +1153,13 @@ namespace TEN::Entities::TR4
break; break;
} }
creature->FiredWeapon = 1;
if (!item->HitStatus) if (!item->HitStatus)
item->ItemFlags[2]--; item->ItemFlags[2]--;
if (!ShotLara(item, &AI, BaddyGunBite, joint1, 15)) if (!ShotLara(item, &AI, BaddyGunBite, joint1, 15))
item->Animation.TargetState = BADDY_STATE_IDLE; item->Animation.TargetState = BADDY_STATE_IDLE;
creature->MuzzleFlash[0].Bite = BaddyGunBite;
creature->MuzzleFlash[0].Delay = 2;
break; break;

View file

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

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_RUN_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto CROC_STATE_SWIM_TURN_RATE_MAX = ANGLE(3.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 }; const auto CrocodileBiteAttackJoints = std::vector<unsigned int>{ 8, 9 };
enum CrocodileState enum CrocodileState
@ -115,7 +115,7 @@ namespace TEN::Entities::TR4
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
auto* object = &Objects[item->ObjectNumber]; auto* object = &Objects[item->ObjectNumber];
auto* creature = GetCreatureInfo(item); auto* creature = GetCreatureInfo(item);
auto head = EulerAngles::Zero, torso = EulerAngles::Zero;
short angle = 0; short angle = 0;
short boneAngle = 0; short boneAngle = 0;
AI_INFO AI; 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) 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; head.y = AI.angle / 3;
boneRot.bone1 = AI.angle / 2; head.x = AI.angle / 2;
boneRot.bone2 = 0; torso.y = 0;
boneRot.bone3 = 0; torso.x = 0;
} }
else else
{ {
boneRot.bone0 = boneAngle; head.y = boneAngle;
boneRot.bone1 = boneAngle; head.x = boneAngle;
boneRot.bone2 = -boneAngle; torso.y = -boneAngle;
boneRot.bone3 = -boneAngle; torso.x = -boneAngle;
} }
CreatureTilt(item, 0); CreatureTilt(item, 0);
CreatureJoint(item, 0, boneRot.bone0); CreatureJoint(item, 0, head.y);
CreatureJoint(item, 1, boneRot.bone1); CreatureJoint(item, 1, head.x);
CreatureJoint(item, 2, boneRot.bone2); CreatureJoint(item, 2, torso.y);
CreatureJoint(item, 3, boneRot.bone3); CreatureJoint(item, 3, torso.x);
CreatureAnimation(itemNumber, angle, 0); CreatureAnimation(itemNumber, angle, 0);
if (item->Animation.ActiveState < CROC_STATE_SWIM_FORWARD) 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_BITE_ATTACK_RANGE = SQUARE(BLOCK(0.55));
constexpr auto DOG_JUMP_ATTACK_RANGE = SQUARE(BLOCK(1)); 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 DogJumpAttackJoints = std::vector<unsigned int>{ 3, 6, 9, 10, 13, 14 };
const auto DogBiteAttackJoints = std::vector<unsigned int>{ 3, 6 }; 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; constexpr auto GUIDE_ATTACK_DAMAGE = 20;
const auto GuideBite1 = BiteInfo(Vector3(0.0f, 20.0f, 180.0f), 18); const auto GuideBite1 = CreatureBiteInfo(Vector3i(0, 20, 180), 18);
const auto GuideBite2 = BiteInfo(Vector3(30.0f, 80.0f, 50.0f), 15); const auto GuideBite2 = CreatureBiteInfo(Vector3i(30, 80, 50), 15);
const auto GuideLeftFingerSwapJoints = std::vector<unsigned int>{ 15 }; const auto GuideLeftFingerSwapJoints = std::vector<unsigned int>{ 15 };
const auto GuideRightHandSwapJoints = std::vector<unsigned int>{ 18 }; const auto GuideRightHandSwapJoints = std::vector<unsigned int>{ 18 };
const auto GuideHeadSwapJoints = std::vector<unsigned int>{ 21 }; const auto GuideHeadSwapJoints = std::vector<unsigned int>{ 21 };
@ -119,7 +119,7 @@ namespace TEN::Entities::TR4
// Ignite torch. // Ignite torch.
if (item->ItemFlags[1] == 2) 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); TriggerFireFlame(pos.x, pos.y - 20, pos.z, FlameType::Trail);
SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->Pose); SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->Pose);
@ -479,7 +479,7 @@ namespace TEN::Entities::TR4
break; break;
case GUIDE_STATE_IGNITE_TORCH: 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; frameNumber = item->Animation.FrameNumber - g_Level.Anims[item->Animation.AnimNumber].frameBase;
random = GetRandomControl(); random = GetRandomControl();

View file

@ -16,7 +16,7 @@ namespace TEN::Entities::TR4
constexpr auto HAMMERHEAD_BITE_ATTACK_DAMAGE = 120; constexpr auto HAMMERHEAD_BITE_ATTACK_DAMAGE = 120;
constexpr auto HAMMERHEAD_ATTACK_RANGE = SQUARE(BLOCK(0.66f)); 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 }; const auto HammerheadBiteAttackJoints = std::vector<unsigned int>{ 10, 12, 13 };
enum HammerheadState enum HammerheadState

View file

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

View file

@ -20,17 +20,16 @@ using namespace TEN::Math;
namespace TEN::Entities::TR4 namespace TEN::Entities::TR4
{ {
const auto HorsemanBite1 = BiteInfo(Vector3::Zero, 6); const auto HorsemanBite1 = CreatureBiteInfo(Vector3i::Zero, 6);
const auto HorsemanBite2 = BiteInfo(Vector3::Zero, 14); const auto HorsemanBite2 = CreatureBiteInfo(Vector3i::Zero, 14);
const auto HorsemanBite3 = BiteInfo(Vector3::Zero, 10); const auto HorsemanBite3 = CreatureBiteInfo(Vector3i::Zero, 10);
const auto HorsemanAxeAttackJoints = std::vector<unsigned int>{ 5, 6 }; const auto HorsemanAxeAttackJoints = std::vector<unsigned int>{ 5, 6 };
const auto HorsemanKickAttackJoints = std::vector<unsigned int>{ 14 }; const auto HorsemanKickAttackJoints = std::vector<unsigned int>{ 14 };
const auto HorsemanMountedAttackJoints = std::vector<unsigned int>{ 5, 6, 10 }; const auto HorsemanMountedAttackJoints = std::vector<unsigned int>{ 5, 6, 10 };
const auto HorsemanShieldAttackJoints = std::vector<unsigned int>{ 10 }; const auto HorsemanShieldAttackJoints = std::vector<unsigned int>{ 10 };
const auto HorseBite1 = CreatureBiteInfo(Vector3i::Zero, 13);
const auto HorseBite1 = BiteInfo(Vector3::Zero, 13); const auto HorseBite2 = CreatureBiteInfo(Vector3i::Zero, 17);
const auto HorseBite2 = BiteInfo(Vector3::Zero, 17); const auto HorseBite3 = CreatureBiteInfo(Vector3i::Zero, 19);
const auto HorseBite3 = BiteInfo(Vector3::Zero, 19);
enum HorsemanState 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_IDLE_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto KTEMPLAR_WALK_TURN_RATE_MAX = ANGLE(7.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 }; const auto KnightTemplarSwordAttackJoints = std::vector<unsigned int>{ 10, 11 };
enum KnightTemplarState 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_WALK_TURN_RATE_MAX = ANGLE(7.0f);
constexpr auto MUMMY_ATTACK_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 MummyBite1 = CreatureBiteInfo(Vector3i::Zero, 11);
const auto MummyBite2 = BiteInfo(Vector3::Zero, 14); const auto MummyBite2 = CreatureBiteInfo(Vector3i::Zero, 14);
const auto MummySwipeAttackJoints = std::vector<unsigned int>{ 11, 14 }; const auto MummySwipeAttackJoints = std::vector<unsigned int>{ 11, 14 };
enum MummyState enum MummyState

View file

@ -271,7 +271,7 @@ namespace TEN::Entities::TR4
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
auto* creature = GetCreatureInfo(item); auto* creature = GetCreatureInfo(item);
OBJECT_BONES mutantJoint; auto head = EulerAngles::Zero, torso = EulerAngles::Zero;
int frameNumber; int frameNumber;
short angle = 0; short angle = 0;
short headY = 0; short headY = 0;
@ -286,7 +286,7 @@ namespace TEN::Entities::TR4
AI_INFO AI; AI_INFO AI;
MutantAIFix(item, &AI); MutantAIFix(item, &AI);
RotateHeadToTarget(item, creature, 9, headY); RotateHeadToTarget(item, creature, 9, head.y);
GetCreatureMood(item, &AI, true); GetCreatureMood(item, &AI, true);
CreatureMood(item, &AI, true); CreatureMood(item, &AI, true);
@ -350,14 +350,16 @@ namespace TEN::Entities::TR4
} }
if (item->Animation.ActiveState != MUTANT_STATE_LOCUST_ATTACK_1) if (item->Animation.ActiveState != MUTANT_STATE_LOCUST_ATTACK_1)
mutantJoint = OBJECT_BONES(headY, AI.xAngle, true); {
else head.x = AI.xAngle;
mutantJoint = OBJECT_BONES(0); torso.x = AI.xAngle;
torso.y = AI.angle;
}
CreatureJoint(item, 0, mutantJoint.bone0); CreatureJoint(item, 0, head.y);
CreatureJoint(item, 1, mutantJoint.bone1); CreatureJoint(item, 1, head.x);
CreatureJoint(item, 2, mutantJoint.bone2); CreatureJoint(item, 2, torso.y);
CreatureJoint(item, 3, mutantJoint.bone3); CreatureJoint(item, 3, torso.x);
CreatureAnimation(itemNumber, angle, 0); 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_WALK_RANGE = SQUARE(BLOCK(2));
constexpr auto SAS_SHOOT_RANGE = SQUARE(BLOCK(3)); 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 SasDragBodyPosition = Vector3i(0, 0, -460);
const auto SasDragBounds = ObjectCollisionBounds const auto SasDragBounds = ObjectCollisionBounds
@ -145,13 +145,8 @@ namespace TEN::Entities::TR4
short joint1 = 0; short joint1 = 0;
short joint2 = 0; short joint2 = 0;
// Handle SAS firing. if (creature.MuzzleFlash[0].Delay != 0)
if (creature.FiredWeapon) creature.MuzzleFlash[0].Delay--;
{
auto pos = GetJointPosition(&item, SasGunBite.meshNum, Vector3i(SasGunBite.Position));
TriggerDynamicLight(pos.x, pos.y, pos.z, 10, 24, 16, 4);
creature.FiredWeapon--;
}
if (item.HitPoints > 0) if (item.HitPoints > 0)
{ {
@ -522,14 +517,15 @@ namespace TEN::Entities::TR4
joint1 = AI.xAngle; joint1 = AI.xAngle;
} }
if (creature.Flags) if (creature.Flags != 0)
{ {
creature.Flags -= 1; creature.Flags -= 1;
} }
else else
{ {
ShotLara(&item, &AI, SasGunBite, joint0, SAS_SHOT_DAMAGE); ShotLara(&item, &AI, SasGunBite, joint0, SAS_SHOT_DAMAGE);
creature.FiredWeapon = 3; creature.MuzzleFlash[0].Bite = SasGunBite;
creature.MuzzleFlash[0].Delay = 2;
creature.Flags = 5; creature.Flags = 5;
} }
@ -540,9 +536,6 @@ namespace TEN::Entities::TR4
item.Animation.TargetState = SAS_STATE_WAIT; item.Animation.TargetState = SAS_STATE_WAIT;
break; break;
default:
break;
} }
if (FlashGrenadeAftershockTimer > 100 && if (FlashGrenadeAftershockTimer > 100 &&
@ -659,7 +652,7 @@ namespace TEN::Entities::TR4
grenadeItem->ObjectNumber = ID_GRENADE; grenadeItem->ObjectNumber = ID_GRENADE;
grenadeItem->RoomNumber = item.RoomNumber; grenadeItem->RoomNumber = item.RoomNumber;
auto pos = GetJointPosition(&item, SasGunBite.meshNum, Vector3i(SasGunBite.Position)); auto pos = GetJointPosition(&item, SasGunBite);
grenadeItem->Pose.Position = pos; grenadeItem->Pose.Position = pos;
auto floorHeight = GetCollision(pos.x, pos.y, pos.z, grenadeItem->RoomNumber).Position.Floor; 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 namespace TEN::Entities::TR4
{ {
const auto SentryGunFlameOffset = Vector3i(-140, 0, 0); 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) void InitializeSentryGun(short itemNumber)
{ {
@ -51,7 +51,7 @@ namespace TEN::Entities::TR4
{ {
if (item->ItemFlags[0]) 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); TriggerDynamicLight(pos.x, pos.y, pos.z, 4 * item->ItemFlags[0] + 12, 24, 16, 4);
item->ItemFlags[0]--; item->ItemFlags[0]--;
} }

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