Merge remote-tracking branch 'origin/master'

This commit is contained in:
hispidence 2023-01-29 18:53:43 +00:00
commit 005e5b25f8
71 changed files with 2069 additions and 1203 deletions

View file

@ -6,25 +6,30 @@ Version 1.0.6
* Fix savegame crash for disabled enemies with partially set activation mask.
* Fix certain enemies not damaging Lara if binoculars or lasersight mode is active.
* Fix invisible Lara after starting a new game from title flyby with hidden Lara.
* Fix double activation of TR4 seth blades when used an OCB different than 0.
* Fix sting attack of TR4 harpy which was neither hurting nor poisoning Lara.
* Fix backholster weapons not updating their sound position together with player.
* Fix black screen bug when there was an obstacle between the fixed camera and the target.
* Fix underwater caustics not appearing without visiting options menu beforehand.
* Fix TR1 rat which crashed the game when it was killed.
* Fix TR1 ape climbing.
* Fix TR1 rat which crashed the game when it was killed.
* Fix TR2 small spider climbing and pathfinding.
* Fix TR4 skeleton spawn when used with OCB 3.
* Fix TR4 SAS teleporting over the blocks he walks by.
* Fix TR3 Shiva and TR4 baddy 2 not blocking bullets.
* Fix TR4 harpy's sting attack which was neither hurting nor poisoning Lara.
* Fix TR4 SAS teleporting over the blocks he walks by.
* Fix TR4 seth blades that were doing a double activation when used an OCB different than 0.
* Fix TR4 skeleton spawn when used with OCB 3.
* Fix TR4 sphinx solving his bugged behaviour that happened if it received a lot of damage.
* Fix TR5 Roman statue and its meshswap.
* Fix TR5 twogun laser guard.
* Fix enemy projectile effect colours.
* Fix enemy shadow position.
* Fix sound positions not updated during flybys.
* Make enemies drop pickups at their origin point, not bounding box centerpoint.
* Fix grenade launcher super ammo emitting too many fragments.
* Fix grenade and rocket launcher lighting.
* Fix ceiling trapdoor and floor trapdoor that Lara couldn't open manually.
* Make enemies drop pickups at first available bounding box corner point, not centerpoint.
* Restore original volumetric explosion effects.
* Add TR3 lizard and Puna.
* Add TR3 boss effects in ID_BOSS_SHIELD and ID_BOSS_SHOCKWAVE_EXPLOSION slots.
* Add undead flag to TR4 sphinx to prevent it from becoming bugged after receiving a lot of damage.
* Add an option to activate Lua or node events from legacy triggers.
* Add more warnings in logs to enemies which animation or required slot is missing.
* Antitriggering an enemy will now cause it to vanish and pause.
@ -37,6 +42,8 @@ Lua API changes
* Moveable:MeshIsVisible is now GetMeshVisible.
* Moveable:SetMeshVisible has been added to replace ShowMesh/HideMesh.
* Moveable:MeshIsSwapped is now GetMeshSwapped
* Primitive Classes (Color, Rotation, Vec3) can now be saved via Levelvars and Gamevars variables.
* OnSave function now gets called just before data is saved, rather than just after.
* Add new function CameraObject::PlayCamera()
* Add new function Misc::GetCameraType()

View file

@ -26,7 +26,7 @@ Flow.EnableLaraInTitle(false)
-- Disable/enable level selection in title level
Flow.EnableLevelSelect(false)
Flow.EnableLevelSelect(true)
-- Disable/enable mass pickup (collect all pickups at once)

View file

@ -2,24 +2,24 @@
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_basic.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_jump.h"
#include "Game/Lara/lara_tests.h"
#include "Game/Lara/lara_monkey.h"
#include "Game/Lara/lara_crawl.h"
#include "Game/Lara/lara_objects.h"
#include "Game/Lara/lara_hang.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_slide.h"
#include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_surface.h"
#include "Game/Lara/lara_swim.h"
#include "Game/Lara/lara_one_gun.h"
#include "Game/Lara/lara_cheat.h"
#include "Game/Lara/lara_climb.h"
#include "Game/Lara/lara_collide.h"
#include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_crawl.h"
#include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_hang.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_initialise.h"
#include "Game/Lara/lara_jump.h"
#include "Game/Lara/lara_monkey.h"
#include "Game/Lara/lara_objects.h"
#include "Game/Lara/lara_one_gun.h"
#include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_slide.h"
#include "Game/Lara/lara_surface.h"
#include "Game/Lara/lara_swim.h"
#include "Game/Lara/lara_tests.h"
#include "Game/animation.h"
#include "Game/camera.h"
@ -34,17 +34,17 @@
#include "Game/items.h"
#include "Game/misc.h"
#include "Game/savegame.h"
#include "Flow/ScriptInterfaceFlowHandler.h"
#include "ScriptInterfaceLevel.h"
#include "Sound/sound.h"
#include "Renderer/Renderer11.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Scripting/Include/ScriptInterfaceLevel.h"
#include "Sound/sound.h"
using namespace TEN::Control::Volumes;
using namespace TEN::Effects::Items;
using namespace TEN::Floordata;
using namespace TEN::Input;
using namespace TEN::Math;
using std::function;
using TEN::Renderer::g_Renderer;
LaraInfo Lara = {};
@ -52,7 +52,7 @@ ItemInfo* LaraItem;
CollisionInfo LaraCollision = {};
byte LaraNodeUnderwater[NUM_LARA_MESHES];
function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1] =
std::function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1] =
{
lara_as_walk_forward,
lara_as_run_forward,
@ -234,7 +234,7 @@ function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1] =
lara_as_turn_180,//173
};
function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1] =
std::function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1] =
{
lara_col_walk_forward,
lara_col_run_forward,

View file

@ -799,9 +799,8 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
auto ray = Ray(origin, directionNorm);
int num = GetSpheres(targetEntity, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
int bestItemNumber = NO_ITEM;
int bestJointIndex = NO_JOINT;
float bestDistance = INFINITY;
for (int i = 0; i < num; i++)
{
auto sphere = BoundingSphere(Vector3(CreatureSpheres[i].x, CreatureSpheres[i].y, CreatureSpheres[i].z), CreatureSpheres[i].r);
@ -811,7 +810,7 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
if (distance < bestDistance)
{
bestDistance = distance;
bestItemNumber = i;
bestJointIndex = i;
}
}
}
@ -824,7 +823,7 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
GetFloor(pos.x, pos.y, pos.z, &roomNumber);
vOrigin.RoomNumber = roomNumber;
if (bestItemNumber < 0)
if (bestJointIndex < 0)
{
auto vTarget = GameVector(target);
GetTargetOnLOS(&vOrigin, &vTarget, false, true);
@ -836,10 +835,10 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
target = origin + (directionNorm * bestDistance);
auto vTarget = GameVector(target);
// NOTE: It seems that items hit by the player in the normal way must have GetTargetOnLOS return false.
// It's strange, but this will replicate original behaviour until we fully understand what is happening.
// NOTE: It seems that entities hit by the player in the normal way must have GetTargetOnLOS return false.
// It's strange, but this replicates original behaviour until we fully understand what is happening.
if (!GetTargetOnLOS(&vOrigin, &vTarget, false, true))
HitTarget(laraItem, targetEntity, &vTarget, weapon.Damage, false);
HitTarget(laraItem, targetEntity, &vTarget, weapon.Damage, false, bestJointIndex);
return FireWeaponType::PossibleHit;
}
@ -1047,7 +1046,7 @@ void LaraTargetInfo(ItemInfo* laraItem, const WeaponInfo& weaponInfo)
lara.TargetArmOrient = orient;
}
void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive)
void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive, int bestJointIndex)
{
const auto& object = Objects[targetEntity->ObjectNumber];
@ -1058,26 +1057,11 @@ void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, i
if (hitPos != nullptr)
{
hitPos->RoomNumber = targetEntity->RoomNumber;
int foundJointIndex = -1;
for (int jointIndex = 0; jointIndex < object.nmeshes; jointIndex++)
{
const auto& mesh = g_Level.Meshes[object.meshIndex + jointIndex];
auto jointPos = GetJointPosition(targetEntity, jointIndex);
float distance = Vector3::Distance(hitPos->ToVector3(), jointPos.ToVector3());
if (distance < mesh.sphere.Radius)
{
foundJointIndex = jointIndex;
break;
}
}
object.HitRoutine(*targetEntity, *laraItem, *hitPos, damage, isExplosive, foundJointIndex);
object.HitRoutine(*targetEntity, *laraItem, *hitPos, damage, isExplosive, bestJointIndex);
}
else
{
object.HitRoutine(*targetEntity, *laraItem, std::nullopt, damage, isExplosive, NO_JOINT);
object.HitRoutine(*targetEntity, *laraItem, std::nullopt, damage, isExplosive, bestJointIndex);
}
}

View file

@ -62,6 +62,6 @@ FireWeaponType FireWeapon(LaraWeaponType weaponType, ItemInfo* targetEntity, Ite
void FindNewTarget(ItemInfo* laraItem, const WeaponInfo& weaponInfo);
void LaraTargetInfo(ItemInfo* laraItem, const WeaponInfo& weaponInfo);
void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive);
void HitTarget(ItemInfo* laraItem, ItemInfo* targetEntity, GameVector* hitPos, int damage, bool isExplosive, int bestJointIndex = NO_JOINT);
void SmashItem(short itemNumber);

View file

@ -331,7 +331,7 @@ short GetLaraSlideDirection(ItemInfo* item, CollisionInfo* coll)
// Get either:
// a) the surface aspect angle (extended slides), or
// b) the derived nearest cardinal direction from it (original slides).
headingAngle = Geometry::GetSurfaceAspectAngle(probe.FloorTilt);
headingAngle = Geometry::GetSurfaceAspectAngle(Geometry::GetFloorNormal(probe.FloorTilt));
if (g_GameFlow->HasSlideExtended())
return headingAngle;
else
@ -538,14 +538,14 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
{
auto probe = GetCollision(item);
short minSlideAngle = ANGLE(33.75f);
short steepness = Geometry::GetSurfaceSteepnessAngle(probe.FloorTilt);
short direction = Geometry::GetSurfaceAspectAngle(probe.FloorTilt);
//short steepness = Geometry::GetSurfaceSlopeAngle(probe.FloorTilt);
//short direction = Geometry::GetSurfaceAspectAngle(probe.FloorTilt);
float velocityMultiplier = 1 / (float)ANGLE(33.75f);
int slideVelocity = std::min<int>(minVelocity + 10 * (steepness * velocityMultiplier), LARA_TERMINAL_VELOCITY);
//int slideVelocity = std::min<int>(minVelocity + 10 * (steepness * velocityMultiplier), LARA_TERMINAL_VELOCITY);
//short deltaAngle = abs((short)(direction - item->Pose.Orientation.y));
g_Renderer.PrintDebugMessage("%d", slideVelocity);
//g_Renderer.PrintDebugMessage("%d", slideVelocity);
//lara->ExtraVelocity.x += slideVelocity;
//lara->ExtraVelocity.y += slideVelocity * phd_sin(steepness);
@ -556,23 +556,12 @@ void ModulateLaraSlideVelocity(ItemInfo* item, CollisionInfo* coll)
void AlignLaraToSurface(ItemInfo* item, float alpha)
{
// Calculate surface angles.
auto floorTilt = GetCollision(item).FloorTilt;
short aspectAngle = Geometry::GetSurfaceAspectAngle(floorTilt);
short steepnessAngle = std::min(Geometry::GetSurfaceSteepnessAngle(floorTilt), ANGLE(70.0f));
short deltaAngle = Geometry::GetShortestAngle(item->Pose.Orientation.y, aspectAngle);
float sinDeltaAngle = phd_sin(deltaAngle);
float cosDeltaAngle = phd_cos(deltaAngle);
// Calculate extra rotation required.
auto extraRot = EulerAngles(
-steepnessAngle * cosDeltaAngle,
0,
steepnessAngle * sinDeltaAngle
) - EulerAngles(item->Pose.Orientation.x, 0, item->Pose.Orientation.z);
// Determine relative orientation adhering to floor normal.
auto floorNormal = Geometry::GetFloorNormal(GetCollision(item).FloorTilt);
auto orient = Geometry::GetRelOrientToNormal(item->Pose.Orientation.y, floorNormal);
// Apply extra rotation according to alpha.
auto extraRot = orient - item->Pose.Orientation;
item->Pose.Orientation += extraRot * alpha;
}

View file

@ -36,7 +36,7 @@ using namespace TEN::Input;
using namespace TEN::Math;
constexpr auto TRIGGER_TIMEOUT = 5;
constexpr auto GRENADE_FRAG_TIMEOUT = 16;
constexpr auto GRENADE_FRAG_TIMEOUT = 4;
constexpr auto GRENADE_FLASH_TIMEOUT = 4;
constexpr auto HARPOON_VELOCITY = CLICK(1);
@ -1430,7 +1430,8 @@ void HandleProjectile(ItemInfo& item, ItemInfo& emitter, const Vector3i& prevPos
pointColl.Position.Ceiling > item.Pose.Position.y)
{
item.Pose.Position = prevPos;
hasHit = hasHitNotByEmitter = true;
hasHit =
hasHitNotByEmitter = true;
}
}
else if (EmitFromProjectile(item, type))
@ -1441,7 +1442,7 @@ void HandleProjectile(ItemInfo& item, ItemInfo& emitter, const Vector3i& prevPos
if (type == ProjectileType::Explosive && item.ItemFlags[3])
{
// Fire trail and water collision for grenade fragments
// Fire trail and water collision for grenade fragments.
TriggerFireFlame(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, FlameType::Medium);
if (TestEnvironment(ENV_FLAG_WATER, item.RoomNumber))
hasHit = true;

View file

@ -4,6 +4,11 @@
using namespace TEN::Math;
namespace TEN::Renderer
{
struct RendererMesh;
}
struct CreatureInfo;
struct FX_INFO;
struct ItemInfo;
@ -18,11 +23,6 @@ constexpr int NUM_PICKUPS_PIECES = ID_PICKUP_ITEM16_COMBO2 - ID_PICKUP_ITEM1_CO
constexpr int NUM_EXAMINES = ID_EXAMINE8 - ID_EXAMINE1 + 1;
constexpr int NUM_EXAMINES_PIECES = ID_EXAMINE8_COMBO2 - ID_EXAMINE1_COMBO1 + 1;
namespace TEN::Renderer
{
struct RendererMesh;
}
#pragma region state_and_animation
enum LaraState
{

View file

@ -2475,11 +2475,11 @@ bool TestLaraSlideJump(ItemInfo* item, CollisionInfo* coll)
// TODO: Broken on diagonal slides?
if (g_GameFlow->HasSlideExtended())
{
auto probe = GetCollision(item);
auto pointColl = GetCollision(item);
short direction = GetLaraSlideDirection(item, coll);
short steepness = Geometry::GetSurfaceSteepnessAngle(probe.FloorTilt);
return (abs((short)(coll->Setup.ForwardAngle - direction)) <= abs(steepness));
short directionAngle = GetLaraSlideDirection(item, coll);
short slopeAngle = Geometry::GetSurfaceSlopeAngle(Geometry::GetFloorNormal(pointColl.FloorTilt));
return (abs(short(coll->Setup.ForwardAngle - directionAngle)) <= abs(slopeAngle));
}
return true;

View file

@ -3,10 +3,8 @@
#include "Game/Lara/lara_struct.h"
#include "Game/Lara/lara_test_structs.h"
using std::vector;
struct ItemInfo;
struct CollisionInfo;
struct ItemInfo;
// -----------------------------
// TEST FUNCTIONS
@ -48,7 +46,7 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll);
void GetTightropeFallOff(ItemInfo* item, int regularity);
#endif
bool TestLaraWeaponType(LaraWeaponType refWeaponType, const vector<LaraWeaponType>& weaponTypeList);
bool TestLaraWeaponType(LaraWeaponType refWeaponType, const std::vector<LaraWeaponType>& weaponTypeList);
bool IsStandingWeapon(ItemInfo* item, LaraWeaponType weaponType);
bool IsVaultState(int state);
bool IsJumpState(int state);

View file

@ -1,6 +1,7 @@
#include "framework.h"
#include "Game/control/control.h"
#include <chrono>
#include <process.h>
#include "Game/camera.h"
@ -12,10 +13,10 @@
#include "Game/effects/debris.h"
#include "Game/effects/drip.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/explosion.h"
#include "Game/effects/footprint.h"
#include "Game/effects/hair.h"
#include "Game/effects/lightning.h"
#include "Game/effects/simple_particle.h"
#include "Game/effects/smoke.h"
#include "Game/effects/spark.h"
@ -51,14 +52,14 @@
#include "Specific/level.h"
#include "Specific/setup.h"
#include "Specific/winmain.h"
#include <chrono>
using namespace std::chrono;
using namespace TEN::Effects;
using namespace TEN::Effects::Drip;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Explosion;
using namespace TEN::Effects::Footprints;
using namespace TEN::Effects::Lightning;
using namespace TEN::Effects::Smoke;
using namespace TEN::Effects::Spark;
using namespace TEN::Entities::Generic;
@ -68,11 +69,6 @@ using namespace TEN::Floordata;
using namespace TEN::Input;
using namespace TEN::Math;
using namespace TEN::Renderer;
using namespace std::chrono;
using std::string;
using std::unordered_map;
using std::vector;
int GameTimer = 0;
int GlobalCounter = 0;
@ -200,7 +196,8 @@ GameStatus ControlPhase(int numFrames)
UpdateGunShells();
UpdateFootprints();
UpdateSplashes();
UpdateLightning();
UpdateElectricitys();
UpdateHelicalLasers();
UpdateDrips();
UpdateRats();
UpdateBats();

View file

@ -136,23 +136,11 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo
if ((Objects[item->ObjectNumber].explodableMeshbits & ShatterItem.bit) &&
LaserSight)
{
if (item->ObjectNumber == ID_GUARD_LASER)
{
short angle = phd_atan(LaraItem->Pose.Position.z - item->Pose.Position.z, LaraItem->Pose.Position.x - item->Pose.Position.x) - item->Pose.Orientation.y;
if (angle > -ANGLE(90) && angle < ANGLE(90))
{
DoDamage(item, INT_MAX);
HitTarget(LaraItem, item, &target2, Weapons[(int)Lara.Control.Weapon.GunType].Damage, 0);
}
}
else
{
item->MeshBits &= ~ShatterItem.bit;
ShatterImpactData.impactDirection = directionNorm;
ShatterImpactData.impactLocation = Vector3(ShatterItem.sphere.x, ShatterItem.sphere.y, ShatterItem.sphere.z);
ShatterObject(&ShatterItem, 0, 128, target2.RoomNumber, 0);
TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0);
}
TriggerRicochetSpark(target2, LaraItem->Pose.Orientation.y, 3, 0);
}
else
{
@ -162,7 +150,29 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo
Lara.Control.Weapon.GunType == LaraWeaponType::HK))
{
if (object->intelligent)
HitTarget(LaraItem, item, &target2, Weapons[(int)Lara.Control.Weapon.GunType].Damage, 0);
{
const auto& weapon = Weapons[(int)Lara.Control.Weapon.GunType];
int num = GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
auto ray = Ray(origin->ToVector3(), directionNorm);
float bestDistance = INFINITY;
int bestJointIndex = NO_JOINT;
for (int i = 0; i < num; i++)
{
auto sphere = BoundingSphere(Vector3(CreatureSpheres[i].x, CreatureSpheres[i].y, CreatureSpheres[i].z), CreatureSpheres[i].r);
float distance = 0.0f;
if (ray.Intersects(sphere, distance))
{
if (distance < bestDistance)
{
bestDistance = distance;
bestJointIndex = i;
}
}
}
HitTarget(LaraItem, item, &target2, Weapons[(int)Lara.Control.Weapon.GunType].Damage, false, bestJointIndex);
}
else
{
// TR5

View file

@ -334,7 +334,7 @@ void Trigger(short const value, short const flags)
}
}
void TestTriggers(FloorInfo* floor, int x, int y, int z, bool heavy, int heavyFlags)
void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activator, bool heavy, int heavyFlags)
{
int flip = -1;
int flipAvailable = 0;
@ -718,20 +718,18 @@ void TestTriggers(FloorInfo* floor, int x, int y, int z, bool heavy, int heavyFl
(int)VolumeActivatorFlags::Moveable |
(int)VolumeActivatorFlags::NPC : (int)VolumeActivatorFlags::Player;
VolumeActivator dummy = nullptr;
switch (trigger & TIMER_BITS)
{
case 0:
HandleEvent(set.OnEnter, dummy);
HandleEvent(set.OnEnter, activator);
break;
case 1:
HandleEvent(set.OnInside, dummy);
HandleEvent(set.OnInside, activator);
break;
case 2:
HandleEvent(set.OnLeave, dummy);
HandleEvent(set.OnLeave, activator);
break;
}
}
@ -755,7 +753,10 @@ void TestTriggers(FloorInfo* floor, int x, int y, int z, bool heavy, int heavyFl
void TestTriggers(ItemInfo* item, bool heavy, int heavyFlags)
{
TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber, heavy, heavyFlags);
auto roomNum = item->RoomNumber;
auto floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNum);
TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, floor, item->Index, heavy, heavyFlags);
}
void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFlags)
@ -767,7 +768,7 @@ void TestTriggers(int x, int y, int z, short roomNumber, bool heavy, int heavyFl
if (floor->Flags.MarkTriggerer && !floor->Flags.MarkTriggererActive)
return;
TestTriggers(floor, x, y, z, heavy, heavyFlags);
TestTriggers(x, y, z, floor, nullptr, heavy, heavyFlags);
}
void ProcessSectorFlags(ItemInfo* item)

View file

@ -75,7 +75,6 @@ void RefreshCamera(short type, short* data);
int TriggerActive(ItemInfo* item);
short* GetTriggerIndex(FloorInfo* floor, int x, int y, int z);
short* GetTriggerIndex(ItemInfo* item);
void TestTriggers(FloorInfo* floor, int x, int y, int z, 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 ProcessSectorFlags(ItemInfo* item);

View file

@ -0,0 +1,329 @@
#include "framework.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/effects.h"
#include "Game/people.h"
#include "Math/Math.h"
#include "Specific/setup.h"
using namespace TEN::Math;
namespace TEN::Effects::Electricity
{
constexpr auto HELICAL_LASER_LIFE_MAX = 18.0f;
std::vector<Electricity> ElectricityArcs = {};
std::vector<HelicalLaser> HelicalLasers = {};
std::array<Vector3, ELECTRICITY_KNOTS_SIZE> ElectricityKnots = {};
std::array<Vector3, ELECTRICITY_BUFFER_SIZE> ElectricityBuffer = {};
// BIG TODO: Make a family of Bezier, B-Spline, and Catmull-Rom curve classes.
// More standard version.
// TODO: Adopt this in place of the one below.
static Vector3 CatmullRomSpline(float alpha, const std::array<Vector3, 4>& knots)
{
auto point1 = knots[1] + ((knots[2] - knots[0]) * (1 / 6.0f));
auto point2 = knots[2];
auto point3 = knots[2] + ((knots[3] - knots[1]) * (-1 / 6.0f));
auto point4 = knots[3];
auto spline = ((point2 * 2) + (point3 - point1) * alpha) +
(((point1 * 2) - (point2 * 5) + (point3 * 4) - point4) * SQUARE(alpha)) +
(((point1 * -1) + (point2 * 3) - (point3 * 3) + point4) * CUBE(alpha));
return spline;
}
// 4-point Catmull-Rom spline interpolation.
// Function takes reference to array of knots and
// calculates using subset of 4 determined alpha value.
static Vector3 ElectricitySpline(const std::array<Vector3, ELECTRICITY_KNOTS_SIZE>& knots, float alpha)
{
alpha *= ELECTRICITY_KNOTS_SIZE - 3;
int span = alpha;
if (span >= (ELECTRICITY_KNOTS_SIZE - 3))
span = ELECTRICITY_KNOTS_SIZE - 4;
float something = alpha - span;
// Determine subset of 4 knots.
const auto& knot0 = knots[span];
const auto& knot1 = knots[span + 1];
const auto& knot2 = knots[span + 2];
const auto& knot3 = knots[span + 3];
auto point1 = knot1 + (knot1 / 2) - (knot2 / 2) - knot2 + (knot3 / 2) + ((-knot0 - Vector3::One) / 2);
auto ret = point1 * something;
auto point2 = ret + Vector3(2.0f) * knot2 - 2 * knot1 - (knot1 / 2) - (knot3 / 2) + knot0;
ret = point2 * something;
auto point3 = ret + (knot2 / 2) + ((-knot0 - Vector3::One) / 2);
ret = point3 * something;
return (ret + knot1);
}
// TODO: Pass const Vector4& for color.
void SpawnElectricity(const Vector3& origin, const Vector3& target, float amplitude, byte r, byte g, byte b, float life, int flags, float width, unsigned int numSegments)
{
auto arc = Electricity();
arc.pos1 = origin;
arc.pos2 = ((origin * 3) + target) / 4;
arc.pos3 = ((target * 3) + origin) / 4;
arc.pos4 = target;
arc.flags = flags;
for (int i = 0; i < arc.interpolation.size(); i++)
{
if (arc.flags & (int)ElectricityFlags::MoveEnd || i < (arc.interpolation.size() - 1))
{
arc.interpolation[i] = Vector3(
fmod(Random::GenerateInt(), amplitude),
fmod(Random::GenerateInt(), amplitude),
fmod(Random::GenerateInt(), amplitude)) -
Vector3(amplitude/ 2);
}
else
{
arc.interpolation[i] = Vector3::Zero;
}
}
arc.r = r;
arc.g = g;
arc.b = b;
arc.life = life;
arc.segments = numSegments;
arc.amplitude = amplitude;
arc.width = width;
ElectricityArcs.push_back(arc);
}
void SpawnElectricityGlow(const Vector3& pos, float scale, byte r, byte g, byte b)
{
auto& spark = *GetFreeParticle();
spark.on = true;
spark.spriteIndex = Objects[ID_MISC_SPRITES].meshIndex;
spark.blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
spark.x = pos.x;
spark.y = pos.y;
spark.z = pos.z;
spark.xVel = 0;
spark.yVel = 0;
spark.zVel = 0;
spark.sR = r;
spark.sG = g;
spark.sB = b;
spark.dR = r;
spark.dG = g;
spark.dB = b;
spark.life = 4;
spark.sLife = 4;
spark.colFadeSpeed = 2;
spark.fadeToBlack = 0;
spark.scalar = 3;
spark.maxYvel = 0;
spark.gravity = 0;
spark.sSize =
spark.dSize =
spark.size = scale + Random::GenerateInt(0, 4);
spark.flags = SP_DEF | SP_SCALE;
}
void SpawnHelicalLaser(const Vector3& origin, const Vector3& target)
{
constexpr auto SEGMENTS_NUM_MAX = 128;
constexpr auto COLOR = Vector4(0.0f, 0.375f, 1.0f, 1.0f);
constexpr auto LENGTH_MAX = BLOCK(4);
constexpr auto ROTATION = ANGLE(-10.0f);
constexpr auto ELECTRICITY_FLAGS = (int)ElectricityFlags::ThinIn | (int)ElectricityFlags::ThinOut;
auto laser = HelicalLaser();
laser.NumSegments = SEGMENTS_NUM_MAX;
laser.Origin = origin;
laser.Target = target;
laser.Orientation2D = Random::GenerateAngle();
laser.LightPosition = origin;
laser.Color = COLOR;
laser.Life = HELICAL_LASER_LIFE_MAX;
laser.Radius = 0.0f;
laser.Length = LENGTH_MAX / 2;
laser.LengthEnd = LENGTH_MAX;
laser.Opacity = 1.0f;
laser.Rotation = ROTATION;
HelicalLasers.push_back(laser);
SpawnElectricity(origin, target, 1, 0, laser.Color.x * UCHAR_MAX, laser.Color.z * UCHAR_MAX, 20, ELECTRICITY_FLAGS, 19, 5);
SpawnElectricity(origin, target, 1, 110, 255, 250, 20, ELECTRICITY_FLAGS, 4, 5);
SpawnElectricityGlow(laser.LightPosition, 0, 0, (laser.Color.x / 2) * UCHAR_MAX, (laser.Color.z / 2) * UCHAR_MAX);
}
void UpdateHelicalLasers()
{
constexpr auto LIFE_START_FADING = HELICAL_LASER_LIFE_MAX / 2;
constexpr auto LENGTH_LERP_ALPHA = 0.25f;
if (HelicalLasers.empty())
return;
for (auto& laser : HelicalLasers)
{
// Set to despawn.
laser.Life -= 1.0f;
if (laser.Life <= 0.0f)
continue;
// Update length.
laser.Length = Lerp(laser.Length, laser.LengthEnd, LENGTH_LERP_ALPHA);
// Update radius.
laser.Radius += 1 / 8.0f;
// Update opacity.
float alpha = laser.Life / LIFE_START_FADING;
laser.Opacity = Lerp(0.0f, 1.0f, alpha);
// Update orientation.
laser.Orientation2D += laser.Rotation;
}
// Despawn inactive effects.
HelicalLasers.erase(
std::remove_if(
HelicalLasers.begin(), HelicalLasers.end(),
[](const HelicalLaser& laser) { return (laser.Life <= 0.0f); }), HelicalLasers.end());
}
void UpdateElectricitys()
{
// No active effects; return early.
if (ElectricityArcs.empty())
return;
for (auto& arc : ElectricityArcs)
{
// Set to despawn.
if (arc.life <= 0.0f)
continue;
// If/when this behaviour is changed, modify AddLightningArc accordingly.
arc.life -= 2.0f;
if (arc.life > 0.0f)
{
// TODO: Find a better way to do this.
auto* posPtr = (Vector3*)&arc.pos2;
for (auto& interpPos : arc.interpolation)
{
*posPtr += interpPos * 2;
interpPos -= (interpPos / 16);
posPtr++;
}
}
}
// Despawn inactive effects.
ElectricityArcs.erase(
std::remove_if(
ElectricityArcs.begin(), ElectricityArcs.end(),
[](const Electricity& arc) { return (arc.life <= 0.0f); }), ElectricityArcs.end());
}
void CalculateElectricitySpline(const Electricity& arc, const std::array<Vector3, ELECTRICITY_KNOTS_SIZE>& knots, std::array<Vector3, ELECTRICITY_BUFFER_SIZE>& buffer)
{
int bufferIndex = 0;
buffer[bufferIndex] = knots[0];
bufferIndex++;
// Splined arc.
if (arc.flags & (int)ElectricityFlags::Spline)
{
float interpStep = 1.0f / ((arc.segments * 3) - 1);
float alpha = interpStep;
if (((arc.segments * 3) - 2) > 0)
{
for (int i = (arc.segments * 3) - 2; i > 0; i--)
{
auto spline = ElectricitySpline(knots, alpha);
auto sphere = BoundingSphere(Vector3::Zero, 8.0f);
auto offset = Random::GeneratePointInSphere(sphere);
buffer[bufferIndex] = spline + offset;
alpha += interpStep;
bufferIndex++;
}
}
}
// Straight arc.
else
{
int numSegments = (arc.segments * 3) - 1;
auto deltaPos = (knots[knots.size() - 1] - knots[0]) / numSegments;
auto pos = knots[0] + deltaPos + Vector3(
fmod(Random::GenerateInt(), arc.amplitude * 2),
fmod(Random::GenerateInt(), arc.amplitude * 2),
fmod(Random::GenerateInt(), arc.amplitude * 2)) -
Vector3(arc.amplitude);
if (((arc.segments * 3) - 2) > 0)
{
for (int i = (arc.segments * 3) - 2; i > 0; i--)
{
buffer[bufferIndex] = pos;
bufferIndex++;
pos += deltaPos + Vector3(
fmod(Random::GenerateInt(), arc.amplitude * 2),
fmod(Random::GenerateInt(), arc.amplitude * 2),
fmod(Random::GenerateInt(), arc.amplitude * 2)) -
Vector3(arc.amplitude);
}
}
}
buffer[bufferIndex] = knots[5];
}
void CalculateHelixSpline(const HelicalLaser& laser, std::array<Vector3, ELECTRICITY_KNOTS_SIZE>& knots, std::array<Vector3, ELECTRICITY_BUFFER_SIZE>& buffer)
{
int bufferIndex = 0;
buffer[bufferIndex] = knots[0];
bufferIndex++;
auto origin = knots[0];
auto target = knots[1];
auto direction = target - origin;
direction.Normalize();
float lengthStep = laser.Length / laser.NumSegments;
float radiusStep = laser.Radius;
auto refPoint = Geometry::RotatePoint(Vector3::Right, EulerAngles(direction));
auto axisAngle = AxisAngle(direction, laser.Orientation2D);
for (int i = 0; i < laser.NumSegments; i++)
{
axisAngle.SetAngle(axisAngle.GetAngle() + ANGLE(25.0f));
auto offset = Geometry::RotatePoint(refPoint * (radiusStep * i), axisAngle);
auto knot = Geometry::TranslatePoint(offset, axisAngle.GetAxis(), lengthStep * i);
buffer[bufferIndex] = origin + knot;
bufferIndex++;
}
buffer[bufferIndex] = knots[1];
}
}

View file

@ -0,0 +1,77 @@
#pragma once
namespace TEN::Effects::Electricity
{
constexpr auto ELECTRICITY_KNOTS_SIZE = 6;
constexpr auto ELECTRICITY_BUFFER_SIZE = 2048;
enum class ElectricityFlags
{
Spline = (1 << 0),
MoveEnd = (1 << 1),
ThinOut = (1 << 2),
ThinIn = (1 << 3),
SparkEnd = (1 << 4)
};
// TODO: Make sense of this struct.
struct Electricity
{
Vector3 pos1;
Vector3 pos2;
Vector3 pos3;
Vector3 pos4;
std::array<Vector3, 3> interpolation = {};
byte r;
byte g;
byte b;
float life;
float sLife;
float amplitude;
float sAmplitude;
float width;
unsigned int segments;
int segmentSize;
int direction;
int rotation;
int type;
int flags;
};
struct HelicalLaser
{
unsigned int NumSegments = 0;
Vector3 Origin = Vector3::Zero;
Vector3 Target = Vector3::Zero;
short Orientation2D = 0;
Vector3 LightPosition = Vector3::Zero; // TODO: Use light cone instead?
Vector4 Color = Vector4::Zero;
float Life = 0.0f;
float Radius = 0.0f;
float Length = 0.0f;
float LengthEnd = 0.0f;
float Opacity = 0.0f;
short Rotation = 0;
};
extern std::vector<Electricity> ElectricityArcs;
extern std::vector<HelicalLaser> HelicalLasers;
extern std::array<Vector3, ELECTRICITY_KNOTS_SIZE> ElectricityKnots;
extern std::array<Vector3, ELECTRICITY_BUFFER_SIZE> ElectricityBuffer;
void SpawnElectricity(const Vector3& origin, const Vector3& target, float amplitude, byte r, byte g, byte b, float life, int flags, float width, unsigned int numSegments);
void SpawnElectricityGlow(const Vector3& pos, float scale, byte r, byte g, byte b);
void SpawnHelicalLaser(const Vector3& origin, const Vector3& target);
void UpdateElectricitys();
void UpdateHelicalLasers();
void CalculateElectricitySpline(const Electricity& arc, const std::array<Vector3, ELECTRICITY_KNOTS_SIZE>& knots, std::array<Vector3, ELECTRICITY_BUFFER_SIZE>& buffer);
void CalculateHelixSpline(const HelicalLaser& laser, std::array<Vector3, ELECTRICITY_KNOTS_SIZE>& knots, std::array<Vector3, ELECTRICITY_BUFFER_SIZE>& buffer);
}

View file

@ -1,404 +0,0 @@
#include "framework.h"
#include "Game/effects/lightning.h"
#include "Game/animation.h"
#include "Game/effects/bubble.h"
#include "Game/effects/drip.h"
#include "Game/effects/effects.h"
#include "Game/effects/smoke.h"
#include "Game/effects/tomb4fx.h"
#include "Game/Lara/lara.h"
#include "Renderer/Renderer11.h"
#include "Sound/sound.h"
#include "Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/setup.h"
#include "Specific/level.h"
#include "Math/Math.h"
using std::vector;
using TEN::Renderer::g_Renderer;
namespace TEN::Effects::Lightning
{
constexpr auto MAX_ENERGYARCS = 32;
int LightningRandomSeed = 0x0D371F947;
float FloatSinCosTable[8192];
Vector3i LightningPos[6];
short LightningBuffer[1024];
std::vector<LIGHTNING_INFO> Lightning;
void InitialiseFloatSinCosTable()
{
for (int i = 0; i < 8192; i++)
{
FloatSinCosTable[i] = sin(i * 0.000095873802f);
}
}
void UpdateLightning()
{
for (int i = 0; i < Lightning.size(); i++)
{
LIGHTNING_INFO* arc = &Lightning[i];
if (arc->life > 0)
{
// If/when this behaviour is changed, please modify AddLightningArc accordingly
arc->life -= 2;
if (arc->life)
{
int* positions = (int*)&arc->pos2;
for (int j = 0; j < 9; j++)
{
*positions += 2 * arc->interpolation[j];
arc->interpolation[j] = (signed char)(arc->interpolation[j] - (arc->interpolation[j] >> 4));
positions++;
}
}
}
}
if (Lightning.size() > 0)
{
Lightning.erase(
std::remove_if(Lightning.begin(), Lightning.end(),
[](const LIGHTNING_INFO& o) { return o.life == 0; }),
Lightning.end());
}
}
LIGHTNING_INFO* TriggerLightning(Vector3i* origin, Vector3i* target, byte amplitude, byte r, byte g, byte b, byte life, char flags, char width, char segments)
{
LIGHTNING_INFO arc;
arc.pos1 = *origin;
arc.pos2.x = ((origin->x * 3) + target->x) >> 2;
arc.pos2.y = ((origin->y * 3) + target->y) >> 2;
arc.pos2.z = ((origin->z * 3) + target->z) >> 2;
arc.pos3.x = ((target->x * 3) + origin->x) >> 2;
arc.pos3.y = ((target->y * 3) + origin->y) >> 2;
arc.pos3.z = ((target->z * 3) + origin->z) >> 2;
arc.pos4 = *target;
arc.flags = flags;
for (int i = 0; i < 9; i++)
{
if (arc.flags & 2 || i < 6)
arc.interpolation[i] = ((unsigned char)(GetRandomControl() % amplitude) - (unsigned char)(amplitude >> 1));
else
arc.interpolation[i] = 0;
}
arc.r = r;
arc.g = g;
arc.b = b;
arc.life = life;
arc.segments = segments;
arc.amplitude = amplitude;
arc.width = width;
Lightning.push_back(arc);
return &Lightning[Lightning.size() - 1];
}
// New function used in TR5, we'll decompile it in the future
void DrawLightningNew()
{
/*Vector4 positions[32];
Vector3 transformed[32];
short clipBuffer[32];
byte r, g, b;
ENERGY_ARC_VBUFFER vbuffer[32];
for (int i = 0; i < 16; i++)
{
ENERGY_ARC* arc = &EnergyArcs[i];
if (arc->life)
{
float dx = arc->pos0.x - LaraItem->pos.Position.x;
float dy = arc->pos0.y - LaraItem->pos.Position.y;
float dz = arc->pos0.z - LaraItem->pos.Position.z;
float x1 = arc->pos1.x - arc->pos0.x;
float y1 = arc->pos1.y - arc->pos0.y;
float z1 = arc->pos1.z - arc->pos0.z;
float x2 = arc->pos2.x - arc->pos0.x;
float y2 = arc->pos2.y - arc->pos0.y;
float z2 = arc->pos2.z - arc->pos0.z;
float x3 = arc->pos3.x - arc->pos0.x;
float y3 = arc->pos3.y - arc->pos0.y;
float z3 = arc->pos3.z - arc->pos0.z;
double factor = 0;
int seed = LightningRandomSeed;
for (int j = 0; j < 32; j++)
{
float rx = 0;
float ry = 0;
float rz = 0;
if (j > 0 && j < 31)
{
// random number generator
int rndX = 1103515245 * seed + 12345;
int rndY = 1103515245 * rndX + 12345;
int rndZ = 1103515245 * rndY + 12345;
float rx = (float)(((rndX >> 10) & 0xF) - 8);
float ry = (float)(((rndY >> 10) & 0xF) - 8);
float rz = (float)(((rndZ >> 10) & 0xF) - 8);
LightningRandomSeed = rndZ;
}
float x = (1.0 - factor) * SQUARE(factor) * 4.0;
float y = (2 * factor - 1.0) * SQUARE(factor);
float z = SQUARE(1.0 - factor) * factor * 4.0;
positions[j].x = x * x1 + y * x2 + z * x3 + 0.0 + rx + dx;
positions[j].y = x * y1 + y * y2 + z * y3 + 0.0 + ry + dy;
positions[j].z = x * z1 + y * z2 + z * z3 + 0.0 + rz + dz;
positions[j].w = 1.0;
factor += 0.03125f;
}
LightningRandomSeed = seed;
for (int j = 0; j < 32; j++)
{
Vector3 transformed = Vector4::Transform()
pPosY = *(pPosZ - 1);
pPosX = *(pPosZ - 2);
pPosY2 = pPosY;
v36 = transform_view._21 * pPosY;
v37 = 0;
tx = transform_view._31 * *pPosZ + v36 + transform_view._11 * pPosX + transform_view._41;
ty = transform_view._22 * pPosY2 + transform_view._12 * pPosX + transform_view._32 * *pPosZ + transform_view._42;
c_sz = transform_view._33 * *pPosZ
+ transform_view._23 * pPosY2
+ transform_view._13 * pPosX
+ transform_view._43;
if (c_sz < znear)
v37 = -128;
t_persp = f_persp / c_sz;
c_sx = t_persp * tx + f_centerx;
*(D3DTLVERTEX_cy - 1) = c_sx;
*D3DTLVERTEX_cy = t_persp * ty + f_centery;
D3DTLVERTEX_cy[1] = t_persp * f_oneopersp;
if (c_sx >= winX)
{
if ((double)phd_winxmax < *(D3DTLVERTEX_cy - 1))
v37 += 2;
}
else
{
++v37;
}
if (*D3DTLVERTEX_cy >= winY)
{
if ((double)phd_winymax < *D3DTLVERTEX_cy)
v37 += 8;
}
else
{
v37 += 4;
}
D3DTLVERTEX_cy[2] = c_sz;
*(_WORD*)someFlag = v37;
D3DTLVERTEX_cy[3] = tx;
D3DTLVERTEX_cy[4] = ty;
someFlag += 2;
pPosZ += 4;
D3DTLVERTEX_cy += 8;
--v32;
}
}
if (arc->life >= 16)
{
r = arc->r;
g = arc->g;
b = arc->b;
}
else
{
r = arc->r >> 4;
g = arc->g >> 4;
b = arc->b >> 4;
}
float length = 0.0;
float width = (float)(arc->width >> 1);
if (arc->flags & 8)
{
length = width * 0.125;
width = 0.0;
}
else if (arc->flags & 4)
{
length = -(width * 0.03125);
}
for (int j = 0; j < 32; j++)
{
short angle = -phd_atan(vbuffer);
float sinAngle = FloatSinCosTable[angle + 0x4000];
width = max(width, 0);
}
v45 = 0;
v46 = (float*)&v125;
do
{
v47 = -j_phd_atan((signed __int64)(v46[7] - *(v46 - 1)), (signed __int64)(v46[8] - *v46));
v48 = FloatSinCosTable[(unsigned __int16)(v47 + 0x4000)];
v49 = v88;
if (v88 <= 0.0)
v49 = 2.0;
v50 = flt_51D15C / v46[2] * v49;
v51 = (*v42 & 8) == 0;
*v86 = FloatSinCosTable[(unsigned __int16)v47] * v50;
v86[1] = v48 * v50;
v88 = v95 + v88;
if (!v51 && v45 == 8)
{
if (*v42 & 4)
v95 = (double)(unsigned __int8)(*(_BYTE*)(arc + 53) >> 1) * -0.035714287;
else
v95 = 0.0;
*v42 &= 0xF7u;
}
v46 += 8;
++v45;
v86 += 4;
} while (v45 < 32);
}
}*/
}
int LSpline(int x, int* knots, int nk)
{
int* k;
int c1, c2, c3, ret, span;
x *= nk - 3;
span = x >> 16;
if (span >= nk - 3)
span = nk - 4;
x -= 65536 * span;
k = &knots[3 * span];
c1 = k[3] + (k[3] >> 1) - (k[6] >> 1) - k[6] + (k[9] >> 1) + ((-k[0] - 1) >> 1);
ret = (long long)c1 * x >> 16;
c2 = ret + 2 * k[6] - 2 * k[3] - (k[3] >> 1) - (k[9] >> 1) + k[0];
ret = (long long)c2 * x >> 16;
c3 = ret + (k[6] >> 1) + ((-k[0] - 1) >> 1);
ret = (long long)c3 * x >> 16;
return ret + k[3];
}
void CalcLightningSpline(Vector3i* pos, short* buffer, LIGHTNING_INFO* arc)
{
buffer[0] = pos->x;
buffer[1] = pos->y;
buffer[2] = pos->z;
buffer += 4;
if (arc->flags & 1)
{
int dp = 65536 / (3 * arc->segments - 1);
int x = dp;
if (3 * arc->segments - 2 > 0)
{
for (int i = 3 * arc->segments - 2; i > 0; i--)
{
short sx = LSpline(x, &pos->x, 6);
buffer[0] = sx + (GetRandomControl() & 0xF) - 8;
short sy = LSpline(x, &pos->y, 6);
buffer[1] = sy + (GetRandomControl() & 0xF) - 8;
short sz = LSpline(x, &pos->z, 6);
buffer[2] = sz + (GetRandomControl() & 0xF) - 8;
x += dp;
buffer += 4;
}
}
}
else
{
int segments = 3 * arc->segments - 1;
int dx = (pos[5].x - pos->x) / segments;
int dy = (pos[5].y - pos->y) / segments;
int dz = (pos[5].z - pos->z) / segments;
int x = dx + (GetRandomControl() % (2 * arc->amplitude)) - arc->amplitude + pos->x;
int y = dy + (GetRandomControl() % (2 * arc->amplitude)) - arc->amplitude + pos->y;
int z = dz + (GetRandomControl() % (2 * arc->amplitude)) - arc->amplitude + pos->z;
if (3 * arc->segments - 2 > 0)
{
for (int i = 3 * arc->segments - 2; i > 0; i--)
{
buffer[0] = x;
buffer[1] = y;
buffer[2] = z;
x += dx + GetRandomControl() % (2 * arc->amplitude) - arc->amplitude;
y += dy + GetRandomControl() % (2 * arc->amplitude) - arc->amplitude;
z += dz + GetRandomControl() % (2 * arc->amplitude) - arc->amplitude;
buffer += 4;
}
}
}
buffer[0] = pos[5].x;
buffer[1] = pos[5].y;
buffer[2] = pos[5].z;
}
void TriggerLightningGlow(int x, int y, int z, byte size, byte r, byte g, byte b)
{
auto* spark = GetFreeParticle();
spark->dG = g;
spark->sG = g;
spark->life = 4;
spark->sLife = 4;
spark->dR = r;
spark->sR = r;
spark->colFadeSpeed = 2;
spark->blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
spark->on = 1;
spark->dB = b;
spark->sB = b;
spark->fadeToBlack = 0;
spark->x = x;
spark->y = y;
spark->z = z;
spark->xVel = 0;
spark->yVel = 0;
spark->zVel = 0;
spark->flags = SP_DEF | SP_SCALE;
spark->scalar = 3;
spark->maxYvel = 0;
spark->spriteIndex = Objects[ID_MISC_SPRITES].meshIndex;
spark->gravity = 0;
spark->dSize = spark->sSize = spark->size = size + (GetRandomControl() & 3);
}
}

View file

@ -1,58 +0,0 @@
#pragma once
#include <cstddef>
#include <vector>
#include "Math/Math.h"
#include "Math/Math.h"
using std::vector;
class Vector3i;
struct ENERGY_ARC;
namespace TEN::Effects::Lightning
{
enum LightningFlags
{
LI_SPLINE = 1,
LI_MOVEEND = 2,
LI_THINOUT = 4,
LI_THININ = 8,
LI_SPARKEND = 16
};
struct LIGHTNING_INFO
{
Vector3i pos1;
Vector3i pos2;
Vector3i pos3;
Vector3i pos4;
signed char interpolation[9];
byte r;
byte g;
byte b;
byte life;
byte amplitude;
byte flags;
byte width;
byte segments;
int sAmplitude;
int segmentSize;
int direction;
int rotation;
int type;
int sLife;
};
extern std::vector<LIGHTNING_INFO> Lightning;
extern int LightningRandomSeed;
extern float FloatSinCosTable[8192];
extern Vector3i LightningPos[6];
extern short LightningBuffer[1024];
void InitialiseFloatSinCosTable();
void UpdateLightning();
LIGHTNING_INFO* TriggerLightning(Vector3i* origin, Vector3i* target, byte amplitude, byte r, byte g, byte b, byte life, char flags, char width, char segments);
void CalcLightningSpline(Vector3i* pos, short* buffer, LIGHTNING_INFO* arc);
void TriggerLightningGlow(int x, int y, int z, byte size, byte r, byte g, byte b);
}

View file

@ -173,6 +173,10 @@ namespace TEN::Effects::Spark
spark.friction = 34;
spark.maxYvel = 0;
spark.gravity = 0;
spark.scalar = 2;
spark.dSize =
spark.sSize =
spark.size = Random::GenerateInt(44, 48);
spark.flags = SP_NONE;
}

View file

@ -17,6 +17,7 @@
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/setup.h"
#include "Scripting/Internal/TEN/Objects/ObjectIDs.h"
using namespace TEN::Floordata;
using namespace TEN::Input;
@ -638,6 +639,18 @@ int GlobalItemReplace(short search, GAME_OBJECT_ID replace)
return changed;
}
const std::string& GetObjectName(GAME_OBJECT_ID id)
{
for (auto it = kObjIDs.begin(); it != kObjIDs.end(); ++it)
{
if (it->second == id)
return it->first;
}
static const std::string unknownSlot = "UNKNOWN_SLOT";
return unknownSlot;
}
std::vector<int> FindAllItems(short objectNumber)
{
std::vector<int> itemList;

View file

@ -187,6 +187,7 @@ void KillItem(short itemNumber);
bool UpdateItemRoom(short itemNumber);
void UpdateAllItems();
void UpdateAllEffects();
const std::string& GetObjectName(GAME_OBJECT_ID id);
std::vector<int> FindAllItems(short objectNumber);
ItemInfo* FindItem(int objectNumber);
int FindItem(ItemInfo* item);

View file

@ -852,6 +852,9 @@ void DropPickups(ItemInfo* item)
origin.y = yPos; // Initialize drop origin Y point as floor height at centerpoint, in case all corner tests fail.
// Also collect objects which are around.
bool collidedWithObjects = GetCollidedObjects(item, extents.Length(), true, CollidedItems, CollidedMeshes, true);
short startAngle = ANGLE(Random::GenerateInt(0, 3) * 90); // Randomize start corner.
// Iterate through 4 corners and find best-fitting position, which is not inside a wall, not on a slope
@ -884,6 +887,46 @@ void DropPickups(ItemInfo* item)
if (collPoint.Position.Floor == NO_HEIGHT || collPoint.Position.FloorSlope)
continue;
// If position is not in the same room, don't use it.
if (collPoint.RoomNumber != item->RoomNumber)
continue;
// Setup a dummy sphere with 1-click diameter for item and static mesh collision tests.
auto sphere = BoundingSphere(candidatePos, CLICK(0.5f));
bool collidedWithObject = false;
// Iterate through all found items and statics around, and determine if dummy sphere
// intersects any of those. If so, try other corner.
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
{
auto* currentItem = CollidedItems[i];
if (!currentItem)
break;
if (GameBoundingBox(currentItem).ToBoundingOrientedBox(currentItem->Pose).Intersects(sphere))
{
collidedWithObject = true;
break;
}
}
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
{
auto* currentMesh = CollidedMeshes[i];
if (!currentMesh)
break;
if (StaticObjects[currentMesh->staticNumber].collisionBox.ToBoundingOrientedBox(currentMesh->pos).Intersects(sphere))
{
collidedWithObject = true;
break;
}
}
if (collidedWithObject)
continue;
// Finally, do height difference tests. If difference is more than one and a half click,
// most likely it's hanging in the air or submerged, so bypass the corner.
if (abs(collPoint.Position.Floor - yPos) > CLICK(1.5f))

View file

@ -10,6 +10,7 @@
constexpr inline auto PI_DIV_4 = PI / 4;
constexpr inline auto RADIAN = PI / 180;
constexpr inline auto SQRT_2 = 1.41421356237309504880168872420969807856967187537694f;
constexpr inline auto EPSILON = 0.00001f;
constexpr inline auto SQUARE = [](auto x) { return (x * x); };
constexpr inline auto CUBE = [](auto x) { return (x * x * x); };

View file

@ -2,10 +2,11 @@
#include "Math/Geometry.h"
#include "Math/Constants.h"
#include "Math/Containers/EulerAngles.h"
#include "Math/Containers/Pose.h"
#include "Math/Containers/Vector3i.h"
#include "Math/Legacy.h"
#include "Math/Objects/AxisAngle.h"
#include "Math/Objects/EulerAngles.h"
#include "Math/Objects/Pose.h"
#include "Math/Objects/Vector3i.h"
namespace TEN::Math::Geometry
{
@ -76,6 +77,32 @@ namespace TEN::Math::Geometry
return (point + (directionNorm * distance));
}
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rotation)
{
auto rotMatrix = rotation.ToRotationMatrix();
return Vector3::Transform(point, rotMatrix);
}
Vector3 RotatePoint(const Vector3& point, const AxisAngle& rotation)
{
auto rotMatrix = rotation.ToRotationMatrix();
return Vector3::Transform(point, rotMatrix);
}
Vector3 GetFloorNormal(const Vector2& tilt)
{
auto normal = Vector3(-tilt.x / 4, -1.0f, -tilt.y / 4);
normal.Normalize();
return normal;
}
Vector3 GetCeilingNormal(const Vector2& tilt)
{
auto normal = Vector3(tilt.x / 4, 1.0f, tilt.y / 4);
normal.Normalize();
return normal;
}
short GetShortestAngle(short fromAngle, short toAngle)
{
if (fromAngle == toAngle)
@ -84,19 +111,21 @@ namespace TEN::Math::Geometry
return short(toAngle - fromAngle);
}
short GetSurfaceSteepnessAngle(Vector2 tilt)
short GetSurfaceSlopeAngle(const Vector3& normal, const Vector3& force)
{
static const short qtrBlockAngleIncrement = ANGLE(45.0f) / 4;
return (short)sqrt(SQUARE(tilt.x * qtrBlockAngleIncrement) + SQUARE(tilt.y * qtrBlockAngleIncrement));
}
short GetSurfaceAspectAngle(Vector2 tilt)
{
if (tilt == Vector2::Zero)
if (normal == -force)
return 0;
return FROM_RAD(atan2(-tilt.x, -tilt.y));
return FROM_RAD(acos(normal.Dot(-force)));
}
short GetSurfaceAspectAngle(const Vector3& normal, const Vector3& force)
{
if (normal == -force)
return 0;
// TODO: Consider normal of downward force.
return FROM_RAD(atan2(normal.x, normal.z));
}
float GetDistanceToLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1)
@ -129,11 +158,30 @@ namespace TEN::Math::Geometry
return EulerAngles(target - origin);
}
EulerAngles GetRelOrientToNormal(short orient2D, const Vector3& normal, const Vector3& force)
{
// TODO: Consider normal of downward force.
// Determine relative angle properties of normal.
short aspectAngle = Geometry::GetSurfaceAspectAngle(normal);
short slopeAngle = Geometry::GetSurfaceSlopeAngle(normal);
short deltaAngle = Geometry::GetShortestAngle(orient2D, aspectAngle);
float sinDeltaAngle = phd_sin(deltaAngle);
float cosDeltaAngle = phd_cos(deltaAngle);
// Calculate relative orientation adhering to normal.
return EulerAngles(
-slopeAngle * cosDeltaAngle,
orient2D,
slopeAngle * sinDeltaAngle);
}
bool IsPointInFront(const Pose& pose, const Vector3& target)
{
return IsPointInFront(pose.Position.ToVector3(), target, pose.Orientation);
}
bool IsPointInFront(const Vector3& origin, const Vector3& target, const EulerAngles& orient)
{
if (origin == target)

View file

@ -1,13 +1,14 @@
#pragma once
class AxisAngle;
class EulerAngles;
class Pose;
class Vector3i;
namespace TEN::Math::Geometry
{
// Since Y is assumed as the vertical axis, only the Y Euler component needs to be considered and
// 2D vector operations can be done in the XZ plane. Maybe revise geometry functions to each take an "up" vector argument someday.
// Since Y is assumed as the vertical axis, 2D operations are simply done in the XZ plane.
// Revise geometry functions to each take a "force" direction argument someday. -- Sezz 2023.01.26
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, const Vector3i& relOffset);
@ -20,13 +21,20 @@ namespace TEN::Math::Geometry
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, float distance);
Vector3 TranslatePoint(const Vector3& point, const Vector3& direction, float distance);
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rotation);
Vector3 RotatePoint(const Vector3& point, const AxisAngle& rotation);
Vector3 GetFloorNormal(const Vector2& tilt);
Vector3 GetCeilingNormal(const Vector2& tilt);
short GetShortestAngle(short fromAngle, short toAngle);
short GetSurfaceSteepnessAngle(Vector2 tilt);
short GetSurfaceAspectAngle(Vector2 tilt);
short GetSurfaceSlopeAngle(const Vector3& normal, const Vector3& force = Vector3::Up); // Up = Down.
short GetSurfaceAspectAngle(const Vector3& normal, const Vector3& force = Vector3::Up); // Up = Down.
float GetDistanceToLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
Vector3 GetClosestPointOnLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
EulerAngles GetOrientToPoint(const Vector3& origin, const Vector3& target);
EulerAngles GetRelOrientToNormal(short orient2D, const Vector3& normal, const Vector3& force = Vector3::Up); // Up = Down.
bool IsPointInFront(const Pose& pose, const Vector3& target);
bool IsPointInFront(const Vector3& origin, const Vector3& target, const EulerAngles& orient);

View file

@ -18,48 +18,3 @@ int phd_atan(int y, int x)
{
return FROM_RAD(atan2(x, y));
}
void GetMatrixFromTrAngle(Matrix& matrix, short* framePtr, int index)
{
short* ptr = &framePtr[0];
ptr += 9;
for (int i = 0; i < index; i++)
ptr += ((*ptr & 0xc000) == 0) ? 2 : 1;
int rot0 = *ptr++;
int frameMode = rot0 & 0xc000;
int rot1;
int rotX;
int rotY;
int rotZ;
switch (frameMode)
{
case 0:
rot1 = *ptr++;
rotX = (rot0 & 0x3ff0) >> 4;
rotY = ((rot1 & 0xfc00) >> 10) | ((rot0 & 0xf) << 6) & 0x3ff;
rotZ = (rot1) & 0x3ff;
matrix = Matrix::CreateFromYawPitchRoll(
rotY * (360.0f / 1024.0f) * RADIAN,
rotX * (360.0f / 1024.0f) * RADIAN,
rotZ * (360.0f / 1024.0f) * RADIAN);
break;
case 0x4000:
matrix = Matrix::CreateRotationX((rot0 & 0xfff) * (360.0f / 4096.0f) * RADIAN);
break;
case 0x8000:
matrix = Matrix::CreateRotationY((rot0 & 0xfff) * (360.0f / 4096.0f) * RADIAN);
break;
case 0xc000:
matrix = Matrix::CreateRotationZ((rot0 & 0xfff) * (360.0f / 4096.0f) * RADIAN);
break;
}
}

View file

@ -13,12 +13,12 @@ struct ColorData
constexpr short ANGLE(float degrees)
{
return (degrees * (65536.0f / 360.0f));
return short(degrees * (65536.0f / 360.0f));
}
constexpr short FROM_RAD(float radians)
{
return ((radians / RADIAN) * (65536.0f / 360.0f));
return short((radians / RADIAN) * (65536.0f / 360.0f));
}
constexpr float TO_DEGREES(short shortAngle)
@ -34,5 +34,3 @@ constexpr float TO_RAD(short shortAngle)
float phd_sin(short x);
float phd_cos(short x);
int phd_atan(int y, int x);
void GetMatrixFromTrAngle(Matrix& matrix, short* framePtr, int index);

View file

@ -1,17 +1,16 @@
#pragma once
#include "Math/Constants.h"
#include "Math/Containers/EulerAngles.h"
#include "Math/Containers/GameBoundingBox.h"
#include "Math/Containers/GameVector.h"
#include "Math/Containers/Pose.h"
#include "Math/Containers/Vector2i.h"
#include "Math/Containers/Vector3i.h"
#include "Math/Geometry.h"
#include "Math/Legacy.h"
#include "Math/Objects/EulerAngles.h"
#include "Math/Objects/GameBoundingBox.h"
#include "Math/Objects/GameVector.h"
#include "Math/Objects/Pose.h"
#include "Math/Objects/Vector2i.h"
#include "Math/Objects/Vector3i.h"
#include "Math/Random.h"
#include "Math/Solvers.h"
#include "Math/Legacy.h"
namespace TEN::Math
{
constexpr inline auto OFFSET_RADIUS = [](auto x) { return ((x * SQRT_2) + 4); };

View file

@ -0,0 +1,160 @@
#include "framework.h"
#include "Math/Objects/AxisAngle.h"
#include "Math/Constants.h"
#include "Math/Legacy.h"
#include "Math/Objects/EulerAngles.h"
//namespace TEN::Math
//{
const AxisAngle AxisAngle::Identity = AxisAngle(Vector3::Zero, 0);
AxisAngle::AxisAngle()
{
}
AxisAngle::AxisAngle(const Vector3& axis, short angle)
{
auto axisNorm = axis;
axisNorm.Normalize();
this->Axis = axisNorm;
this->Angle = angle;
}
AxisAngle::AxisAngle(const EulerAngles& eulers)
{
*this = AxisAngle(eulers.ToQuaternion());
}
// NOTE: Some precision drift may occur.
AxisAngle::AxisAngle(const Quaternion& quat)
{
float scale = sqrt(1.0f - quat.w * quat.w);
auto axis = Vector3(quat) / scale;
axis.Normalize();
float angle = 2.0f * acos(quat.w);
this->Axis = axis;
this->Angle = FROM_RAD(angle);
}
AxisAngle::AxisAngle(const Matrix& rotMatrix)
{
// Decompose matrix into quaternion.
auto scale = Vector3::Zero;
auto quat = Quaternion::Identity;
auto translation = Vector3::Zero;
auto rotMatrixCopy = rotMatrix;
rotMatrixCopy.Decompose(scale, quat, translation);
// Convert quaternion to AxisAngle.
*this = AxisAngle(quat);
// Extract rotation axis from matrix.
auto rotAxis = Vector3::TransformNormal(Vector3::Right, rotMatrix);
// Check if rotation axis and unit axis are pointing in opposite directions.
float dot = rotAxis.Dot(Axis);
if (dot < 0.0f)
{
// Negate angle and unit axis to ensure the angle stays within [0, PI] range.
this->Angle = -Angle;
this->Axis = -Axis;
}
}
Vector3 AxisAngle::GetAxis() const
{
return Axis;
}
short AxisAngle::GetAngle() const
{
return Angle;
}
void AxisAngle::SetAxis(const Vector3& axis)
{
auto axisNorm = axis;
axisNorm.Normalize();
this->Axis = axisNorm;
}
void AxisAngle::SetAngle(short angle)
{
this->Angle = angle;
}
void AxisAngle::Slerp(const AxisAngle& axisAngleTo, float alpha)
{
*this = Slerp(*this, axisAngleTo, alpha);
}
AxisAngle AxisAngle::Slerp(const AxisAngle& axisAngleFrom, const AxisAngle& axisAngleTo, float alpha)
{
auto axis = Vector3::Zero;
float angle = 0.0f;
// Find angle between the two axes.
float angleBetweenAxes = acos(axisAngleFrom.GetAxis().Dot(axisAngleTo.GetAxis()));
// If angle between the axes is close to 0, do simple interpolation of angle values.
if (abs(angleBetweenAxes) <= EPSILON)
{
axis = axisAngleFrom.GetAxis();
angle = TO_RAD(axisAngleFrom.GetAngle()) + (TO_RAD(axisAngleTo.GetAngle() - axisAngleFrom.GetAngle()) * alpha);
return AxisAngle(axis, FROM_RAD(angle));
}
float sinAngle = sin(angleBetweenAxes);
float weight0 = sin((1.0f - alpha) * angleBetweenAxes) / sinAngle;
float weight1 = sin(alpha * angleBetweenAxes) / sinAngle;
// Slerp axes and angles.
axis = (axisAngleFrom.GetAxis() * weight0) + (axisAngleTo.GetAxis() * weight1);
angle = (TO_RAD(axisAngleFrom.GetAngle()) * weight0) + (TO_RAD(axisAngleTo.GetAngle()) * weight1);
return AxisAngle(axis, FROM_RAD(angle));
}
Quaternion AxisAngle::ToQuaternion() const
{
return Quaternion::CreateFromAxisAngle(Axis, TO_RAD(Angle));
}
Matrix AxisAngle::ToRotationMatrix() const
{
return Matrix::CreateFromAxisAngle(Axis, TO_RAD(Angle));
}
bool AxisAngle::operator ==(const AxisAngle& axisAngle) const
{
return ((Axis == axisAngle.GetAxis()) && (Angle == axisAngle.GetAngle()));
}
bool AxisAngle::operator !=(const AxisAngle& axisAngle) const
{
return !(*this == axisAngle);
}
AxisAngle& AxisAngle::operator =(const AxisAngle& axisAngle)
{
this->Axis = axisAngle.GetAxis();
this->Angle = axisAngle.GetAngle();
return *this;
}
AxisAngle& AxisAngle::operator *=(const AxisAngle& axisAngle)
{
*this = *this * axisAngle;
return *this;
}
AxisAngle AxisAngle::operator *(const AxisAngle& axisAngle) const
{
auto quat0 = this->ToQuaternion();
auto quat1 = axisAngle.ToQuaternion();
return AxisAngle(quat0 * quat1);
}
//}

View file

@ -0,0 +1,48 @@
#pragma once
class EulerAngles;
//namespace TEN::Math
//{
class AxisAngle
{
private:
// Components
Vector3 Axis = Vector3::Zero;
short Angle = 0;
public:
// Constants
static const AxisAngle Identity;
// Constructors
AxisAngle();
AxisAngle(const Vector3& axis, short angle);
AxisAngle(const EulerAngles& eulers);
AxisAngle(const Quaternion& quat);
AxisAngle(const Matrix& rotMatrix);
// Getters
Vector3 GetAxis() const;
short GetAngle() const;
// Setters
void SetAxis(const Vector3& axis);
void SetAngle(short angle);
// Utilities
void Slerp(const AxisAngle& axisAngleTo, float alpha = 1.0f);
static AxisAngle Slerp(const AxisAngle& axisAngleFrom, const AxisAngle& axisAngleTo, float alpha = 1.0f);
// Converters
Quaternion ToQuaternion() const;
Matrix ToRotationMatrix() const;
// Operators
bool operator ==(const AxisAngle& axisAngle) const;
bool operator !=(const AxisAngle& axisAngle) const;
AxisAngle& operator =(const AxisAngle& axisAngle);
AxisAngle& operator *=(const AxisAngle& axisAngle);
AxisAngle operator *(const AxisAngle& axisAngle) const;
};
//}

View file

@ -1,9 +1,10 @@
#include "framework.h"
#include "Math/Containers/EulerAngles.h"
#include "Math/Objects/EulerAngles.h"
#include "Math/Constants.h"
#include "Math/Geometry.h"
#include "Math/Legacy.h"
#include "Math/Objects/AxisAngle.h"
using namespace TEN::Math;
@ -32,9 +33,14 @@ using namespace TEN::Math;
this->z = 0;
}
EulerAngles::EulerAngles(const AxisAngle& axisAngle)
{
*this = EulerAngles(axisAngle.ToQuaternion());
}
EulerAngles::EulerAngles(const Quaternion& quat)
{
static constexpr auto singularityThreshold = 0.9999995f;
static constexpr auto singularityThreshold = 1.0f - EPSILON;
// Handle singularity case.
float sinP = ((quat.w * quat.x) - (quat.y * quat.z)) * 2;
@ -66,12 +72,11 @@ using namespace TEN::Math;
*this = EulerAngles(FROM_RAD(pitch), FROM_RAD(yaw), FROM_RAD(roll));
}
// TODO: Check.
EulerAngles::EulerAngles(const Matrix& rotMatrix)
{
this->x = FROM_RAD(asin(rotMatrix._31));
this->y = FROM_RAD(-atan2(rotMatrix._21, rotMatrix._11));
this->z = FROM_RAD(atan2(rotMatrix._32, rotMatrix._33));
this->x = FROM_RAD(asin(-rotMatrix._32));
this->y = FROM_RAD(atan2(rotMatrix._31, rotMatrix._33));
this->z = FROM_RAD(atan2(rotMatrix._12, rotMatrix._22));
}
bool EulerAngles::Compare(const EulerAngles& eulers0, const EulerAngles& eulers1, short epsilon)
@ -86,19 +91,6 @@ using namespace TEN::Math;
return false;
}
void EulerAngles::InterpolateConstant(const EulerAngles& eulersTo, short angularVel)
{
*this = InterpolateConstant(*this, eulersTo, angularVel);
}
EulerAngles EulerAngles::InterpolateConstant(const EulerAngles& eulersFrom, const EulerAngles& eulerTo, short angularVel)
{
return EulerAngles(
InterpolateConstant(eulersFrom.x, eulerTo.x, angularVel),
InterpolateConstant(eulersFrom.y, eulerTo.y, angularVel),
InterpolateConstant(eulersFrom.z, eulerTo.z, angularVel));
}
void EulerAngles::Lerp(const EulerAngles& eulersTo, float alpha, short epsilon)
{
*this = Lerp(*this, eulersTo, alpha, epsilon);
@ -112,6 +104,33 @@ using namespace TEN::Math;
Lerp(eulersFrom.z, eulersTo.z, alpha, epsilon));
}
void EulerAngles::Slerp(const EulerAngles& eulersTo, float alpha)
{
*this = Slerp(*this, eulersTo, alpha);
}
EulerAngles EulerAngles::Slerp(const EulerAngles& eulersFrom, const EulerAngles& eulersTo, float alpha)
{
auto quatFrom = eulersFrom.ToQuaternion();
auto quatTo = eulersTo.ToQuaternion();
auto quat = Quaternion::Slerp(quatFrom, quatTo, alpha);
return EulerAngles(quat);
}
void EulerAngles::InterpolateConstant(const EulerAngles& eulersTo, short angularVel)
{
*this = InterpolateConstant(*this, eulersTo, angularVel);
}
EulerAngles EulerAngles::InterpolateConstant(const EulerAngles& eulersFrom, const EulerAngles& eulerTo, short angularVel)
{
return EulerAngles(
InterpolateConstant(eulersFrom.x, eulerTo.x, angularVel),
InterpolateConstant(eulersFrom.y, eulerTo.y, angularVel),
InterpolateConstant(eulersFrom.z, eulerTo.z, angularVel));
}
Vector3 EulerAngles::ToDirection() const
{
float sinX = sin(TO_RAD(x));
@ -126,6 +145,11 @@ using namespace TEN::Math;
cosY * cosX);
}
AxisAngle EulerAngles::ToAxisAngle() const
{
return AxisAngle(*this);
}
Quaternion EulerAngles::ToQuaternion() const
{
return Quaternion::CreateFromYawPitchRoll(TO_RAD(y), TO_RAD(x), TO_RAD(z));
@ -143,7 +167,7 @@ using namespace TEN::Math;
bool EulerAngles::operator !=(const EulerAngles& eulers) const
{
return ((x != eulers.x) || (y != eulers.y) || (z != eulers.z));
return !(*this == eulers);
}
EulerAngles& EulerAngles::operator =(const EulerAngles& eulers)
@ -221,7 +245,7 @@ using namespace TEN::Math;
float EulerAngles::ClampAlpha(float alpha)
{
return ((abs(alpha) > 1.0f) ? 1.0f : abs(alpha));
return std::clamp(alpha, 0.0f, 1.0f);
}
bool EulerAngles::Compare(short angle0, short angle1, short epsilon)

View file

@ -1,4 +1,5 @@
#pragma once
#include "Math/Objects/AxisAngle.h"
//namespace TEN::Math
//{
@ -11,12 +12,13 @@
short z = 0;
// Constants
static const EulerAngles Zero;
static const EulerAngles Zero; // TODO: Should be "Identity", not "Zero".
// Constructors
EulerAngles();
EulerAngles(short x, short y, short z);
EulerAngles(const Vector3& direction);
EulerAngles(const AxisAngle& axisAngle);
EulerAngles(const Quaternion& quat);
EulerAngles(const Matrix& rotMatrix);
@ -24,11 +26,14 @@
static bool Compare(const EulerAngles& eulers0, const EulerAngles& eulers1, short epsilon = 2);
void Lerp(const EulerAngles& eulersTo, float alpha = 1.0f, short epsilon = 2);
static EulerAngles Lerp(const EulerAngles& eulersFrom, const EulerAngles& eulersTo, float alpha = 1.0f, short epsilon = 2);
void Slerp(const EulerAngles& eulersTo, float alpha = 1.0f);
static EulerAngles Slerp(const EulerAngles& eulersFrom, const EulerAngles& eulersTo, float alpha = 1.0f);
void InterpolateConstant(const EulerAngles& eulersTo, short angularVel);
EulerAngles InterpolateConstant(const EulerAngles& eulersFrom, const EulerAngles& eulerTo, short angularVel);
static EulerAngles InterpolateConstant(const EulerAngles& eulersFrom, const EulerAngles& eulerTo, short angularVel);
// Converters
Vector3 ToDirection() const;
AxisAngle ToAxisAngle() const;
Quaternion ToQuaternion() const;
Matrix ToRotationMatrix() const;

View file

@ -1,10 +1,10 @@
#include "framework.h"
#include "Math/Containers/GameBoundingBox.h"
#include "Math/Objects/GameBoundingBox.h"
#include "Game/animation.h"
#include "Game/items.h"
#include "Math/Containers/EulerAngles.h"
#include "Math/Containers/Pose.h"
#include "Math/Objects/EulerAngles.h"
#include "Math/Objects/Pose.h"
//namespace TEN::Math
//{

View file

@ -1,7 +1,7 @@
#include "framework.h"
#include "Math/Containers/GameVector.h"
#include "Math/Objects/GameVector.h"
#include "Math/Containers/Vector3i.h"
#include "Math/Objects/Vector3i.h"
//namespace TEN::Math
//{
@ -58,7 +58,7 @@
bool GameVector::operator !=(const GameVector& vector) const
{
return ((x != vector.x) || (y != vector.y) || (z != vector.z) || (RoomNumber != vector.RoomNumber));
return !(*this == vector);
}
GameVector& GameVector::operator =(const GameVector& vector)

View file

@ -1,9 +1,9 @@
#include "framework.h"
#include "Math/Containers/Pose.h"
#include "Math/Objects/Pose.h"
#include "Math/Containers/EulerAngles.h"
#include "Math/Containers/Vector3i.h"
#include "Math/Geometry.h"
#include "Math/Objects/EulerAngles.h"
#include "Math/Objects/Vector3i.h"
using namespace TEN::Math;
@ -80,6 +80,6 @@ using namespace TEN::Math;
}
bool Pose::operator !=(const Pose& pose) const
{
return ((Position != pose.Position) || (Orientation != pose.Orientation));
return !(*this == pose);
}
//}

View file

@ -1,6 +1,6 @@
#pragma once
#include "Math/Containers/EulerAngles.h"
#include "Math/Containers/Vector3i.h"
#include "Math/Objects/EulerAngles.h"
#include "Math/Objects/Vector3i.h"
//namespace TEN::Math
//{

View file

@ -1,5 +1,5 @@
#include "framework.h"
#include "Math/Containers/Vector2i.h"
#include "Math/Objects/Vector2i.h"
namespace TEN::Math
{
@ -43,7 +43,7 @@ namespace TEN::Math
bool Vector2i::operator !=(const Vector2i& vector) const
{
return ((x != vector.x) || (y != vector.y));
return !(*this == vector);
}
Vector2i& Vector2i::operator =(const Vector2i& vector)

View file

@ -1,5 +1,5 @@
#include "framework.h"
#include "Math/Containers/Vector3i.h"
#include "Math/Objects/Vector3i.h"
//namespace TEN::Math
//{
@ -45,7 +45,7 @@
bool Vector3i::operator !=(const Vector3i& vector) const
{
return ((x != vector.x) || (y != vector.y) || (z != vector.z));
return !(*this == vector);
}
Vector3i& Vector3i::operator =(const Vector3i& vector)

View file

@ -168,9 +168,9 @@ namespace TEN::Effects::Boss
smoke.x = effectPos.x;
smoke.y = effectPos.y;
smoke.z = effectPos.z;
smoke.xVel = Random::GenerateInt(BLOCK(0.5f), BLOCK(0.5f));
smoke.xVel = Random::GenerateFloat(BLOCK(0.5f), BLOCK(0.5f));
smoke.yVel = GetRandomControl() - 128;
smoke.zVel = Random::GenerateInt(BLOCK(0.5f), BLOCK(0.5f));
smoke.zVel = Random::GenerateFloat(BLOCK(0.5f), BLOCK(0.5f));
smoke.sR = 75;
smoke.sG = 125;
smoke.sB = 175;
@ -212,6 +212,12 @@ namespace TEN::Effects::Boss
{
auto pos = Random::GeneratePointInSphere(sphere);
SpawnExplosionSmoke(pos);
TriggerExplosionSparks(
item.Pose.Position.x + (Random::GenerateInt(0, 127) - 64 * 2),
(item.Pose.Position.y - CLICK(2)) + (Random::GenerateInt(0, 127) - 64 * 2),
item.Pose.Position.z + (Random::GenerateInt(0, 127) - 64 * 2),
2, -2, 2, item.RoomNumber);
}
}
@ -222,18 +228,31 @@ namespace TEN::Effects::Boss
{
auto pos = Random::GeneratePointInSphere(sphere);
SpawnExplosionSmoke(pos);
TriggerExplosionSparks(
item.Pose.Position.x + (Random::GenerateInt(0, 127) - 64 * 2),
(item.Pose.Position.y - CLICK(2)) + (Random::GenerateInt(0, 127) - 64 * 2),
item.Pose.Position.z + (Random::GenerateInt(0, 127) - 64 * 2),
2, -2, 2, item.RoomNumber);
}
sphere = BoundingSphere(item.Pose.Position.ToVector3() + Vector3(0.0f, -CLICK(2), 0.0f), BLOCK(1 / 16.0f));
auto shockwavePos = Pose(Random::GeneratePointInSphere(sphere), item.Pose.Orientation);
int speed = Random::GenerateInt(BLOCK(0.5f), BLOCK(1.6f));
auto orient2D = Random::GenerateAngle();
auto orient2D = Random::GenerateAngle(ANGLE(-24.0f), ANGLE(24.0f));
TriggerShockwave(
&shockwavePos, 300, BLOCK(0.5f), speed,
color.x * UCHAR_MAX, color.y * UCHAR_MAX, color.z * UCHAR_MAX,
36, orient2D, 0);
TriggerExplosionSparks(
item.Pose.Position.x + (Random::GenerateInt(0, 127) - 64 * 2),
(item.Pose.Position.y - CLICK(2)) + (Random::GenerateInt(0, 127) - 64 * 2),
item.Pose.Position.z + (Random::GenerateInt(0, 127) - 64 * 2),
2, -2, 2, item.RoomNumber);
SoundEffect(SFX_TR3_BLAST_CIRCLE, &shockwavePos);
}

View file

@ -6,7 +6,7 @@
#include "Game/collision/collide_room.h"
#include "Game/collision/sphere.h"
#include "Game/effects/effects.h"
#include "Game/effects/lightning.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/item_fx.h"
#include "Game/effects/tomb4fx.h"
#include "Game/effects/weather.h"
@ -20,10 +20,10 @@
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Input;
using namespace TEN::Effects::Items;
using namespace TEN::Effects::Lightning;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Items;
using namespace TEN::Input;
namespace TEN::Entities::Effects
{
@ -400,8 +400,8 @@ namespace TEN::Entities::Effects
byte g = (GetRandomControl() & 0x3F) + 192;
byte b = (GetRandomControl() & 0x3F) + 192;
auto origin = item->Pose.Position;
Vector3i target;
auto origin = item->Pose.Position.ToVector3();
auto target = Vector3::Zero;
if (!(GlobalCounter & 3))
{
@ -412,9 +412,9 @@ namespace TEN::Entities::Effects
target.z = item->Pose.Position.z + 2048 * phd_cos(item->Pose.Orientation.y + ANGLE(180.0f));
if (GetRandomControl() & 3)
TriggerLightning(&origin, &target, (GetRandomControl() & 0x1F) + 64, 0, g, b, 24, 0, 32, 3);
SpawnElectricity(origin, target, (GetRandomControl() & 0x1F) + 64, 0, g, b, 24, 0, 32, 3);
else
TriggerLightning(&origin, &target, (GetRandomControl() & 0x1F) + 96, 0, g, b, 32, LI_SPLINE, 32, 3);
SpawnElectricity(origin, target, (GetRandomControl() & 0x1F) + 96, 0, g, b, 32, (int)ElectricityFlags::Spline, 32, 3);
}
}
@ -423,39 +423,39 @@ namespace TEN::Entities::Effects
short targetItemNumber = item->ItemFlags[((GlobalCounter >> 2) & 1) + 2];
auto* targetItem = &g_Level.Items[targetItemNumber];
target = GetJointPosition(targetItem, 0, Vector3i(0, -64, 20));
target = GetJointPosition(targetItem, 0, Vector3i(0, -64, 20)).ToVector3();
if (!(GlobalCounter & 3))
{
if (GetRandomControl() & 3)
TriggerLightning(&origin, &target, (GetRandomControl() & 0x1F) + 64, 0, g, b, 24, 0, 32, 5);
SpawnElectricity(origin, target, (GetRandomControl() & 0x1F) + 64, 0, g, b, 24, 0, 32, 5);
else
TriggerLightning(&origin, &target, (GetRandomControl() & 0x1F) + 96, 0, g, b, 32, LI_SPLINE, 32, 5);
SpawnElectricity(origin, target, (GetRandomControl() & 0x1F) + 96, 0, g, b, 32, (int)ElectricityFlags::Spline, 32, 5);
}
if (item->TriggerFlags != 3 || targetItem->TriggerFlags)
TriggerLightningGlow(target.x, target.y, target.z, 64, 0, g, b);
SpawnElectricityGlow(target, 64, 0, g, b);
}
if ((GlobalCounter & 3) == 2)
{
origin = item->Pose.Position;
target = Vector3i(
origin = item->Pose.Position.ToVector3();
target = Vector3(
(GetRandomControl() & 0x1FF) + origin.x - 256,
(GetRandomControl() & 0x1FF) + origin.y - 256,
(GetRandomControl() & 0x1FF) + origin.z - 256
);
(GetRandomControl() & 0x1FF) + origin.z - 256);
TriggerLightning(&origin, &target, (GetRandomControl() & 0xF) + 16, 0, g, b, 24, LI_SPLINE | LI_MOVEEND, 32, 3);
TriggerLightningGlow(target.x, target.y, target.z, 64, 0, g, b);
SpawnElectricity(origin, target, (GetRandomControl() & 0xF) + 16, 0, g, b, 24, (int)ElectricityFlags::Spline | (int)ElectricityFlags::MoveEnd, 32, 3);
SpawnElectricityGlow(target, 64, 0, g, b);
}
}
else
{
// Small fires
if (item->ItemFlags[0] != 0)
{
item->ItemFlags[0]--;
}
else
{
item->ItemFlags[0] = (GetRandomControl() & 3) + 8;

View file

@ -43,7 +43,7 @@ using namespace TEN::Entities::Generic;
static void StartObject(ObjectInfo* object)
{
for (int objectNumber = ID_TRAPDOOR1; objectNumber <= ID_CEILING_TRAPDOOR2; objectNumber++)
for (int objectNumber = ID_TRAPDOOR1; objectNumber <= ID_TRAPDOOR3; objectNumber++)
{
object = &Objects[objectNumber];
if (object->loaded)
@ -59,6 +59,38 @@ static void StartObject(ObjectInfo* object)
}
}
for (int objectNumber = ID_FLOOR_TRAPDOOR1; objectNumber <= ID_FLOOR_TRAPDOOR2; objectNumber++)
{
object = &Objects[objectNumber];
if (object->loaded)
{
object->initialise = InitialiseTrapDoor;
object->collision = FloorTrapDoorCollision;
object->control = TrapDoorControl;
object->floorBorder = TrapDoorFloorBorder;
object->ceilingBorder = TrapDoorCeilingBorder;
object->floor = TrapDoorFloor;
object->ceiling = TrapDoorCeiling;
object->SetupHitEffect(true);
}
}
for (int objectNumber = ID_CEILING_TRAPDOOR1; objectNumber <= ID_CEILING_TRAPDOOR2; objectNumber++)
{
object = &Objects[objectNumber];
if (object->loaded)
{
object->initialise = InitialiseTrapDoor;
object->collision = CeilingTrapDoorCollision;
object->control = TrapDoorControl;
object->floorBorder = TrapDoorFloorBorder;
object->ceilingBorder = TrapDoorCeilingBorder;
object->floor = TrapDoorFloor;
object->ceiling = TrapDoorCeiling;
object->SetupHitEffect(true);
}
}
object = &Objects[ID_BRIDGE_FLAT];
if (object->loaded)
{

View file

@ -4,8 +4,8 @@
#include "Game/control/box.h"
#include "Game/control/los.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/item_fx.h"
#include "Game/effects/lightning.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/misc.h"
#include "Math/Math.h"
@ -14,8 +14,8 @@
#include "Specific/setup.h"
using namespace TEN::Effects::Boss;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Items;
using namespace TEN::Effects::Lightning;
namespace TEN::Entities::Creatures::TR3
{
@ -220,10 +220,10 @@ namespace TEN::Entities::Creatures::TR3
{
auto target = GameVector(pos, item.RoomNumber);
TriggerLightning(&origin.ToVector3i(), &target.ToVector3i(), 1, 0, 255, 180, 30, LI_THININ | LI_SPLINE | LI_MOVEEND, 8, 12);
TriggerLightning(&origin.ToVector3i(), &target.ToVector3i(), 1, 180, 255, 0, 30, LI_THININ | LI_SPLINE | LI_MOVEEND, 3, 12);
TriggerLightning(&origin.ToVector3i(), &target.ToVector3i(), Random::GenerateInt(25, 50), 100, 200, 200, 30, LI_THININ | LI_THINOUT, 4, 12);
TriggerLightning(&origin.ToVector3i(), &target.ToVector3i(), Random::GenerateInt(25, 50), 100, 250, 255, 30, LI_THININ | LI_THINOUT, 2, 12);
SpawnElectricity(origin.ToVector3(), target.ToVector3(), 1, 0, 255, 180, 30, (int)(int)(int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::MoveEnd, 8, 12);
SpawnElectricity(origin.ToVector3(), target.ToVector3(), 1, 180, 255, 0, 30, (int)(int)(int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::MoveEnd, 3, 12);
SpawnElectricity(origin.ToVector3(), target.ToVector3(), Random::GenerateInt(25, 50), 100, 200, 200, 30, (int)(int)(int)ElectricityFlags::ThinIn | (int)(int)ElectricityFlags::ThinOut, 4, 12);
SpawnElectricity(origin.ToVector3(), target.ToVector3(), Random::GenerateInt(25, 50), 100, 250, 255, 30, (int)(int)(int)ElectricityFlags::ThinIn | (int)(int)ElectricityFlags::ThinOut, 2, 12);
TriggerDynamicLight(origin.x, origin.y, origin.z, 20, 0, 255, 0);
SpawnLizard(item);
@ -238,20 +238,20 @@ namespace TEN::Entities::Creatures::TR3
auto target2 = GameVector(Geometry::TranslatePoint(origin.ToVector3(), pos - origin.ToVector3(), PUNA_ATTACK_RANGE / 6), creature.Enemy->RoomNumber);
auto target3 = GameVector(Geometry::TranslatePoint(origin1.ToVector3(), pos - origin1.ToVector3(), PUNA_ATTACK_RANGE / 10), creature.Enemy->RoomNumber);
TriggerLightning(&origin.ToVector3i(), &target2.ToVector3i(), Random::GenerateInt(15, 40), 20, 160, 160, 20, LI_THINOUT | LI_THININ, 4, 6);
TriggerLightning(&origin.ToVector3i(), &target2.ToVector3i(), Random::GenerateInt(25, 35), 20, 160, 160, 20, LI_THINOUT | LI_THININ, 2, 7);
SpawnElectricity(origin.ToVector3(), target2.ToVector3(), Random::GenerateInt(15, 40), 20, 160, 160, 20, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 4, 6);
SpawnElectricity(origin.ToVector3(), target2.ToVector3(), Random::GenerateInt(25, 35), 20, 160, 160, 20, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 2, 7);
TriggerLightning(&target2.ToVector3i(), &origin1.ToVector3i(), Random::GenerateInt(15, 40), 20, 160, 160, 20, LI_THINOUT | LI_THININ, 4, 6);
TriggerLightning(&target2.ToVector3i(), &origin1.ToVector3i(), Random::GenerateInt(25, 35), 20, 160, 160, 20, LI_THINOUT | LI_THININ, 2, 7);
SpawnElectricity(target2.ToVector3(), origin1.ToVector3(), Random::GenerateInt(15, 40), 20, 160, 160, 20, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 4, 6);
SpawnElectricity(target2.ToVector3(), origin1.ToVector3(), Random::GenerateInt(25, 35), 20, 160, 160, 20, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 2, 7);
TriggerLightning(&origin1.ToVector3i(), &target3.ToVector3i(), Random::GenerateInt(15, 40), 20, 160, 160, 20, LI_THINOUT | LI_THININ, 4, 9);
TriggerLightning(&origin1.ToVector3i(), &target3.ToVector3i(), Random::GenerateInt(25, 35), 20, 160, 160, 20, LI_THINOUT | LI_THININ, 2, 10);
SpawnElectricity(origin1.ToVector3(), target3.ToVector3(), Random::GenerateInt(15, 40), 20, 160, 160, 20, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 4, 9);
SpawnElectricity(origin1.ToVector3(), target3.ToVector3(), Random::GenerateInt(25, 35), 20, 160, 160, 20, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 2, 10);
TriggerLightning(&origin2.ToVector3i(), &target3.ToVector3i(), Random::GenerateInt(15, 40), 20, 160, 160, 16, LI_THINOUT | LI_THININ, 4, 7);
TriggerLightning(&origin2.ToVector3i(), &target3.ToVector3i(), Random::GenerateInt(25, 35), 20, 160, 160, 16, LI_THINOUT | LI_THININ, 2, 8);
SpawnElectricity(origin2.ToVector3(), target3.ToVector3(), Random::GenerateInt(15, 40), 20, 160, 160, 16, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 4, 7);
SpawnElectricity(origin2.ToVector3(), target3.ToVector3(), Random::GenerateInt(25, 35), 20, 160, 160, 16, (int)(int)ElectricityFlags::ThinOut | (int)(int)(int)ElectricityFlags::ThinIn, 2, 8);
TriggerLightning(&origin.ToVector3i(), &target.ToVector3i(), 1, 20, 160, 160, 30, LI_THININ | LI_SPLINE | LI_MOVEEND, 12, 12);
TriggerLightning(&origin.ToVector3i(), &target.ToVector3i(), 1, 80, 160, 160, 30, LI_THININ | LI_SPLINE | LI_MOVEEND, 5, 12);
SpawnElectricity(origin.ToVector3(), target.ToVector3(), 1, 20, 160, 160, 30, (int)(int)(int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::MoveEnd, 12, 12);
SpawnElectricity(origin.ToVector3(), target.ToVector3(), 1, 80, 160, 160, 30, (int)(int)(int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::MoveEnd, 5, 12);
TriggerDynamicLight(origin.x, origin.y, origin.z, 20, 0, 255, 255);

View file

@ -199,7 +199,7 @@ namespace TEN::Entities::Creatures::TR3
const auto& object = Objects[item.ObjectNumber];
auto& creature = *GetCreatureInfo(&item);
if (creature.Flags == 0 && (item.TestFlags(2, 1) || item.TestFlags(2, 2)))
if (creature.Flags == 0 && (item.TestFlagField(2, 1) || item.TestFlagField(2, 2)))
{
creature.Flags = 1;
@ -233,14 +233,14 @@ namespace TEN::Entities::Creatures::TR3
creature.Flags--;
}
if (item.TestFlags(2, 0) && !isDead)
if (item.TestFlagField(2, 0) && !isDead)
{
item.Animation.TargetState = SHIVA_STATE_IDLE;
creature.Flags = -45;
item.SetFlagField(1, 0);
item.SetFlagField(1, 1); // Is alive (for savegame).
}
else if (item.TestFlags(2, 0) && isDead)
else if (item.TestFlagField(2, 0) && isDead)
{
item.SetFlagField(1, 0);
item.SetFlagField(1, 2); // Is dead.
@ -262,7 +262,7 @@ namespace TEN::Entities::Creatures::TR3
// Joint index used for swapping mesh.
item.SetFlagField(0, 0);
if (item.TestFlags(1, 0))
if (item.TestFlagField(1, 0))
{
for (int jointIndex = 0; jointIndex < object.nmeshes; jointIndex++)
SwapShivaMeshToStone(item, jointIndex);

View file

@ -249,7 +249,7 @@ namespace TEN::Entities::TR4
if (creature->ReachedGoal)
{
TestTriggers(target->Pose.Position.x, target->Pose.Position.y, target->Pose.Position.z, target->RoomNumber, true);
TestTriggers(target, true);
if (Lara.Location < item->ItemFlags[3] && item->Animation.ActiveState != 2 && item->Animation.TargetState != 2)
{

View file

@ -673,12 +673,7 @@ namespace TEN::Entities::TR4
item->Pose = enemy->Pose;
else if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 120)
{
TestTriggers(
creature->AITarget->Pose.Position.x,
creature->AITarget->Pose.Position.y,
creature->AITarget->Pose.Position.z,
creature->AITarget->RoomNumber,
true);
TestTriggers(creature->AITarget, true);
creature->ReachedGoal = false;
creature->Enemy = nullptr;
@ -789,12 +784,7 @@ namespace TEN::Entities::TR4
break;
}
TestTriggers(
creature->AITarget->Pose.Position.x,
creature->AITarget->Pose.Position.y,
creature->AITarget->Pose.Position.z,
creature->AITarget->RoomNumber,
true);
TestTriggers(creature->AITarget, true);
item->AIBits = FOLLOW;
creature->ReachedGoal = false;

View file

@ -9,11 +9,11 @@
#include "Game/effects/effects.h"
#include "Specific/Input/Input.h"
#include "Game/animation.h"
#include "Game/effects/lightning.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/debris.h"
using namespace TEN::Effects::Electricity;
using namespace TEN::Input;
using namespace TEN::Effects::Lightning;
void InitialiseObelisk(short itemNumber)
{
@ -45,9 +45,9 @@ void ObeliskControl(short itemNumber)
{
auto* item = &g_Level.Items[itemNumber];
short someNumber;
Pose pos;
Pose pos2;
short someNumber = 0;
auto pos = Vector3::Zero;
auto pos2 = Vector3::Zero;
if (TriggerActive(item))
{
@ -73,61 +73,54 @@ void ObeliskControl(short itemNumber)
someNumber = (GetRandomControl() & 0xFFF) + 3456;
}
pos.Position.x = item->Pose.Position.x + (3456 * phd_sin(item->Pose.Orientation.y + ANGLE(90.0f)));
pos.Position.y = item->Pose.Position.y - CLICK(1);
pos.Position.z = item->Pose.Position.z + (3456 * phd_cos(item->Pose.Orientation.y + ANGLE(90.0f)));
pos.x = item->Pose.Position.x + (3456 * phd_sin(item->Pose.Orientation.y + ANGLE(90.0f)));
pos.y = item->Pose.Position.y - CLICK(1);
pos.z = item->Pose.Position.z + (3456 * phd_cos(item->Pose.Orientation.y + ANGLE(90.0f)));
pos2.Position.x = item->Pose.Position.x + (someNumber * phd_sin(item->Pose.Orientation.y + ANGLE(90.0f)));
pos2.Position.y = item->Pose.Position.y;
pos2.Position.x = item->Pose.Position.z + (someNumber * phd_cos(item->Pose.Orientation.z + ANGLE(90.0f)));
pos2.x = item->Pose.Position.x + (someNumber * phd_sin(item->Pose.Orientation.y + ANGLE(90.0f)));
pos2.y = item->Pose.Position.y;
pos2.x = item->Pose.Position.z + (someNumber * phd_cos(item->Pose.Orientation.z + ANGLE(90.0f)));
if (abs(pos.Position.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos.Position.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos.Position.z - LaraItem->Pose.Position.z) < SECTOR(20) &&
abs(pos2.Position.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos2.Position.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos2.Position.z - LaraItem->Pose.Position.z) < SECTOR(20))
if (abs(pos.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos.z - LaraItem->Pose.Position.z) < SECTOR(20) &&
abs(pos2.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos2.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos2.z - LaraItem->Pose.Position.z) < SECTOR(20))
{
if (!(GlobalCounter & 3))
{
TriggerLightning(
(Vector3i*)&pos,
(Vector3i*)&pos2,
SpawnElectricity(
pos, pos2,
(GetRandomControl() & 0x1F) + 32,
r,
g,
b,
24,
1,
32,
5);
r, g, b, 24, 1, 32, 5);
}
TriggerLightningGlow(pos.Position.x, pos.Position.y, pos.Position.z, 48, r, g, b);
SpawnElectricityGlow(pos, 48, r, g, b);
}
}
}
if (item->ItemFlags[3] >= 256 && item->TriggerFlags == 2)
{
pos.Position.x = item->Pose.Position.x + SECTOR(8) * phd_sin(item->Pose.Orientation.y);
pos.Position.y = item->Pose.Position.y;
pos.Position.z = item->Pose.Position.z + SECTOR(8) * phd_cos(item->Pose.Orientation.y + ANGLE(90.0f));
pos.x = item->Pose.Position.x + SECTOR(8) * phd_sin(item->Pose.Orientation.y);
pos.y = item->Pose.Position.y;
pos.z = item->Pose.Position.z + SECTOR(8) * phd_cos(item->Pose.Orientation.y + ANGLE(90.0f));
SoundEffect(SFX_TR4_ELECTRIC_ARCING_LOOP, &pos);
SoundEffect(SFX_TR4_ELECTRIC_ARCING_LOOP, &Pose(Vector3i(pos)));
if (GlobalCounter & 1)
{
pos2.Position.x = (GetRandomControl() & 0x3FF) + pos.Position.x - 512;
pos2.Position.y = (GetRandomControl() & 0x3FF) + pos.Position.y - 512;
pos2.Position.z = (GetRandomControl() & 0x3FF) + pos.Position.z - 512;
pos2.x = (GetRandomControl() & 0x3FF) + pos.x - 512;
pos2.y = (GetRandomControl() & 0x3FF) + pos.y - 512;
pos2.z = (GetRandomControl() & 0x3FF) + pos.z - 512;
if (abs(pos.Position.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos.Position.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos.Position.z - LaraItem->Pose.Position.z) < SECTOR(20) &&
abs(pos2.Position.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos2.Position.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos2.Position.z - LaraItem->Pose.Position.z) < SECTOR(20))
if (abs(pos.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos.z - LaraItem->Pose.Position.z) < SECTOR(20) &&
abs(pos2.x - LaraItem->Pose.Position.x) < SECTOR(20) &&
abs(pos2.y - LaraItem->Pose.Position.y) < SECTOR(20) &&
abs(pos2.z - LaraItem->Pose.Position.z) < SECTOR(20))
{
if (item->ItemFlags[2] != NO_ITEM)
{
@ -135,8 +128,8 @@ void ObeliskControl(short itemNumber)
ExplodeItemNode(item2, 0, 0, 128);
KillItem(item->ItemFlags[2]);
TriggerExplosionSparks(pos.Position.x, pos.Position.y, pos.Position.z, 3, -2, 0, item2->RoomNumber);
TriggerExplosionSparks(pos.Position.x, pos.Position.y, pos.Position.z, 3, -1, 0, item2->RoomNumber);
TriggerExplosionSparks(pos.x, pos.y, pos.z, 3, -2, 0, item2->RoomNumber);
TriggerExplosionSparks(pos.x, pos.y, pos.z, 3, -1, 0, item2->RoomNumber);
item->ItemFlags[2] = NO_ITEM;
item2 = FindItem(ID_PUZZLE_ITEM1_COMBO1);
@ -146,17 +139,10 @@ void ObeliskControl(short itemNumber)
SoundEffect(SFX_TR4_EXPLOSION2, &item2->Pose);
}
TriggerLightning(
(Vector3i*)&pos,
(Vector3i*)&pos2,
SpawnElectricity(
pos, pos2,
(GetRandomControl() & 0xF) + 16,
r,
g,
b,
24,
3,
24,
3);
r, g, b, 24, 3, 24, 3);
}
}
}

View file

@ -0,0 +1,540 @@
#include "framework.h"
#include "Objects/TR5/Entity/HeavyGuard.h"
#include "Game/animation.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h"
#include "Game/control/box.h"
#include "Game/control/control.h"
#include "Game/control/los.h"
#include "Game/effects/debris.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/item_fx.h"
#include "Game/effects/spark.h"
#include "Game/effects/tomb4fx.h"
#include "Game/effects/effects.h"
#include "Game/itemdata/creature_info.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/misc.h"
#include "Game/people.h"
#include "Math/Math.h"
#include "Sound/sound.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Items;
using namespace TEN::Effects::Spark;
namespace TEN::Entities::Creatures::TR5
{
constexpr auto HEAVY_GUARD_RAYGUN_DAMAGE = 250;
constexpr auto HEAVY_GUARD_ALERT_RANGE = SQUARE(BLOCK(2));
constexpr auto HEAVY_GUARD_IDLE_AIM_RANGE = SQUARE(BLOCK(3));
constexpr auto HEAVY_GUARD_CLOSE_RANGE = SQUARE(BLOCK(1));
constexpr auto HEAVY_GUARD_IDLE_TURN_RATE_MAX = ANGLE(2.0f);
constexpr auto HEAVY_GUARD_WALK_TURN_RATE_MAX = ANGLE(5.0f);
constexpr auto HEAVY_GUARD_PLAYER_ALERT_VELOCITY = 20.0f;
constexpr auto HEAVY_GUARD_RAYGUN_PLAYER_BURN_HEALTH = 500;
const auto HeavyGuardHandJoints = std::vector<int>{ 8, 5 };
const auto HeavyGuardRaygunLaserOffsets = std::vector<Vector3>
{
Vector3(0.0f, 230.0f, 40.0f),
Vector3(8.0f, 230.0f, 40.0f),
};
const auto HeavyGuardHeadBite = BiteInfo(Vector3(0.0f, -200.0f, 0.0f), 2);
enum HeavyGuardState
{
// No state 0.
HEAVY_GUARD_STATE_IDLE = 1,
HEAVY_GUARD_STATE_WALK_FORWARD = 2,
HEAVY_GUARD_STATE_WALK_RAYGUN_ATTACK_RIGHT = 3,
HEAVY_GUARD_STATE_WALK_RAYGUN_ATTACK_LEFT = 4,
HEAVY_GUARD_STATE_IDLE_TO_AIM = 5,
HEAVY_GUARD_STATE_DUAL_RAYGUN_ATTACK = 6,
HEAVY_GUARD_STATE_DEATH = 7,
HEAVY_GUARD_STATE_TURN_180 = 8,
HEAVY_GUARD_STATE_MISFIRE = 9,
HEAVY_GUARD_STATE_RATTLE_RAYGUN = 10,
HEAVY_GUARD_STATE_FALL_START = 11,
HEAVY_GUARD_STATE_FALL_CONTINUE = 12,
HEAVY_GUARD_STATE_FALL_DEATH = 13
};
enum HeavyGuardAnim
{
HEAVY_GUARD_ANIM_WALK_FORWARD = 0,
HEAVY_GUARD_ANIM_WALK_RAYGUN_ATTACK_RIGHT = 1,
HEAVY_GUARD_ANIM_WALK_RAYGUN_ATTACK_LEFT = 2,
HEAVY_GUARD_ANIM_DEATH = 3,
HEAVY_GUARD_ANIM_IDLE_TO_WALK_FORWARD = 4,
HEAVY_GUARD_ANIM_WALK_FORWARD_TO_IDLE = 5,
HEAVY_GUARD_ANIM_IDLE = 6,
HEAVY_GUARD_ANIM_IDLE_TO_AIM = 7,
HEAVY_GUARD_ANIM_AIM = 8,
HEAVY_GUARD_ANIM_DUAL_RAYGUN_ATTACK = 9,
HEAVY_GUARD_ANIM_AIM_TO_IDLE = 10,
HEAVY_GUARD_ANIM_TURN_180 = 11,
HEAVY_GUARD_ANIM_MISFIRE = 12,
HEAVY_GUARD_ANIM_RATTLE_RAYGUN = 13,
HEAVY_GUARD_ANIM_FALL_DEATH_START = 14,
HEAVY_GUARD_ANIM_FALL_DEATH_CONTINUE = 15,
HEAVY_GUARD_ANIM_FALL_DEATH_END = 16
};
// TODO: Demagic these.
enum class HeavyGuardFlags
{
};
const void SpawnRaygunSmoke(const Vector3& pos, const EulerAngles& orient, float life)
{
auto& smoke = *GetFreeParticle();
float scale = (life * 12) * phd_cos(orient.x);
smoke.on = true;
smoke.blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
smoke.x = pos.x;
smoke.y = pos.y;
smoke.z = pos.z;
smoke.xVel = Random::GenerateInt(-25, 25);
smoke.yVel = life * 3;
smoke.zVel = Random::GenerateInt(-25, 25);
smoke.sB = (Random::GenerateInt(128, 256) * life) / 16;
smoke.sR =
smoke.sG = smoke.sB - (smoke.sB / 4);
smoke.dR = 0;
smoke.dB = (Random::GenerateInt(32, 160) * life) / 16;
smoke.dG =
smoke.dB / 4;
smoke.colFadeSpeed = Random::GenerateInt(8, 12);
smoke.fadeToBlack = 8;
smoke.sLife =
smoke.life = Random::GenerateFloat(24.0f, 28.0f);
smoke.friction = 0;
smoke.rotAng = Random::GenerateAngle(0, ANGLE(22.5f));
smoke.rotAdd = Random::GenerateAngle(ANGLE(-0.35f), ANGLE(0.35f));
smoke.gravity = Random::GenerateAngle(ANGLE(0.175f), ANGLE(0.35f));
smoke.maxYvel = 0;
smoke.scalar = 1;
smoke.size =
smoke.sSize = scale;
smoke.dSize = 1;
smoke.flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF;
}
const void FireHeavyGuardRaygun(ItemInfo& item, bool isRight, bool noLaser)
{
static constexpr auto raygunSmokeLife = 16.0f;
static constexpr auto raygunBurnPrimaryColor = Vector3(0.2f, 0.4f, 1.0f);
static constexpr auto raygunBurnSecondaryColor = Vector3(0.2f, 0.3f, 0.8f);
const auto& creature = *GetCreatureInfo(&item);
auto origin = GetJointPosition(&item, HeavyGuardHandJoints[isRight], HeavyGuardRaygunLaserOffsets[isRight]).ToVector3();
auto target = GetJointPosition(
&item,
HeavyGuardHandJoints[isRight],
Vector3i(HeavyGuardRaygunLaserOffsets[isRight] + Vector3(0, BLOCK(4), 0))).ToVector3();
auto orient = Geometry::GetOrientToPoint(origin, target);
if (noLaser)
{
SpawnRaygunSmoke(origin, orient, abs(item.ItemFlags[isRight]));
return;
}
SpawnHelicalLaser(origin, target);
item.ItemFlags[isRight] = raygunSmokeLife;
SpawnRaygunSmoke(origin, orient, raygunSmokeLife);
SpawnRaygunSmoke(origin, orient, raygunSmokeLife);
SpawnRaygunSmoke(origin, orient, raygunSmokeLife);
auto origin2 = GameVector(origin, item.RoomNumber);
auto target2 = GameVector(target);
auto hitPos = Vector3i::Zero;
MESH_INFO* hitJoint = nullptr;
if (ObjectOnLOS2(&origin2, &target2, &hitPos, &hitJoint, ID_LARA) == GetLaraInfo(creature.Enemy)->ItemNumber)
{
if (LaraItem->HitPoints <= HEAVY_GUARD_RAYGUN_PLAYER_BURN_HEALTH)
{
ItemCustomBurn(LaraItem, raygunBurnPrimaryColor, raygunBurnSecondaryColor, 1 * FPS);
DoDamage(LaraItem, INT_MAX);
}
else
{
DoDamage(LaraItem, HEAVY_GUARD_RAYGUN_DAMAGE);
}
}
}
void InitialiseHeavyGuard(short itemNumber)
{
auto& item = g_Level.Items[itemNumber];
SetAnimation(&item, HEAVY_GUARD_ANIM_IDLE);
}
void HeavyGuardControl(short itemNumber)
{
if (!CreatureActive(itemNumber))
return;
auto& item = g_Level.Items[itemNumber];
auto& creature = *GetCreatureInfo(&item);
short headingAngle = 0;
auto headOrient = EulerAngles::Zero;
auto torsoOrient = EulerAngles::Zero;
AI_INFO ai;
CreatureAIInfo(&item, &ai);
if (item.ItemFlags[0] || item.ItemFlags[1])
{
for (int i = 0; i < 2; i++)
{
if (item.ItemFlags[i])
{
FireHeavyGuardRaygun(item, (bool)i, true);
if (item.ItemFlags[i] > 0)
{
auto pos1 = GetJointPosition(&item, HeavyGuardHandJoints[i], HeavyGuardRaygunLaserOffsets[i]);
short blue = item.ItemFlags[i] << 4;
short green = blue >> 2;
short red = 0;
TriggerDynamicLight(pos1.x, pos1.y, pos1.z, item.ItemFlags[i] + 8, red, green, blue);
item.ItemFlags[i]--;
}
else
{
item.ItemFlags[i]++;
}
}
}
}
if (item.HitPoints <= 0)
{
if (item.Animation.ActiveState != HEAVY_GUARD_STATE_DEATH && item.TriggerFlags != 1)
{
SetAnimation(&item, HEAVY_GUARD_ANIM_DEATH);
}
else if (item.TriggerFlags == 1)
{
switch (item.Animation.ActiveState)
{
case HEAVY_GUARD_STATE_FALL_START:
{
int frame = item.Animation.FrameNumber;
int frameStart = g_Level.Anims[item.Animation.AnimNumber].frameBase;
if (frame == GetFrameNumber(&item, 48) || frame == GetFrameNumber(&item, 15))
{
short roomNumber = item.RoomNumber;
GetFloor(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, &roomNumber),
TestTriggers(&item, true, 0);
}
break;
}
case HEAVY_GUARD_STATE_FALL_CONTINUE:
item.Animation.IsAirborne = true;
if (item.Pose.Position.y >= item.Floor)
{
item.Pose.Position.y = item.Floor;
item.Animation.TargetState = HEAVY_GUARD_STATE_FALL_DEATH;
item.Animation.IsAirborne = false;
item.Animation.Velocity.y = 0.0f;
CreatureEffect2(&item, HeavyGuardHeadBite, 10, item.Pose.Orientation.y, DoBloodSplat);
}
break;
case HEAVY_GUARD_STATE_FALL_DEATH:
item.Pose.Position.y = item.Floor;
break;
default:
SetAnimation(&item, HEAVY_GUARD_ANIM_FALL_DEATH_START);
creature.LOT.IsJumping = true;
break;
}
}
}
else
{
if (item.AIBits)
GetAITarget(&creature);
else
creature.Enemy = LaraItem;
CreatureAIInfo(&item, &ai);
AI_INFO laraAI;
if (creature.Enemy->IsLara())
{
laraAI.angle = ai.angle;
laraAI.distance = ai.distance;
}
else
{
int dx = LaraItem->Pose.Position.x - item.Pose.Position.x;
int dz = LaraItem->Pose.Position.z - item.Pose.Position.z;
laraAI.angle = phd_atan(dz, dx) - item.Pose.Orientation.y;
laraAI.distance = SQUARE(dx) + SQUARE(dz);
}
GetCreatureMood(&item, &ai, creature.Enemy != LaraItem);
CreatureMood(&item, &ai, creature.Enemy != LaraItem);
headingAngle = CreatureTurn(&item, creature.MaxTurn);
if (((laraAI.distance < HEAVY_GUARD_ALERT_RANGE &&
(laraAI.angle < ANGLE(90.0f) && laraAI.angle > ANGLE(-90.0f) ||
LaraItem->Animation.Velocity.z > HEAVY_GUARD_PLAYER_ALERT_VELOCITY)) ||
item.HitStatus ||
TargetVisible(&item, &laraAI)) &&
abs(item.Pose.Position.y - LaraItem->Pose.Position.y) < BLOCK(1.5f))
{
creature.Enemy = LaraItem;
AlertAllGuards(itemNumber);
}
switch (item.Animation.ActiveState)
{
case HEAVY_GUARD_STATE_IDLE:
creature.MaxTurn = 0;
creature.Flags = 0;
if (!(item.AIBits & GUARD))
{
if (abs(ai.angle) < HEAVY_GUARD_IDLE_TURN_RATE_MAX)
{
item.Pose.Orientation.y += ai.angle;
}
else
{
if (ai.angle >= 0)
item.Pose.Orientation.y += HEAVY_GUARD_IDLE_TURN_RATE_MAX;
else
item.Pose.Orientation.y -= HEAVY_GUARD_IDLE_TURN_RATE_MAX;
}
headOrient.y = laraAI.angle / 2;
torsoOrient.x = ai.xAngle / 2;
torsoOrient.y = laraAI.angle / 2;
}
if (item.AIBits & GUARD)
{
headOrient.y = AIGuard(&creature);
}
else
{
if (laraAI.angle > ANGLE(112.0f) || laraAI.angle < ANGLE(-112.0f))
{
item.Animation.TargetState = HEAVY_GUARD_STATE_TURN_180;
}
else if (!Targetable(&item, &ai))
{
if (item.TriggerFlags != 1 && ai.distance > HEAVY_GUARD_CLOSE_RANGE)
item.Animation.TargetState = HEAVY_GUARD_STATE_WALK_FORWARD;
}
else if (ai.distance < HEAVY_GUARD_IDLE_AIM_RANGE || ai.zoneNumber != ai.enemyZone)
{
item.Animation.TargetState = HEAVY_GUARD_STATE_IDLE_TO_AIM;
}
else if (item.AIBits != MODIFY)
{
if (item.TriggerFlags != 1)
item.Animation.TargetState = HEAVY_GUARD_STATE_WALK_FORWARD;
}
}
break;
case HEAVY_GUARD_STATE_WALK_FORWARD:
creature.MaxTurn = HEAVY_GUARD_WALK_TURN_RATE_MAX;
if (ai.distance < HEAVY_GUARD_CLOSE_RANGE)
{
item.Animation.TargetState = HEAVY_GUARD_STATE_IDLE;
break;
}
if (Targetable(&item, &ai) &&
laraAI.angle < ANGLE(33.0f) && laraAI.angle > ANGLE(-33.0f))
{
if (item.Animation.FrameNumber >= GetFrameNumber(&item, 29))
item.Animation.TargetState = HEAVY_GUARD_STATE_WALK_RAYGUN_ATTACK_RIGHT;
else
item.Animation.TargetState = HEAVY_GUARD_STATE_WALK_RAYGUN_ATTACK_LEFT;
}
else
{
if (laraAI.angle > ANGLE(112.0f) || laraAI.angle < ANGLE(-112.0f))
item.Animation.TargetState = HEAVY_GUARD_STATE_IDLE;
else
item.Animation.TargetState = HEAVY_GUARD_STATE_WALK_FORWARD;
}
break;
case HEAVY_GUARD_STATE_WALK_RAYGUN_ATTACK_RIGHT:
case HEAVY_GUARD_STATE_WALK_RAYGUN_ATTACK_LEFT:
headOrient.y = laraAI.angle;
if (item.Animation.FrameNumber == g_Level.Anims[item.Animation.AnimNumber].frameBase)
{
if (item.Animation.ActiveState == HEAVY_GUARD_STATE_WALK_RAYGUN_ATTACK_LEFT)
FireHeavyGuardRaygun(item, false, false);
else
FireHeavyGuardRaygun(item, true, false);
}
break;
case HEAVY_GUARD_STATE_IDLE_TO_AIM:
creature.Flags = 0;
creature.MaxTurn = 0;
headOrient.y = laraAI.angle / 2;
torsoOrient.x = ai.xAngle / 2;
torsoOrient.y = laraAI.angle / 2;
if (abs(ai.angle) < HEAVY_GUARD_IDLE_TURN_RATE_MAX)
{
item.Pose.Orientation.y += ai.angle;
}
else if (ai.angle >= 0)
{
item.Pose.Orientation.y += HEAVY_GUARD_IDLE_TURN_RATE_MAX;
}
else
{
item.Pose.Orientation.y -= HEAVY_GUARD_IDLE_TURN_RATE_MAX;
}
if (item.TriggerFlags == 2)
{
item.Animation.TargetState = HEAVY_GUARD_STATE_MISFIRE;
}
else if (item.TriggerFlags == 3)
{
item.Animation.TargetState = HEAVY_GUARD_STATE_RATTLE_RAYGUN;
}
else if (Targetable(&item, &ai) &&
(ai.distance <= HEAVY_GUARD_IDLE_AIM_RANGE || ai.distance < HEAVY_GUARD_CLOSE_RANGE))
{
item.Animation.TargetState = HEAVY_GUARD_STATE_DUAL_RAYGUN_ATTACK;
}
else
{
item.Animation.TargetState = HEAVY_GUARD_STATE_IDLE;
}
break;
case HEAVY_GUARD_STATE_DUAL_RAYGUN_ATTACK:
headOrient.y = laraAI.angle / 2;
torsoOrient.x = ai.xAngle;
torsoOrient.y = laraAI.angle / 2;
if (abs(ai.angle) < HEAVY_GUARD_IDLE_TURN_RATE_MAX)
{
item.Pose.Orientation.y += ai.angle;
}
else
{
if (ai.angle >= 0)
item.Pose.Orientation.y += HEAVY_GUARD_IDLE_TURN_RATE_MAX;
else
item.Pose.Orientation.y -= HEAVY_GUARD_IDLE_TURN_RATE_MAX;
}
if (item.Animation.FrameNumber == (g_Level.Anims[item.Animation.AnimNumber].frameBase + 17))
{
FireHeavyGuardRaygun(item, false, false);
FireHeavyGuardRaygun(item, true, false);
}
break;
case HEAVY_GUARD_STATE_TURN_180:
creature.Flags = 0;
creature.MaxTurn = 0;
if (ai.angle < 0)
item.Pose.Orientation.y += HEAVY_GUARD_IDLE_TURN_RATE_MAX;
else
item.Pose.Orientation.y -= HEAVY_GUARD_IDLE_TURN_RATE_MAX;
if (TestLastFrame(&item))
item.Pose.Orientation.y += ANGLE(180.0f);
break;
case HEAVY_GUARD_STATE_MISFIRE:
headOrient.y = laraAI.angle / 2;
torsoOrient.x = ai.xAngle / 2;
torsoOrient.y = laraAI.angle / 2;
if (item.Animation.FrameNumber == (g_Level.Anims[item.Animation.AnimNumber].frameBase + 18))
{
FireHeavyGuardRaygun(item, false, false);
item.ItemFlags[1] = -16;
item.TriggerFlags = 3;
}
break;
case HEAVY_GUARD_STATE_RATTLE_RAYGUN:
item.TriggerFlags = 0;
break;
}
}
CreatureJoint(&item, 0, torsoOrient.y);
CreatureJoint(&item, 1, torsoOrient.x);
CreatureJoint(&item, 2, headOrient.y);
CreatureAnimation(itemNumber, headingAngle, 0);
}
void HeavyGuardHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex)
{
if (pos.has_value())
{
if (jointIndex == 2)
{
DoBloodSplat(pos->x, pos->y, pos->z, 10, source.Pose.Orientation.y, pos->RoomNumber);
DoDamage(&target, INT_MAX);
}
else
{
SoundEffect(SFX_TR4_BADDY_SWORD_RICOCHET, &target.Pose);
TriggerRicochetSpark(*pos, source.Pose.Orientation.y, 3, 0);
}
}
}
}

View file

@ -0,0 +1,12 @@
#pragma once
class EulerAngles;
class GameVector;
struct ItemInfo;
namespace TEN::Entities::Creatures::TR5
{
void InitialiseHeavyGuard(short itemNumber);
void HeavyGuardControl(short itemNumber);
void HeavyGuardHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex);
}

View file

@ -5,8 +5,8 @@
#include "Game/collision/collide_room.h"
#include "Game/control/box.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/item_fx.h"
#include "Game/effects/lightning.h"
#include "Game/effects/spark.h"
#include "Game/effects/tomb4fx.h"
#include "Game/itemdata/creature_info.h"
@ -20,8 +20,8 @@
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Items;
using namespace TEN::Effects::Lightning;
using namespace TEN::Effects::Spark;
namespace TEN::Entities::Creatures::TR5
@ -213,27 +213,28 @@ namespace TEN::Entities::Creatures::TR5
if (randomIndex < item.ItemFlags[0])
{
auto pos = GetJointPosition(&item, CyborgJoints[randomIndex], Vector3i(0, 0, 50));
auto pos = GetJointPosition(&item, CyborgJoints[randomIndex], Vector3i(0, 0, 50)).ToVector3();
TriggerLightningGlow(pos.x, pos.y, pos.z, 48, 32, 32, 64);
SpawnElectricityGlow(pos, 48, 32, 32, 64);
SpawnCyborgSpark(pos.ToVector3());
SpawnCyborgSpark(pos);
TriggerDynamicLight(pos.x, pos.y, pos.z, Random::GenerateInt(4, 20), 31, 63, 127);
SoundEffect(SFX_TR5_HITMAN_SPARKS_SHORT, &item.Pose);
if (randomIndex == 5 || randomIndex == 7 || randomIndex == 10)
{
auto pos2 = Vector3i::Zero;
auto pos2 = Vector3::Zero;
auto pointColl2 = GetCollision(pos2.x, pos2.y, pos2.z, item.RoomNumber);
switch (randomIndex)
{
case 5:
pos2 = GetJointPosition(&item, 15, Vector3i(0, 0, 50));
pos2 = GetJointPosition(&item, 15, Vector3i(0, 0, 50)).ToVector3();
break;
case 7:
pos2 = GetJointPosition(&item, 6, Vector3i(0, 0, 50));
pos2 = GetJointPosition(&item, 6, Vector3i(0, 0, 50)).ToVector3();
pointColl2 = GetCollision(pos2.x, pos2.y, pos2.z, item.RoomNumber);
if (TestEnvironment(ENV_FLAG_WATER, pointColl2.RoomNumber) && item.HitPoints > 0)
@ -246,11 +247,11 @@ namespace TEN::Entities::Creatures::TR5
break;
case 10:
pos2 = GetJointPosition(&item, 12, Vector3i(0, 0, 50));
pos2 = GetJointPosition(&item, 12, Vector3i(0, 0, 50)).ToVector3();
break;
}
TriggerLightning(&pos, &pos2, Random::GenerateInt(8, 16), 32, 64, 128, 24, (LI_SPLINE | LI_THINOUT | LI_THININ), 6, 3);
SpawnElectricity(pos, pos2, Random::GenerateInt(8, 16), 32, 64, 128, 24, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::ThinIn, 6, 3);
}
}

View file

@ -937,7 +937,7 @@ namespace TEN::Entities::Creatures::TR5
}
else
{
TestTriggers(creature->Enemy->Pose.Position.x, creature->Enemy->Pose.Position.y, creature->Enemy->Pose.Position.z, enemy->RoomNumber, true);
TestTriggers(enemy, true);
item->Animation.RequiredState = GUARD_STATE_WALK_FORWARD;
if (creature->Enemy->Flags & 2)

View file

@ -5,8 +5,8 @@
#include "Game/control/los.h"
#include "Game/effects/debris.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/item_fx.h"
#include "Game/effects/lightning.h"
#include "Game/effects/tomb4fx.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
@ -21,8 +21,8 @@
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Items;
using namespace TEN::Effects::Lightning;
using namespace TEN::Math;
// NOTES:
@ -359,7 +359,8 @@ namespace TEN::Entities::Creatures::TR5
// Start firing from eye.
origin1.RoomNumber = item->RoomNumber;
guardian->LOS[i] = LOS(&origin1, &eye);
guardian->fireArcs[i] = TriggerLightning(&origin1.ToVector3i(), &eye.ToVector3i(), (GetRandomControl() & 1) + 3, color.x, color.y, color.z, 46, ( LI_THININ | LI_SPLINE | LI_THINOUT), 6, 10);
SpawnElectricity(origin1.ToVector3(), eye.ToVector3(), (GetRandomControl() & 1) + 3, color.x, color.y, color.z, 46, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinIn | (int)ElectricityFlags::ThinOut, 6, 10);
guardian->fireArcs[i] = &ElectricityArcs.back();
StopSoundEffect(SFX_TR5_GOD_HEAD_CHARGE);
SoundEffect(SFX_TR5_GOD_HEAD_BLAST, &item->Pose);
}
@ -367,14 +368,14 @@ namespace TEN::Entities::Creatures::TR5
if (GlobalCounter & 1)
{
SpawnGuardianSparks(origin1.ToVector3(), colorSparks, 3);
TriggerLightningGlow(origin1.x, origin1.y, origin1.z, (GetRandomControl() & 3) + 32, color.x, color.y, color.z);
SpawnElectricityGlow(origin1.ToVector3(), (GetRandomControl() & 3) + 32, color.x, color.y, color.z);
TriggerDynamicLight(origin1.x, origin1.y, origin1.z, (GetRandomControl() & 3) + 16, color.x, color.y, color.z);
if (!guardian->LOS[i] && guardian->fireArcs[i] != nullptr)
{
TriggerLightningGlow(guardian->fireArcs[i]->pos4.x, guardian->fireArcs[i]->pos4.y, guardian->fireArcs[i]->pos4.z, (GetRandomControl() & 3) + 16, color.x, color.y, color.z);
SpawnElectricityGlow(guardian->fireArcs[i]->pos4, (GetRandomControl() & 3) + 16, color.x, color.y, color.z);
TriggerDynamicLight(guardian->fireArcs[i]->pos4.x, guardian->fireArcs[i]->pos4.y, guardian->fireArcs[i]->pos4.z, (GetRandomControl() & 3) + 6, color.x, color.y, color.z);
SpawnGuardianSparks(guardian->fireArcs[i]->pos4.ToVector3(), colorSparks, 3);
SpawnGuardianSparks(guardian->fireArcs[i]->pos4, colorSparks, 3);
}
}
@ -545,8 +546,8 @@ namespace TEN::Entities::Creatures::TR5
size = 32;
}
auto origin = Vector3i::Zero;
auto target = GetJointPosition(&g_Level.Items[guardian->BaseItem], 0, GuardianBasePosition);
auto origin = Vector3::Zero;
auto target = GetJointPosition(&g_Level.Items[guardian->BaseItem], 0, GuardianBasePosition).ToVector3();
for (int i = 0; i < GUARDIAN_CHARGE_ARC_COUNT; i++)
{
@ -562,8 +563,8 @@ namespace TEN::Entities::Creatures::TR5
continue;
}
origin = GetJointPosition(&g_Level.Items[guardian->BaseItem], 0, GuardianChargePositions[i]);
TriggerLightning(&origin, &target, Random::GenerateInt(8, 16), color.x, color.y, color.z, 16, (LI_SPLINE | LI_THINOUT | LI_THININ), 6, 5);
origin = GetJointPosition(&g_Level.Items[guardian->BaseItem], 0, GuardianChargePositions[i]).ToVector3();
SpawnElectricity(origin, target, Random::GenerateInt(8, 16), color.x, color.y, color.z, 16, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::ThinIn, 6, 5);
}
if (GlobalCounter & 1)
@ -572,20 +573,20 @@ namespace TEN::Entities::Creatures::TR5
{
if (item->MeshBits.Test(GuardianEyeJoints[i]))
{
origin = GetJointPosition(item, GuardianEyeJoints[i]);
TriggerLightningGlow(origin.x, origin.y, origin.z, size + (GetRandomControl() & 3), color.x, color.y, color.z);
SpawnGuardianSparks(origin.ToVector3(), colorSparks, 3);
origin = GetJointPosition(item, GuardianEyeJoints[i]).ToVector3();
SpawnElectricityGlow(origin, size + (GetRandomControl() & 3), color.x, color.y, color.z);
SpawnGuardianSparks(origin, colorSparks, 3);
}
}
TriggerLightningGlow(target.x, target.y, target.z, (GetRandomControl() & 3) + size + 8, color.x, color.y, color.z);
SpawnElectricityGlow(target, (GetRandomControl() & 3) + size + 8, color.x, color.y, color.z);
TriggerDynamicLight(target.x, target.y, target.z, (GetRandomControl() & 3) + 16, color.x, color.y, color.z);
}
if (!(GlobalCounter & 3))
TriggerLightning(&target, &item->Pose.Position, Random::GenerateInt(8, 16), color.x, color.y, color.z, 16, (LI_SPLINE | LI_THINOUT | LI_THININ), 6, 5);
SpawnElectricity(target, item->Pose.Position.ToVector3(), Random::GenerateInt(8, 16), color.x, color.y, color.z, 16, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::ThinIn, 6, 5);
SpawnGuardianSparks(target.ToVector3(), colorSparks, 3, 1);
SpawnGuardianSparks(target, colorSparks, 3, 1);
}
void DoGuardianDeath(int itemNumber, ItemInfo& item)

View file

@ -1,7 +1,7 @@
#pragma once
#include "Game/effects/lightning.h"
#include "Game/effects/Electricity.h"
using namespace TEN::Effects::Lightning;
using namespace TEN::Effects::Electricity;
namespace TEN::Entities::Creatures::TR5
{
@ -12,8 +12,8 @@ namespace TEN::Entities::Creatures::TR5
struct GuardianInfo
{
Vector3i target;
std::array<LIGHTNING_INFO*, GUARDIAN_FIRE_ARC_COUNT> fireArcs = {}; // elptr
std::array<LIGHTNING_INFO*, GUARDIAN_CHARGE_ARC_COUNT> chargeArcs = {}; // blptr
std::array<Electricity*, GUARDIAN_FIRE_ARC_COUNT> fireArcs = {}; // elptr
std::array<Electricity*, GUARDIAN_CHARGE_ARC_COUNT> chargeArcs = {}; // blptr
bool LOS[2];
byte trackSpeed;
bool trackLara;

View file

@ -1,36 +1,41 @@
#include "framework.h"
#include "tr5_roman_statue.h"
#include "Game/items.h"
#include "Game/effects/tomb4fx.h"
#include "Game/effects/effects.h"
#include "Game/effects/spark.h"
#include "Objects/TR5/Entity/tr5_roman_statue.h"
#include "Game/animation.h"
#include "Game/collision/collide_room.h"
#include "Game/control/box.h"
#include "Game/people.h"
#include "Game/effects/debris.h"
#include "Game/animation.h"
#include "Game/effects/lightning.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/spark.h"
#include "Game/effects/tomb4fx.h"
#include "Game/itemdata/creature_info.h"
#include "Game/items.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/misc.h"
#include "Game/people.h"
#include "Math/Math.h"
#include "Sound/sound.h"
#include "Specific/setup.h"
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Lightning;
using namespace TEN::Math;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Spark;
using namespace TEN::Math;
namespace TEN::Entities::Creatures::TR5
{
constexpr auto ROMAN_STATUE_GRENADE_SUPER_AMMO_LIMITER = 2.0f;
constexpr auto ROMAN_STATUE_EXPLOSIVE_DAMAGE_COEFF = 2.0f;
const auto RomanStatueBite = BiteInfo(Vector3::Zero, 15);
struct RomanStatueInfo
{
Vector3i Position;
LIGHTNING_INFO* EnergyArcs[8];
unsigned int Count;
Vector3i Position = Vector3i::Zero;
Electricity* EnergyArcs[8] = {};
unsigned int Count = 0;
};
RomanStatueInfo RomanStatueData;
@ -56,19 +61,27 @@ namespace TEN::Entities::Creatures::TR5
// TODO
enum RomanStatueAnim
{
STATUE_ANIM_HIT = 5,
STATUE_ANIM_RECOIL = 5,
STATUE_ANIM_DEATH = 14,
STATUE_ANIM_START_JUMP_DOWN = 16
};
enum RomanStatueHitMeshFlags
{
MS_LIGHT_DMG = 0x10,
MS_MEDIUM_DMG = 0x410,
MS_HARD_DMG = 0x510,
MS_HEAVY_DMG = 0x10510
};
static void RomanStatueHitEffect(ItemInfo* item, Vector3i* pos, int joint)
{
*pos = GetJointPosition(item, joint, *pos);
if (!(GetRandomControl() & 0x1F))
{
short fxNumber = CreateNewEffect(item->RoomNumber);
if (fxNumber != -1)
int fxNumber = CreateNewEffect(item->RoomNumber);
if (fxNumber != NO_ITEM)
{
auto* fx = &EffectList[fxNumber];
@ -147,49 +160,11 @@ namespace TEN::Entities::Creatures::TR5
spark->dSize = spark->sSize = spark->size = size + (GetRandomControl() & 3);
}
static void TriggerRomanStatueScreamingSparks(int x, int y, int z, short xv, short yv, short zv, int flags)
{
auto* spark = GetFreeParticle();
spark->on = 1;
spark->sR = 0;
spark->sG = 0;
spark->sB = 0;
spark->dR = 64;
if (flags)
{
spark->dG = (GetRandomControl() & 0x3F) - 64;
spark->dB = spark->dG / 2;
}
else
{
spark->dB = (GetRandomControl() & 0x3F) - 64;
spark->dG = spark->dB / 2;
}
spark->colFadeSpeed = 4;
spark->fadeToBlack = 4;
spark->life = 16;
spark->sLife = 16;
spark->x = x;
spark->y = y;
spark->z = z;
spark->xVel = xv;
spark->yVel = yv;
spark->zVel = zv;
spark->blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
spark->friction = 34;
spark->maxYvel = 0;
spark->gravity = 0;
spark->flags = SP_NONE;
}
static void TriggerRomanStatueAttackEffect1(short itemNum, int factor)
{
auto* spark = GetFreeParticle();
spark->on = 1;
spark->on = true;
spark->sR = 0;
spark->sB = (GetRandomControl() & 0x3F) - 96;
spark->dB = (GetRandomControl() & 0x3F) - 96;
@ -229,32 +204,31 @@ namespace TEN::Entities::Creatures::TR5
static void RomanStatueAttack(Pose* pos, short roomNumber, short count)
{
short fxNumber = CreateNewEffect(roomNumber);
int fxNumber = CreateNewEffect(roomNumber);
if (fxNumber == NO_ITEM)
return;
if (fxNumber != NO_ITEM)
{
auto* fx = &EffectList[fxNumber];
auto* fx = &EffectList[fxNumber];
fx->pos.Position.x = pos->Position.x;
fx->pos.Position.y = pos->Position.y;
fx->pos.Position.z = pos->Position.z;
fx->pos.Orientation.x = pos->Orientation.x;
fx->pos.Orientation.y = pos->Orientation.y;
fx->pos.Orientation.z = 0;
fx->roomNumber = roomNumber;
fx->counter = 16 * count + 15;
fx->flag1 = 1;
fx->objectNumber = ID_BUBBLES;
fx->speed = (GetRandomControl() & 0x1F) + 64;
fx->frameNumber = Objects[ID_BUBBLES].meshIndex + 8;
}
fx->pos.Position.x = pos->Position.x;
fx->pos.Position.y = pos->Position.y;
fx->pos.Position.z = pos->Position.z;
fx->pos.Orientation.x = pos->Orientation.x;
fx->pos.Orientation.y = pos->Orientation.y;
fx->pos.Orientation.z = 0;
fx->roomNumber = roomNumber;
fx->counter = 16 * count + 15;
fx->flag1 = 1;
fx->objectNumber = ID_BUBBLES;
fx->speed = (GetRandomControl() & 0x1F) + 64;
fx->frameNumber = Objects[ID_BUBBLES].meshIndex + 8;
}
void TriggerRomanStatueMissileSparks(Vector3i* pos, char fxObject)
{
auto* spark = GetFreeParticle();
spark->on = 1;
spark->on = true;
spark->sR = 0;
spark->sG = (GetRandomControl() & 0x3F) - 96;
spark->sB = spark->sG / 2;
@ -312,59 +286,57 @@ namespace TEN::Entities::Creatures::TR5
auto prevMeshSwapBits = item->Model.MeshIndex;
// At determined HP values, roman statues sheds material.
if (item->HitPoints < 1 && !item->TestMeshSwapFlags(0x10000))
if (item->Effect.Type == EffectType::Fire)
item->Effect.Type = EffectType::None;
// At determined HP values, the statue sheds material.
if (item->HitPoints < 1 && !item->TestMeshSwapFlags(MS_HEAVY_DMG))
{
ExplodeItemNode(item, 16, 0, 8);
item->MeshBits |= 0x10000;
item->SetMeshSwapFlags(0x10000);
item->MeshBits |= MS_HEAVY_DMG;
item->SetMeshSwapFlags(MS_HEAVY_DMG);
}
else if (item->HitPoints < 75 && !item->TestMeshSwapFlags(0x100))
else if (item->HitPoints < 75 && !item->TestMeshSwapFlags(MS_HARD_DMG))
{
ExplodeItemNode(item, 8, 0, 8);
item->MeshBits |= 0x100;
item->SetMeshSwapFlags(0x100);
item->MeshBits |= MS_HARD_DMG;
item->SetMeshSwapFlags(MS_HARD_DMG);
}
else if (item->HitPoints < 150 && !item->TestMeshSwapFlags(0x400))
else if (item->HitPoints < 150 && !item->TestMeshSwapFlags(MS_MEDIUM_DMG))
{
ExplodeItemNode(item, 10, 0, 32);
ExplodeItemNode(item, 11, 0, 32);
item->MeshBits |= 0x400u;
item->SetMeshSwapFlags(0x400);
item->MeshBits |= MS_MEDIUM_DMG;
item->SetMeshSwapFlags(MS_MEDIUM_DMG);
}
else if (item->HitPoints < 225 && !item->TestMeshSwapFlags(0x10))
else if (item->HitPoints < 225 && !item->TestMeshSwapFlags(MS_LIGHT_DMG))
{
ExplodeItemNode(item, 4, 0, 8);
item->MeshBits |= 0x10;
item->SetMeshSwapFlags(0x10);
item->MeshBits |= MS_LIGHT_DMG;
item->SetMeshSwapFlags(MS_LIGHT_DMG);
}
// Play hit animation.
// Set recoil animation.
if (prevMeshSwapBits != item->Model.MeshIndex)
{
item->Animation.TargetState = STATUE_STATE_HIT;
item->Animation.ActiveState = STATUE_STATE_HIT;
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + STATUE_ANIM_HIT;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
}
SetAnimation(item, STATUE_ANIM_RECOIL);
if (item->HitPoints > 0)
{
creature->Enemy = LaraItem;
AI_INFO AI;
CreatureAIInfo(item, &AI);
AI_INFO ai;
CreatureAIInfo(item, &ai);
GetCreatureMood(item, &AI, true);
CreatureMood(item, &AI, true);
GetCreatureMood(item, &ai, true);
CreatureMood(item, &ai, true);
angle = CreatureTurn(item, creature->MaxTurn);
if (AI.ahead)
if (ai.ahead)
{
joint0 = AI.angle / 2;
joint2 = AI.angle / 2;
joint1 = AI.xAngle;
joint0 = ai.angle / 2;
joint2 = ai.angle / 2;
joint1 = ai.xAngle;
}
creature->MaxTurn = 0;
@ -373,33 +345,39 @@ namespace TEN::Entities::Creatures::TR5
byte color;
int deltaFrame;
bool unknown;
color = (GetRandomControl() & 0x3F) + 128;
switch (item->Animation.ActiveState)
{
case STATUE_STATE_IDLE:
creature->Flags = 0;
joint2 = AI.angle;
joint2 = ai.angle;
if (creature->Mood == MoodType::Attack)
{
creature->MaxTurn = ANGLE(2.0f);
}
else
{
creature->MaxTurn = 0;
item->Animation.TargetState = STATUE_STATE_WALK;
creature->MaxTurn = 0;
}
if (item->AIBits ||
!(GetRandomControl() & 0x1F) &&
(AI.distance > pow(SECTOR(1), 2) ||
(ai.distance > pow(SECTOR(1), 2) ||
creature->Mood != MoodType::Attack))
{
joint2 = AIGuard((CreatureInfo*)creature);
}
else if (AI.angle > ANGLE(112.5f) || AI.angle < -ANGLE(112.5f))
item->Animation.TargetState = STATUE_STATE_TURN_180;
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
else if (ai.angle > ANGLE(112.5f) || ai.angle < -ANGLE(112.5f))
{
if (AI.bite & ((GetRandomControl() & 3) == 0))
item->Animation.TargetState = STATUE_STATE_TURN_180;
}
else if (ai.ahead && ai.distance < pow(SECTOR(1), 2))
{
if (ai.bite & ((GetRandomControl() & 3) == 0))
item->Animation.TargetState = STATUE_STATE_ATTACK_1;
else if (GetRandomControl() & 1)
item->Animation.TargetState = STATUE_STATE_ATTACK_2;
@ -417,14 +395,14 @@ namespace TEN::Entities::Creatures::TR5
if (item->TriggerFlags == 1)
{
if (Targetable(item, &AI) && GetRandomControl() & 1)
if (Targetable(item, &ai) && Random::TestProbability(1 / 2.0f))
{
item->Animation.TargetState = STATUE_STATE_ENERGY_ATTACK;
break;
}
}
if (item->TriggerFlags || AI.distance >= pow(SECTOR(2.5f), 2) || !AI.bite)
if (item->TriggerFlags || ai.distance >= pow(SECTOR(2.5f), 2) || !ai.bite)
{
item->Animation.TargetState = STATUE_STATE_WALK;
break;
@ -454,24 +432,24 @@ namespace TEN::Entities::Creatures::TR5
deltaFrame2 = 16;
}
else
{
deltaFrame2 = 4 * (62 - deltaFrame);
}
color = (deltaFrame2 * ((GetRandomControl() & 0x3F) + 128)) / 16;
if (item->TriggerFlags)
TriggerDynamicLight(pos.x, pos.y, pos.z, 16, 0, color, color / 2);
TriggerDynamicLight(pos2.x, pos2.y, pos2.z, 16, 0, color, color / 2);
else
TriggerDynamicLight(pos.x, pos.y, pos.z, 16, 0, color / 2, color);
TriggerDynamicLight(pos2.x, pos2.y, pos2.z, 16, 0, color / 2, color);
for (int i = 0; i < 2; i++)
{
int R = 64;
int B = (GetRandomControl() & 0x3F) - 64;
int G = B;
auto sparkColor = Vector3(R, G, B);
TriggerAttackSpark(pos.ToVector3(), sparkColor);
}
if (item->TriggerFlags)
TriggerAttackSpark(pos2.ToVector3(), Vector3(0, color, color / 2));
else
TriggerAttackSpark(pos2.ToVector3(), Vector3(0, color / 2, color));
}
}
if (deltaFrame <= 90 || deltaFrame >= 130)
@ -483,7 +461,7 @@ namespace TEN::Entities::Creatures::TR5
pos = Vector3i(-40, 64, GetRandomControl() % 360);
pos = GetJointPosition(item, 14, pos);
color = (GetRandomControl() & 0x3F) + 128;
pos2 = GetJointPosition(item, 14, Vector3i(-48, 48, 450));
pos1.x = (GetRandomControl() & 0xFFF) + item->Pose.Position.x - SECTOR(2);
pos1.y = item->Pose.Position.y - (GetRandomControl() & 0x3FF) - SECTOR(4);
@ -500,9 +478,9 @@ namespace TEN::Entities::Creatures::TR5
arc->pos4.z = pos2.z;
if (item->TriggerFlags)
TriggerLightningGlow(pos1.x, pos1.y, pos1.z, 16, 0, color, color / 2);
SpawnElectricityGlow(pos.ToVector3(), 16, 0, color, color / 2);
else
TriggerLightningGlow(pos1.x, pos1.y, pos1.z, 16, 0, color / 2, color);
SpawnElectricityGlow(pos.ToVector3(), 16, 0, color / 2, color);
continue;
}
@ -515,33 +493,34 @@ namespace TEN::Entities::Creatures::TR5
if (item->TriggerFlags)
{
/*RomanStatueData.energyArcs[i] = TriggerEnergyArc(
(Vector3i*)& dest.Orientation.x,
(Vector3i*)& dest,
(GetRandomControl() & 0x3F) + 16,
(color >> 1) | ((color | 0x180000) << 8),
15,
48,
5);*/
SpawnElectricity(
pos1.ToVector3(), pos.ToVector3(),
Random::GenerateInt(64, 80),
0, color, color / 2,
50, (int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut, 2, 10);
RomanStatueData.EnergyArcs[i] = &ElectricityArcs.back();
TriggerLightningGlow(pos.x, pos.y, pos.z, 16, 0, color, color / 2);
TriggerRomanStatueShockwaveAttackSparks(pos1.x, pos1.y, pos1.z, 84, 164, 10, 128);
SpawnElectricityGlow(RomanStatueData.EnergyArcs[i]->pos4, 36, 0, color, color / 2);
RomanStatueData.EnergyArcs[i] = nullptr;
unknown = 1;
continue;
}
/*RomanStatueData.energyArcs[i] = TriggerEnergyArc(
&pos2,
&pos1,
(GetRandomControl() & 0x3F) + 16,
color | (((color >> 1) | 0x180000) << 8),
15,
48,
5);*/
SpawnElectricity(
pos1.ToVector3(), pos.ToVector3(),
Random::GenerateInt(64, 80),
0, color / 2, color,
50, (int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut, 2, 10);
RomanStatueData.EnergyArcs[i] = &ElectricityArcs.back();
TriggerLightningGlow(pos.x, pos.y, pos.z, 16, 0, color / 2, color);
SpawnElectricityGlow(RomanStatueData.EnergyArcs[i]->pos4, 36, 0, color / 2, color);
RomanStatueData.EnergyArcs[i] = nullptr;
unknown = true;
}
break;
case STATUE_STATE_ATTACK_1:
@ -550,15 +529,17 @@ namespace TEN::Entities::Creatures::TR5
case STATUE_STATE_ATTACK_4:
creature->MaxTurn = 0;
if (abs(AI.angle) >= ANGLE(2.0f))
if (abs(ai.angle) >= ANGLE(2.0f))
{
if (AI.angle >= 0)
if (ai.angle >= 0)
item->Pose.Orientation.y += ANGLE(2.0f);
else
item->Pose.Orientation.y -= ANGLE(2.0f);
}
else
item->Pose.Orientation.y += AI.angle;
{
item->Pose.Orientation.y += ai.angle;
}
if (item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 10)
{
@ -626,7 +607,9 @@ namespace TEN::Entities::Creatures::TR5
TriggerRomanStatueAttackEffect1(itemNumber, deltaFrame);
}
else
{
TriggerRomanStatueAttackEffect1(itemNumber, deltaFrame2);
}
}
}
@ -634,31 +617,35 @@ namespace TEN::Entities::Creatures::TR5
case STATUE_STATE_WALK:
creature->Flags = 0;
joint2 = AI.angle;
joint2 = ai.angle;
if (creature->Mood == MoodType::Attack)
{
creature->MaxTurn = ANGLE(7.0f);
}
else
{
creature->MaxTurn = 0;
if (abs(AI.angle) >= ANGLE(2.0f))
if (abs(ai.angle) >= ANGLE(2.0f))
{
if (AI.angle > 0)
if (ai.angle > 0)
item->Pose.Orientation.y += ANGLE(2.0f);
else
item->Pose.Orientation.y -= ANGLE(2.0f);
}
else
item->Pose.Orientation.y += AI.angle;
{
item->Pose.Orientation.y += ai.angle;
}
}
if (AI.distance < pow(SECTOR(1), 2))
if (ai.distance < pow(SECTOR(1), 2))
{
item->Animation.TargetState = STATUE_STATE_IDLE;
break;
}
if (AI.bite && AI.distance < pow(SECTOR(1.75f), 2))
if (ai.bite && ai.distance < pow(SECTOR(1.75f), 2))
{
item->Animation.TargetState = 9;
break;
@ -666,14 +653,14 @@ namespace TEN::Entities::Creatures::TR5
if (item->TriggerFlags == 1)
{
if (Targetable(item, &AI) && !(GetRandomControl() & 3))
if (Targetable(item, &ai) && !(GetRandomControl() & 3))
{
item->Animation.TargetState = STATUE_STATE_IDLE;
break;
}
}
if (item->TriggerFlags || AI.distance >= pow(SECTOR(2.5f), 2))
if (item->TriggerFlags || ai.distance >= pow(SECTOR(2.5f), 2))
item->Animation.TargetState = STATUE_STATE_WALK;
else
item->Animation.TargetState = STATUE_STATE_IDLE;
@ -684,7 +671,7 @@ namespace TEN::Entities::Creatures::TR5
creature->MaxTurn = 0;
creature->Flags = 0;
if (AI.angle > 0)
if (ai.angle > 0)
item->Pose.Orientation.y -= ANGLE(2.0f);
else
item->Pose.Orientation.y += ANGLE(2.0f);
@ -713,32 +700,32 @@ namespace TEN::Entities::Creatures::TR5
pos2 = GetJointPosition(item, 14, Vector3i(-48, 48, 450));
auto orient = Geometry::GetOrientToPoint(pos2.ToVector3(), pos1.ToVector3());
auto attackPos = Pose(pos2, orient);
auto attackPose = Pose(pos2, orient);
short roomNumber = item->RoomNumber;
GetFloor(pos2.x, pos2.y, pos2.z, &roomNumber);
RomanStatueAttack(&attackPos, roomNumber, 1);
RomanStatueAttack(&attackPose, roomNumber, 1);
TriggerRomanStatueShockwaveAttackSparks(
attackPos.Position.x,
attackPos.Position.y,
attackPos.Position.z,
attackPose.Position.x,
attackPose.Position.y,
attackPose.Position.z,
0,
(((GetRandomControl() & 0x3F) + 128) / 2),
(((GetRandomControl() & 0x3F) + 128)),
64);
RomanStatueData.Count = 16;
RomanStatueData.Position.x = attackPos.Position.x;
RomanStatueData.Position.y = attackPos.Position.y;
RomanStatueData.Position.z = attackPos.Position.z;
RomanStatueData.Position = attackPose.Position;
if (item->ItemFlags[0])
item->ItemFlags[0]--;
}
else if (deltaFrame < 10 || deltaFrame > 49)
{
break;
}
deltaFrame -= 10;
if (deltaFrame < 32)
@ -755,9 +742,7 @@ namespace TEN::Entities::Creatures::TR5
if (i == 0)
{
TriggerDynamicLight(
pos2.x,
pos2.y,
pos2.z,
pos2.x, pos2.y, pos2.z,
8,
0,
(deltaFrame * ((GetRandomControl() & 0x3F) + 128)) / 32,
@ -769,38 +754,30 @@ namespace TEN::Entities::Creatures::TR5
if (arc && deltaFrame && deltaFrame != 24)
{
if (deltaFrame < 16)
{
arc->life = 56;
arc->r = 0;
arc->g = g;
arc->b = b;
}
arc->pos1 = pos1;
arc->pos4 = pos2;
arc->pos1 = pos1.ToVector3();
arc->pos4 = pos2.ToVector3();
}
else if (deltaFrame >= 16)
else if (deltaFrame < 16)
{
if (deltaFrame == 24)
{
/*TriggerEnergyArc(&pos1, &pos2, 0, ((GetRandomControl() & 0x3F) + 128),
(((GetRandomControl() & 0x3F) + 128) / 2), 256, 32, 32, ENERGY_ARC_NO_RANDOMIZE,
ENERGY_ARC_STRAIGHT_LINE);*/
}
}
else
{
/*TriggerEnergyArc(&pos1, &pos2, 0, g, b, 256, 24, 32, ENERGY_ARC_NO_RANDOMIZE,
ENERGY_ARC_STRAIGHT_LINE);*/
SpawnElectricity(
pos1.ToVector3(), pos2.ToVector3(),
Random::GenerateInt(8, 16),
84, 164, 10,
50, (int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut, 6, 2);
RomanStatueData.EnergyArcs[i] = &ElectricityArcs.back();
/*RomanStatueData.energyArcs[i] = TriggerEnergyArc(
&pos1,
&pos2,
(GetRandomControl() & 7) + 8,
cb | ((cg | 0x180000) << 8),
12,
64,
4);*/
}
else if (deltaFrame == 24)
{
color = (GetRandomControl() & 0x3F) + 128;
SpawnElectricity(
pos1.ToVector3(), pos2.ToVector3(),
Random::GenerateInt(18, 26),
0, color, color / 2,
50, (int)ElectricityFlags::ThinIn | (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut, 8, 2);
}
}
}
@ -855,8 +832,7 @@ namespace TEN::Entities::Creatures::TR5
auto pos = Vector3i(
(GetRandomControl() & 0x1F) - 16,
86,
(GetRandomControl() & 0x1F) - 16
);
(GetRandomControl() & 0x1F) - 16);
RomanStatueHitEffect(item, &pos, 10);
}
@ -865,8 +841,7 @@ namespace TEN::Entities::Creatures::TR5
auto pos = Vector3i(
-40,
(GetRandomControl() & 0x7F) + 148,
(GetRandomControl() & 0x3F) - 32
);
(GetRandomControl() & 0x3F) - 32);
RomanStatueHitEffect(item, &pos, 4);
}
@ -875,8 +850,7 @@ namespace TEN::Entities::Creatures::TR5
auto pos = Vector3i(
(GetRandomControl() & 0x3F) + 54,
-170,
(GetRandomControl() & 0x1F) + 27
);
(GetRandomControl() & 0x1F) + 27);
RomanStatueHitEffect(item, &pos, 8);
}
@ -885,6 +859,7 @@ namespace TEN::Entities::Creatures::TR5
void RomanStatueHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex)
{
const auto& player = *GetLaraInfo(&source);
const auto& object = Objects[target.ObjectNumber];
if (object.hitEffect == HitEffect::Richochet && pos.has_value())
@ -893,6 +868,21 @@ namespace TEN::Entities::Creatures::TR5
SoundEffect(SFX_TR5_SWORD_GOD_HIT_METAL, &target.Pose);
}
DoItemHit(&target, damage, isExplosive);
if (pos.has_value() && (player.Control.Weapon.GunType == LaraWeaponType::Pistol ||
player.Control.Weapon.GunType == LaraWeaponType::Shotgun ||
player.Control.Weapon.GunType == LaraWeaponType::Uzi ||
player.Control.Weapon.GunType == LaraWeaponType::HK ||
player.Control.Weapon.GunType == LaraWeaponType::Revolver))
{
DoItemHit(&target, damage, isExplosive);
}
else if (player.Weapons[(int)LaraWeaponType::GrenadeLauncher].SelectedAmmo == WeaponAmmoType::Ammo2)
{
DoItemHit(&target, damage / ROMAN_STATUE_GRENADE_SUPER_AMMO_LIMITER, isExplosive);
}
else
{
DoItemHit(&target, damage * ROMAN_STATUE_EXPLOSIVE_DAMAGE_COEFF, isExplosive);
}
}
}

View file

@ -412,7 +412,7 @@ void ClassicRollingBallControl(short itemNum)
item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z);
TestTriggers(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, roomNum, true);
TestTriggers(item, true);
DoVehicleCollision(item, CLICK(1.5f));
if (item->Pose.Position.y >= item->Floor - CLICK(1))

View file

@ -15,6 +15,7 @@
// Creatures
#include "Objects/TR5/Entity/AutoGun.h" // OK
#include "Objects/TR5/Entity/HeavyGuard.h" // OK
#include "Objects/TR5/Entity/tr5_brownbeast.h" // OK
#include "Objects/TR5/Entity/tr5_chef.h" // OK
#include "Objects/TR5/Entity/tr5_cyborg.h" // OK
@ -442,15 +443,15 @@ static void StartEntity(ObjectInfo *obj)
obj = &Objects[ID_GUARD_LASER];
if (obj->loaded)
{
obj->initialise = InitialiseGuardLaser;
obj->initialise = InitialiseHeavyGuard;
obj->collision = CreatureCollision;
//obj->control = GuardControlLaser;
obj->control = HeavyGuardControl;
obj->shadowType = ShadowMode::All;
obj->HitRoutine = HeavyGuardHit;
obj->biteOffset = 0;
obj->HitPoints = 24;
obj->pivotLength = 50;
obj->radius = 128;
obj->explodableMeshbits = 4;
obj->intelligent = true;
obj->undead = true;
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
@ -625,7 +626,6 @@ static void StartEntity(ObjectInfo *obj)
obj->pivotLength = 50;
obj->radius = 256;
obj->intelligent = true;
obj->LotType = LotType::Human;
obj->meshSwapSlot = ID_MESHSWAP_ROMAN_GOD1 + i;
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
obj->SetBoneRotationFlags(13, ROT_X | ROT_Y);

View file

@ -9,6 +9,7 @@
#include "Game/animation.h"
#include "Game/gui.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Specific/level.h"
#include "Specific/fast_vector.h"
#include "Renderer/Renderer11Enums.h"
@ -50,6 +51,7 @@ class EulerAngles;
struct CAMERA_INFO;
struct RendererRectangle;
using namespace TEN::Effects::Electricity;
using namespace TEN::Gui;
namespace TEN::Renderer
@ -517,7 +519,8 @@ namespace TEN::Renderer
void DrawFires(RenderView& view);
void DrawParticles(RenderView& view);
void DrawSmokes(RenderView& view);
void DrawLightning(RenderView& view);
void DrawElectricity(RenderView& view);
void DrawHelicalLasers(RenderView& view);
void DrawBlood(RenderView& view);
void DrawWeatherParticles(RenderView& view);
void DrawDrips(RenderView& view);

View file

@ -1522,7 +1522,8 @@ namespace TEN::Renderer
DrawRipples(view);
DrawSplashes(view);
DrawShockwaves(view);
DrawLightning(view);
DrawElectricity(view);
DrawHelicalLasers(view);
DrawRopes(view);
// Here is where we actually output sprites
@ -1933,7 +1934,10 @@ namespace TEN::Renderer
int meshIndex = Objects[ID_CAUSTICS_TEXTURES].meshIndex;
int causticsFrame = nmeshes ? meshIndex + ((GlobalCounter) % nmeshes) : 0;
BindTexture(TEXTURE_CAUSTICS, m_sprites[causticsFrame].Texture, SAMPLER_NONE);
if (m_sprites.size() > causticsFrame)
{
BindTexture(TEXTURE_CAUSTICS, m_sprites[causticsFrame].Texture, SAMPLER_NONE);
}
// Set shadow map data
if (shadowLight != nullptr)

View file

@ -10,9 +10,9 @@
#include "Game/effects/debris.h"
#include "Game/effects/drip.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/explosion.h"
#include "Game/effects/footprint.h"
#include "Game/effects/lightning.h"
#include "Game/effects/simple_particle.h"
#include "Game/effects/smoke.h"
#include "Game/effects/spark.h"
@ -27,8 +27,10 @@
#include "Specific/level.h"
#include "Specific/setup.h"
using namespace TEN::Effects::Lightning;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Footprints;
using namespace TEN::Entities::Creatures::TR5;
using namespace TEN::Math;
extern BLOOD_STRUCT Blood[MAX_SPARKS_BLOOD];
@ -42,7 +44,7 @@ extern Particle Particles[MAX_PARTICLES];
extern SPLASH_STRUCT Splashes[MAX_SPLASHES];
extern RIPPLE_STRUCT Ripples[MAX_RIPPLES];
// TODO: EnemyBites must be eradicated and kept directly in object structs or passed to gunflash functions!
// TODO: EnemyBites must be eradicated and kept directly in object structs or passed to gunflash functions.
BiteInfo EnemyBites[12] =
{
@ -55,15 +57,14 @@ BiteInfo EnemyBites[12] =
{ 0, -110, 480, 13 },
{ -20, -80, 190, -10 },
{ 10, -60, 200, 13 },
{ 10, -60, 200, 11 }, // Baddy 2
{ 20, -60, 400, 7 }, // SAS
{ 0, -64, 250, 7 } // Troops
{ 10, -60, 200, 11 }, // Baddy 2
{ 20, -60, 400, 7 }, // SAS
{ 0, -64, 250, 7 } // Troops
};
namespace TEN::Renderer
{
using namespace TEN::Effects::Footprints;
using std::vector;
constexpr auto ELECTRICITY_RANGE_MAX = BLOCK(24);
struct RendererSpriteBucket
{
@ -74,82 +75,109 @@ namespace TEN::Renderer
bool IsSoftParticle;
};
void Renderer11::DrawLightning(RenderView& view)
void Renderer11::DrawHelicalLasers(RenderView& view)
{
for (int i = 0; i < Lightning.size(); i++)
if (HelicalLasers.empty())
return;
for (const auto& laser : HelicalLasers)
{
LIGHTNING_INFO* arc = &Lightning[i];
if (laser.Life <= 0.0f)
continue;
if (arc->life)
auto color = laser.Color;
color.w = laser.Opacity;
ElectricityKnots[0] = laser.Target;
ElectricityKnots[1] = laser.Origin;
for (int j = 0; j < 2; j++)
ElectricityKnots[j] -= laser.Target;
CalculateHelixSpline(laser, ElectricityKnots, ElectricityBuffer);
if (abs(ElectricityKnots[0].x) <= ELECTRICITY_RANGE_MAX &&
abs(ElectricityKnots[0].y) <= ELECTRICITY_RANGE_MAX &&
abs(ElectricityKnots[0].z) <= ELECTRICITY_RANGE_MAX)
{
LightningPos[0].x = arc->pos1.x;
LightningPos[0].y = arc->pos1.y;
LightningPos[0].z = arc->pos1.z;
int bufferIndex = 0;
memcpy(&LightningPos[1], arc, 48);
LightningPos[5].x = arc->pos4.x;
LightningPos[5].y = arc->pos4.y;
LightningPos[5].z = arc->pos4.z;
for (int j = 0; j < 6; j++)
auto& interpPosArray = ElectricityBuffer;
for (int s = 0; s < laser.NumSegments ; s++)
{
LightningPos[j].x -= LaraItem->Pose.Position.x;
LightningPos[j].y -= LaraItem->Pose.Position.y;
LightningPos[j].z -= LaraItem->Pose.Position.z;
auto origin = laser.Target + interpPosArray[bufferIndex];
bufferIndex++;
auto target = laser.Target + interpPosArray[bufferIndex];
auto center = (origin + target) / 2;
auto direction = target - origin;
direction.Normalize();
AddSpriteBillboardConstrained(
&m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LIGHTHING],
center,
color,
PI_DIV_2, 1.0f, Vector2(5 * 8.0f, Vector3::Distance(origin, target)), BLENDMODE_ADDITIVE, direction, true, view);
}
}
}
}
CalcLightningSpline(&LightningPos[0], LightningBuffer, arc);
void Renderer11::DrawElectricity(RenderView& view)
{
if (ElectricityArcs.empty())
return;
if (abs(LightningPos[0].x) <= 24576 && abs(LightningPos[0].y) <= 24576 && abs(LightningPos[0].z) <= 24576)
for (const auto& arc : ElectricityArcs)
{
if (arc.life <= 0)
continue;
ElectricityKnots[0] = arc.pos1;
memcpy(&ElectricityKnots[1], &arc, 96); // TODO: What? Copying 94 / 4 = 24 floats, or 24 / 3 = 8 Vector3 objects, but that doesn't fit. Does it spill into the buffer?
ElectricityKnots[5] = arc.pos4;
for (int j = 0; j < ElectricityKnots.size(); j++)
ElectricityKnots[j] -= LaraItem->Pose.Position.ToVector3();
CalculateElectricitySpline(arc, ElectricityKnots, ElectricityBuffer);
if (abs(ElectricityKnots[0].x) <= ELECTRICITY_RANGE_MAX &&
abs(ElectricityKnots[0].y) <= ELECTRICITY_RANGE_MAX &&
abs(ElectricityKnots[0].z) <= ELECTRICITY_RANGE_MAX)
{
int bufferIndex = 0;
auto& interpPosArray = ElectricityBuffer;
for (int s = 0; s < ((arc.segments * 3) - 1); s++)
{
short* interpolatedPos = &LightningBuffer[0];
auto origin = (LaraItem->Pose.Position + interpPosArray[bufferIndex]).ToVector3();
bufferIndex++;
auto target = (LaraItem->Pose.Position + interpPosArray[bufferIndex]).ToVector3();
for (int s = 0; s < 3 * arc->segments - 1; s++)
auto center = (origin + target) / 2;
auto direction = target - origin;
direction.Normalize();
byte r, g, b;
if (arc.life >= 16)
{
int ix = LaraItem->Pose.Position.x + interpolatedPos[0];
int iy = LaraItem->Pose.Position.y + interpolatedPos[1];
int iz = LaraItem->Pose.Position.z + interpolatedPos[2];
interpolatedPos += 4;
int ix2 = LaraItem->Pose.Position.x + interpolatedPos[0];
int iy2 = LaraItem->Pose.Position.y + interpolatedPos[1];
int iz2 = LaraItem->Pose.Position.z + interpolatedPos[2];
byte r, g, b;
if (arc->life >= 16)
{
r = arc->r;
g = arc->g;
b = arc->b;
}
else
{
r = arc->life * arc->r / 16;
g = arc->life * arc->g / 16;
b = arc->life * arc->b / 16;
}
Vector3 pos1 = Vector3(ix, iy, iz);
Vector3 pos2 = Vector3(ix2, iy2, iz2);
Vector3 d = pos2 - pos1;
d.Normalize();
Vector3 c = (pos1 + pos2) / 2.0f;
AddSpriteBillboardConstrained(&m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LIGHTHING],
c,
Vector4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f),
(PI / 2),
1.0f,
{ arc->width * 8.0f,
Vector3::Distance(pos1, pos2) },
BLENDMODE_ADDITIVE,
d, true, view);
r = arc.r;
g = arc.g;
b = arc.b;
}
else
{
r = (arc.life * arc.r) / 16;
g = (arc.life * arc.g) / 16;
b = (arc.life * arc.b) / 16;
}
AddSpriteBillboardConstrained(
&m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LIGHTHING],
center,
Vector4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f),
PI_DIV_2, 1.0f, Vector2(arc.width * 8, Vector3::Distance(origin, target)), BLENDMODE_ADDITIVE, direction, true, view);
}
}
}
@ -803,7 +831,7 @@ namespace TEN::Renderer
Texture2D Renderer11::CreateDefaultNormalTexture()
{
vector<byte> data = { 128, 128, 255, 1 };
std::vector<byte> data = { 128, 128, 255, 1 };
return Texture2D(m_device.Get(), 1, 1, data.data());
}
@ -1183,8 +1211,8 @@ namespace TEN::Renderer
void Renderer11::DrawDebris(RenderView& view, bool transparent)
{
extern vector<DebrisFragment> DebrisFragments;
vector<RendererVertex> vertices;
extern std::vector<DebrisFragment> DebrisFragments;
std::vector<RendererVertex> vertices;
BLEND_MODES lastBlendMode = BLEND_MODES::BLENDMODE_UNSET;

View file

@ -170,7 +170,7 @@ namespace TEN::Renderer
// FIXME: This is an urgent hack to fix stack overflow crashes.
// See https://github.com/MontyTRC89/TombEngine/issues/947 for details.
static constexpr int MAX_SEARCH_DEPTH = 128;
static constexpr int MAX_SEARCH_DEPTH = 64;
if (m_rooms[to].Visited && count > MAX_SEARCH_DEPTH)
{
TENLog("Maximum room collection depth of " + std::to_string(MAX_SEARCH_DEPTH) +
@ -226,11 +226,12 @@ namespace TEN::Renderer
Camera.pos.x - (door->AbsoluteVertices[0].x),
Camera.pos.y - (door->AbsoluteVertices[0].y),
Camera.pos.z - (door->AbsoluteVertices[0].z));
door->CameraToDoor.Normalize();
}
if (door->Normal.x * door->CameraToDoor.x +
door->Normal.y * door->CameraToDoor.y +
door->Normal.z * door->CameraToDoor.z < 0)
door->Normal.z * door->CameraToDoor.z <= 0)
{
continue;
}

View file

@ -55,9 +55,19 @@ namespace TEN::Renderer
{
// Pop the last bone in the stack
RendererBone *bone = Bones[--nextBone];
if (!bone) return;//otherwise inventory crashes mm
bool calculateMatrix = (mask >> bone->Index) & 1;
if (!bone)
{
return; // Otherwise inventory crashes
}
if (frmptr[0]->angles.size() <= bone->Index || (frac && frmptr[1]->angles.size() <= bone->Index))
{
TENLog("Attempt to animate object ID " + GetObjectName((GAME_OBJECT_ID)item->ObjectNumber) +
" with incorrect animation data. Bad set of animations for a slot?", LogLevel::Error);
return;
}
bool calculateMatrix = (mask >> bone->Index) & 1;
if (calculateMatrix)
{
auto p = Vector3(frmptr[0]->offsetX, frmptr[0]->offsetY, frmptr[0]->offsetZ);
@ -510,4 +520,4 @@ namespace TEN::Renderer
return s;
}
}
}

View file

@ -14,7 +14,7 @@
#include "Game/effects/weather.h"
#include "Sound/sound.h"
#include "Specific/setup.h"
#include "Game/effects/lightning.h"
#include "Game/effects/Electricity.h"
#include "Effects/BlendIDs.h"
#include "Effects/EffectIDs.h"
#include "ReservedScriptNames.h"
@ -26,9 +26,9 @@ Functions to generate effects.
@pragma nostrip
*/
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Explosion;
using namespace TEN::Effects::Lightning;
using namespace TEN::Effects::Spark;
namespace Effects
@ -46,15 +46,8 @@ namespace Effects
//@tparam bool endDrift If true, the end of the arc will be able to gradually drift away from its destination in a random direction (default false)
static void EmitLightningArc(Vec3 src, Vec3 dest, TypeOrNil<ScriptColor> color, TypeOrNil<float> lifetime, TypeOrNil<int> amplitude, TypeOrNil<int> beamWidth, TypeOrNil<int> segments, TypeOrNil<bool> smooth, TypeOrNil<bool> endDrift)
{
Vector3i p1;
p1.x = src.x;
p1.y = src.y;
p1.z = src.z;
Vector3i p2;
p2.x = dest.x;
p2.y = dest.y;
p2.z = dest.z;
auto p1 = Vector3(src.x, src.y, src.z);
auto p2 = Vector3(dest.x, dest.y, dest.z);
int segs = USE_IF_HAVE(int, segments, 10);
@ -95,7 +88,7 @@ namespace Effects
ScriptColor col = USE_IF_HAVE(ScriptColor, color, ScriptColor( 255, 255, 255 ));
TEN::Effects::Lightning::TriggerLightning(&p1, &p2, byteAmplitude, col.GetR(), col.GetG(), col.GetB(), byteLife, flags, width, segs);
SpawnElectricity(p1, p2, byteAmplitude, col.GetR(), col.GetG(), col.GetB(), byteLife, flags, width, segs);
}
/*** Emit a particle.

View file

@ -6,7 +6,7 @@
#include "Game/savegame.h"
#include "Sound/sound.h"
#include "ReservedScriptNames.h"
#include "Game/effects/lightning.h"
#include "Game/effects/Electricity.h"
#include "ScriptUtil.h"
#include "Objects/Moveable/MoveableObject.h"
#include "Vec3/Vec3.h"
@ -14,7 +14,7 @@
#include "Color/Color.h"
#include "LevelFunc.h"
using namespace TEN::Effects::Lightning;
using namespace TEN::Effects::Electricity;
/***
Saving data, triggering functions, and callbacks for level-specific scripts.

View file

@ -28,8 +28,8 @@ Functions that don't fit in the other modules.
@pragma nostrip
*/
using namespace TEN::Input;
using namespace TEN::Effects::Environment;
using namespace TEN::Input;
namespace Misc
{
@ -190,7 +190,9 @@ namespace Misc
return false;
}
else
{
return true;
}
}
static bool KeyIsHeld(int actionIndex)
@ -253,11 +255,11 @@ namespace Misc
//@tparam Vec3 posA first position
//@tparam Vec3 posB second position
//@treturn int the direct distance from one position to the other
static int CalculateDistance(Vec3 const& pos1, Vec3 const& pos2)
static int CalculateDistance(const Vec3& pos1, const Vec3& pos2)
{
auto p1 = Vector3{ (float)pos1.x, (float)pos1.y, (float)pos1.z };
auto p2 = Vector3{ (float)pos2.x, (float)pos2.y, (float)pos2.z };
return static_cast<int>(round(Vector3::Distance(p1, p2)));
auto p1 = Vector3(pos1.x, pos1.y, pos1.z);
auto p2 = Vector3(pos2.x, pos2.y, pos2.z);
return (int)round(Vector3::Distance(p1, p2));
}
///Calculate the horizontal distance between two positions.
@ -265,11 +267,11 @@ namespace Misc
//@tparam Vec3 posA first position
//@tparam Vec3 posB second position
//@treturn int the direct distance on the XZ plane from one position to the other
static int CalculateHorizontalDistance(Vec3 const& pos1, Vec3 const& pos2)
static int CalculateHorizontalDistance(const Vec3& pos1, const Vec3& pos2)
{
auto p1 = Vector2{ (float)pos1.x, (float)pos1.z };
auto p2 = Vector2{ (float)pos2.x, (float)pos2.z };
return static_cast<int>(round(Vector2::Distance(p1, p2)));
auto p1 = Vector2(pos1.x, pos1.z);
auto p2 = Vector2(pos2.x, pos2.z);
return (int)round(Vector2::Distance(p1, p2));
}
///Translate a pair of percentages to screen-space pixel coordinates.
@ -321,67 +323,67 @@ namespace Misc
ObjCamera(LaraItem, 0, LaraItem, 0, false);
}
void Register(sol::state * state, sol::table & parent) {
sol::table table_misc{ state->lua_state(), sol::create };
parent.set(ScriptReserved_Misc, table_misc);
void Register(sol::state * state, sol::table & parent)
{
sol::table tableMisc{ state->lua_state(), sol::create };
parent.set(ScriptReserved_Misc, tableMisc);
///Vibrate gamepad, if possible.
//@function Vibrate
//@tparam float strength
//@tparam float time (in seconds, default: 0.3)
table_misc.set_function(ScriptReserved_Vibrate, &Vibrate);
tableMisc.set_function(ScriptReserved_Vibrate, &Vibrate);
table_misc.set_function(ScriptReserved_FadeIn, &FadeIn);
table_misc.set_function(ScriptReserved_FadeOut, &FadeOut);
table_misc.set_function(ScriptReserved_FadeOutComplete, &FadeOutComplete);
tableMisc.set_function(ScriptReserved_FadeIn, &FadeIn);
tableMisc.set_function(ScriptReserved_FadeOut, &FadeOut);
tableMisc.set_function(ScriptReserved_FadeOutComplete, &FadeOutComplete);
table_misc.set_function(ScriptReserved_SetCineBars, &SetCineBars);
tableMisc.set_function(ScriptReserved_SetCineBars, &SetCineBars);
table_misc.set_function(ScriptReserved_SetFOV, &SetFOV);
table_misc.set_function(ScriptReserved_GetFOV, &GetFOV);
table_misc.set_function(ScriptReserved_GetCameraType, &GetCameraType);
table_misc.set_function(ScriptReserved_SetAmbientTrack, &SetAmbientTrack);
tableMisc.set_function(ScriptReserved_SetFOV, &SetFOV);
tableMisc.set_function(ScriptReserved_GetFOV, &GetFOV);
tableMisc.set_function(ScriptReserved_GetCameraType, &GetCameraType);
tableMisc.set_function(ScriptReserved_SetAmbientTrack, &SetAmbientTrack);
table_misc.set_function(ScriptReserved_PlayAudioTrack, &PlayAudioTrack);
table_misc.set_function(ScriptReserved_StopAudioTrack, &StopAudioTrack);
table_misc.set_function(ScriptReserved_StopAudioTracks, &StopAudioTracks);
tableMisc.set_function(ScriptReserved_PlayAudioTrack, &PlayAudioTrack);
tableMisc.set_function(ScriptReserved_StopAudioTrack, &StopAudioTrack);
tableMisc.set_function(ScriptReserved_StopAudioTracks, &StopAudioTracks);
table_misc.set_function(ScriptReserved_PlaySound, &PlaySoundEffect);
tableMisc.set_function(ScriptReserved_PlaySound, &PlaySoundEffect);
/// Check if particular action key is held
//@function KeyIsHeld
//@tparam Misc.ActionID action action mapping index to check
table_misc.set_function(ScriptReserved_KeyIsHeld, &KeyIsHeld);
tableMisc.set_function(ScriptReserved_KeyIsHeld, &KeyIsHeld);
/// Check if particular action key was hit (once)
//@function KeyIsHit
//@tparam Misc.ActionID action action mapping index to check
table_misc.set_function(ScriptReserved_KeyIsHit, &KeyIsHit);
tableMisc.set_function(ScriptReserved_KeyIsHit, &KeyIsHit);
/// Emulate pushing of a certain action key
//@function KeyPush
//@tparam Misc.ActionID action action mapping index to push
table_misc.set_function(ScriptReserved_KeyPush, &KeyPush);
tableMisc.set_function(ScriptReserved_KeyPush, &KeyPush);
/// Clears particular input from action key
//@function KeyClear
//@tparam Misc.ActionID action action mapping index to clear
table_misc.set_function(ScriptReserved_KeyClear, &KeyClear);
tableMisc.set_function(ScriptReserved_KeyClear, &KeyClear);
table_misc.set_function(ScriptReserved_CalculateDistance, &CalculateDistance);
table_misc.set_function(ScriptReserved_CalculateHorizontalDistance, &CalculateHorizontalDistance);
table_misc.set_function(ScriptReserved_HasLineOfSight, &HasLineOfSight);
tableMisc.set_function(ScriptReserved_CalculateDistance, &CalculateDistance);
tableMisc.set_function(ScriptReserved_CalculateHorizontalDistance, &CalculateHorizontalDistance);
tableMisc.set_function(ScriptReserved_HasLineOfSight, &HasLineOfSight);
table_misc.set_function(ScriptReserved_PercentToScreen, &PercentToScreen);
table_misc.set_function(ScriptReserved_ScreenToPercent, &ScreenToPercent);
tableMisc.set_function(ScriptReserved_PercentToScreen, &PercentToScreen);
tableMisc.set_function(ScriptReserved_ScreenToPercent, &ScreenToPercent);
table_misc.set_function(ScriptReserved_FlipMap, &FlipMap);
table_misc.set_function(ScriptReserved_PlayFlyBy, &PlayFlyBy);
table_misc.set_function(ScriptReserved_ResetObjCamera, &ResetObjCamera);
tableMisc.set_function(ScriptReserved_FlipMap, &FlipMap);
tableMisc.set_function(ScriptReserved_PlayFlyBy, &PlayFlyBy);
tableMisc.set_function(ScriptReserved_ResetObjCamera, &ResetObjCamera);
LuaHandler handler{ state };
handler.MakeReadOnlyTable(table_misc, ScriptReserved_ActionID, kActionIDs);
handler.MakeReadOnlyTable(table_misc, ScriptReserved_CameraType, kCameraType);
handler.MakeReadOnlyTable(tableMisc, ScriptReserved_ActionID, kActionIDs);
handler.MakeReadOnlyTable(tableMisc, ScriptReserved_CameraType, kCameraType);
}
}

View file

@ -1,9 +1,11 @@
#pragma once
namespace sol {
namespace sol
{
class state;
};
namespace Misc {
void Register(sol::state* lua, sol::table &);
namespace Misc
{
void Register(sol::state* lua, sol::table&);
};

View file

@ -1,5 +1,5 @@
#pragma once
#include "Math/Containers/EulerAngles.h"
#include "Math/Objects/EulerAngles.h"
namespace sol
{

View file

@ -1226,25 +1226,36 @@ void LoadBoxes()
{
// Read boxes
int numBoxes = ReadInt32();
TENLog("Num boxes: " + std::to_string(numBoxes), LogLevel::Info);
g_Level.Boxes.resize(numBoxes);
ReadBytes(g_Level.Boxes.data(), numBoxes * sizeof(BOX_INFO));
TENLog("Num boxes: " + std::to_string(numBoxes), LogLevel::Info);
// Read overlaps
int numOverlaps = ReadInt32();
TENLog("Num overlaps: " + std::to_string(numOverlaps), LogLevel::Info);
g_Level.Overlaps.resize(numOverlaps);
ReadBytes(g_Level.Overlaps.data(), numOverlaps * sizeof(OVERLAP));
TENLog("Num overlaps: " + std::to_string(numOverlaps), LogLevel::Info);
// Read zones
int numZoneGroups = ReadInt32();
TENLog("Num zone groups: " + std::to_string(numZoneGroups), LogLevel::Info);
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < (int)ZoneType::MaxZone; j++)
for (int j = 0; j < numZoneGroups; j++)
{
g_Level.Zones[j][i].resize(numBoxes);
ReadBytes(g_Level.Zones[j][i].data(), numBoxes * sizeof(int));
if (j >= (int)ZoneType::MaxZone)
{
int excessiveZoneGroups = numZoneGroups - j + 1;
TENLog("Level file contains extra pathfinding data, number of excessive zone groups is " +
std::to_string(excessiveZoneGroups) + ". These zone groups will be ignored.", LogLevel::Warning);
LevelDataPtr += numBoxes * sizeof(int);
}
else
{
g_Level.Zones[j][i].resize(numBoxes);
ReadBytes(g_Level.Zones[j][i].data(), numBoxes * sizeof(int));
}
}
}

View file

@ -163,9 +163,11 @@ CALL gen.bat</Command>
<ClInclude Include="Game\control\volume.h" />
<ClInclude Include="Game\debug\debug.h" />
<ClInclude Include="Game\effects\item_fx.h" />
<ClInclude Include="Game\effects\lightning.h" />
<ClInclude Include="Game\effects\Electricity.h" />
<ClInclude Include="Game\GuiObjects.h" />
<ClInclude Include="Math\Objects\AxisAngle.h" />
<ClInclude Include="Objects\TR1\Trap\DamoclesSword.h" />
<ClInclude Include="Objects\TR5\Entity\HeavyGuard.h" />
<ClInclude Include="Objects\TR3\Entity\Lizard.h" />
<ClInclude Include="Objects\TR3\Entity\PunaBoss.h" />
<ClInclude Include="Objects\Effects\Boss.h" />
@ -188,19 +190,19 @@ CALL gen.bat</Command>
<ClInclude Include="Game\Lara\lara_jump.h" />
<ClInclude Include="Game\Lara\lara_overhang.h" />
<ClInclude Include="Game\Lara\lara_test_structs.h" />
<ClInclude Include="Math\Containers\GameBoundingBox.h" />
<ClInclude Include="Math\Objects\GameBoundingBox.h" />
<ClInclude Include="Math\Solvers.h" />
<ClInclude Include="Objects\game_object_ids.h" />
<ClInclude Include="Math\Containers\GameVector.h" />
<ClInclude Include="Math\Containers\EulerAngles.h" />
<ClInclude Include="Math\Objects\GameVector.h" />
<ClInclude Include="Math\Objects\EulerAngles.h" />
<ClInclude Include="Math\Constants.h" />
<ClInclude Include="Math\Geometry.h" />
<ClInclude Include="Math\Legacy.h" />
<ClInclude Include="Math\Math.h" />
<ClInclude Include="Math\Containers\Pose.h" />
<ClInclude Include="Math\Objects\Pose.h" />
<ClInclude Include="Math\Random.h" />
<ClInclude Include="Math\Containers\Vector2i.h" />
<ClInclude Include="Math\Containers\Vector3i.h" />
<ClInclude Include="Math\Objects\Vector2i.h" />
<ClInclude Include="Math\Objects\Vector3i.h" />
<ClInclude Include="Objects\Generic\Object\burning_torch.h" />
<ClInclude Include="Objects\Generic\Object\polerope.h" />
<ClInclude Include="Objects\Generic\Object\rope.h" />
@ -639,7 +641,9 @@ CALL gen.bat</Command>
<ClCompile Include="Game\collision\floordata.cpp" />
<ClCompile Include="Game\effects\footprint.cpp" />
<ClCompile Include="Game\GuiObjects.cpp" />
<ClCompile Include="Math\Objects\AxisAngle.cpp" />
<ClCompile Include="Objects\TR1\Trap\DamoclesSword.cpp" />
<ClCompile Include="Objects\TR5\Entity\HeavyGuard.cpp" />
<ClCompile Include="Objects\TR3\Entity\Lizard.cpp" />
<ClCompile Include="Objects\TR3\Entity\PunaBoss.cpp" />
<ClCompile Include="Objects\Effects\Boss.cpp" />
@ -661,7 +665,7 @@ CALL gen.bat</Command>
<ClCompile Include="Game\Lara\lara_overhang.cpp" />
<ClCompile Include="Game\Lara\lara_slide.cpp" />
<ClCompile Include="Game\Lara\lara_tests.cpp" />
<ClCompile Include="Game\effects\lightning.cpp" />
<ClCompile Include="Game\effects\Electricity.cpp" />
<ClCompile Include="Game\misc.cpp" />
<ClCompile Include="Game\Gui.cpp" />
<ClCompile Include="Game\effects\simple_particle.cpp" />
@ -671,16 +675,16 @@ CALL gen.bat</Command>
<ClCompile Include="Game\pickup\pickup_misc_items.cpp" />
<ClCompile Include="Game\pickup\pickup_weapon.cpp" />
<ClCompile Include="Game\room.cpp" />
<ClCompile Include="Math\Containers\GameBoundingBox.cpp" />
<ClCompile Include="Math\Containers\GameVector.cpp" />
<ClCompile Include="Math\Containers\EulerAngles.cpp" />
<ClCompile Include="Math\Objects\GameBoundingBox.cpp" />
<ClCompile Include="Math\Objects\GameVector.cpp" />
<ClCompile Include="Math\Objects\EulerAngles.cpp" />
<ClCompile Include="Math\Geometry.cpp" />
<ClCompile Include="Math\Containers\Pose.cpp" />
<ClCompile Include="Math\Objects\Pose.cpp" />
<ClCompile Include="Math\Legacy.cpp" />
<ClCompile Include="Math\Math.cpp" />
<ClCompile Include="Math\Random.cpp" />
<ClCompile Include="Math\Containers\Vector2i.cpp" />
<ClCompile Include="Math\Containers\Vector3i.cpp" />
<ClCompile Include="Math\Objects\Vector2i.cpp" />
<ClCompile Include="Math\Objects\Vector3i.cpp" />
<ClCompile Include="Math\Solvers.cpp" />
<ClCompile Include="Objects\Generic\Object\burning_torch.cpp" />
<ClCompile Include="Objects\Generic\Object\polerope.cpp" />