mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
005e5b25f8
71 changed files with 2069 additions and 1203 deletions
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
329
TombEngine/Game/effects/Electricity.cpp
Normal file
329
TombEngine/Game/effects/Electricity.cpp
Normal 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];
|
||||
}
|
||||
}
|
77
TombEngine/Game/effects/Electricity.h
Normal file
77
TombEngine/Game/effects/Electricity.h
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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); };
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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); };
|
||||
|
|
160
TombEngine/Math/Objects/AxisAngle.cpp
Normal file
160
TombEngine/Math/Objects/AxisAngle.cpp
Normal 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);
|
||||
}
|
||||
//}
|
48
TombEngine/Math/Objects/AxisAngle.h
Normal file
48
TombEngine/Math/Objects/AxisAngle.h
Normal 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;
|
||||
};
|
||||
//}
|
|
@ -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)
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
//{
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
//}
|
|
@ -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
|
||||
//{
|
|
@ -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)
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
540
TombEngine/Objects/TR5/Entity/HeavyGuard.cpp
Normal file
540
TombEngine/Objects/TR5/Entity/HeavyGuard.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
TombEngine/Objects/TR5/Entity/HeavyGuard.h
Normal file
12
TombEngine/Objects/TR5/Entity/HeavyGuard.h
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#pragma once
|
||||
#include "Math/Containers/EulerAngles.h"
|
||||
#include "Math/Objects/EulerAngles.h"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue