Implemented Claw Mutant from TR3 (#1061)

* Foundation commit for ID_MONSTER_MUTANT

* Implemented claw mutant from TR3

* Fixed vs2019 project

* Fixed scripting for claw mutant

* finished plasma ball effect

* Improved claw mutant

Fixed blood not appearing.
Fixed precision for the projectile.

* Improved precision for claw mutant

Now it take lara being crouch into account.

* Demagic and reformat

* Formatting; use GetAnimData()

* Fix merge error

* Refine claw mutant enums and constants

* Update Changes.txt

* Increased the attack range in idle.

---------

Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
Co-authored-by: Sezz <sezzary@outlook.com>
Co-authored-by: Kubsy <80340234+Kubsy@users.noreply.github.com>
This commit is contained in:
TokyoSU 2023-04-20 19:13:43 +02:00 committed by GitHub
parent 4419345f93
commit da2066f51a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 700 additions and 128 deletions

View file

@ -2,8 +2,9 @@ Version 1.0.9
============= =============
- Add TR3 Wall mounted blade. - Add TR3 Wall mounted blade.
- Add TR3 Claw mutant.
- Add removable puzzles from puzzle holes and puzzle dones. - Add removable puzzles from puzzle holes and puzzle dones.
- employed by setting the trigger type as "Switch" for either puzzle hole or puzzle done. - employed by setting the trigger type as "Switch" for either puzzle hole or puzzle done.
- Can be mixed with puzzle done and puzzle holes of the same or different type. - Can be mixed with puzzle done and puzzle holes of the same or different type.
- Fix Cold bar triggered when room is dry (only trigger if and only if the room is water). - Fix Cold bar triggered when room is dry (only trigger if and only if the room is water).
- Fix spiky wall speed value. - Fix spiky wall speed value.

View file

@ -11,12 +11,14 @@
#include "Game/items.h" #include "Game/items.h"
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Math/Math.h" #include "Math/Math.h"
#include "Objects/TR3/Entity/tr3_claw_mutant.h"
#include "Objects/TR4/Entity/tr4_mutant.h" #include "Objects/TR4/Entity/tr4_mutant.h"
#include "Objects/TR4/Entity/tr4_demigod.h" #include "Objects/TR4/Entity/tr4_demigod.h"
#include "Renderer/Renderer11Enums.h" #include "Renderer/Renderer11Enums.h"
#include "Specific/level.h" #include "Specific/level.h"
using namespace TEN::Effects::Items; using namespace TEN::Effects::Items;
using namespace TEN::Entities::Creatures::TR3;
using namespace TEN::Entities::TR4; using namespace TEN::Entities::TR4;
using namespace TEN::Math; using namespace TEN::Math;
@ -58,9 +60,13 @@ namespace TEN::Entities::Effects
flame.fxObj = fxNumber; flame.fxObj = fxNumber;
if (fx.flag1 == 1) if (fx.flag1 == 1)
{
flame.scalar = 3; flame.scalar = 3;
}
else else
{
flame.scalar = 2; flame.scalar = 2;
}
flame.sSize = flame.size = (GetRandomControl() & 7) + 64; flame.sSize = flame.size = (GetRandomControl() & 7) + 64;
flame.dSize = flame.size / 32; flame.dSize = flame.size / 32;
@ -92,9 +98,13 @@ namespace TEN::Entities::Effects
flame.rotAng = GetRandomControl() & 0xFFF; flame.rotAng = GetRandomControl() & 0xFFF;
if (GetRandomControl() & 1) if (GetRandomControl() & 1)
{
flame.rotAdd = -32 - (GetRandomControl() & 0x1F); flame.rotAdd = -32 - (GetRandomControl() & 0x1F);
}
else else
{
flame.rotAdd = (GetRandomControl() & 0x1F) + 32; flame.rotAdd = (GetRandomControl() & 0x1F) + 32;
}
flame.gravity = 0; flame.gravity = 0;
flame.maxYvel = 0; flame.maxYvel = 0;
@ -135,7 +145,7 @@ namespace TEN::Entities::Effects
} }
else else
{ {
if (fx.flag1 == (int)MissileType::Mutant) if (fx.flag1 == (int)MissileType::CrocgodMutant)
{ {
if (fx.counter) if (fx.counter)
fx.counter--; fx.counter--;
@ -152,15 +162,20 @@ namespace TEN::Entities::Effects
if (fx.speed < maxVelocity) if (fx.speed < maxVelocity)
{ {
if (fx.flag1 == (int)MissileType::Mutant) if (fx.flag1 == (int)MissileType::CrocgodMutant)
{
fx.speed++; fx.speed++;
}
else else
{
fx.speed += 3; fx.speed += 3;
}
} }
if (fx.speed < maxVelocity && if (fx.speed < maxVelocity &&
fx.flag1 != (int)MissileType::SophiaLeighNormal && fx.flag1 != (int)MissileType::SophiaLeighNormal &&
fx.flag1 != (int)MissileType::SophiaLeighLarge) fx.flag1 != (int)MissileType::SophiaLeighLarge &&
fx.flag1 != (int)MissileType::ClawMutantPlasma)
{ {
short dy = orient.y - fx.pos.Orientation.y; short dy = orient.y - fx.pos.Orientation.y;
if (abs(dy) > abs(ANGLE(180.0f))) if (abs(dy) > abs(ANGLE(180.0f)))
@ -171,8 +186,6 @@ namespace TEN::Entities::Effects
dx = -dx; dx = -dx;
dy >>= 3; dy >>= 3;
dx >>= 3;
if (dy < -maxRotation) if (dy < -maxRotation)
{ {
dy = -maxRotation; dy = -maxRotation;
@ -182,6 +195,7 @@ namespace TEN::Entities::Effects
dy = maxRotation; dy = maxRotation;
} }
dx >>= 3;
if (dx < -maxRotation) if (dx < -maxRotation)
{ {
dx = -maxRotation; dx = -maxRotation;
@ -192,85 +206,86 @@ namespace TEN::Entities::Effects
} }
fx.pos.Orientation.x += dx; fx.pos.Orientation.x += dx;
if (fx.flag1 != (int)MissileType::Demigod3Radial && (fx.flag1 != (int)MissileType::CrocgodMutant || !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 == (int)MissileType::Mutant) if (fx.flag1 == (int)MissileType::CrocgodMutant)
fx.pos.Orientation.z += 16 * fx.speed; fx.pos.Orientation.z += 16 * fx.speed;
auto prevPos = fx.pos.Position; auto prevPos = fx.pos.Position;
int speed = (fx.speed * phd_cos(fx.pos.Orientation.x)); int speed = (fx.speed * phd_cos(fx.pos.Orientation.x));
fx.pos.Position.x += (speed * phd_sin(fx.pos.Orientation.y)); fx.pos.Position.x += speed * phd_sin(fx.pos.Orientation.y);
fx.pos.Position.y += -((fx.speed * phd_sin(fx.pos.Orientation.x))) + fx.fallspeed; fx.pos.Position.y += -((fx.speed * phd_sin(fx.pos.Orientation.x))) + fx.fallspeed;
fx.pos.Position.z += (speed * phd_cos(fx.pos.Orientation.y)); fx.pos.Position.z += speed * phd_cos(fx.pos.Orientation.y);
auto probe = GetCollision(fx.pos.Position.x, fx.pos.Position.y, fx.pos.Position.z, fx.roomNumber); auto pointColl = GetCollision(fx.pos.Position.x, fx.pos.Position.y, fx.pos.Position.z, fx.roomNumber);
if (fx.pos.Position.y >= probe.Position.Floor || fx.pos.Position.y <= probe.Position.Ceiling) if (fx.pos.Position.y >= pointColl.Position.Floor || fx.pos.Position.y <= pointColl.Position.Ceiling)
{ {
fx.pos.Position = prevPos; fx.pos.Position = prevPos;
if (fx.flag1 != (int)MissileType::Mutant && switch ((MissileType)fx.flag1)
fx.flag1 != (int)MissileType::SophiaLeighNormal && {
fx.flag1 != (int)MissileType::SophiaLeighLarge) case MissileType::SethLarge:
TriggerShockwave(&fx.pos, 32, 160, 64, 64, 128, 00, 24, EulerAngles(fx.pos.Orientation.x - ANGLE(90.0f), 0, 0), 0, true, false, (int)ShockwaveStyle::Normal);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber);
BubblesShatterFunction(&fx, 0, -32); BubblesShatterFunction(&fx, 0, -32);
break;
if (fx.flag1 == (int)MissileType::SethLarge) case MissileType::SophiaLeighNormal:
{ TriggerShockwave(&fx.pos, 5, 32, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.x - ANGLE(90.0f), 0, 0), 0, true, false, (int)ShockwaveStyle::Normal);
TriggerShockwave(&fx.pos, 32, 160, 64, 64, 128, 00, 24, EulerAngles((((~g_Level.Rooms[fx.roomNumber].flags) / 16) & 2) * 65536, 0.0f, 0.0f), 0, true, false, (int)ShockwaveStyle::Normal);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber); TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber);
} break;
else if (fx.flag1 == (int)MissileType::SophiaLeighNormal)
{
TriggerShockwave(&fx.pos, 5, 32, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.y + ANGLE(180), 0.0f, 0.0f), 0, true, false, (int)ShockwaveStyle::Normal);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber);
}
else if (fx.flag1 == (int)MissileType::SophiaLeighLarge)
{
TriggerShockwave(&fx.pos, 10, 64, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.y + ANGLE(180), 0.0f, 0.0f), 0, true, false, (int)ShockwaveStyle::Normal);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber);
}
else
{
if (fx.flag1)
{
if (fx.flag1 == (int)MissileType::Demigod3Single || fx.flag1 == (int)MissileType::Demigod3Radial)
{
TriggerShockwave(&fx.pos, 32, 160, 64, 0, 96, 128, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
}
else if (fx.flag1 == (int)MissileType::Demigod2)
{
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 64, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
}
else
{
if (fx.flag1 != (int)MissileType::Harpy)
{
if (fx.flag1 == (int)MissileType::Mutant)
{
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 0, fx.roomNumber);
TriggerShockwave(&fx.pos, 48, 240, 64, 128, 96, 0, 24, EulerAngles::Zero, 15, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y -= 128;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 15, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y += 256;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 15, true, false, (int)ShockwaveStyle::Normal);
}
} case MissileType::SophiaLeighLarge:
else TriggerShockwave(&fx.pos, 10, 64, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.x - ANGLE(90.0f), 0, 0), 0, true, false, (int)ShockwaveStyle::Normal);
{ TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber);
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 128, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal); break;
}
} case MissileType::Demigod3Single:
} case MissileType::Demigod3Radial:
else TriggerShockwave(&fx.pos, 32, 160, 64, 0, 96, 128, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
BubblesShatterFunction(&fx, 0, -32);
break;
case MissileType::Demigod2:
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 64, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
BubblesShatterFunction(&fx, 0, -32);
break;
case MissileType::Harpy:
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 128, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
BubblesShatterFunction(&fx, 0, -32);
break;
case MissileType::CrocgodMutant:
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 0, fx.roomNumber);
TriggerShockwave(&fx.pos, 48, 240, 64, 128, 96, 0, 24, EulerAngles::Zero, 15, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y -= 128;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 15, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y += 256;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 15, true, false, (int)ShockwaveStyle::Normal);
break;
case MissileType::ClawMutantPlasma:
for (int i = 0; i < 6; i++)
{ {
TriggerShockwave(&fx.pos, 32, 160, 64, 0, 128, 64, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal); SpawnClawMutantPlasmaFlameBall(fxNumber, Vector3(20.0f, 32.0f, 20.0f), Vector3(Random::GenerateFloat(-115.0f, 185.0f), 0, Random::GenerateFloat(-115.0f, 185.0f)), 24);
SpawnClawMutantPlasmaFlameBall(fxNumber, Vector3(20.0f, 32.0f, 20.0f), Vector3(Random::GenerateFloat(-115.0f, 185.0f), 0, Random::GenerateFloat(-115.0f, 185.0f)), 24);
SpawnClawMutantPlasmaFlameBall(fxNumber, Vector3(20.0f, 32.0f, 20.0f), Vector3(Random::GenerateFloat(-115.0f, 185.0f), 0, Random::GenerateFloat(-115.0f, 185.0f)), 24);
} }
break;
default:
TriggerShockwave(&fx.pos, 32, 160, 64, 0, 128, 64, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
BubblesShatterFunction(&fx, 0, -32);
break;
} }
KillEffect(fxNumber); KillEffect(fxNumber);
@ -280,93 +295,110 @@ 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 != (int)MissileType::Mutant && switch ((MissileType)fx.flag1)
fx.flag1 != (int)MissileType::SophiaLeighNormal &&
fx.flag1 != (int)MissileType::SophiaLeighLarge)
BubblesShatterFunction(&fx, 0, -32);
KillEffect(fxNumber);
if (fx.flag1 == (int)MissileType::SethLarge)
{ {
case MissileType::SethLarge:
TriggerShockwave(&fx.pos, 48, 240, 64, 0, 128, 64, 24, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal); TriggerShockwave(&fx.pos, 48, 240, 64, 0, 128, 64, 24, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber); TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 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));
} break;
else if (fx.flag1 == (int)MissileType::SophiaLeighNormal)
{ case MissileType::SophiaLeighLarge:
TriggerShockwave(&fx.pos, 5, 32, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.y, 0.0f, 0.0f), fx.flag2, true, false, (int)ShockwaveStyle::Normal); TriggerShockwave(&fx.pos, 5, 32, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.y, 0.0f, 0.0f), fx.flag2, true, false, (int)ShockwaveStyle::Normal);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber); TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber);
} break;
else if (fx.flag1 == (int)MissileType::SophiaLeighLarge)
{ case MissileType::SophiaLeighNormal:
TriggerShockwave(&fx.pos, 10, 64, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.y, 0.0f, 0.0f), fx.flag2, true, false, (int)ShockwaveStyle::Normal); TriggerShockwave(&fx.pos, 10, 64, 128, 0, 128, 128, 24, EulerAngles(fx.pos.Orientation.y, 0.0f, 0.0f), fx.flag2, true, false, (int)ShockwaveStyle::Normal);
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber); TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 2, fx.roomNumber);
} break;
else if (fx.flag1)
{ case MissileType::Demigod3Single:
switch (fx.flag1) case MissileType::Demigod3Radial:
TriggerShockwave(&fx.pos, 32, 160, 64, 0, 96, 128, 16, EulerAngles::Zero, 10, true, false, (int)ShockwaveStyle::Normal);
BubblesShatterFunction(&fx, 0, -32);
break;
case MissileType::Demigod2:
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 64, 0, 16, EulerAngles::Zero, 5, true, false, (int)ShockwaveStyle::Normal);
BubblesShatterFunction(&fx, 0, -32);
break;
case MissileType::ClawMutantPlasma:
DoDamage(LaraItem, fx.flag2);
for (int i = 0; i < 3; i++)
{ {
case (int)MissileType::Demigod3Single: SpawnClawMutantPlasmaFlameBall(fxNumber, Vector3(20.0f, 32.0f, 20.0f), Vector3(Random::GenerateFloat(-115.0f, 185.0f), 0, Random::GenerateFloat(-115.0f, 185.0f)), 24);
case (int)MissileType::Demigod3Radial: SpawnClawMutantPlasmaFlameBall(fxNumber, Vector3(20.0f, 32.0f, 20.0f), Vector3(Random::GenerateFloat(-115.0f, 185.0f), 0, Random::GenerateFloat(-115.0f, 185.0f)), 24);
TriggerShockwave(&fx.pos, 32, 160, 64, 0, 96, 128, 16, EulerAngles::Zero, 10, true, false, (int)ShockwaveStyle::Normal); SpawnClawMutantPlasmaFlameBall(fxNumber, Vector3(20.0f, 32.0f, 20.0f), Vector3(Random::GenerateFloat(-115.0f, 185.0f), 0, Random::GenerateFloat(-115.0f, 185.0f)), 24);
break;
case (int)MissileType::Demigod2:
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 64, 0, 16, EulerAngles::Zero, 5, true, false, (int)ShockwaveStyle::Normal);
break;
case (int)MissileType::Harpy:
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 128, 0, 16, EulerAngles::Zero, 3, true, false, (int)ShockwaveStyle::Normal);
break;
case (int)MissileType::Mutant:
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 0, fx.roomNumber);
TriggerShockwave(&fx.pos, 48, 240, 64, 128, 96, 0, 24, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y -= 128;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y += 256;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
ItemBurn(LaraItem);
break;
} }
break;
case MissileType::Harpy:
TriggerShockwave(&fx.pos, 32, 160, 64, 128, 128, 0, 16, EulerAngles::Zero, 3, true, false, (int)ShockwaveStyle::Normal);
BubblesShatterFunction(&fx, 0, -32);
break;
case MissileType::CrocgodMutant:
TriggerExplosionSparks(prevPos.x, prevPos.y, prevPos.z, 3, -2, 0, fx.roomNumber);
TriggerShockwave(&fx.pos, 48, 240, 64, 128, 96, 0, 24, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y -= 128;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
fx.pos.Position.y += 256;
TriggerShockwave(&fx.pos, 48, 240, 48, 128, 112, 0, 16, EulerAngles::Zero, 0, true, false, (int)ShockwaveStyle::Normal);
ItemBurn(LaraItem);
break;
default:
TriggerShockwave(&fx.pos, 24, 88, 48, 0, 128, 64, 16, EulerAngles::Zero, 1, true, false, (int)ShockwaveStyle::Normal);
break;
} }
else
{ KillEffect(fxNumber);
TriggerShockwave(&fx.pos, 24, 88, 48, 0, 128, 64, 16, EulerAngles((((~g_Level.Rooms[fx.roomNumber].flags) / 16) & 2) * 65536, 0.0f, 0.0f), 1, true, false, (int)ShockwaveStyle::Normal);
}
} }
else else
{ {
if (probe.RoomNumber != fx.roomNumber) if (pointColl.RoomNumber != fx.roomNumber)
EffectNewRoom(fxNumber, probe.RoomNumber); EffectNewRoom(fxNumber, pointColl.RoomNumber);
auto deltaPos = prevPos - fx.pos.Position; auto deltaPos = prevPos - fx.pos.Position;
if (Wibble & 4) if (Wibble & 4)
{ {
switch (fx.flag1) switch ((MissileType)fx.flag1)
{ {
default: default:
case (int)MissileType::SethLarge: case MissileType::SethLarge:
TriggerSethMissileFlame(fxNumber, 32 * deltaPos.x, 32 * deltaPos.y, 32 * deltaPos.z); TriggerSethMissileFlame(fxNumber, deltaPos.x * 32, deltaPos.y * 32, deltaPos.z * 32);
break; break;
case (int)MissileType::Harpy: case MissileType::Harpy:
TriggerHarpyFlameFlame(fxNumber, 16 * deltaPos.x, 16 * deltaPos.y, 16 * deltaPos.z); TriggerHarpyFlameFlame(fxNumber, deltaPos.x * 16, deltaPos.y * 16, deltaPos.z * 16);
break; break;
case (int)MissileType::Demigod3Single: case MissileType::Demigod3Single:
case (int)MissileType::Demigod3Radial: case MissileType::Demigod3Radial:
case (int)MissileType::Demigod2: case MissileType::Demigod2:
TriggerDemigodMissileFlame(fxNumber, 16 * deltaPos.x, 16 * deltaPos.y, 16 * deltaPos.z); TriggerDemigodMissileFlame(fxNumber, deltaPos.x * 16, deltaPos.y * 16, deltaPos.z * 16);
break; break;
case (int)MissileType::Mutant: case MissileType::ClawMutantPlasma:
TriggerCrocgodMissileFlame(fxNumber, 16 * deltaPos.x, 16 * deltaPos.y, 16 * deltaPos.z); for (int i = 0; i < 3; i++)
SpawnClawMutantPlasmaFlameBall(fxNumber, Vector3(deltaPos.x, deltaPos.y * 16, deltaPos.z), Vector3::Zero, 10.0f);
break;
case MissileType::CrocgodMutant:
TriggerCrocgodMissileFlame(fxNumber, deltaPos.x * 16, deltaPos.y * 16, deltaPos.z * 16);
break; break;
} }
} }
} }
if (fx.flag1 == (int)MissileType::ClawMutantPlasma)
TriggerDynamicLight(fx.pos.Position.x, fx.pos.Position.y, fx.pos.Position.z, 8, 0, 64, 128);
} }
} }

View file

@ -10,9 +10,10 @@ namespace TEN::Entities::Effects
Demigod3Single = 3, Demigod3Single = 3,
Demigod3Radial = 4, Demigod3Radial = 4,
Demigod2 = 5, Demigod2 = 5,
Mutant = 6, CrocgodMutant = 6,
SophiaLeighNormal = 7, SophiaLeighNormal = 7,
SophiaLeighLarge = 8 SophiaLeighLarge = 8,
ClawMutantPlasma = 9
}; };
void ControlEnemyMissile(short fxNumber); void ControlEnemyMissile(short fxNumber);

View file

@ -0,0 +1,510 @@
#include "framework.h"
#include "Objects/TR3/Entity/tr3_claw_mutant.h"
#include "Game/animation.h"
#include "Game/control/box.h"
#include "Game/effects/effects.h"
#include "Game/Lara/lara_helpers.h"
#include "Math/Math.h"
#include "Objects/Effects/enemy_missile.h"
#include "Game/misc.h"
#include "Game/people.h"
#include "Specific/setup.h"
using namespace TEN::Entities::Effects;
using namespace TEN::Math;
// ItemFlags[5] flag enables damage left (0 = enabled, 1 = disabled).
// ItemFlags[6] flag enables damage right (0 = enabled, 1 = disabled).
namespace TEN::Entities::Creatures::TR3
{
constexpr auto CLAW_MUTANT_CLAW_ATTACK_DAMAGE = 100;
constexpr auto CLAW_MUTANT_PLASMA_ATTACK_DAMAGE = 200;
constexpr auto CLAW_MUTANT_WALK_CHANCE = 1 / 64.0f;
constexpr auto CLAW_MUTANT_IDLE_CLAW_ATTACK_RANGE = SQUARE(BLOCK(1.5f));
constexpr auto CLAW_MUTANT_IDLE_DUAL_CLAW_ATTACK_RANGE = SQUARE(BLOCK(1.5f));
constexpr auto CLAW_MUTANT_WALK_CLAW_ATTACK_RANGE = SQUARE(BLOCK(1));
constexpr auto CLAW_MUTANT_RUN_CLAW_ATTACK_RANGE = SQUARE(BLOCK(2));
constexpr auto CLAW_MUTANT_PLASMA_ATTACK_RANGE = SQUARE(BLOCK(3));
constexpr auto CLAW_MUTANT_PLASMA_VELOCITY = 250;
constexpr auto CLAW_MUTANT_WALK_TURN_RATE_MAX = ANGLE(3.0f);
constexpr auto CLAW_MUTANT_RUN_TURN_RATE_MAX = ANGLE(4.0f);
const auto ClawMutantLeftBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 7);
const auto ClawMutantRightBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 4);
const auto ClawMutantTailBite = BiteInfo(Vector3(-32.0f, -16.0f, -119.0f), 13);
enum ClawMutantState
{
CLAW_MUTANT_STATE_IDLE = 0,
CLAW_MUTANT_STATE_WALK = 1,
CLAW_MUTANT_STATE_RUN = 2,
CLAW_MUTANT_STATE_RUN_CLAW_ATTACK = 3,
CLAW_MUTANT_STATE_WALK_CLAW_ATTACK_LEFT = 4,
CLAW_MUTANT_STATE_WALK_CLAW_ATTACK_RIGHT = 5,
CLAW_MUTANT_STATE_IDLE_CLAW_ATTACK_LEFT = 6,
CLAW_MUTANT_STATE_IDLE_CLAW_ATTACK_RIGHT = 7,
CLAW_MUTANT_STATE_DEATH = 8,
CLAW_MUTANT_STATE_IDLE_DUAL_CLAW_ATTACK = 9,
CLAW_MUTANT_STATE_PLASMA_ATTACK = 10
};
enum ClawMutantAnim
{
CLAW_MUTANT_ANIM_IDLE = 0,
CLAW_MUTANT_ANIM_IDLE_TO_WALK_LEFT = 1,
CLAW_MUTANT_ANIM_IDLE_TO_RUN_LEFT = 2,
CLAW_MUTANT_ANIM_WALK_TO_IDLE_RIGHT = 3,
CLAW_MUTANT_ANIM_WALK_TO_IDLE_LEFT = 4,
CLAW_MUTANT_ANIM_RUN_TO_IDLE_RIGHT = 5,
CLAW_MUTANT_ANIM_RUN_TO_IDLE_LEFT = 6,
CLAW_MUTANT_ANIM_WALK = 7,
CLAW_MUTANT_ANIM_WALK_TO_RUN = 8,
CLAW_MUTANT_ANIM_RUN = 9,
CLAW_MUTANT_ANIM_RUN_TO_WALK_LEFT = 10,
CLAW_MUTANT_ANIM_RUN_TO_WALK_RIGHT = 11,
CLAW_MUTANT_ANIM_RUN_CLAW_ATTACK = 12,
CLAW_MUTANT_ANIM_RUN_CLAW_ATTACK_CANCEL = 13,
CLAW_MUTANT_ANIM_IDLE_CLAW_ATTACK_LEFT = 14,
CLAW_MUTANT_ANIM_IDLE_CLAW_ATTACK_RIGHT = 15,
CLAW_MUTANT_ANIM_IDLE_DUAL_CLAW_ATTACK = 16,
CLAW_MUTANT_ANIM_WALK_CLAW_ATTACK_LEFT = 17,
CLAW_MUTANT_ANIM_WALK_CLAW_ATTACK_RIGHT = 18,
CLAW_MUTANT_ANIM_PLASMA_ATTACK = 19,
CLAW_MUTANT_ANIM_DEATH = 20
};
static void SpawnClawMutantPlasma(int itemNumber)
{
auto& plasma = *GetFreeParticle();
plasma.on = true;
plasma.sB = 255;
plasma.sG = 48 + (GetRandomControl() & 31);
plasma.sR = 48;
plasma.dB = 192 + (GetRandomControl() & 63);
plasma.dG = 128 + (GetRandomControl() & 63);
plasma.dR = 32;
plasma.colFadeSpeed = 12 + (GetRandomControl() & 3);
plasma.fadeToBlack = 8;
plasma.sLife =
plasma.life = (GetRandomControl() & 7) + 24;
plasma.blendMode = BLEND_MODES::BLENDMODE_ADDITIVE;
plasma.extras = 0;
plasma.dynamic = -1;
plasma.x = ((GetRandomControl() & 15) - 8);
plasma.y = 0;
plasma.z = ((GetRandomControl() & 15) - 8);
plasma.xVel = ((GetRandomControl() & 31) - 16);
plasma.yVel = (GetRandomControl() & 15) + 16;
plasma.zVel = ((GetRandomControl() & 31) - 16);
plasma.friction = 3;
if (Random::TestProbability(1 / 2.0f))
{
plasma.flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF | SP_ITEM | SP_NODEATTACH;
plasma.rotAng = GetRandomControl() & 4095;
if (Random::TestProbability(1 / 2.0f))
{
plasma.rotAdd = -(GetRandomControl() & 15) - 16;
}
else
{
plasma.rotAdd = (GetRandomControl() & 15) + 16;
}
}
else
{
plasma.flags = SP_SCALE | SP_DEF | SP_EXPDEF | SP_ITEM | SP_NODEATTACH;
}
plasma.gravity = (GetRandomControl() & 31) + 16;
plasma.maxYvel = (GetRandomControl() & 7) + 16;
plasma.fxObj = itemNumber;
plasma.nodeNumber = ParticleNodeOffsetIDs::NodeClawMutantPlasma;
plasma.spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex;
plasma.scalar = 1;
int size = (GetRandomControl() & 31) + 64;
plasma.size =
plasma.sSize = size;
plasma.dSize = size / 4;
}
static void SpawnClawMutantPlasmaBall(ItemInfo& item)
{
const auto& creature = *GetCreatureInfo(&item);
int plasmaBall = CreateNewEffect(item.RoomNumber);
if (plasmaBall == NO_ITEM)
return;
auto enemyPos = creature.Enemy->Pose.Position;
if (creature.Enemy->IsLara() && GetLaraInfo(creature.Enemy)->Control.IsLow)
{
enemyPos.y -= CLICK(1);
}
else
{
enemyPos.y -= CLICK(2);
}
auto& fx = EffectList[plasmaBall];
auto jointPos = GetJointPosition(item, ClawMutantTailBite.meshNum, ClawMutantTailBite.Position);
auto orient = Geometry::GetOrientToPoint(jointPos.ToVector3(), enemyPos.ToVector3());
fx.pos.Position = jointPos;
fx.pos.Orientation.x = orient.x;
fx.pos.Orientation.y = orient.y;
fx.objectNumber = ID_ENERGY_BUBBLES;
fx.color = Vector4::Zero;
fx.speed = CLAW_MUTANT_PLASMA_VELOCITY;
fx.flag2 = CLAW_MUTANT_PLASMA_ATTACK_DAMAGE;
fx.flag1 = (int)MissileType::ClawMutantPlasma;
fx.fallspeed = 0;
}
static void SpawnMutantPlasmaLight(ItemInfo& item)
{
int bright = item.Animation.FrameNumber - GetAnimData(item).frameBase;
if (bright > 16)
{
bright = GetAnimData(item).frameBase + 28 + 16 - item.Animation.FrameNumber;
if (bright > 16)
bright = 16;
}
if (bright > 0)
{
auto pos = GetJointPosition(item, 13, Vector3i(-32, -16, -192));
int rnd = GetRandomControl();
byte r, g, b;
b = 31 - ((rnd / 16) & 3);
g = 24 - ((rnd / 64) & 3);
r = rnd & 7;
r = (r * bright) / 16;
g = (g * bright) / 16;
b = (b * bright) / 16;
TriggerDynamicLight(pos.x, pos.y, pos.z, bright, r, g, b);
}
}
static void DamageTargetWithClaw(ItemInfo& source, ItemInfo& target)
{
if (source.ItemFlags[5] == 0 && source.TouchBits.Test(ClawMutantLeftBite.meshNum))
{
DoDamage(&target, CLAW_MUTANT_CLAW_ATTACK_DAMAGE / 2);
CreatureEffect2(&source, ClawMutantLeftBite, 10, source.Pose.Orientation.y, DoBloodSplat);
source.ItemFlags[5] = 1;
}
if (source.ItemFlags[6] == 0 && source.TouchBits.Test(ClawMutantRightBite.meshNum))
{
DoDamage(&target, CLAW_MUTANT_CLAW_ATTACK_DAMAGE / 2);
CreatureEffect2(&source, ClawMutantRightBite, 10, source.Pose.Orientation.y, DoBloodSplat);
source.ItemFlags[6] = 1;
}
}
void SpawnClawMutantPlasmaFlameBall(int fxNumber, const Vector3& vel, const Vector3& offset, float life)
{
auto& plasma = *GetFreeParticle();
plasma.on = true;
plasma.sB = 255;
plasma.sG = 48 + (GetRandomControl() & 31);
plasma.sR = 48;
plasma.dB = 192 + (GetRandomControl() & 63);
plasma.dG = 128 + (GetRandomControl() & 63);
plasma.dR = 32;
plasma.colFadeSpeed = 12 + (GetRandomControl() & 3);
plasma.fadeToBlack = 8;
plasma.sLife =
plasma.life = (GetRandomControl() & 7) + life;
plasma.blendMode = BLENDMODE_ADDITIVE;
plasma.extras = 0;
plasma.dynamic = -1;
plasma.x = offset.x + ((GetRandomControl() & 15) - 8);
plasma.y = 0;
plasma.z = offset.z + ((GetRandomControl() & 15) - 8);
plasma.xVel = vel.x;
plasma.yVel = vel.y;
plasma.zVel = vel.z;
plasma.friction = 5;
if (Random::TestProbability(1 / 2.0f))
{
plasma.flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF | SP_FX;
plasma.rotAng = GetRandomControl() & 4095;
if (Random::TestProbability(1 / 2.0f))
{
plasma.rotAdd = -(GetRandomControl() & 15) - 16;
}
else
{
plasma.rotAdd = (GetRandomControl() & 15) + 16;
}
}
else
{
plasma.flags = SP_SCALE | SP_DEF | SP_EXPDEF | SP_FX;
}
plasma.fxObj = fxNumber;
plasma.spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex;
plasma.scalar = 1;
plasma.gravity =
plasma.maxYvel = 0;
int size = (GetRandomControl() & 31) + 64;
plasma.size =
plasma.sSize = size;
plasma.dSize /= 16;
plasma.yVel = (GetRandomControl() & 511) - 256;
plasma.xVel *= 2;
plasma.zVel *= 2;
plasma.scalar = 2;
plasma.friction = 85;
plasma.gravity = 22;
}
void ClawMutantControl(short itemNumber)
{
if (!CreatureActive(itemNumber))
return;
auto& item = g_Level.Items[itemNumber];
auto& object = Objects[item.ObjectNumber];
auto& creature = *GetCreatureInfo(&item);
short headingAngle = 0;
auto extraHeadRot = EulerAngles::Zero;
auto extraTorsoRot = EulerAngles::Zero;
if (item.HitPoints <= 0)
{
if (item.Animation.ActiveState != CLAW_MUTANT_STATE_DEATH)
SetAnimation(&item, CLAW_MUTANT_ANIM_DEATH);
int frameEnd = GetAnimData(item, CLAW_MUTANT_ANIM_DEATH).frameEnd;
if (item.Animation.FrameNumber >= frameEnd)
CreatureDie(itemNumber, true);
}
else
{
if (item.AIBits)
GetAITarget(&creature);
AI_INFO ai;
CreatureAIInfo(&item, &ai);
GetCreatureMood(&item, &ai, ai.zoneNumber == ai.enemyZone ? true : false);
CreatureMood(&item, &ai, ai.zoneNumber == ai.enemyZone ? true : false);
headingAngle = CreatureTurn(&item, creature.MaxTurn);
bool canShoot = (Targetable(&item, &ai) &&
((ai.distance > CLAW_MUTANT_PLASMA_ATTACK_RANGE && !item.ItemFlags[0]) || ai.zoneNumber != ai.enemyZone));
switch (item.Animation.ActiveState)
{
case CLAW_MUTANT_STATE_IDLE:
item.ItemFlags[5] = 0;
item.ItemFlags[6] = 0;
creature.MaxTurn = 0;
if (item.AIBits & GUARD)
{
extraHeadRot.y = AIGuard(&creature);
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE;
break;
}
else if (item.AIBits & PATROL1)
{
extraHeadRot.y = 0;
item.Animation.TargetState = CLAW_MUTANT_STATE_WALK;
}
else if (creature.Mood == MoodType::Escape)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_RUN;
}
else if (ai.bite && ai.distance < CLAW_MUTANT_IDLE_CLAW_ATTACK_RANGE)
{
extraTorsoRot.x = ai.xAngle;
extraTorsoRot.y = ai.angle;
if (ai.angle < 0)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE_CLAW_ATTACK_LEFT;
}
else
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE_CLAW_ATTACK_RIGHT;
}
}
else if (ai.bite && ai.distance < CLAW_MUTANT_IDLE_DUAL_CLAW_ATTACK_RANGE)
{
extraTorsoRot.x = ai.xAngle;
extraTorsoRot.y = ai.angle;
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE_DUAL_CLAW_ATTACK;
}
else if (canShoot)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_PLASMA_ATTACK;
}
else if (creature.Mood == MoodType::Bored)
{
if (Random::TestProbability(CLAW_MUTANT_WALK_CHANCE))
item.Animation.TargetState = CLAW_MUTANT_STATE_WALK;
}
else if (item.Animation.RequiredState != NO_STATE)
{
item.Animation.TargetState = item.Animation.RequiredState;
}
else
{
item.Animation.TargetState = CLAW_MUTANT_STATE_RUN;
}
break;
case CLAW_MUTANT_STATE_WALK:
item.ItemFlags[5] = 0;
item.ItemFlags[6] = 0;
creature.MaxTurn = CLAW_MUTANT_WALK_TURN_RATE_MAX;
if (ai.ahead)
extraHeadRot.y = ai.angle;
if (item.AIBits & PATROL1)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_WALK;
extraHeadRot.y = 0;
}
else if (ai.bite && ai.distance < CLAW_MUTANT_WALK_CLAW_ATTACK_RANGE)
{
if (ai.angle < 0)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_WALK_CLAW_ATTACK_LEFT;
}
else
{
item.Animation.TargetState = CLAW_MUTANT_STATE_WALK_CLAW_ATTACK_RIGHT;
}
}
else if (canShoot)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE;
}
else if (creature.Mood == MoodType::Escape || creature.Mood == MoodType::Attack)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_RUN;
}
break;
case CLAW_MUTANT_STATE_RUN:
item.ItemFlags[5] = 0;
item.ItemFlags[6] = 0;
creature.MaxTurn = CLAW_MUTANT_RUN_TURN_RATE_MAX;
if (ai.ahead)
extraHeadRot.y = ai.angle;
if (item.AIBits & GUARD)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE;
}
else if (creature.Mood == MoodType::Bored)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE;
}
else if (creature.Flags != 0 && ai.ahead)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE;
}
else if (ai.bite && ai.distance < CLAW_MUTANT_RUN_CLAW_ATTACK_RANGE)
{
if (creature.Enemy != nullptr && creature.Enemy->Animation.Velocity.z == 0.0f)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE;
}
else
{
item.Animation.TargetState = CLAW_MUTANT_STATE_RUN_CLAW_ATTACK;
}
}
else if (canShoot)
{
item.Animation.TargetState = CLAW_MUTANT_STATE_IDLE;
}
break;
case CLAW_MUTANT_STATE_WALK_CLAW_ATTACK_LEFT:
case CLAW_MUTANT_STATE_WALK_CLAW_ATTACK_RIGHT:
case CLAW_MUTANT_STATE_RUN_CLAW_ATTACK:
case CLAW_MUTANT_STATE_IDLE_DUAL_CLAW_ATTACK:
case CLAW_MUTANT_STATE_IDLE_CLAW_ATTACK_LEFT:
case CLAW_MUTANT_STATE_IDLE_CLAW_ATTACK_RIGHT:
if (ai.ahead)
{
extraTorsoRot.x = ai.xAngle;
extraTorsoRot.y = ai.angle;
}
DamageTargetWithClaw(item, *creature.Enemy);
break;
case CLAW_MUTANT_STATE_PLASMA_ATTACK:
if (ai.ahead)
{
extraTorsoRot.x = ai.xAngle;
extraTorsoRot.y = ai.angle;
}
if (item.Animation.FrameNumber == GetFrameIndex(&item, 0) && Random::TestProbability(1 / 4.0f) == 0)
item.ItemFlags[0] = 1;
if (item.Animation.FrameNumber < GetFrameIndex(&item, 28))
{
SpawnClawMutantPlasma(itemNumber);
}
else if (item.Animation.FrameNumber == GetFrameIndex(&item, 28))
{
SpawnClawMutantPlasmaBall(item);
}
SpawnMutantPlasmaLight(item);
break;
}
}
CreatureJoint(&item, 0, extraTorsoRot.x);
CreatureJoint(&item, 1, extraTorsoRot.y);
CreatureJoint(&item, 2, extraHeadRot.y);
CreatureAnimation(itemNumber, headingAngle, 0);
}
}

View file

@ -0,0 +1,7 @@
#pragma once
namespace TEN::Entities::Creatures::TR3
{
void ClawMutantControl(short itemNumber);
void SpawnClawMutantPlasmaFlameBall(int fxNumber, const Vector3& vel, const Vector3& offset, float life);
}

View file

@ -17,11 +17,11 @@
#include "Objects/TR3/Entity/WaspMutant.h" // OK #include "Objects/TR3/Entity/WaspMutant.h" // OK
#include "Objects/TR3/Entity/tr3_tony.h" // OK #include "Objects/TR3/Entity/tr3_tony.h" // OK
#include "Objects/TR3/Entity/tr3_civvy.h" // OK #include "Objects/TR3/Entity/tr3_civvy.h" // OK
#include "Objects/TR3/Entity/tr3_claw_mutant.h" // OK
#include "Objects/TR3/Entity/tr3_cobra.h" // OK #include "Objects/TR3/Entity/tr3_cobra.h" // OK
#include "Objects/TR3/Entity/tr3_fish_emitter.h" // OK #include "Objects/TR3/Entity/tr3_fish_emitter.h" // OK
#include "Objects/TR3/Entity/tr3_flamethrower.h" // OK #include "Objects/TR3/Entity/tr3_flamethrower.h" // OK
#include "Objects/TR3/Entity/tr3_monkey.h" // OK #include "Objects/TR3/Entity/tr3_monkey.h" // OK
#include "Objects/TR3/Entity/tr3_mp_gun.h" // OK #include "Objects/TR3/Entity/tr3_mp_gun.h" // OK
#include "Objects/TR3/Entity/tr3_mp_stick.h" // OK #include "Objects/TR3/Entity/tr3_mp_stick.h" // OK
#include "Objects/TR3/Entity/tr3_raptor.h" // OK #include "Objects/TR3/Entity/tr3_raptor.h" // OK
@ -381,6 +381,22 @@ static void StartEntity(ObjectInfo* obj)
obj->SetBoneRotationFlags(1, ROT_X | ROT_Z); obj->SetBoneRotationFlags(1, ROT_X | ROT_Z);
obj->SetupHitEffect(); obj->SetupHitEffect();
} }
obj = &Objects[ID_CLAW_MUTANT];
if (obj->loaded)
{
obj->initialise = InitialiseCreature;
obj->control = ClawMutantControl;
obj->collision = CreatureCollision;
obj->shadowType = ShadowMode::All;
obj->HitPoints = 130;
obj->intelligent = true;
obj->radius = 204;
obj->pivotLength = 0;
obj->SetBoneRotationFlags(0, ROT_X | ROT_Z);
obj->SetBoneRotationFlags(7, ROT_Y);
obj->SetupHitEffect();
}
} }
static void StartObject(ObjectInfo* obj) static void StartObject(ObjectInfo* obj)

View file

@ -228,8 +228,8 @@ enum GAME_OBJECT_ID : short
ID_BOSS_SHIELD, ID_BOSS_SHIELD,
ID_BOSS_EXPLOSION_SHOCKWAVE, ID_BOSS_EXPLOSION_SHOCKWAVE,
ID_BOSS_EXPLOSION_RING, ID_BOSS_EXPLOSION_RING,
ID_CLAW_MUTANT = 293,
ID_WASP_MUTANT = 294, ID_WASP_MUTANT,
ID_SPRINGBOARD = 320, ID_SPRINGBOARD = 320,
ID_ROLLING_SPINDLE, ID_ROLLING_SPINDLE,

View file

@ -756,8 +756,7 @@ namespace TEN::Renderer
for (fxNum = r->fxNumber; fxNum != NO_ITEM; fxNum = EffectList[fxNum].nextFx) for (fxNum = r->fxNumber; fxNum != NO_ITEM; fxNum = EffectList[fxNum].nextFx)
{ {
FX_INFO *fx = &EffectList[fxNum]; FX_INFO *fx = &EffectList[fxNum];
if (fx->objectNumber < 0 || fx->color.w <= 0)
if (fx->objectNumber < 0)
continue; continue;
ObjectInfo *obj = &Objects[fx->objectNumber]; ObjectInfo *obj = &Objects[fx->objectNumber];

View file

@ -235,6 +235,7 @@ The following constants are inside ObjID.
BOSS_SHIELD BOSS_SHIELD
BOSS_EXPLOSION_SHOCKWAVE BOSS_EXPLOSION_SHOCKWAVE
BOSS_EXPLOSION_RING BOSS_EXPLOSION_RING
CLAW_MUTANT
WASP_MUTANT WASP_MUTANT
SPRINGBOARD SPRINGBOARD
ROLLING_SPINDLE ROLLING_SPINDLE
@ -1406,6 +1407,7 @@ static const std::unordered_map<std::string, GAME_OBJECT_ID> kObjIDs {
{ "BOSS_SHIELD", ID_BOSS_SHIELD }, { "BOSS_SHIELD", ID_BOSS_SHIELD },
{ "BOSS_EXPLOSION_SHOCKWAVE", ID_BOSS_EXPLOSION_SHOCKWAVE }, { "BOSS_EXPLOSION_SHOCKWAVE", ID_BOSS_EXPLOSION_SHOCKWAVE },
{ "BOSS_EXPLOSION_RING", ID_BOSS_EXPLOSION_RING }, { "BOSS_EXPLOSION_RING", ID_BOSS_EXPLOSION_RING },
{ "CLAW_MUTANT", ID_CLAW_MUTANT },
{ "WASP_MUTANT", ID_WASP_MUTANT }, { "WASP_MUTANT", ID_WASP_MUTANT },
{ "SPRINGBOARD", ID_SPRINGBOARD }, { "SPRINGBOARD", ID_SPRINGBOARD },
{ "ROLLING_SPINDLE", ID_ROLLING_SPINDLE }, { "ROLLING_SPINDLE", ID_ROLLING_SPINDLE },

View file

@ -321,6 +321,7 @@ CALL gen.bat</Command>
<ClInclude Include="Objects\TR3\Entity\PunaBoss.h" /> <ClInclude Include="Objects\TR3\Entity\PunaBoss.h" />
<ClInclude Include="Objects\TR3\Entity\Shiva.h" /> <ClInclude Include="Objects\TR3\Entity\Shiva.h" />
<ClInclude Include="Objects\TR3\Entity\SophiaLeigh.h" /> <ClInclude Include="Objects\TR3\Entity\SophiaLeigh.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_claw_mutant.h" />
<ClInclude Include="Objects\TR3\Entity\WaspMutant.h" /> <ClInclude Include="Objects\TR3\Entity\WaspMutant.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_civvy.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_civvy.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_cobra.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_cobra.h" />
@ -757,6 +758,7 @@ CALL gen.bat</Command>
<ClCompile Include="Objects\TR3\Entity\Shiva.cpp" /> <ClCompile Include="Objects\TR3\Entity\Shiva.cpp" />
<ClCompile Include="Objects\TR3\Entity\SophiaLeigh.cpp" /> <ClCompile Include="Objects\TR3\Entity\SophiaLeigh.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_civvy.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_civvy.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_claw_mutant.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_cobra.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_cobra.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_fish_emitter.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_fish_emitter.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_flamethrower.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_flamethrower.cpp" />

View file

@ -329,6 +329,7 @@ CALL gen.bat</Command>
<ClInclude Include="Objects\TR3\Entity\tr3_fish_emitter.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_fish_emitter.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_flamethrower.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_flamethrower.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_monkey.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_monkey.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_claw_mutant.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_mp_gun.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_mp_gun.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_mp_stick.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_mp_stick.h" />
<ClInclude Include="Objects\TR3\Entity\tr3_raptor.h" /> <ClInclude Include="Objects\TR3\Entity\tr3_raptor.h" />
@ -760,6 +761,7 @@ CALL gen.bat</Command>
<ClCompile Include="Objects\TR3\Entity\tr3_fish_emitter.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_fish_emitter.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_flamethrower.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_flamethrower.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_monkey.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_monkey.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_claw_mutant.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_mp_gun.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_mp_gun.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_mp_stick.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_mp_stick.cpp" />
<ClCompile Include="Objects\TR3\Entity\tr3_raptor.cpp" /> <ClCompile Include="Objects\TR3\Entity\tr3_raptor.cpp" />