mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Tr1 electric ball (#1413)
Implementation of the Electric Ball from Tomb Raider I, inside Level 5: St Francis Folly
This commit is contained in:
parent
a97548467e
commit
2a89abe66d
12 changed files with 582 additions and 20 deletions
|
@ -2,20 +2,22 @@
|
|||
#include "Game/effects/Electricity.h"
|
||||
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/effects/spark.h"
|
||||
#include "Game/people.h"
|
||||
#include "Game/Setup.h"
|
||||
#include "Math/Math.h"
|
||||
|
||||
using namespace TEN::Math;
|
||||
using namespace TEN::Effects::Spark;
|
||||
|
||||
namespace TEN::Effects::Electricity
|
||||
{
|
||||
constexpr auto HELICAL_LASER_LIFE_MAX = 18.0f;
|
||||
|
||||
std::vector<Electricity> ElectricityArcs = {};
|
||||
std::vector<HelicalLaser> HelicalLasers = {};
|
||||
std::vector<HelicalLaser> HelicalLasers = {};
|
||||
|
||||
std::array<Vector3, ELECTRICITY_KNOTS_SIZE> ElectricityKnots = {};
|
||||
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.
|
||||
|
@ -30,8 +32,8 @@ namespace TEN::Effects::Electricity
|
|||
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));
|
||||
(((point1 * 2) - (point2 * 5) + (point3 * 4) - point4) * SQUARE(alpha)) +
|
||||
(((point1 * -1) + (point2 * 3) - (point3 * 3) + point4) * CUBE(alpha));
|
||||
return spline;
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,7 @@ namespace TEN::Effects::Electricity
|
|||
fmod(Random::GenerateInt(), amplitude),
|
||||
fmod(Random::GenerateInt(), amplitude),
|
||||
fmod(Random::GenerateInt(), amplitude)) -
|
||||
Vector3(amplitude/ 2);
|
||||
Vector3(amplitude / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -130,17 +132,53 @@ namespace TEN::Effects::Electricity
|
|||
spark.maxYvel = 0;
|
||||
spark.gravity = 0;
|
||||
spark.sSize =
|
||||
spark.dSize =
|
||||
spark.size = scale + Random::GenerateInt(0, 4);
|
||||
spark.dSize =
|
||||
spark.size = scale + Random::GenerateInt(0, 4);
|
||||
spark.flags = SP_DEF | SP_SCALE;
|
||||
}
|
||||
|
||||
void SpawnElectricEffect(const ItemInfo& item, int jointNumber, const Vector3i& offset, const float spawnRadius, float beamOriginRadius, float beamTargetRadius, int frequency, const Vector3& pos)
|
||||
{
|
||||
//TODO: Make electric effect correctly spawn random on any mesh bounds surface. On water surface too.
|
||||
|
||||
int randomIndex = Random::GenerateInt(0, 100);
|
||||
Vector3 pos1 = Vector3::Zero;
|
||||
|
||||
if (randomIndex < frequency)
|
||||
{
|
||||
if (pos != Vector3::Zero)
|
||||
{
|
||||
|
||||
pos1 = pos + offset.ToVector3();
|
||||
}
|
||||
else
|
||||
{
|
||||
pos1 = GetJointPosition(item, jointNumber, offset).ToVector3();
|
||||
}
|
||||
|
||||
auto sphere = BoundingSphere(pos1, spawnRadius);
|
||||
auto pos2 = Random::GeneratePointOnSphere(sphere);
|
||||
|
||||
SpawnElectricityGlow(pos2, 28, 32, 32, 64);
|
||||
|
||||
SpawnCyborgSpark(pos2);
|
||||
SpawnDynamicLight(pos2.x, pos2.y, pos2.z, Random::GenerateInt(4, 8), 31, 63, 127);
|
||||
|
||||
sphere = BoundingSphere(pos1, beamOriginRadius);
|
||||
auto sphere1 = BoundingSphere(pos1, beamTargetRadius);
|
||||
pos1 = Random::GeneratePointOnSphere(sphere);
|
||||
pos2 = Random::GeneratePointOnSphere(sphere1);
|
||||
|
||||
SpawnElectricity(pos1, pos2, Random::GenerateInt(8, 16), 32, 64, 128, 24, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::ThinIn, 6, 8);
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
|
@ -164,7 +202,7 @@ namespace TEN::Effects::Electricity
|
|||
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()
|
||||
{
|
||||
|
@ -273,7 +311,7 @@ namespace TEN::Effects::Electricity
|
|||
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),
|
||||
|
@ -330,4 +368,4 @@ namespace TEN::Effects::Electricity
|
|||
|
||||
buffer[bufferIndex] = knots[1];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -108,9 +108,9 @@ namespace TEN::Effects::Electricity
|
|||
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 SpawnElectricEffect(const ItemInfo& item, int jointNumber, const Vector3i& offset, const float spawnRadius, float beamOriginRadius, float beamTargetRadius, int frequency, const Vector3& pos);
|
||||
void SpawnElectricityGlow(const Vector3& pos, float scale, byte r, byte g, byte b);
|
||||
void SpawnHelicalLaser(const Vector3& origin, const Vector3& target);
|
||||
|
||||
void UpdateElectricityArcs();
|
||||
void UpdateHelicalLasers();
|
||||
|
||||
|
|
|
@ -272,6 +272,7 @@ enum class ShockwaveStyle
|
|||
Normal = 0,
|
||||
Sophia = 1,
|
||||
Knockback = 2,
|
||||
Invisible = 3,
|
||||
};
|
||||
|
||||
#define ENERGY_ARC_STRAIGHT_LINE 0
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace TEN::Effects::Boss
|
|||
{
|
||||
ShockwaveExplosion = (1 << 0),
|
||||
Shield = (1 << 1),
|
||||
Lizard = (1 << 2)
|
||||
Lizard = (1 << 2)
|
||||
};
|
||||
|
||||
void ShieldControl(int itemNumber);
|
||||
|
|
471
TombEngine/Objects/TR1/Trap/ElectricBall.cpp
Normal file
471
TombEngine/Objects/TR1/Trap/ElectricBall.cpp
Normal file
|
@ -0,0 +1,471 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR1/Trap/ElectricBall.h"
|
||||
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/effects/tomb4fx.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Game/Setup.h"
|
||||
#include "Math/Math.h"
|
||||
#include "Game/control/los.h"
|
||||
#include "Game/effects/item_fx.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Objects/Effects/Boss.h"
|
||||
#include "Game/effects/spark.h"
|
||||
|
||||
using namespace TEN::Math;
|
||||
using namespace TEN::Effects::Electricity;
|
||||
using namespace TEN::Effects::Items;
|
||||
using namespace TEN::Effects::Spark;
|
||||
using namespace TEN::Effects::Boss;
|
||||
|
||||
namespace TEN::Entities::Traps
|
||||
{
|
||||
enum TargetType
|
||||
{
|
||||
None,
|
||||
Target
|
||||
};
|
||||
|
||||
struct ElectricBallInfo
|
||||
{
|
||||
Vector3i Position = Vector3i::Zero;
|
||||
Color Colorw;
|
||||
Electricity* EnergyArcs = {};
|
||||
Electricity* MainEnergyArc = {};
|
||||
unsigned int Count = 0;
|
||||
};
|
||||
|
||||
ElectricBallInfo EldectricBallData;
|
||||
|
||||
constexpr auto ELECTRIC_BALL_LIGHTNING_DAMAGE = 40;
|
||||
constexpr auto ELECTRIC_BALL_DISTANCE_RANDOM_TARGET = 7;
|
||||
constexpr auto ELECTRIC_BALL_DISTANCE_LARA_TARGET = 3;
|
||||
const auto ElectricBallBite = CreatureBiteInfo(Vector3::Zero, 0);
|
||||
|
||||
void TriggerElectricBallShockwaveAttackSparks(int x, int y, int z, byte r, byte g, byte b, byte size)
|
||||
{
|
||||
auto* spark = GetFreeParticle();
|
||||
|
||||
spark->dG = g;
|
||||
spark->sG = g;
|
||||
spark->colFadeSpeed = 2;
|
||||
spark->dR = r;
|
||||
spark->sR = r;
|
||||
spark->blendMode = BlendMode::Additive;
|
||||
spark->life = 4;
|
||||
spark->sLife = 4;
|
||||
spark->x = x;
|
||||
spark->on = 1;
|
||||
spark->dB = b;
|
||||
spark->sB = b;
|
||||
spark->fadeToBlack = 4;
|
||||
spark->y = y;
|
||||
spark->z = z;
|
||||
spark->zVel = 0;
|
||||
spark->yVel = 0;
|
||||
spark->xVel = 0;
|
||||
spark->flags = SP_SCALE | SP_DEF;
|
||||
spark->scalar = 3;
|
||||
spark->maxYvel = 0;
|
||||
spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LENS_FLARE_LIGHT;
|
||||
spark->gravity = 0;
|
||||
spark->dSize = spark->sSize = spark->size = size + (GetRandomControl() & 3);
|
||||
}
|
||||
|
||||
const void SpawnElectricSmoke(const Vector3& pos, const EulerAngles& orient, float life)
|
||||
{
|
||||
auto& smoke = *GetFreeParticle();
|
||||
|
||||
float scale = (life * 18) * phd_cos(orient.x);
|
||||
|
||||
smoke.on = true;
|
||||
smoke.blendMode = BlendMode::Additive;
|
||||
smoke.spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_LENS_FLARE_LIGHT;// SPR_LENSFLARE_LIGHT;
|
||||
|
||||
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(28, 196) * life) / 16;
|
||||
smoke.sR =
|
||||
smoke.sG = smoke.sB - (smoke.sB / 4);
|
||||
|
||||
smoke.dB = (Random::GenerateInt(62, 80) * life) / 16;
|
||||
smoke.dR = smoke.dB / 3;
|
||||
smoke.dG =
|
||||
smoke.dB /2 ;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void InitializeElectricBall(short itemNumber)
|
||||
{
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
|
||||
EldectricBallData.Colorw = item.Model.Color;
|
||||
|
||||
//Distance before switch off.
|
||||
item.ItemFlags[0] = ELECTRIC_BALL_DISTANCE_RANDOM_TARGET;
|
||||
|
||||
//Distance to attack Lara.
|
||||
item.ItemFlags[1] = ELECTRIC_BALL_DISTANCE_LARA_TARGET;
|
||||
|
||||
item.ItemFlags[2] = TargetType::None;
|
||||
}
|
||||
|
||||
static std::vector<int> GetTargetEntityList(const ItemInfo& item)
|
||||
{
|
||||
auto entityList = std::vector<int>{};
|
||||
|
||||
for (auto& currentEntity : g_Level.Items)
|
||||
{
|
||||
if (currentEntity.ObjectNumber == ID_ELECTRIC_BALL_IMPACT_POINT &&
|
||||
currentEntity.RoomNumber == item.RoomNumber &&
|
||||
currentEntity.TriggerFlags == item.TriggerFlags)
|
||||
{
|
||||
entityList.push_back(currentEntity.Index);
|
||||
}
|
||||
}
|
||||
|
||||
return entityList;
|
||||
}
|
||||
|
||||
static Vector3 GetTargetPosition(ItemInfo& item)
|
||||
{
|
||||
if (item.ItemFlags[2] != TargetType::None)
|
||||
{
|
||||
const auto& targetEntity = g_Level.Items[item.ItemFlags[2]];
|
||||
return targetEntity.Pose.Position.ToVector3();
|
||||
}
|
||||
|
||||
// Failsafe.
|
||||
const auto& creature = *GetCreatureInfo(&item);
|
||||
return creature.Target.ToVector3();
|
||||
}
|
||||
|
||||
static int GetTargetItemNumber(const ItemInfo& item)
|
||||
{
|
||||
if (item.ObjectNumber == ID_ELECTRIC_BALL && !Objects[ID_ELECTRIC_BALL_IMPACT_POINT].loaded)
|
||||
return NO_VALUE;
|
||||
|
||||
auto targetList = GetTargetEntityList(item);
|
||||
if (targetList.empty())
|
||||
return NO_VALUE;
|
||||
|
||||
if (targetList.size() == 1)
|
||||
return targetList[0];
|
||||
else
|
||||
return targetList[Random::GenerateInt(0, (int)targetList.size() - 1 )];
|
||||
}
|
||||
|
||||
void SpawnElectricBallLightning(ItemInfo& item, const Vector3& pos, const CreatureBiteInfo& bite)
|
||||
{
|
||||
auto offset = Vector3::Zero;
|
||||
auto origin = GameVector(GetJointPosition(&item, bite), item.RoomNumber);
|
||||
auto targetRandom = Vector3i::Zero;
|
||||
auto target = GameVector(pos, item.RoomNumber);
|
||||
constexpr auto SPAWN_RADIUS = BLOCK(0.30f);
|
||||
auto orient = Geometry::GetOrientToPoint(origin.ToVector3(), target.ToVector3());
|
||||
auto pos1 = GetJointPosition(item, 0, Vector3i(0, 200, 0)).ToVector3();
|
||||
auto targetNew = GameVector::Zero;
|
||||
auto hitPos = Vector3i::Zero;
|
||||
|
||||
static constexpr auto raygunSmokeLife = 16.0f;
|
||||
auto distance = origin - target;
|
||||
if (item.ItemFlags[3] == 0)
|
||||
{
|
||||
auto random = Random::GenerateFloat(0.20f, 0.60f);
|
||||
auto halftarget = origin.ToVector3() + Vector3(random, random, random) * (target.ToVector3() - origin.ToVector3());
|
||||
StopSoundEffect(SFX_TR5_GOD_HEAD_CHARGE);
|
||||
SoundEffect(SFX_TR5_GOD_HEAD_BLAST, &item.Pose);
|
||||
|
||||
SpawnElectricity(origin.ToVector3(), halftarget, 5, 32, 64, 128, 60, (int)ElectricityFlags::Spline , 8, 12);
|
||||
auto arc = ElectricityArcs.back();;
|
||||
|
||||
SpawnElectricityGlow(arc.pos1, 68, 32, 32, 64);
|
||||
|
||||
targetRandom = Vector3i(Random::GenerateInt(515, 650), 0, Random::GenerateInt(615, 810));
|
||||
targetNew = GameVector((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), item.RoomNumber);
|
||||
|
||||
SpawnElectricity(arc.pos4, target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 60, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 4, 12);
|
||||
TriggerRicochetSpark(targetNew, 12, 12, Color(32, 64, 128, 1.0f));
|
||||
SpawnElectricSmoke((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), EulerAngles::Identity, raygunSmokeLife);
|
||||
TriggerShockwave((Pose*)&targetNew, 1, 15, 3, 32, 64, 128, 80, EulerAngles::Identity, 8, false, true, true, (int)ShockwaveStyle::Invisible);
|
||||
SpawnElectricity((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 160, (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 12, 12);
|
||||
SpawnElectricity((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 160, (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 12, 12);
|
||||
|
||||
if (Random::GenerateInt(0, 100) > 20)
|
||||
{
|
||||
halftarget = arc.pos4 + Vector3(random, random, random) * (target.ToVector3() - arc.pos4);
|
||||
|
||||
SpawnElectricity(arc.pos4, halftarget, 10, 32, 64, 128, 60, (int)ElectricityFlags::Spline, 8, 12);//3
|
||||
|
||||
arc = ElectricityArcs.back();
|
||||
}
|
||||
//Main Arc
|
||||
targetRandom = Vector3i(Random::GenerateInt(5, 20), 0, Random::GenerateInt(5, 20));
|
||||
targetNew = GameVector((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), item.RoomNumber);
|
||||
|
||||
SpawnElectricity(arc.pos4, target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 60, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 8, 12);
|
||||
TriggerRicochetSpark(targetNew, 12, 12, Color(32, 64, 128, 1.0f));
|
||||
SpawnElectricSmoke((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), EulerAngles::Identity, raygunSmokeLife);
|
||||
TriggerShockwave((Pose*)&targetNew, 1, 15, 3, 32, 64, 128, 80, EulerAngles::Identity, 8, false, true, true, (int)ShockwaveStyle::Invisible);
|
||||
SpawnElectricity((target.ToVector3() - Vector3(0, 220, 0)) + targetRandom.ToVector3(), target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 160, (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 12, 12);
|
||||
SpawnElectricity((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 160, (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 12, 12);
|
||||
|
||||
if (Random::GenerateInt(0, 100) > 50)
|
||||
{
|
||||
|
||||
halftarget = arc.pos4 + Vector3(random, random, random) * (target.ToVector3() - arc.pos4);
|
||||
|
||||
SpawnElectricity(arc.pos4, halftarget, 10, 32, 64, 128, 60, (int)ElectricityFlags::Spline, 8, 12);
|
||||
|
||||
arc = ElectricityArcs.back();
|
||||
}
|
||||
|
||||
targetRandom = Vector3(Random::GenerateInt(225, 650), random, Random::GenerateInt(125, 150));
|
||||
targetNew = GameVector((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), item.RoomNumber);
|
||||
|
||||
SpawnElectricity(arc.pos4, target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 60, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 8, 12);
|
||||
TriggerRicochetSpark(targetNew, 12, 12, Color(32, 64, 128, 1.0f));
|
||||
SpawnElectricSmoke((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), EulerAngles::Identity, raygunSmokeLife);
|
||||
TriggerShockwave((Pose*)&targetNew, 1, 15, 3, 32, 64, 128, 80, EulerAngles::Identity, 8, false, true, true, (int)ShockwaveStyle::Invisible);
|
||||
SpawnElectricity((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 160, (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 12, 12);
|
||||
SpawnElectricity((target.ToVector3() - Vector3(0, 120, 0)) + targetRandom.ToVector3(), target.ToVector3() + targetRandom.ToVector3(), 15, 32, 64, 128, 160, (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 12, 12);
|
||||
|
||||
item.ItemFlags[3] = Random::GenerateInt(120,230);
|
||||
item.ItemFlags[4] = 120;
|
||||
item.ItemFlags[6] = 20;
|
||||
item.ItemFlags[2] = TargetType::None;
|
||||
auto sparkOrigin = GetJointPosition(item, 0, Vector3i(0, 300, 0)).ToVector3();
|
||||
|
||||
TriggerElectricBallShockwaveAttackSparks(origin.x, origin.y, origin.z, 128, 128, 200, 128);
|
||||
}
|
||||
|
||||
if (item.ItemFlags[4] > 90)
|
||||
{
|
||||
if (item.ItemFlags[4] > 0)
|
||||
{
|
||||
short blue =( item.ItemFlags[4] << 2) + Random::GenerateInt(2,18);
|
||||
short green = blue >> 2;
|
||||
short red = 0;
|
||||
|
||||
SpawnDynamicLight(pos1.x, pos1.y, pos1.z, (item.ItemFlags[4] +8) / 5, red, green, blue);
|
||||
item.ItemFlags[3]--;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.ItemFlags[4] > 0)
|
||||
item.ItemFlags[4]--;
|
||||
|
||||
if (item.ItemFlags[4] > 110)
|
||||
{
|
||||
auto sphere = BoundingSphere(pos1, SPAWN_RADIUS);
|
||||
auto pos2 = Random::GeneratePointOnSphere(sphere);
|
||||
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
|
||||
pos2 = Random::GeneratePointInSphere(sphere);
|
||||
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
}
|
||||
|
||||
int randomIndex = Random::GenerateInt(0, 100);
|
||||
|
||||
SpawnElectricEffect(item, 0, Vector3i(0, 0, 0), BLOCK(0.35f), BLOCK(0.45f), BLOCK(0.25f), 6, Vector3::Zero);
|
||||
|
||||
if (randomIndex < 6)
|
||||
{
|
||||
if (item.ItemFlags[4] > 60)
|
||||
{
|
||||
pos1 = GetJointPosition(item, 0, Vector3i(0, 100, 0)).ToVector3();
|
||||
auto sphere = BoundingSphere(pos1, SPAWN_RADIUS);
|
||||
auto pos2 = Random::GeneratePointOnSphere(sphere);
|
||||
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.ItemFlags[4] > 75 && randomIndex < 37)
|
||||
|
||||
{
|
||||
pos1 = GetJointPosition(item, 0, Vector3i(0, 160, 0)).ToVector3();
|
||||
auto sphere = BoundingSphere(pos1, BLOCK(0.25f));
|
||||
auto pos2 = Random::GeneratePointOnSphere(sphere);
|
||||
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
SpawnElectricSmoke(pos2, EulerAngles::Identity, raygunSmokeLife);
|
||||
}
|
||||
|
||||
if (item.ItemFlags[4] > 45)
|
||||
{
|
||||
SoundEffect(SFX_TR4_LARA_ELECTRIC_CRACKLES, &item.Pose);
|
||||
SoundEffect(SFX_TR4_LARA_ELECTRIC_LOOP, &item.Pose);
|
||||
SoundEffect(SFX_TR5_DOOR_BEAM, &item.Pose);
|
||||
}
|
||||
|
||||
if (item.ItemFlags[3] < 30)
|
||||
{
|
||||
int intensity = 0.01f;
|
||||
|
||||
if (item.Model.Color.x < 4.0f)
|
||||
item.Model.Color.x += 0.05f;
|
||||
else
|
||||
item.Model.Color.x = 4.0f;
|
||||
|
||||
if (item.Model.Color.y < 4.0f)
|
||||
item.Model.Color.y += 0.05f;
|
||||
else
|
||||
item.Model.Color.y = 4.0f;
|
||||
|
||||
if (item.Model.Color.z < 4.0f)
|
||||
item.Model.Color.z += 0.09f;
|
||||
else
|
||||
item.Model.Color.z = 4.0f;
|
||||
|
||||
SoundEffect(SFX_TR5_GOD_HEAD_CHARGE, &item.Pose);
|
||||
}
|
||||
else if (item.ItemFlags[4] > 80)
|
||||
{
|
||||
if (item.Model.Color.x <= EldectricBallData.Colorw.x)
|
||||
item.Model.Color.x = EldectricBallData.Colorw.x;
|
||||
else
|
||||
item.Model.Color.x -= 0.1f;
|
||||
|
||||
if (item.Model.Color.y <= EldectricBallData.Colorw.y)
|
||||
item.Model.Color.y = EldectricBallData.Colorw.y;
|
||||
else
|
||||
item.Model.Color.y -= 0.1f;
|
||||
|
||||
if (item.Model.Color.z <= EldectricBallData.Colorw.z)
|
||||
item.Model.Color.z = EldectricBallData.Colorw.z;
|
||||
else
|
||||
item.Model.Color.z -= 0.1f;
|
||||
}
|
||||
|
||||
if (item.ItemFlags[3] > 28)
|
||||
{
|
||||
SpawnElectricityGlow(origin.ToVector3(), 108, 22, 22, 54);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnElectricityGlow(origin.ToVector3(), 128, 102, 102, 204);
|
||||
SpawnElectricity(origin.ToVector3() - Vector3(0, 1300, 0), origin.ToVector3() - (targetRandom.ToVector3() * 8), 15, 32, 64, 128, 30, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 8, 12);
|
||||
SpawnElectricity(origin.ToVector3() - Vector3(0, 1300, 0), origin.ToVector3() - (targetRandom.ToVector3() * 8), 15, 32, 64, 128, 30, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 8, 12);
|
||||
SpawnElectricity(origin.ToVector3() + Vector3(0, 300, 0), origin.ToVector3() + (targetRandom.ToVector3() * 8), 15, 32, 64, 128, 30, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 8, 12);
|
||||
SpawnElectricity(origin.ToVector3() + Vector3(0, 300, 0), origin.ToVector3() + (targetRandom.ToVector3() * 8), 15, 32, 64, 128, 30, (int)ElectricityFlags::Spline | (int)ElectricityFlags::ThinOut | (int)ElectricityFlags::MoveEnd, 8, 12);
|
||||
SpawnElectricEffect(item, 0, Vector3i(0, 0, 0), BLOCK(0.45f), BLOCK(0.45f), BLOCK(0.25f), 80, Vector3::Zero);
|
||||
}
|
||||
|
||||
if (item.ItemFlags[6] > 0)
|
||||
{
|
||||
if (ObjectOnLOS2(&origin, &target, &hitPos, nullptr, ID_LARA) == LaraItem->Index)
|
||||
{
|
||||
if (LaraItem->HitPoints <= ELECTRIC_BALL_LIGHTNING_DAMAGE)
|
||||
{
|
||||
ItemElectricBurn(LaraItem);
|
||||
DoDamage(LaraItem, ELECTRIC_BALL_LIGHTNING_DAMAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoDamage(LaraItem, ELECTRIC_BALL_LIGHTNING_DAMAGE);
|
||||
}
|
||||
}
|
||||
item.ItemFlags[6]--;
|
||||
SoundEffect(SFX_TR5_GOD_HEAD_LASER_LOOPS, &item.Pose);
|
||||
SoundEffect(SFX_TR4_ELECTRIC_ARCING_LOOP, &item.Pose);
|
||||
}
|
||||
}
|
||||
|
||||
void ControlElectricBall(short itemNumber)
|
||||
{
|
||||
if (!CreatureActive(itemNumber))
|
||||
return;
|
||||
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
|
||||
auto& object = Objects[item.ObjectNumber];
|
||||
const auto& laraItem = *LaraItem;
|
||||
static auto targetPos = Vector3i::Zero;
|
||||
|
||||
AI_INFO ai;
|
||||
CreatureAIInfo(&item, &ai);
|
||||
|
||||
if (ai.distance > SQUARE(BLOCK(item.ItemFlags[0])))
|
||||
{
|
||||
item.ItemFlags[3] = 70;
|
||||
item.ItemFlags[4] = 70;
|
||||
item.Model.Color = EldectricBallData.Colorw;
|
||||
item.ItemFlags[2] = TargetType::None;
|
||||
return;
|
||||
}
|
||||
|
||||
SoundEffect(SFX_TR5_DOOR_BEAM, &item.Pose);
|
||||
|
||||
auto targetPlayer = GameVector(GetJointPosition(LaraItem, LM_TORSO), LaraItem->RoomNumber);
|
||||
auto origin = GameVector(GetJointPosition(item, 0), item.RoomNumber);
|
||||
bool los = LOS(&origin, &targetPlayer);
|
||||
FloorInfo* floor = GetFloor(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, &item.RoomNumber);
|
||||
|
||||
if (ai.distance < SQUARE(BLOCK(item.ItemFlags[1])) && los)
|
||||
{
|
||||
targetPos = Vector3(LaraItem->Pose.Position.x, GetFloorHeight(floor, item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z), LaraItem->Pose.Position.z);
|
||||
|
||||
item.ItemFlags[2] = TargetType::Target;
|
||||
item.ItemFlags[5] = LaraItem->Index;
|
||||
}
|
||||
|
||||
else if ((item.ObjectNumber == ID_ELECTRIC_BALL &&
|
||||
Objects[ID_ELECTRIC_BALL_IMPACT_POINT].loaded &&
|
||||
item.ItemFlags[2] == TargetType::None))
|
||||
{
|
||||
item.ItemFlags[2] = (short)GetTargetItemNumber(item);
|
||||
targetPos = GetTargetPosition(item);
|
||||
|
||||
item.ItemFlags[5] = g_Level.Items[item.ItemFlags[2]].Index;
|
||||
}
|
||||
|
||||
if (item.ItemFlags[2] != TargetType::None)
|
||||
{
|
||||
SpawnElectricBallLightning(item, targetPos.ToVector3(), ElectricBallBite);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Model.Color = EldectricBallData.Colorw;
|
||||
item.ItemFlags[2] = TargetType::None;
|
||||
}
|
||||
|
||||
if (item.ItemFlags[3] > 0)
|
||||
item.ItemFlags[3]--;
|
||||
}
|
||||
|
||||
void InitializeElectricBallImpactPoint(short itemNumber)
|
||||
{
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
}
|
||||
}
|
||||
|
||||
|
14
TombEngine/Objects/TR1/Trap/ElectricBall.h
Normal file
14
TombEngine/Objects/TR1/Trap/ElectricBall.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
struct ItemInfo;
|
||||
class Vector3i;
|
||||
struct CreatureBiteInfo;
|
||||
|
||||
namespace TEN::Entities::Traps
|
||||
{
|
||||
void InitializeElectricBall(short itemNumber);
|
||||
void SpawnElectricBallLightning(ItemInfo& item, const Vector3& pos, const CreatureBiteInfo& bite);
|
||||
void ControlElectricBall(short itemNumber);
|
||||
void InitializeElectricBallImpactPoint(short itemNumber);
|
||||
void TriggerElectricBallShockwaveAttackSparks(int x, int y, int z, byte r, byte g, byte b, byte size);
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
#include "Objects/TR1/Trap/DamoclesSword.h"
|
||||
#include "Objects/TR1/Trap/SlammingDoors.h"
|
||||
#include "Objects/TR1/Trap/SwingingBlade.h"
|
||||
#include "Objects/TR1/Trap/ElectricBall.h"
|
||||
|
||||
using namespace TEN::Entities::Creatures::TR1;
|
||||
using namespace TEN::Entities::Traps;
|
||||
|
@ -269,6 +270,27 @@ static void StartTrap(ObjectInfo* obj)
|
|||
obj->shadowType = ShadowMode::All;
|
||||
obj->SetHitEffect(true);
|
||||
}
|
||||
|
||||
obj = &Objects[ID_ELECTRIC_BALL];
|
||||
if (obj->loaded)
|
||||
{
|
||||
obj->Initialize = InitializeElectricBall;
|
||||
obj->control = ControlElectricBall;
|
||||
obj->collision = GenericSphereBoxCollision;
|
||||
obj->shadowType = ShadowMode::All;
|
||||
obj->HitPoints = NOT_TARGETABLE;
|
||||
obj->radius = 512;
|
||||
obj->intelligent = true;
|
||||
obj->SetHitEffect(true);
|
||||
}
|
||||
|
||||
obj = &Objects[ID_ELECTRIC_BALL_IMPACT_POINT];
|
||||
if (obj->loaded)
|
||||
{
|
||||
obj->Initialize = InitializeElectricBallImpactPoint;
|
||||
obj->drawRoutine = nullptr;
|
||||
obj->usingDrawAnimatingItem = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void StartProjectiles(ObjectInfo* obj)
|
||||
|
|
|
@ -83,7 +83,7 @@ void ControlTeleporter(short itemNumber)
|
|||
BYTE1(v21) |= 0x80u;
|
||||
TriggerLightningGlow(src.x, src.y, src.z, (item->itemFlags[0] >> 3) | ((v20 | (v21 << 8)) << 7));
|
||||
v22 = GetRandomControl();
|
||||
TriggerDynamicLight(src.x, src.y, src.z, (v22 & 3) + (item->itemFlags[0] >> 5) + 8, v12, v16, v13);
|
||||
SpawnDynamicLight(src.x, src.y, src.z, (v22 & 3) + (item->itemFlags[0] >> 5) + 8, v12, v16, v13);
|
||||
}
|
||||
LOBYTE(v3) = GetRandomControl();
|
||||
if (v3 & 1)
|
||||
|
|
|
@ -369,6 +369,8 @@ enum GAME_OBJECT_ID : short
|
|||
ID_ELECTRIC_CLEANER,
|
||||
ID_SLAMMING_DOORS,
|
||||
ID_SWINGING_BLADE,
|
||||
ID_ELECTRIC_BALL,
|
||||
ID_ELECTRIC_BALL_IMPACT_POINT,
|
||||
|
||||
ID_PUZZLE_ITEM1 = 500,
|
||||
ID_PUZZLE_ITEM2,
|
||||
|
|
|
@ -308,9 +308,16 @@ namespace TEN::Renderer
|
|||
|
||||
AddSpriteBillboardConstrained(
|
||||
&_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, dir, true, view);
|
||||
center,
|
||||
Vector4(255.0f, 255.0f, 255.0f, 0.5f),
|
||||
PI_DIV_2, 1.0f, Vector2(4, Vector3::Distance(origin, target)), BlendMode::Additive, dir, true, view);
|
||||
|
||||
|
||||
AddSpriteBillboardConstrained(
|
||||
&_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, dir, true, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -864,6 +871,9 @@ namespace TEN::Renderer
|
|||
p2 = Vector3::Transform(p2, rotMatrix);
|
||||
p3 = Vector3::Transform(p3, rotMatrix);
|
||||
|
||||
if (shockwave->style == (int)ShockwaveStyle::Invisible)
|
||||
return;
|
||||
|
||||
if (shockwave->style == (int)ShockwaveStyle::Normal)
|
||||
{
|
||||
angle -= PI / 8.0f;
|
||||
|
|
|
@ -1553,6 +1553,8 @@ static const std::unordered_map<std::string, GAME_OBJECT_ID> kObjIDs{
|
|||
{ "ELECTRIC_CLEANER", ID_ELECTRIC_CLEANER },
|
||||
{ "SLAMMING_DOORS", ID_SLAMMING_DOORS },
|
||||
{ "SWINGING_BLADE", ID_SWINGING_BLADE },
|
||||
{ "ELECTRIC_BALL", ID_ELECTRIC_BALL },
|
||||
{ "ELECTRIC_BALL_IMPACT_POINT", ID_ELECTRIC_BALL_IMPACT_POINT },
|
||||
{ "PUZZLE_ITEM1", ID_PUZZLE_ITEM1 },
|
||||
{ "PUZZLE_ITEM2", ID_PUZZLE_ITEM2 },
|
||||
{ "PUZZLE_ITEM3", ID_PUZZLE_ITEM3 },
|
||||
|
|
|
@ -604,6 +604,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings.
|
|||
<ClInclude Include="Objects\TR1\Entity\tr1_wolf.h" />
|
||||
<ClInclude Include="Objects\TR1\Entity\WingedMutant.h" />
|
||||
<ClInclude Include="Objects\TR1\Trap\DamoclesSword.h" />
|
||||
<ClInclude Include="Objects\TR1\Trap\ElectricBall.h" />
|
||||
<ClInclude Include="Objects\TR1\Trap\SlammingDoors.h" />
|
||||
<ClInclude Include="Objects\TR1\tr1_objects.h" />
|
||||
<ClInclude Include="Objects\TR1\Trap\SwingingBlade.h" />
|
||||
|
@ -1130,6 +1131,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings.
|
|||
<ClCompile Include="Objects\TR1\Entity\WingedMutant.cpp" />
|
||||
<ClCompile Include="Objects\TR1\tr1_objects.cpp" />
|
||||
<ClCompile Include="Objects\TR1\Trap\DamoclesSword.cpp" />
|
||||
<ClCompile Include="Objects\TR1\Trap\ElectricBall.cpp" />
|
||||
<ClCompile Include="Objects\TR1\Trap\SlammingDoors.cpp" />
|
||||
<ClCompile Include="Objects\TR1\Trap\SwingingBlade.cpp" />
|
||||
<ClCompile Include="Objects\TR2\Entity\Bartoli.cpp" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue