Merge branch 'master' into Enemy-fix

This commit is contained in:
TokyoSU 2023-01-22 10:35:21 +01:00
commit 5d53e50f52
12 changed files with 471 additions and 160 deletions

View file

@ -2,6 +2,7 @@ Version 1.0.6
============= =============
* Fix major pathfinding bug which could have caused lots of issues with enemy behaviour. * Fix major pathfinding bug which could have caused lots of issues with enemy behaviour.
* Fix potential random crashes due to incorrect rendering behaviour.
* Fix savegame crash for disabled enemies with partially set activation mask. * 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 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 invisible Lara after starting a new game from title flyby with hidden Lara.
@ -13,6 +14,8 @@ Version 1.0.6
* Fix TR1 BIG_RAT which crashed the game when it was killed. * Fix TR1 BIG_RAT which crashed the game when it was killed.
* Fix TR4 SKELETON spawn when used with OCB 3 * Fix TR4 SKELETON spawn when used with OCB 3
* Fix TR4 SAS teleporting over the blocks he walks by. * Fix TR4 SAS teleporting over the blocks he walks by.
* Fix enemy projectile effect colours.
* Restore original volumetric explosion effects.
* Add undead flag to TR4 sphinx to prevent it from becoming bugged after receiving a lot of damage. * 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 an option to activate Lua or node events from legacy triggers.
* Antitriggering an enemy will now cause it to vanish and pause. * Antitriggering an enemy will now cause it to vanish and pause.

View file

@ -1399,7 +1399,7 @@ void ExplodeProjectile(ItemInfo& item, const Vector3i& prevPos)
} }
else else
{ {
TriggerShockwave(&item.Pose, 48, 304, 96, 0, 96, 128, 24, 0, 0); TriggerShockwave(&item.Pose, 48, 304, 96, 128, 96, 0, 24, 0, 0);
item.Pose.Position.y += CLICK(1.0f / 2); item.Pose.Position.y += CLICK(1.0f / 2);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 0, item.RoomNumber); TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 0, item.RoomNumber);

View file

@ -160,6 +160,23 @@ Particle* GetFreeParticle()
return spark; return spark;
} }
void SetSpriteSequence(Particle& particle, GAME_OBJECT_ID objectID)
{
if (particle.life <= 0)
{
particle.on = false;
ParticleDynamics[particle.dynamic].On = false;
}
float particleAge = particle.sLife - particle.life;
if (particleAge > particle.life )
return;
int numSprites = -Objects[objectID].nmeshes - 1;
float normalizedAge = particleAge / particle.life;
particle.spriteIndex = Objects[objectID].meshIndex + (int)round(Lerp(0.0f, numSprites, normalizedAge));
}
void UpdateSparks() void UpdateSparks()
{ {
auto bounds = GameBoundingBox(LaraItem); auto bounds = GameBoundingBox(LaraItem);
@ -169,8 +186,7 @@ void UpdateSparks()
LaraItem->Pose.Position.y + bounds.Y1, LaraItem->Pose.Position.y + bounds.Y1,
LaraItem->Pose.Position.y + bounds.Y2, LaraItem->Pose.Position.y + bounds.Y2,
LaraItem->Pose.Position.z + bounds.Z1, LaraItem->Pose.Position.z + bounds.Z1,
LaraItem->Pose.Position.z + bounds.Z2 LaraItem->Pose.Position.z + bounds.Z2);
);
for (int i = 0; i < MAX_PARTICLES; i++) for (int i = 0; i < MAX_PARTICLES; i++)
{ {
@ -188,7 +204,7 @@ void UpdateSparks()
spark->on = false; spark->on = false;
continue; continue;
} }
int life = spark->sLife - spark->life; int life = spark->sLife - spark->life;
if (life < spark->colFadeSpeed) if (life < spark->colFadeSpeed)
{ {
@ -228,11 +244,19 @@ void UpdateSparks()
if (spark->sLife - spark->life == spark->extras >> 3 && if (spark->sLife - spark->life == spark->extras >> 3 &&
spark->extras & 7) spark->extras & 7)
{ {
int unk; int explosionType;
if (spark->flags & SP_UNDERWEXP) if (spark->flags & SP_UNDERWEXP)
unk = 1; {
explosionType = 1;
}
else if (spark->flags & SP_PLASMAEXP)
{
explosionType = 2;
}
else else
unk = (spark->flags & SP_PLASMAEXP) >> 12; {
explosionType = 0;
}
for (int j = 0; j < (spark->extras & 7); j++) for (int j = 0; j < (spark->extras & 7); j++)
{ {
@ -241,13 +265,13 @@ void UpdateSparks()
spark->z, spark->z,
(spark->extras & 7) - 1, (spark->extras & 7) - 1,
spark->dynamic, spark->dynamic,
unk, explosionType,
(spark->extras & 7)); spark->roomNumber);
spark->dynamic = -1; spark->dynamic = -1;
} }
if (unk == 1) if (explosionType == 1)
{ {
TriggerExplosionBubble( TriggerExplosionBubble(
spark->x, spark->x,
@ -285,16 +309,17 @@ void UpdateSparks()
spark->z += Weather.Wind().z; spark->z += Weather.Wind().z;
} }
int dl = (spark->sLife - spark->life << 16) / spark->sLife; int dl = ((spark->sLife - spark->life) * 65536) / spark->sLife;
int ds = dl * (spark->dSize - spark->sSize); spark->size = (spark->sSize + ((dl * (spark->dSize - spark->sSize)) / 65536));
float alpha = (spark->sLife - spark->life) / (float)spark->sLife;
spark->size = Lerp(spark->sSize, spark->dSize, alpha); if (spark->flags & SP_EXPLOSION)
SetSpriteSequence(*spark, ID_EXPLOSION_SPRITES);
if ((spark->flags & SP_FIRE && LaraItem->Effect.Type == EffectType::None) || if ((spark->flags & SP_FIRE && LaraItem->Effect.Type == EffectType::None) ||
(spark->flags & SP_DAMAGE) || (spark->flags & SP_DAMAGE) ||
(spark->flags & SP_POISON)) (spark->flags & SP_POISON))
{ {
ds = spark->size * (spark->scalar / 2.0); int ds = spark->size * (spark->scalar / 2.0);
if (spark->x + ds > DeadlyBounds.X1 && spark->x - ds < DeadlyBounds.X2) if (spark->x + ds > DeadlyBounds.X1 && spark->x - ds < DeadlyBounds.X2)
{ {
@ -324,13 +349,14 @@ void UpdateSparks()
if (spark->on && spark->dynamic != -1) if (spark->on && spark->dynamic != -1)
{ {
auto* dynsp = &ParticleDynamics[spark->dynamic]; auto* dynsp = &ParticleDynamics[spark->dynamic];
if (dynsp->Flags & 3) if (dynsp->Flags & 3)
{ {
int random = GetRandomControl(); int random = GetRandomControl();
byte x = spark->x + 16 * (random & 0xF); int x = spark->x + 16 * (random & 0xF);
byte y = spark->y + (random & 0xF0); int y = spark->y + (random & 0xF0);
byte z = spark->z + ((random >> 4) & 0xF0); int z = spark->z + ((random >> 4) & 0xF0);
byte r, g, b; byte r, g, b;
@ -354,6 +380,7 @@ void UpdateSparks()
b = -8 * dl + 128; b = -8 * dl + 128;
g = -8 * dl - (random & 0x1F) + 255; g = -8 * dl - (random & 0x1F) + 255;
r = 32 * (4 - dl); r = 32 * (4 - dl);
if (32 * (4 - dl) < 0) if (32 * (4 - dl) < 0)
r = 0; r = 0;
} }
@ -368,7 +395,7 @@ void UpdateSparks()
r = 255 - (dl << 6) - (random & 0x1F); r = 255 - (dl << 6) - (random & 0x1F);
} }
if (spark->flags & 0x2000) if (spark->flags & SP_PLASMAEXP)
{ {
int falloff; int falloff;
if (dynsp->Falloff <= 28) if (dynsp->Falloff <= 28)
@ -403,7 +430,8 @@ void TriggerCyborgSpark(int x, int y, int z, short xv, short yv, short zv)
int dx = LaraItem->Pose.Position.x - x; int dx = LaraItem->Pose.Position.x - x;
int dz = LaraItem->Pose.Position.z - z; int dz = LaraItem->Pose.Position.z - z;
if (dx >= -16384 && dx <= 16384 && dz >= -16384 && dz <= 16384) if (dx >= -BLOCK(16) && dx <= BLOCK(16) &&
dz >= -BLOCK(16) && dz <= BLOCK(16))
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -439,7 +467,164 @@ void TriggerCyborgSpark(int x, int y, int z, short xv, short yv, short zv)
void TriggerExplosionSparks(int x, int y, int z, int extraTrig, int dynamic, int uw, int roomNumber) void TriggerExplosionSparks(int x, int y, int z, int extraTrig, int dynamic, int uw, int roomNumber)
{ {
TriggerExplosion(Vector3(x, y, z), 512, true, false, true, roomNumber); static constexpr auto rotationMax = 30;
static constexpr auto lifeMax = 44;
static const auto extrasTable = std::array<unsigned char, 4>{ 0, 4, 7, 10 };
int dx = LaraItem->Pose.Position.x - x;
int dz = LaraItem->Pose.Position.z - z;
int scalar = 1;
if (dx < -BLOCK(16) || dx > BLOCK(16) ||
dz < -BLOCK(16) || dz > BLOCK(16))
{
return;
}
if (roomNumber < 0)
{
roomNumber = -roomNumber;
scalar = 1;
}
auto& spark = *GetFreeParticle();
spark.on = true;
spark.sR = 255;
if (uw == 1)
{
spark.sG = (GetRandomControl() & 0x3F) + 128;
spark.sB = 32;
spark.dR = 192;
spark.dG = (GetRandomControl() & 0x1F) + 64;
spark.dB = 0;
spark.colFadeSpeed = 7;
spark.fadeToBlack = 8;
spark.life = (GetRandomControl() & 7) + 16;
spark.sLife = spark.life;
spark.roomNumber = roomNumber;
}
else
{
spark.sG = (GetRandomControl() & 0xF) + 32;
spark.sB = 0;
spark.dR = (GetRandomControl() & 0x3F) + 192;
spark.dG = (GetRandomControl() & 0x3F) + 128;
spark.dB = 32;
spark.colFadeSpeed = 8;
spark.fadeToBlack = 16;
spark.life = (GetRandomControl() & 7) + lifeMax;
spark.sLife = spark.life;
}
spark.extras = unsigned char(extraTrig | ((extrasTable[extraTrig] + (GetRandomControl() & 7) + 28) << 3));
spark.dynamic = (char)dynamic;
if (dynamic == -2)
{
int i = 0;
for (i = 0; i < 8; i++)
{
auto dynsp = &ParticleDynamics[i];
if (!dynsp->On)
{
dynsp->On = true;
dynsp->Falloff = 4;
if (uw == 1)
dynsp->Flags = 2;
else
dynsp->Flags = 1;
spark.dynamic = (char)i;
break;
}
}
if (i == 8)
spark.dynamic = -1;
}
spark.xVel = (GetRandomControl() & 0xFFF) - 2048;
spark.yVel = (GetRandomControl() & 0xFFF) - 2048;
spark.zVel = (GetRandomControl() & 0xFFF) - 2048;
if (dynamic != -2 || uw == 1)
{
spark.x = (GetRandomControl() & 0x1F) + x - 16;
spark.y = (GetRandomControl() & 0x1F) + y - 16;
spark.z = (GetRandomControl() & 0x1F) + z - 16;
}
else
{
spark.x = (GetRandomControl() & 0x1FF) + x - 256;
spark.y = (GetRandomControl() & 0x1FF) + y - 256;
spark.z = (GetRandomControl() & 0x1FF) + z - 256;
}
if (uw == 1)
{
spark.friction = 17;
}
else
spark.friction = 51;
if (GetRandomControl() & 1)
{
if (uw == 1)
spark.flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF | SP_UNDERWEXP;
else
spark.flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF | SP_EXPLOSION;
spark.rotAng = GetRandomControl() & 0xF;
spark.rotAdd = (GetRandomControl() & 0xF) + rotationMax;
}
else if (uw == 1)
{
spark.flags = SP_SCALE | SP_DEF | SP_EXPDEF | SP_UNDERWEXP;
}
else
{
spark.flags = SP_SCALE | SP_DEF | SP_EXPDEF | SP_EXPLOSION;
}
spark.scalar = 3;
spark.gravity = 0;
spark.size = (GetRandomControl() & 0xF) + 40;
spark.sSize = spark.size * scalar;
spark.dSize = spark.size * (scalar + 1);
spark.size *= scalar;
GetRandomControl();
spark.maxYvel = 0;
if (uw == 2)
{
unsigned char r = spark.sR;
unsigned char g = spark.sG;
unsigned char b = spark.sB;
spark.sR = b;
spark.sG = r;
spark.sB = g;
r = spark.dR;
g = spark.dG;
b = spark.dB;
spark.dR = b;
spark.dG = r;
spark.dB = g;
spark.flags |= SP_PLASMAEXP;
}
else if (extraTrig)
{
TriggerExplosionSmoke(x, y, z, uw);
}
else
{
TriggerExplosionSmokeEnd(x, y, z, uw);
}
} }
void TriggerExplosionBubbles(int x, int y, int z, short roomNumber) void TriggerExplosionBubbles(int x, int y, int z, short roomNumber)
@ -447,7 +632,8 @@ void TriggerExplosionBubbles(int x, int y, int z, short roomNumber)
int dx = LaraItem->Pose.Position.x - x; int dx = LaraItem->Pose.Position.x - x;
int dz = LaraItem->Pose.Position.z - z; int dz = LaraItem->Pose.Position.z - z;
if (dx >= -ANGLE(90.0f) && dx <= ANGLE(90.0f) && dz >= -ANGLE(90.0f) && dz <= ANGLE(90.0f)) if (dx >= -BLOCK(16) && dx <= BLOCK(16) &&
dz >= -BLOCK(16) && dz <= BLOCK(16))
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -573,7 +759,8 @@ void TriggerExplosionSmoke(int x, int y, int z, int uw)
int dx = LaraItem->Pose.Position.x - x; int dx = LaraItem->Pose.Position.x - x;
int dz = LaraItem->Pose.Position.z - z; int dz = LaraItem->Pose.Position.z - z;
if (dx >= -16384 && dx <= 16384 && dz >= -16384 && dz <= 16384) if (dx >= -BLOCK(16) && dx <= BLOCK(16) &&
dz >= -BLOCK(16) && dz <= BLOCK(16))
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -617,7 +804,8 @@ void TriggerSuperJetFlame(ItemInfo* item, int yvel, int deadly)
long dx = LaraItem->Pose.Position.x - item->Pose.Position.x; long dx = LaraItem->Pose.Position.x - item->Pose.Position.x;
long dz = LaraItem->Pose.Position.z - item->Pose.Position.z; long dz = LaraItem->Pose.Position.z - item->Pose.Position.z;
if (dx >= -16384 && dx <= 16384 && dz >= -16384 && dz <= 16384) if (dx >= -BLOCK(16) && dx <= BLOCK(16) &&
dz >= -BLOCK(16) && dz <= BLOCK(16))
{ {
int size = (GetRandomControl() & 0x1FF) - yvel; int size = (GetRandomControl() & 0x1FF) - yvel;
auto* sptr = GetFreeParticle(); auto* sptr = GetFreeParticle();
@ -666,13 +854,21 @@ void TriggerSuperJetFlame(ItemInfo* item, int yvel, int deadly)
sptr->zVel = (GetRandomControl() & 0xFF) - 128; sptr->zVel = (GetRandomControl() & 0xFF) - 128;
if (item->Pose.Orientation.y == 0) if (item->Pose.Orientation.y == 0)
{
sptr->zVel = -(size - (size >> 2)); sptr->zVel = -(size - (size >> 2));
}
else if (item->Pose.Orientation.y == ANGLE(90.0f)) else if (item->Pose.Orientation.y == ANGLE(90.0f))
{
sptr->xVel = -(size - (size >> 2)); sptr->xVel = -(size - (size >> 2));
else if (item->Pose.Orientation.y == -ANGLE(180.0f)) }
else if (item->Pose.Orientation.y == ANGLE(-180.0f))
{
sptr->zVel = size - (size >> 2); sptr->zVel = size - (size >> 2);
}
else else
{
sptr->xVel = size - (size >> 2); sptr->xVel = size - (size >> 2);
}
} }
} }
@ -685,11 +881,12 @@ void SetupSplash(const SPLASH_SETUP* const setup, int room)
for (int i = 0; i < MAX_SPLASHES; i++) for (int i = 0; i < MAX_SPLASHES; i++)
{ {
SPLASH_STRUCT& splash = Splashes[i]; SPLASH_STRUCT& splash = Splashes[i];
if (!splash.isActive) if (!splash.isActive)
{ {
if (numSplashesSetup == 0) if (numSplashesSetup == 0)
{ {
float splashPower = fmin(256, setup->splashPower); float splashPower = fmin(256, setup->splashPower);
splash.isActive = true; splash.isActive = true;
splash.x = setup->x; splash.x = setup->x;
splash.y = setup->y; splash.y = setup->y;
@ -703,8 +900,8 @@ void SetupSplash(const SPLASH_SETUP* const setup, int room)
splash.height = 0; splash.height = 0;
splash.heightVel = -16; splash.heightVel = -16;
splash.outerRad = setup->innerRadius / 3; splash.outerRad = setup->innerRadius / 3;
splash.outerRadVel = splashVelocity*1.5f; splash.outerRadVel = splashVelocity * 1.5f;
splash.spriteSequenceStart = 8; //Splash Texture splash.spriteSequenceStart = 8; // Splash texture.
numSplashesSetup++; numSplashesSetup++;
} }
else else
@ -734,12 +931,13 @@ void SetupSplash(const SPLASH_SETUP* const setup, int room)
float t = vel / (splashVelocity / 2) + 16; float t = vel / (splashVelocity / 2) + 16;
t = fmax(0, fmin(t, 1)); t = fmax(0, fmin(t, 1));
splash.life = Lerp(48.0f, 70.0f, t); splash.life = Lerp(48.0f, 70.0f, t);
splash.spriteSequenceStart = 4; //Splash Texture splash.spriteSequenceStart = 4; // Splash texture.
splash.spriteSequenceEnd = 7; //Splash Texture splash.spriteSequenceEnd = 7; // Splash texture.
splash.animationSpeed = fmin(0.6f,(1 / splash.outerRadVel)*2); splash.animationSpeed = fmin(0.6f,(1 / splash.outerRadVel)*2);
numSplashesSetup++; numSplashesSetup++;
} }
if (numSplashesSetup == NUM_SPLASHES) if (numSplashesSetup == NUM_SPLASHES)
break; break;

View file

@ -3,8 +3,9 @@
#include "Renderer/Renderer11Enums.h" #include "Renderer/Renderer11Enums.h"
enum class LaraWeaponType; enum class LaraWeaponType;
struct ItemInfo; enum GAME_OBJECT_ID : short;
struct CollisionInfo; struct CollisionInfo;
struct ItemInfo;
enum RIPPLE_TYPE enum RIPPLE_TYPE
{ {
@ -198,6 +199,8 @@ extern FX_INFO EffectList[NUM_EFFECTS];
Particle* GetFreeParticle(); Particle* GetFreeParticle();
void SetSpriteSequence(Particle& particle, GAME_OBJECT_ID objectID);
void DetatchSpark(int num, SpriteEnumFlag type); void DetatchSpark(int num, SpriteEnumFlag type);
void UpdateSparks(); void UpdateSparks();
void TriggerRicochetSpark(const GameVector& pos, short angle, int count, int unk); void TriggerRicochetSpark(const GameVector& pos, short angle, int count, int unk);

View file

@ -22,15 +22,26 @@ using namespace TEN::Math;
namespace TEN::Entities::Effects namespace TEN::Entities::Effects
{ {
void TriggerSethMissileFlame(short fxNum, short xVel, short yVel, short zVel) enum class MissileType
{ {
auto* fx = &EffectList[fxNum]; SethNormal = 0,
SethLarge = 1,
Harpy = 2,
Demigod3Single = 3,
Demigod3Radial = 4,
Demigod2 = 5,
Mutant = 6
};
void TriggerSethMissileFlame(short fxNumber, short xVel, short yVel, short zVel)
{
auto* fx = &EffectList[fxNumber];
int dx = LaraItem->Pose.Position.x - fx->pos.Position.x; int dx = LaraItem->Pose.Position.x - fx->pos.Position.x;
int dz = LaraItem->Pose.Position.z - fx->pos.Position.z; int dz = LaraItem->Pose.Position.z - fx->pos.Position.z;
if (dx >= -SECTOR(16) && dx <= SECTOR(16) && if (dx >= -BLOCK(16) && dx <= BLOCK(16) &&
dz >= -SECTOR(16) && dz <= SECTOR(16)) dz >= -BLOCK(16) && dz <= BLOCK(16))
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -59,16 +70,16 @@ namespace TEN::Entities::Effects
spark->rotAdd = -32 - (GetRandomControl() & 0x1F); spark->rotAdd = -32 - (GetRandomControl() & 0x1F);
else else
spark->rotAdd = (GetRandomControl() & 0x1F) + 32; spark->rotAdd = (GetRandomControl() & 0x1F) + 32;
spark->gravity = 0; spark->gravity = 0;
spark->maxYvel = 0; spark->maxYvel = 0;
spark->fxObj = fxNum; spark->fxObj = fxNumber;
if (fx->flag1 == 1) if (fx->flag1 == 1)
spark->scalar = 3; spark->scalar = 3;
else else
spark->scalar = 2; spark->scalar = 2;
spark->sSize = spark->size = (GetRandomControl() & 7) + 64; spark->sSize = spark->size = (GetRandomControl() & 7) + 64;
spark->dSize = spark->size / 32; spark->dSize = spark->size / 32;
} }
@ -81,7 +92,8 @@ namespace TEN::Entities::Effects
int dx = LaraItem->Pose.Position.x - fx->pos.Position.x; int dx = LaraItem->Pose.Position.x - fx->pos.Position.x;
int dz = LaraItem->Pose.Position.z - fx->pos.Position.z; int dz = LaraItem->Pose.Position.z - fx->pos.Position.z;
if (dx >= -16384 && dx <= 16384 && dz >= -16384 && dz <= 16384) if (dx >= -BLOCK(16) && dx <= BLOCK(16) &&
dz >= -BLOCK(16) && dz <= BLOCK(16))
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -143,14 +155,14 @@ namespace TEN::Entities::Effects
int maxRotation = 0; int maxRotation = 0;
int maxVelocity = 0; int maxVelocity = 0;
if (fx->flag1 == 1) if (fx->flag1 == (int)MissileType::SethLarge)
{ {
maxRotation = ANGLE(2.8f); maxRotation = ANGLE(2.8f);
maxVelocity = CLICK(1); maxVelocity = CLICK(1);
} }
else else
{ {
if (fx->flag1 == 6) if (fx->flag1 == (int)MissileType::Mutant)
{ {
if (fx->counter) if (fx->counter)
fx->counter--; fx->counter--;
@ -158,14 +170,16 @@ namespace TEN::Entities::Effects
maxRotation = ANGLE(1.4f); maxRotation = ANGLE(1.4f);
} }
else else
{
maxRotation = ANGLE(4.5f); maxRotation = ANGLE(4.5f);
}
maxVelocity = CLICK(0.75f); maxVelocity = CLICK(0.75f);
} }
if (fx->speed < maxVelocity) if (fx->speed < maxVelocity)
{ {
if (fx->flag1 == 6) if (fx->flag1 == (int)MissileType::Mutant)
fx->speed++; fx->speed++;
else else
fx->speed += 3; fx->speed += 3;
@ -182,24 +196,31 @@ namespace TEN::Entities::Effects
dx >>= 3; dx >>= 3;
if (dy < -maxRotation) if (dy < -maxRotation)
{
dy = -maxRotation; dy = -maxRotation;
}
else if (dy > maxRotation) else if (dy > maxRotation)
{
dy = maxRotation; dy = maxRotation;
}
if (dx < -maxRotation) if (dx < -maxRotation)
{
dx = -maxRotation; dx = -maxRotation;
}
else if (dx > maxRotation) else if (dx > maxRotation)
{
dx = maxRotation; dx = maxRotation;
}
fx->pos.Orientation.x += dx; fx->pos.Orientation.x += dx;
if (fx->flag1 != 4 && (fx->flag1 != 6 || !fx->counter)) if (fx->flag1 != (int)MissileType::Demigod3Radial && (fx->flag1 != (int)MissileType::Mutant || !fx->counter))
fx->pos.Orientation.y += dy; fx->pos.Orientation.y += dy;
} }
fx->pos.Orientation.z += 16 * fx->speed; fx->pos.Orientation.z += 16 * fx->speed;
if (fx->flag1 == 6) if (fx->flag1 == (int)MissileType::Mutant)
fx->pos.Orientation.z += 16 * fx->speed; fx->pos.Orientation.z += 16 * fx->speed;
int oldX = fx->pos.Position.x; int oldX = fx->pos.Position.x;
@ -219,10 +240,10 @@ namespace TEN::Entities::Effects
fx->pos.Position.y = oldY; fx->pos.Position.y = oldY;
fx->pos.Position.z = oldZ; fx->pos.Position.z = oldZ;
if (fx->flag1 != 6) if (fx->flag1 != (int)MissileType::Mutant)
BubblesShatterFunction(fx, 0, -32); BubblesShatterFunction(fx, 0, -32);
if (fx->flag1 == 1) if (fx->flag1 == (int)MissileType::SethLarge)
{ {
TriggerShockwave(&fx->pos, 32, 160, 64, 64, 128, 00, 24, (((~g_Level.Rooms[fx->roomNumber].flags) / 16) & 2) * 65536, 0); TriggerShockwave(&fx->pos, 32, 160, 64, 64, 128, 00, 24, (((~g_Level.Rooms[fx->roomNumber].flags) / 16) & 2) * 65536, 0);
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 2, fx->roomNumber); TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 2, fx->roomNumber);
@ -231,31 +252,39 @@ namespace TEN::Entities::Effects
{ {
if (fx->flag1) if (fx->flag1)
{ {
if (fx->flag1 == 3 || fx->flag1 == 4) if (fx->flag1 == (int)MissileType::Demigod3Single || fx->flag1 == (int)MissileType::Demigod3Radial)
TriggerShockwave(&fx->pos, 32, 160, 64, 128, 64, 0, 16, 0, 0); {
else if (fx->flag1 == 5)
TriggerShockwave(&fx->pos, 32, 160, 64, 0, 96, 128, 16, 0, 0); TriggerShockwave(&fx->pos, 32, 160, 64, 0, 96, 128, 16, 0, 0);
}
else if (fx->flag1 == (int)MissileType::Demigod2)
{
TriggerShockwave(&fx->pos, 32, 160, 64, 128, 64, 0, 16, 0, 0);
}
else else
{ {
if (fx->flag1 != 2) if (fx->flag1 != (int)MissileType::Harpy)
{ {
if (fx->flag1 == 6) if (fx->flag1 == (int)MissileType::Mutant)
{ {
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, fx->roomNumber); TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, fx->roomNumber);
TriggerShockwave(&fx->pos, 48, 240, 64, 0, 96, 128, 24, 0, 15); TriggerShockwave(&fx->pos, 48, 240, 64, 128, 96, 0, 24, 0, 15);
fx->pos.Position.y -= 128; fx->pos.Position.y -= 128;
TriggerShockwave(&fx->pos, 48, 240, 48, 0, 112, 128, 16, 0, 15); TriggerShockwave(&fx->pos, 48, 240, 48, 128, 112, 0, 16, 0, 15);
fx->pos.Position.y += 256; fx->pos.Position.y += 256;
TriggerShockwave(&fx->pos, 48, 240, 48, 0, 112, 128, 16, 0, 15); TriggerShockwave(&fx->pos, 48, 240, 48, 128, 112, 0, 16, 0, 15);
} }
} }
else else
TriggerShockwave(&fx->pos, 32, 160, 64, 0, 128, 128, 16, 0, 0); {
TriggerShockwave(&fx->pos, 32, 160, 64, 128, 128, 0, 16, 0, 0);
}
} }
} }
else else
TriggerShockwave(&fx->pos, 32, 160, 64, 64, 128, 0, 16, 0, 0); {
TriggerShockwave(&fx->pos, 32, 160, 64, 0, 128, 64, 16, 0, 0);
}
} }
KillEffect(fxNum); KillEffect(fxNum);
@ -265,14 +294,14 @@ namespace TEN::Entities::Effects
if (ItemNearLara(fx->pos.Position, 200)) if (ItemNearLara(fx->pos.Position, 200))
{ {
LaraItem->HitStatus = true; LaraItem->HitStatus = true;
if (fx->flag1 != 6) if (fx->flag1 != (int)MissileType::Mutant)
BubblesShatterFunction(fx, 0, -32); BubblesShatterFunction(fx, 0, -32);
KillEffect(fxNum); KillEffect(fxNum);
if (fx->flag1 == 1) if (fx->flag1 == (int)MissileType::SethLarge)
{ {
TriggerShockwave(&fx->pos, 48, 240, 64, 64, 128, 0, 24, 0, 0); TriggerShockwave(&fx->pos, 48, 240, 64, 0, 128, 64, 24, 0, 0);
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 2, fx->roomNumber); TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 2, fx->roomNumber);
ItemCustomBurn(LaraItem, Vector3(0.0f, 0.8f, 0.1f), Vector3(0.0f, 0.9f, 0.8f)); ItemCustomBurn(LaraItem, Vector3(0.0f, 0.8f, 0.1f), Vector3(0.0f, 0.9f, 0.8f));
} }
@ -280,32 +309,34 @@ namespace TEN::Entities::Effects
{ {
switch (fx->flag1) switch (fx->flag1)
{ {
case 3: case (int)MissileType::Demigod3Single:
case 4: case (int)MissileType::Demigod3Radial:
TriggerShockwave(&fx->pos, 32, 160, 64, 128, 64, 0, 16, 0, 10); TriggerShockwave(&fx->pos, 32, 160, 64, 0, 96, 128, 16, 0, 10);
break; break;
case 5: case (int)MissileType::Demigod2:
TriggerShockwave(&fx->pos, 32, 160, 64, 0, 96, 128, 16, 0, 5); TriggerShockwave(&fx->pos, 32, 160, 64, 128, 64, 0, 16, 0, 5);
break; break;
case 2: case (int)MissileType::Harpy:
TriggerShockwave(&fx->pos, 32, 160, 64, 0, 128, 128, 16, 0, 3); TriggerShockwave(&fx->pos, 32, 160, 64, 128, 128, 0, 16, 0, 3);
break; break;
case 6: case (int)MissileType::Mutant:
TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, fx->roomNumber); TriggerExplosionSparks(oldX, oldY, oldZ, 3, -2, 0, fx->roomNumber);
TriggerShockwave(&fx->pos, 48, 240, 64, 0, 96, 128, 24, 0, 0); TriggerShockwave(&fx->pos, 48, 240, 64, 128, 96, 0, 24, 0, 0);
fx->pos.Position.y -= 128; fx->pos.Position.y -= 128;
TriggerShockwave(&fx->pos, 48, 240, 48, 0, 112, 128, 16, 0, 0); TriggerShockwave(&fx->pos, 48, 240, 48, 128, 112, 0, 16, 0, 0);
fx->pos.Position.y += 256; fx->pos.Position.y += 256;
TriggerShockwave(&fx->pos, 48, 240, 48, 0, 112, 128, 16, 0, 0); TriggerShockwave(&fx->pos, 48, 240, 48, 128, 112, 0, 16, 0, 0);
ItemBurn(LaraItem); ItemBurn(LaraItem);
break; break;
} }
} }
else else
TriggerShockwave(&fx->pos, 24, 88, 48, 64, 128, 0, 16, (((~g_Level.Rooms[fx->roomNumber].flags) / 16) & 2) * 65536, 1); {
TriggerShockwave(&fx->pos, 24, 88, 48, 0, 128, 64, 16, (((~g_Level.Rooms[fx->roomNumber].flags) / 16) & 2) * 65536, 1);
}
} }
else else
{ {
@ -321,21 +352,21 @@ namespace TEN::Entities::Effects
switch (fx->flag1) switch (fx->flag1)
{ {
default: default:
case 1: case (int)MissileType::SethLarge:
TriggerSethMissileFlame(fxNum, 32 * dx, 32 * dy, 32 * dz); TriggerSethMissileFlame(fxNum, 32 * dx, 32 * dy, 32 * dz);
break; break;
case 2: case (int)MissileType::Harpy:
TriggerHarpyFlameFlame(fxNum, 16 * dx, 16 * dy, 16 * dz); TriggerHarpyFlameFlame(fxNum, 16 * dx, 16 * dy, 16 * dz);
break; break;
case 3: case (int)MissileType::Demigod3Single:
case 4: case (int)MissileType::Demigod3Radial:
case 5: case (int)MissileType::Demigod2:
TriggerDemigodMissileFlame(fxNum, 16 * dx, 16 * dy, 16 * dz); TriggerDemigodMissileFlame(fxNum, 16 * dx, 16 * dy, 16 * dz);
break; break;
case 6: case (int)MissileType::Mutant:
TriggerCrocgodMissileFlame(fxNum, 16 * dx, 16 * dy, 16 * dz); TriggerCrocgodMissileFlame(fxNum, 16 * dx, 16 * dy, 16 * dz);
break; break;
} }

View file

@ -87,17 +87,17 @@ namespace TEN::Entities::Creatures::TR1
if (item->IsCreature()) if (item->IsCreature())
{ {
auto* creature = GetCreatureInfo(item); auto& creature = *GetCreatureInfo(item);
if (waterDepth != NO_HEIGHT) if (waterDepth != NO_HEIGHT)
{ {
creature->LOT.Step = BLOCK(20); creature.LOT.Step = BLOCK(20);
creature->LOT.Drop = -BLOCK(20); creature.LOT.Drop = -BLOCK(20);
} }
else else
{ {
creature->LOT.Step = CLICK(1); creature.LOT.Step = CLICK(1);
creature->LOT.Drop = -CLICK(1); creature.LOT.Drop = -CLICK(1);
} }
} }
@ -132,14 +132,14 @@ namespace TEN::Entities::Creatures::TR1
} }
else else
{ {
AI_INFO AI; AI_INFO ai;
CreatureAIInfo(item, &AI); CreatureAIInfo(item, &ai);
if (AI.ahead) if (ai.ahead)
head = AI.angle; head = ai.angle;
GetCreatureMood(item, &AI, false); GetCreatureMood(item, &ai, false);
CreatureMood(item, &AI, false); CreatureMood(item, &ai, false);
angle = CreatureTurn(item, creature->MaxTurn); angle = CreatureTurn(item, creature->MaxTurn);
switch (item->Animation.ActiveState) switch (item->Animation.ActiveState)
@ -147,7 +147,7 @@ namespace TEN::Entities::Creatures::TR1
case BIG_RAT_STATE_IDLE: case BIG_RAT_STATE_IDLE:
if (item->Animation.RequiredState) if (item->Animation.RequiredState)
item->Animation.TargetState = item->Animation.RequiredState; item->Animation.TargetState = item->Animation.RequiredState;
else if (AI.bite && AI.distance < BIG_RAT_LAND_BITE_ATTACK_RANGE) else if (ai.bite && ai.distance < BIG_RAT_LAND_BITE_ATTACK_RANGE)
item->Animation.TargetState = BIG_RAT_STATE_LAND_BITE_ATTACK; item->Animation.TargetState = BIG_RAT_STATE_LAND_BITE_ATTACK;
else else
item->Animation.TargetState = BIG_RAT_STATE_RUN_FORWARD; item->Animation.TargetState = BIG_RAT_STATE_RUN_FORWARD;
@ -163,15 +163,15 @@ namespace TEN::Entities::Creatures::TR1
break; break;
} }
if (AI.ahead && item->TouchBits.Test(BigRatBite.meshNum)) if (ai.ahead && item->TouchBits.Test(BigRatBite.meshNum))
{ {
item->Animation.TargetState = BIG_RAT_STATE_IDLE; item->Animation.TargetState = BIG_RAT_STATE_IDLE;
} }
else if (AI.bite && AI.distance < BIG_RAT_POUNCE_ATTACK_RANGE) else if (ai.bite && ai.distance < BIG_RAT_POUNCE_ATTACK_RANGE)
{ {
item->Animation.TargetState = BIG_RAT_STATE_POUNCE_ATTACK; item->Animation.TargetState = BIG_RAT_STATE_POUNCE_ATTACK;
} }
else if (AI.ahead && Random::TestProbability(BIG_RAT_REAR_POSE_CHANCE)) else if (ai.ahead && Random::TestProbability(BIG_RAT_REAR_POSE_CHANCE))
{ {
item->Animation.TargetState = BIG_RAT_STATE_IDLE; item->Animation.TargetState = BIG_RAT_STATE_IDLE;
item->Animation.RequiredState = BIG_RAT_STATE_REAR_POSE; item->Animation.RequiredState = BIG_RAT_STATE_REAR_POSE;
@ -180,7 +180,7 @@ namespace TEN::Entities::Creatures::TR1
break; break;
case BIG_RAT_STATE_LAND_BITE_ATTACK: case BIG_RAT_STATE_LAND_BITE_ATTACK:
if (!item->Animation.RequiredState && AI.ahead && if (!item->Animation.RequiredState && ai.ahead &&
item->TouchBits.Test(BigRatBite.meshNum)) item->TouchBits.Test(BigRatBite.meshNum))
{ {
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE); DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
@ -191,7 +191,7 @@ namespace TEN::Entities::Creatures::TR1
break; break;
case BIG_RAT_STATE_POUNCE_ATTACK: case BIG_RAT_STATE_POUNCE_ATTACK:
if (!item->Animation.RequiredState && AI.ahead && if (!item->Animation.RequiredState && ai.ahead &&
item->TouchBits.Test(BigRatBite.meshNum)) item->TouchBits.Test(BigRatBite.meshNum))
{ {
DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE); DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE);
@ -216,13 +216,13 @@ namespace TEN::Entities::Creatures::TR1
break; break;
} }
if (AI.ahead && item->TouchBits.Test(BigRatBite.meshNum)) if (ai.ahead && item->TouchBits.Test(BigRatBite.meshNum))
item->Animation.TargetState = BIG_RAT_STATE_SWIM_BITE_ATTACK; item->Animation.TargetState = BIG_RAT_STATE_SWIM_BITE_ATTACK;
break; break;
case BIG_RAT_STATE_SWIM_BITE_ATTACK: case BIG_RAT_STATE_SWIM_BITE_ATTACK:
if (!item->Animation.RequiredState && AI.ahead && if (!item->Animation.RequiredState && ai.ahead &&
item->TouchBits.Test(BigRatBite.meshNum)) item->TouchBits.Test(BigRatBite.meshNum))
{ {
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE); DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);

View file

@ -1067,7 +1067,8 @@ namespace TEN::Entities::Vehicles
if (spark->sLife < 9) if (spark->sLife < 9)
spark->sLife = spark->life = 9; spark->sLife = spark->life = 9;
spark->blendMode = BLEND_MODES::BLENDMODE_SCREEN; // TODO: Switch back to screen blend mode once rendering for it is refactored. -- Sezz 2023.01.14
spark->blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
spark->colFadeSpeed = 4; spark->colFadeSpeed = 4;
spark->fadeToBlack = 4; spark->fadeToBlack = 4;
spark->extras = 0; spark->extras = 0;

View file

@ -23,6 +23,12 @@ using namespace TEN::Math::Random;
namespace TEN::Entities::TR4 namespace TEN::Entities::TR4
{ {
constexpr auto DEMIGOD_IDLE_RANGE = SQUARE(BLOCK(2));
constexpr auto DEMIGOD_WALK_RANGE = SQUARE(BLOCK(3));
constexpr auto DEMIGOD1_WALK_RANGE = SQUARE(BLOCK(3));
constexpr auto DEMIGOD2_RADIAL_PROJECTILE_ATTACK_RANGE = SQUARE(BLOCK(5));
constexpr auto DEMIGOD3_RADIAL_PROJECTILE_ATTACK_RANGE = SQUARE(BLOCK(5));
enum DemigodState enum DemigodState
{ {
DEMIGOD_STATE_IDLE = 0, DEMIGOD_STATE_IDLE = 0,
@ -122,8 +128,8 @@ namespace TEN::Entities::TR4
int dx = LaraItem->Pose.Position.x - fx->pos.Position.x; int dx = LaraItem->Pose.Position.x - fx->pos.Position.x;
int dz = LaraItem->Pose.Position.z - fx->pos.Position.z; int dz = LaraItem->Pose.Position.z - fx->pos.Position.z;
if (dx >= -SECTOR(16) && dx <= SECTOR(16) && if (dx >= -BLOCK(16) && dx <= BLOCK(16) &&
dz >= -SECTOR(16) && dz <= SECTOR(16)) dz >= -BLOCK(16) && dz <= BLOCK(16))
{ {
auto* spark = GetFreeParticle(); auto* spark = GetFreeParticle();
@ -200,7 +206,7 @@ namespace TEN::Entities::TR4
fx->flag1 = flags; fx->flag1 = flags;
fx->speed = (GetRandomControl() & 0x1F) + 96; fx->speed = (GetRandomControl() & 0x1F) + 96;
fx->objectNumber = ID_ENERGY_BUBBLES; fx->objectNumber = ID_ENERGY_BUBBLES;
fx->frameNumber = Objects[ID_ENERGY_BUBBLES].meshIndex + (flags >= 4, flags - 1, flags); fx->frameNumber = Objects[ID_ENERGY_BUBBLES].meshIndex + ((flags >= 4) ? flags - 1 : flags);
} }
} }
@ -436,7 +442,7 @@ namespace TEN::Entities::TR4
if (item->ObjectNumber == ID_DEMIGOD1) if (item->ObjectNumber == ID_DEMIGOD1)
{ {
if (AI.distance >= pow(SECTOR(3), 2)) if (AI.distance >= DEMIGOD1_WALK_RANGE)
{ {
item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD; item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD;
break; break;
@ -451,7 +457,7 @@ namespace TEN::Entities::TR4
break; break;
} }
if (AI.distance <= pow(SECTOR(3), 2)) if (AI.distance <= DEMIGOD1_WALK_RANGE)
{ {
item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD; item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD;
break; break;
@ -471,15 +477,15 @@ namespace TEN::Entities::TR4
break; break;
} }
if (AI.distance <= pow(SECTOR(2), 2) ||
AI.distance >= pow(SECTOR(5), 2))
{
item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD;
break;
}
if (item->ObjectNumber == ID_DEMIGOD3) if (item->ObjectNumber == ID_DEMIGOD3)
{ {
if (AI.distance <= DEMIGOD_IDLE_RANGE ||
AI.distance >= DEMIGOD3_RADIAL_PROJECTILE_ATTACK_RANGE)
{
item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD;
break;
}
if (TestProbability(0.25f)) if (TestProbability(0.25f))
{ {
item->Animation.TargetState = DEMIGOD3_STATE_RADIAL_AIM; item->Animation.TargetState = DEMIGOD3_STATE_RADIAL_AIM;
@ -488,13 +494,17 @@ namespace TEN::Entities::TR4
} }
} }
item->Animation.TargetState = DEMIGOD2_STATE_RADIAL_PROJECTILE_ATTACK; if (AI.distance > DEMIGOD_WALK_RANGE && item->ObjectNumber == ID_DEMIGOD2)
item->Animation.TargetState = DEMIGOD2_STATE_RADIAL_AIM;
else
item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD;
break; break;
case DEMIGOD_STATE_WALK_FORWARD: case DEMIGOD_STATE_WALK_FORWARD:
creature->MaxTurn = ANGLE(7.0f); creature->MaxTurn = ANGLE(7.0f);
if (AI.distance < pow(SECTOR(2), 2)) if (AI.distance < DEMIGOD_IDLE_RANGE)
{ {
item->Animation.TargetState = DEMIGOD_STATE_IDLE; item->Animation.TargetState = DEMIGOD_STATE_IDLE;
break; break;
@ -502,7 +512,7 @@ namespace TEN::Entities::TR4
if (item->ObjectNumber == ID_DEMIGOD1) if (item->ObjectNumber == ID_DEMIGOD1)
{ {
if (AI.distance < pow(SECTOR(3), 2)) if (AI.distance < DEMIGOD1_WALK_RANGE)
{ {
item->Animation.TargetState = DEMIGOD_STATE_IDLE; item->Animation.TargetState = DEMIGOD_STATE_IDLE;
break; break;
@ -517,7 +527,7 @@ namespace TEN::Entities::TR4
} }
} }
if (AI.distance > pow(SECTOR(3), 2)) if (AI.distance > DEMIGOD_WALK_RANGE)
{ {
if (item->ObjectNumber == ID_DEMIGOD2) if (item->ObjectNumber == ID_DEMIGOD2)
item->Animation.TargetState = DEMIGOD2_STATE_RADIAL_PROJECTILE_ATTACK; item->Animation.TargetState = DEMIGOD2_STATE_RADIAL_PROJECTILE_ATTACK;
@ -530,7 +540,7 @@ namespace TEN::Entities::TR4
case DEMIGOD_STATE_RUN_FORWARD: case DEMIGOD_STATE_RUN_FORWARD:
creature->MaxTurn = ANGLE(7.0f); creature->MaxTurn = ANGLE(7.0f);
if (AI.distance < pow(SECTOR(2), 2)) if (AI.distance < DEMIGOD_IDLE_RANGE)
{ {
item->Animation.TargetState = DEMIGOD_STATE_IDLE; item->Animation.TargetState = DEMIGOD_STATE_IDLE;
break; break;
@ -538,7 +548,7 @@ namespace TEN::Entities::TR4
if (item->ObjectNumber == ID_DEMIGOD1) if (item->ObjectNumber == ID_DEMIGOD1)
{ {
if (AI.distance < pow(SECTOR(3), 2)) if (AI.distance < DEMIGOD1_WALK_RANGE)
{ {
item->Animation.TargetState = DEMIGOD_STATE_IDLE; item->Animation.TargetState = DEMIGOD_STATE_IDLE;
break; break;
@ -546,13 +556,13 @@ namespace TEN::Entities::TR4
} }
else else
{ {
if (Targetable(item, &AI) || item->ObjectNumber == ID_DEMIGOD3 && AI.distance > pow(SECTOR(2), 2)) if (Targetable(item, &AI) || item->ObjectNumber == ID_DEMIGOD3 && AI.distance > DEMIGOD_IDLE_RANGE)
{ {
item->Animation.TargetState = DEMIGOD_STATE_IDLE; item->Animation.TargetState = DEMIGOD_STATE_IDLE;
break; break;
} }
if (AI.distance < pow(SECTOR(3), 2)) if (AI.distance < DEMIGOD_WALK_RANGE)
item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD; item->Animation.TargetState = DEMIGOD_STATE_WALK_FORWARD;
} }
@ -594,7 +604,7 @@ namespace TEN::Entities::TR4
case DEMIGOD2_STATE_RADIAL_PROJECTILE_ATTACK: case DEMIGOD2_STATE_RADIAL_PROJECTILE_ATTACK:
creature->MaxTurn = ANGLE(7.0f); creature->MaxTurn = ANGLE(7.0f);
if (Targetable(item, &AI)) if (Targetable(item, &AI))
item->Animation.TargetState = DEMIGOD2_STATE_RADIAL_UNAIM; item->Animation.TargetState = DEMIGOD2_STATE_RADIAL_UNAIM;
@ -602,17 +612,25 @@ namespace TEN::Entities::TR4
case DEMIGOD3_STATE_RADIAL_AIM: case DEMIGOD3_STATE_RADIAL_AIM:
creature->MaxTurn = ANGLE(7.0f); creature->MaxTurn = ANGLE(7.0f);
if (!Targetable(item, &AI) && AI.distance < pow(SECTOR(5), 2))
if (!Targetable(item, &AI) && AI.distance < DEMIGOD3_RADIAL_PROJECTILE_ATTACK_RANGE)
item->Animation.TargetState = DEMIGOD3_STATE_RADIAL_PROJECTILE_ATTACK; item->Animation.TargetState = DEMIGOD3_STATE_RADIAL_PROJECTILE_ATTACK;
break; break;
case DEMIGOD2_STATE_RADIAL_AIM:
creature->MaxTurn = ANGLE(7.0f);
if (!Targetable(item, &AI) && AI.distance < DEMIGOD2_RADIAL_PROJECTILE_ATTACK_RANGE)
item->Animation.TargetState = DEMIGOD2_STATE_RADIAL_PROJECTILE_ATTACK;
break;
case DEMIGOD3_STATE_RADIAL_PROJECTILE_ATTACK: case DEMIGOD3_STATE_RADIAL_PROJECTILE_ATTACK:
creature->MaxTurn = ANGLE(7.0f); creature->MaxTurn = ANGLE(7.0f);
DoDemigodEffects(itemNumber); DoDemigodEffects(itemNumber);
if (!Targetable(item, &AI) || AI.distance < pow(SECTOR(5), 2) || !GetRandomControl()) if (!Targetable(item, &AI) || AI.distance < DEMIGOD3_RADIAL_PROJECTILE_ATTACK_RANGE || !GetRandomControl())
{ {
item->Animation.TargetState = DEMIGOD_STATE_IDLE; item->Animation.TargetState = DEMIGOD_STATE_IDLE;
break; break;
@ -663,7 +681,7 @@ namespace TEN::Entities::TR4
else else
item->Pose.Orientation.y += AI.angle; item->Pose.Orientation.y += AI.angle;
if (AI.distance >= pow(SECTOR(3), 2) || if (AI.distance >= DEMIGOD1_WALK_RANGE ||
!AI.bite && !AI.bite &&
(LaraItem->Animation.ActiveState < LS_LADDER_IDLE || (LaraItem->Animation.ActiveState < LS_LADDER_IDLE ||
LaraItem->Animation.ActiveState > LS_LADDER_DOWN || LaraItem->Animation.ActiveState > LS_LADDER_DOWN ||

View file

@ -52,7 +52,7 @@ namespace TEN::Entities::TR4
const auto SethPounceAttackJoints1 = std::vector<unsigned int>{ 13, 14, 15 }; const auto SethPounceAttackJoints1 = std::vector<unsigned int>{ 13, 14, 15 };
const auto SethPounceAttackJoints2 = std::vector<unsigned int>{ 16, 17, 18 }; const auto SethPounceAttackJoints2 = std::vector<unsigned int>{ 16, 17, 18 };
constexpr auto LARA_STATE_SETH_DEATH = 13; constexpr auto LARA_STATE_SETH_DEATH = 14;
constexpr auto LARA_ANIM_SETH_DEATH = 14; constexpr auto LARA_ANIM_SETH_DEATH = 14;
enum SethState enum SethState
@ -659,7 +659,7 @@ namespace TEN::Entities::TR4
fx.flag1 = flags; fx.flag1 = flags;
fx.objectNumber = ID_ENERGY_BUBBLES; fx.objectNumber = ID_ENERGY_BUBBLES;
fx.speed = Random::GenerateInt(0, 32) - ((flags == 1) ? 64 : 0) + 96; fx.speed = Random::GenerateInt(0, 32) - ((flags == 1) ? 64 : 0) + 96;
fx.frameNumber = Objects[ID_ENERGY_BUBBLES].meshIndex + 2 - flags; fx.frameNumber = Objects[ID_ENERGY_BUBBLES].meshIndex + flags;
} }
void SethKillAttack(ItemInfo* item, ItemInfo* laraItem) void SethKillAttack(ItemInfo* item, ItemInfo* laraItem)
@ -673,7 +673,7 @@ namespace TEN::Entities::TR4
laraItem->Animation.ActiveState = LARA_STATE_SETH_DEATH; laraItem->Animation.ActiveState = LARA_STATE_SETH_DEATH;
laraItem->Animation.TargetState = LARA_STATE_SETH_DEATH; laraItem->Animation.TargetState = LARA_STATE_SETH_DEATH;
laraItem->Animation.IsAirborne = false; laraItem->Animation.IsAirborne = false;
laraItem->Pose = Pose(item->Pose.Position, 0, item->Pose.Orientation.y, 0); laraItem->Pose = Pose(item->Pose.Position, item->Pose.Orientation);
if (item->RoomNumber != laraItem->RoomNumber) if (item->RoomNumber != laraItem->RoomNumber)
ItemNewRoom(lara.ItemNumber, item->RoomNumber); ItemNewRoom(lara.ItemNumber, item->RoomNumber);
@ -687,9 +687,12 @@ namespace TEN::Entities::TR4
lara.Control.Weapon.GunType = LaraWeaponType::None; lara.Control.Weapon.GunType = LaraWeaponType::None;
Camera.pos.RoomNumber = laraItem->RoomNumber; Camera.pos.RoomNumber = laraItem->RoomNumber;
Camera.type = CameraType::Chase; Camera.type = CameraType::Fixed;
Camera.flags = CF_FOLLOW_CENTER; ForcedFixedCamera.x = item->Pose.Position.x + ((BLOCK(2) * phd_sin(item->Pose.Orientation.y)));
Camera.targetAngle = ANGLE(170.0f); ForcedFixedCamera.y = item->Pose.Position.y - BLOCK(1);
Camera.targetElevation = ANGLE(-25.0f); ForcedFixedCamera.z = item->Pose.Position.z + ((BLOCK(2) * phd_cos(item->Pose.Orientation.y)));
ForcedFixedCamera.RoomNumber = item->RoomNumber;
UseForcedFixedCamera = true;
} }
} }

View file

@ -61,24 +61,38 @@ void ExplosionControl(short itemNumber)
if (TriggerActive(item)) if (TriggerActive(item))
{ {
item->Flags |= IFLAG_INVISIBLE; item->Flags |= IFLAG_INVISIBLE;
if (item->ItemFlags[0] < item->TriggerFlags) if (item->ItemFlags[0] < item->TriggerFlags)
{
++item->ItemFlags[0]; ++item->ItemFlags[0];
}
else if (item->ItemFlags[0] == item->TriggerFlags) else if (item->ItemFlags[0] == item->TriggerFlags)
{ {
int flag; int flag;
++item->ItemFlags[0]; ++item->ItemFlags[0];
if (TestEnvironment(ENV_FLAG_WATER, item->RoomNumber))
if (TestEnvironment(ENV_FLAG_WATER, item->RoomNumber) ||
TestEnvironment(ENV_FLAG_SWAMP, item->RoomNumber))
{
flag = 1; flag = 1;
}
else else
{
flag = item->ItemFlags[1] == 1 ? 2 : 0; flag = item->ItemFlags[1] == 1 ? 2 : 0;
}
SoundEffect(SFX_TR4_EXPLOSION1, &item->Pose, SoundEnvironment::Land, 1.5f); SoundEffect(SFX_TR4_EXPLOSION1, &item->Pose, SoundEnvironment::Land, 1.5f);
SoundEffect(SFX_TR4_EXPLOSION2, &item->Pose); SoundEffect(SFX_TR4_EXPLOSION2, &item->Pose);
TriggerExplosionSparks(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, 3, -2, flag, item->RoomNumber); TriggerExplosionSparks(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, 3, -2, flag, item->RoomNumber);
for (int i = 0; i < item->ItemFlags[2]; ++i) for (int i = 0; i < item->ItemFlags[2]; ++i)
TriggerExplosionSparks(item->Pose.Position.x + (GetRandomControl() % 128 - 64) * item->ItemFlags[2], item->Pose.Position.y + (GetRandomControl() % 128 - 64) * item->ItemFlags[2], item->Pose.Position.z + (GetRandomControl() % 128 - 64) * item->ItemFlags[2], 2, 0, i, item->RoomNumber); {
TriggerExplosionSparks(
item->Pose.Position.x + (GetRandomControl() % 128 - 64) * item->ItemFlags[2],
item->Pose.Position.y + (GetRandomControl() % 128 - 64) * item->ItemFlags[2],
item->Pose.Position.z + (GetRandomControl() % 128 - 64) * item->ItemFlags[2],
2, 0, flag, item->RoomNumber);
}
Pose pos; Pose pos;
pos.Position.x = item->Pose.Position.x; pos.Position.x = item->Pose.Position.x;
@ -88,11 +102,12 @@ void ExplosionControl(short itemNumber)
if (item->ItemFlags[3]) if (item->ItemFlags[3])
{ {
if (flag == 2) if (flag == 2)
TriggerShockwave(&pos, 48, 32 * item->ItemFlags[2] + 304, 4 * item->ItemFlags[2] + 96, 128, 96, 0, 24, 2048, 0);
else
TriggerShockwave(&pos, 48, 32 * item->ItemFlags[2] + 304, 4 * item->ItemFlags[2] + 96, 0, 96, 128, 24, 2048, 0); TriggerShockwave(&pos, 48, 32 * item->ItemFlags[2] + 304, 4 * item->ItemFlags[2] + 96, 0, 96, 128, 24, 2048, 0);
else
TriggerShockwave(&pos, 48, 32 * item->ItemFlags[2] + 304, 4 * item->ItemFlags[2] + 96, 128, 96, 0, 24, 2048, 0);
} }
else if (flag == 2)
if (flag != 2)
{ {
auto vec = GetJointPosition(LaraItem, LM_HIPS); auto vec = GetJointPosition(LaraItem, LM_HIPS);
@ -100,12 +115,12 @@ void ExplosionControl(short itemNumber)
int dy = vec.y - item->Pose.Position.y; int dy = vec.y - item->Pose.Position.y;
int dz = vec.z - item->Pose.Position.z; int dz = vec.z - item->Pose.Position.z;
if (abs(dx) < SECTOR(1) && if (abs(dx) < BLOCK(1) &&
abs(dy) < SECTOR(1) && abs(dy) < BLOCK(1) &&
abs(dz) < SECTOR(1)) abs(dz) < BLOCK(1))
{ {
int distance = sqrt(pow(dx, 2) + pow(dy, 2) + pow(dz, 2)); int distance = sqrt(pow(dx, 2) + pow(dy, 2) + pow(dz, 2));
if (distance < SECTOR(2)) if (distance < BLOCK(2))
{ {
DoDamage(LaraItem, distance / 16); DoDamage(LaraItem, distance / 16);
@ -125,7 +140,7 @@ void ExplosionControl(short itemNumber)
{ {
TriggerExplosionSparks(CollidedItems[i]->Pose.Position.x, CollidedItems[i]->Pose.Position.y, CollidedItems[i]->Pose.Position.z, 3, -2, 0, CollidedItems[i]->RoomNumber); TriggerExplosionSparks(CollidedItems[i]->Pose.Position.x, CollidedItems[i]->Pose.Position.y, CollidedItems[i]->Pose.Position.z, 3, -2, 0, CollidedItems[i]->RoomNumber);
CollidedItems[i]->Pose.Position.y -= 128; CollidedItems[i]->Pose.Position.y -= 128;
TriggerShockwave(&CollidedItems[i]->Pose, 48, 304, 96, 0, 96, 128, 24, 0, 0); TriggerShockwave(&CollidedItems[i]->Pose, 48, 304, 96, 128, 96, 0, 24, 0, 0);
CollidedItems[i]->Pose.Position.y += 128; CollidedItems[i]->Pose.Position.y += 128;
ExplodeItemNode(CollidedItems[i], 0, 0, 80); ExplodeItemNode(CollidedItems[i], 0, 0, 80);
SmashObject(CollidedItems[i] - g_Level.Items.data()); SmashObject(CollidedItems[i] - g_Level.Items.data());
@ -151,7 +166,7 @@ void ExplosionControl(short itemNumber)
{ {
TriggerExplosionSparks(CollidedMeshes[i]->pos.Position.x, CollidedMeshes[i]->pos.Position.y, CollidedMeshes[i]->pos.Position.z, 3, -2, 0, item->RoomNumber); TriggerExplosionSparks(CollidedMeshes[i]->pos.Position.x, CollidedMeshes[i]->pos.Position.y, CollidedMeshes[i]->pos.Position.z, 3, -2, 0, item->RoomNumber);
CollidedMeshes[i]->pos.Position.y -= 128; CollidedMeshes[i]->pos.Position.y -= 128;
TriggerShockwave(&CollidedMeshes[i]->pos, 40, 176, 64, 0, 96, 128, 16, 0, 0); TriggerShockwave(&CollidedMeshes[i]->pos, 40, 176, 64, 128, 96, 0, 16, 0, 0);
CollidedMeshes[i]->pos.Position.y += 128; CollidedMeshes[i]->pos.Position.y += 128;
SoundEffect(GetShatterSound(CollidedMeshes[i]->staticNumber), &CollidedMeshes[i]->pos); SoundEffect(GetShatterSound(CollidedMeshes[i]->staticNumber), &CollidedMeshes[i]->pos);
ShatterObject(NULL, CollidedMeshes[i], -128, item->RoomNumber, 0); ShatterObject(NULL, CollidedMeshes[i], -128, item->RoomNumber, 0);
@ -175,7 +190,9 @@ void ExplosionControl(short itemNumber)
} }
} }
else else
{
KillItem(itemNumber); KillItem(itemNumber);
}
} }
} }
} }

View file

@ -29,6 +29,26 @@ namespace TEN::Renderer
m_meshes.clear(); m_meshes.clear();
TENLog("Allocated renderer object memory.", LogLevel::Info);
m_animatedTextures.resize(g_Level.AnimatedTextures.size());
for (int i = 0; i < g_Level.AnimatedTextures.size(); i++)
{
TEXTURE* texture = &g_Level.AnimatedTextures[i];
Texture2D normal;
if (texture->normalMapData.size() < 1) {
normal = CreateDefaultNormalTexture();
}
else {
normal = Texture2D(m_device.Get(), texture->normalMapData.data(), texture->normalMapData.size());
}
TexturePair tex = std::make_tuple(Texture2D(m_device.Get(), texture->colorMapData.data(), texture->colorMapData.size()), normal);
m_animatedTextures[i] = tex;
}
if (m_animatedTextures.size() > 0)
TENLog("Generated " + std::to_string(m_animatedTextures.size()) + " animated textures.", LogLevel::Info);
std::transform(g_Level.AnimatedTexturesSequences.begin(), g_Level.AnimatedTexturesSequences.end(), std::back_inserter(m_animatedTextureSets), [](ANIMATED_TEXTURES_SEQUENCE& sequence) { std::transform(g_Level.AnimatedTexturesSequences.begin(), g_Level.AnimatedTexturesSequences.end(), std::back_inserter(m_animatedTextureSets), [](ANIMATED_TEXTURES_SEQUENCE& sequence) {
RendererAnimatedTextureSet set{}; RendererAnimatedTextureSet set{};
set.NumTextures = sequence.numFrames; set.NumTextures = sequence.numFrames;
@ -48,6 +68,9 @@ namespace TEN::Renderer
return set; return set;
}); });
if (m_animatedTextureSets.size() > 0)
TENLog("Generated " + std::to_string(m_animatedTextureSets.size()) + " animated texture sets.", LogLevel::Info);
m_roomTextures.resize(g_Level.RoomTextures.size()); m_roomTextures.resize(g_Level.RoomTextures.size());
for (int i = 0; i < g_Level.RoomTextures.size(); i++) for (int i = 0; i < g_Level.RoomTextures.size(); i++)
{ {
@ -70,20 +93,8 @@ namespace TEN::Renderer
#endif #endif
} }
m_animatedTextures.resize(g_Level.AnimatedTextures.size()); if (m_roomTextures.size() > 0)
for (int i = 0; i < g_Level.AnimatedTextures.size(); i++) TENLog("Generated " + std::to_string(m_roomTextures.size()) + " room texture atlases.", LogLevel::Info);
{
TEXTURE *texture = &g_Level.AnimatedTextures[i];
Texture2D normal;
if (texture->normalMapData.size() < 1) {
normal = CreateDefaultNormalTexture();
}
else {
normal = Texture2D(m_device.Get(), texture->normalMapData.data(), texture->normalMapData.size());
}
TexturePair tex = std::make_tuple(Texture2D(m_device.Get(), texture->colorMapData.data(), texture->colorMapData.size()), normal);
m_animatedTextures[i] = tex;
}
m_moveablesTextures.resize(g_Level.MoveablesTextures.size()); m_moveablesTextures.resize(g_Level.MoveablesTextures.size());
for (int i = 0; i < g_Level.MoveablesTextures.size(); i++) for (int i = 0; i < g_Level.MoveablesTextures.size(); i++)
@ -107,6 +118,9 @@ namespace TEN::Renderer
#endif #endif
} }
if (m_moveablesTextures.size() > 0)
TENLog("Generated " + std::to_string(m_moveablesTextures.size()) + " moveable texture atlases.", LogLevel::Info);
m_staticsTextures.resize(g_Level.StaticsTextures.size()); m_staticsTextures.resize(g_Level.StaticsTextures.size());
for (int i = 0; i < g_Level.StaticsTextures.size(); i++) for (int i = 0; i < g_Level.StaticsTextures.size(); i++)
{ {
@ -129,6 +143,9 @@ namespace TEN::Renderer
#endif #endif
} }
if (m_staticsTextures.size() > 0)
TENLog("Generated " + std::to_string(m_staticsTextures.size()) + " static mesh texture atlases.", LogLevel::Info);
m_spritesTextures.resize(g_Level.SpritesTextures.size()); m_spritesTextures.resize(g_Level.SpritesTextures.size());
for (int i = 0; i < g_Level.SpritesTextures.size(); i++) for (int i = 0; i < g_Level.SpritesTextures.size(); i++)
{ {
@ -136,8 +153,13 @@ namespace TEN::Renderer
m_spritesTextures[i] = Texture2D(m_device.Get(), texture->colorMapData.data(), texture->colorMapData.size()); m_spritesTextures[i] = Texture2D(m_device.Get(), texture->colorMapData.data(), texture->colorMapData.size());
} }
if (m_spritesTextures.size() > 0)
TENLog("Generated " + std::to_string(m_spritesTextures.size()) + " sprite atlases.", LogLevel::Info);
m_skyTexture = Texture2D(m_device.Get(), g_Level.SkyTexture.colorMapData.data(), g_Level.SkyTexture.colorMapData.size()); m_skyTexture = Texture2D(m_device.Get(), g_Level.SkyTexture.colorMapData.data(), g_Level.SkyTexture.colorMapData.size());
TENLog("Loaded sky texture.", LogLevel::Info);
int totalVertices = 0; int totalVertices = 0;
int totalIndices = 0; int totalIndices = 0;
for (auto& room : g_Level.Rooms) for (auto& room : g_Level.Rooms)
@ -153,9 +175,13 @@ namespace TEN::Renderer
roomsVertices.resize(totalVertices); roomsVertices.resize(totalVertices);
roomsIndices.resize(totalIndices); roomsIndices.resize(totalIndices);
TENLog("Loaded total " + std::to_string(totalVertices) + " room vertices.", LogLevel::Info);
int lastVertex = 0; int lastVertex = 0;
int lastIndex = 0; int lastIndex = 0;
TENLog("Preparing room data...", LogLevel::Info);
for (int i = 0; i < g_Level.Rooms.size(); i++) for (int i = 0; i < g_Level.Rooms.size(); i++)
{ {
ROOM_INFO& room = g_Level.Rooms[i]; ROOM_INFO& room = g_Level.Rooms[i];
@ -404,6 +430,8 @@ namespace TEN::Renderer
} }
); );
TENLog("Preparing object data...", LogLevel::Info);
bool skinPresent = false; bool skinPresent = false;
bool hairsPresent = false; bool hairsPresent = false;
@ -707,6 +735,8 @@ namespace TEN::Renderer
m_moveablesVertexBuffer = VertexBuffer(m_device.Get(), moveablesVertices.size(), moveablesVertices.data()); m_moveablesVertexBuffer = VertexBuffer(m_device.Get(), moveablesVertices.size(), moveablesVertices.data());
m_moveablesIndexBuffer = IndexBuffer(m_device.Get(), moveablesIndices.size(), moveablesIndices.data()); m_moveablesIndexBuffer = IndexBuffer(m_device.Get(), moveablesIndices.size(), moveablesIndices.data());
TENLog("Preparing static mesh data...", LogLevel::Info);
totalVertices = 0; totalVertices = 0;
totalIndices = 0; totalIndices = 0;
for (int i = 0; i < StaticObjectsIds.size(); i++) for (int i = 0; i < StaticObjectsIds.size(); i++)
@ -752,6 +782,8 @@ namespace TEN::Renderer
m_staticsVertexBuffer = VertexBuffer(m_device.Get(), 1); m_staticsVertexBuffer = VertexBuffer(m_device.Get(), 1);
m_staticsIndexBuffer = IndexBuffer(m_device.Get(), 1); m_staticsIndexBuffer = IndexBuffer(m_device.Get(), 1);
} }
TENLog("Preparing sprite data...", LogLevel::Info);
// Step 5: prepare sprites // Step 5: prepare sprites
m_sprites.resize(g_Level.Sprites.size()); m_sprites.resize(g_Level.Sprites.size());

View file

@ -1264,11 +1264,16 @@ namespace TEN::Renderer
using TEN::Effects::Smoke::SmokeParticles; using TEN::Effects::Smoke::SmokeParticles;
using TEN::Effects::Smoke::SmokeParticle; using TEN::Effects::Smoke::SmokeParticle;
for (int i = 0; i < SmokeParticles.size(); i++) for (const auto& smoke : SmokeParticles)
{ {
SmokeParticle& s = SmokeParticles[i]; if (!smoke.active)
if (!s.active) continue; continue;
AddSpriteBillboard(&m_sprites[Objects[ID_SMOKE_SPRITES].meshIndex + s.sprite], s.position, s.color, s.rotation, 1.0f, { s.size, s.size }, BLENDMODE_ALPHABLEND, true, view);
// TODO: Switch back to alpha blend mode once rendering for it is refactored. -- Sezz 2023.01.14
AddSpriteBillboard(
&m_sprites[Objects[ID_SMOKE_SPRITES].meshIndex + smoke.sprite],
smoke.position,
smoke.color, smoke.rotation, 1.0f, { smoke.size, smoke.size }, BLENDMODE_ADDITIVE, true, view);
} }
} }