mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-05-08 11:38:08 +03:00
Damocles sword backport (#846)
* Port Damocles sword * Simplify; play sound * Simplify * Add GenerateVector3InBox() function; update Damocles sword blood generation * Rename local variable * Organise include * Fix translation? * Use GetShortestAngle() * Use bools * Add comment * Don't use item->Floor * Fix wrong sign * Rename function * Fix damocles sword translation * Update range check * Cleanup * Fix signs * Fix translation * Demagic impale depth; increase blood * Shake camera upon impact * Update comment * Cleanup * Use ItemFlags * Cleanup * Update comment * Simplify * Optimise Co-authored-by: Sezz <sezzary@outlook.com>
This commit is contained in:
parent
db6df48184
commit
62655f51a3
11 changed files with 203 additions and 33 deletions
|
@ -1033,7 +1033,8 @@ void DoLotsOfBlood(int x, int y, int z, int speed, short direction, short roomNu
|
|||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
DoBloodSplat(x + 256 - (GetRandomControl() * 512 / 0x8000),
|
||||
DoBloodSplat(
|
||||
x + 256 - (GetRandomControl() * 512 / 0x8000),
|
||||
y + 256 - (GetRandomControl() * 512 / 0x8000),
|
||||
z + 256 - (GetRandomControl() * 512 / 0x8000),
|
||||
speed, direction, roomNumber);
|
||||
|
|
|
@ -54,19 +54,19 @@
|
|||
// NOTE: Previously phd_RotBoundingBoxNoPersp().
|
||||
void GameBoundingBox::RotateNoPersp(const EulerAngles& orient, const GameBoundingBox& bounds)
|
||||
{
|
||||
auto world = orient.ToRotationMatrix();
|
||||
auto bMin = Vector3(bounds.X1, bounds.Y1, bounds.Z1);
|
||||
auto bMax = Vector3(bounds.X2, bounds.Y2, bounds.Z2);
|
||||
auto rotMatrix = orient.ToRotationMatrix();
|
||||
auto boxMin = Vector3(bounds.X1, bounds.Y1, bounds.Z1);
|
||||
auto boxMax = Vector3(bounds.X2, bounds.Y2, bounds.Z2);
|
||||
|
||||
bMin = Vector3::Transform(bMin, world);
|
||||
bMax = Vector3::Transform(bMax, world);
|
||||
boxMin = Vector3::Transform(boxMin, rotMatrix);
|
||||
boxMax = Vector3::Transform(boxMax, rotMatrix);
|
||||
|
||||
this->X1 = (int)round(bMin.x);
|
||||
this->X2 = (int)round(bMax.x);
|
||||
this->Y1 = (int)round(bMin.y);
|
||||
this->Y2 = (int)round(bMax.y);
|
||||
this->Z1 = (int)round(bMin.z);
|
||||
this->Z2 = (int)round(bMax.z);
|
||||
this->X1 = (int)round(boxMin.x);
|
||||
this->X2 = (int)round(boxMax.x);
|
||||
this->Y1 = (int)round(boxMin.y);
|
||||
this->Y2 = (int)round(boxMax.y);
|
||||
this->Z1 = (int)round(boxMin.z);
|
||||
this->Z2 = (int)round(boxMax.z);
|
||||
}
|
||||
|
||||
BoundingOrientedBox GameBoundingBox::ToBoundingOrientedBox(const Pose& pose) const
|
||||
|
|
|
@ -50,6 +50,18 @@ namespace TEN::Math::Random
|
|||
return (vector * length);
|
||||
}
|
||||
|
||||
Vector3 GenerateVector3InBox(const BoundingOrientedBox& box)
|
||||
{
|
||||
auto rotMatrix = Matrix::CreateFromQuaternion(box.Orientation);
|
||||
auto vector = Vector3(
|
||||
GenerateFloat(-box.Extents.x, box.Extents.x),
|
||||
GenerateFloat(-box.Extents.y, box.Extents.y),
|
||||
GenerateFloat(-box.Extents.z, box.Extents.z)
|
||||
);
|
||||
|
||||
return Vector3::Transform(vector, rotMatrix);
|
||||
}
|
||||
|
||||
bool TestProbability(float probability)
|
||||
{
|
||||
probability = std::clamp(probability, 0.0f, 1.0f);
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace TEN::Math::Random
|
|||
Vector2 GenerateVector2(float length = 1.0f);
|
||||
Vector3 GenerateVector3(float length = 1.0f);
|
||||
Vector3 GenerateVector3InCone(const Vector3& direction, float semiangleInDeg, float length = 1.0f);
|
||||
Vector3 GenerateVector3InBox(const BoundingOrientedBox& box);
|
||||
|
||||
bool TestProbability(float probability);
|
||||
}
|
||||
|
|
138
TombEngine/Objects/TR1/Trap/DamoclesSword.cpp
Normal file
138
TombEngine/Objects/TR1/Trap/DamoclesSword.cpp
Normal file
|
@ -0,0 +1,138 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR1/Trap/DamoclesSword.h"
|
||||
|
||||
#include "Game/camera.h"
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/effects/effects.h"
|
||||
#include "Game/Lara/lara.h"
|
||||
#include "Math/Math.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math;
|
||||
|
||||
namespace TEN::Entities::Traps::TR1
|
||||
{
|
||||
constexpr auto DAMOCLES_SWORD_DAMAGE = 100;
|
||||
|
||||
constexpr auto DAMOCLES_SWORD_VELOCITY_MIN = BLOCK(1.0f / 20);
|
||||
constexpr auto DAMOCLES_SWORD_VELOCITY_MAX = BLOCK(1.0f / 8);
|
||||
|
||||
constexpr auto DAMOCLES_SWORD_IMPALE_DEPTH = -BLOCK(1.0f / 8);
|
||||
constexpr auto DAMOCLES_SWORD_ACTIVATE_RANGE_2D = BLOCK(3.0f / 2);
|
||||
constexpr auto DAMOCLES_SWORD_ACTIVATE_RANGE_VERTICAL = BLOCK(3);
|
||||
|
||||
const auto DAMOCLES_SWORD_TURN_RATE_MAX = ANGLE(5.0f);
|
||||
|
||||
void SetupDamoclesSword(ObjectInfo* object)
|
||||
{
|
||||
object->initialise = InitialiseDamoclesSword;
|
||||
object->control = ControlDamoclesSword;
|
||||
object->collision = CollideDamoclesSword;
|
||||
//object->shadowSize = UNIT_SHADOW;
|
||||
object->savePosition = true;
|
||||
object->saveAnim = true;
|
||||
object->saveFlags = true;
|
||||
}
|
||||
|
||||
void InitialiseDamoclesSword(short itemNumber)
|
||||
{
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
|
||||
item.Pose.Orientation.y = Random::GenerateAngle();
|
||||
item.Animation.Velocity.y = DAMOCLES_SWORD_VELOCITY_MIN;
|
||||
item.ItemFlags[0] = Random::GenerateAngle(-DAMOCLES_SWORD_TURN_RATE_MAX, DAMOCLES_SWORD_TURN_RATE_MAX); // NOTE: ItemFlags[0] stores random turn rate.
|
||||
}
|
||||
|
||||
void ControlDamoclesSword(short itemNumber)
|
||||
{
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
const auto& laraItem = *LaraItem;
|
||||
|
||||
// Fall toward player.
|
||||
if (item.Animation.IsAirborne)
|
||||
{
|
||||
item.Pose.Orientation.y += item.ItemFlags[0]; // NOTE: ItemFlags[0] stores random turn rate.
|
||||
|
||||
// Calculate vertical velocity.
|
||||
item.Animation.Velocity.y += (item.Animation.Velocity.y < DAMOCLES_SWORD_VELOCITY_MAX) ? GRAVITY : 1.0f;
|
||||
|
||||
// Translate sword.
|
||||
short headingAngle = Geometry::GetOrientToPoint(item.Pose.Position.ToVector3(), laraItem.Pose.Position.ToVector3()).y;
|
||||
TranslateItem(&item, headingAngle, item.ItemFlags[1], item.Animation.Velocity.y); // NOTE: ItemFlags[1] stores calculated forward velocity.
|
||||
|
||||
int vPos = item.Pose.Position.y;
|
||||
auto pointColl = GetCollision(&item);
|
||||
|
||||
// Impale floor.
|
||||
if ((pointColl.Position.Floor - vPos) <= DAMOCLES_SWORD_IMPALE_DEPTH)
|
||||
{
|
||||
SoundEffect(SFX_TR1_DAMOCLES_ROOM_SWORD, &item.Pose);
|
||||
float distance = Vector3::Distance(item.Pose.Position.ToVector3(), Camera.pos.ToVector3());
|
||||
Camera.bounce = -((BLOCK(7.0f / 2) - distance) * abs(item.Animation.Velocity.y)) / BLOCK(7.0f / 2);
|
||||
|
||||
item.Animation.IsAirborne = false;
|
||||
item.Status = ItemStatus::ITEM_DEACTIVATED;
|
||||
item.ItemFlags[0] = 0; // NOTE: ItemFlags[0] stores random turn rate.
|
||||
|
||||
RemoveActiveItem(itemNumber);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Scan for player.
|
||||
if (item.Pose.Position.y < GetCollision(&item).Position.Floor)
|
||||
{
|
||||
item.Pose.Orientation.y += item.ItemFlags[0]; // NOTE: ItemFlags[0] stores random turn rate.
|
||||
|
||||
// Check vertical position to player.
|
||||
if (item.Pose.Position.y >= laraItem.Pose.Position.y)
|
||||
return;
|
||||
|
||||
// Check vertical distance.
|
||||
float distanceV = laraItem.Pose.Position.y - item.Pose.Position.y;
|
||||
if (distanceV > DAMOCLES_SWORD_ACTIVATE_RANGE_VERTICAL)
|
||||
return;
|
||||
|
||||
// Check 2D distance.
|
||||
float distance2D = Vector2i::Distance(
|
||||
Vector2i(item.Pose.Position.x, item.Pose.Position.z),
|
||||
Vector2i(laraItem.Pose.Position.x, laraItem.Pose.Position.z));
|
||||
if (distance2D > DAMOCLES_SWORD_ACTIVATE_RANGE_2D)
|
||||
return;
|
||||
|
||||
// Drop sword.
|
||||
// TODO: Have 2D velocity also take vertical distance into account.
|
||||
item.Animation.IsAirborne = true;
|
||||
item.ItemFlags[1] = distance2D / 32; // NOTE: ItemFlags[1] stores calculated forward velocity.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CollideDamoclesSword(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
|
||||
{
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
|
||||
if (!TestBoundsCollide(&item, laraItem, coll->Setup.Radius))
|
||||
return;
|
||||
|
||||
if (coll->Setup.EnableObjectPush)
|
||||
ItemPushItem(&item, laraItem, coll, false, true);
|
||||
|
||||
if (item.Animation.IsAirborne)
|
||||
{
|
||||
DoDamage(laraItem, DAMOCLES_SWORD_DAMAGE);
|
||||
|
||||
auto bloodBounds = GameBoundingBox(laraItem).ToBoundingOrientedBox(laraItem->Pose);
|
||||
auto bloodPos = Vector3i(Random::GenerateVector3InBox(bloodBounds) + bloodBounds.Center);
|
||||
|
||||
auto orientToSword = Geometry::GetOrientToPoint(laraItem->Pose.Position.ToVector3(), item.Pose.Position.ToVector3());
|
||||
short randAngleOffset = Random::GenerateAngle(ANGLE(-11.25f), ANGLE(11.25f));
|
||||
short bloodHeadingAngle = orientToSword.y + randAngleOffset;
|
||||
|
||||
DoLotsOfBlood(bloodPos.x, bloodPos.y, bloodPos.z, laraItem->Animation.Velocity.z, bloodHeadingAngle, laraItem->RoomNumber, 20);
|
||||
}
|
||||
}
|
||||
}
|
14
TombEngine/Objects/TR1/Trap/DamoclesSword.h
Normal file
14
TombEngine/Objects/TR1/Trap/DamoclesSword.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
struct ObjectInfo;
|
||||
|
||||
namespace TEN::Entities::Traps::TR1
|
||||
{
|
||||
void SetupDamoclesSword(ObjectInfo* object);
|
||||
void InitialiseDamoclesSword(short itemNumber);
|
||||
|
||||
void ControlDamoclesSword(short itemNumber);
|
||||
void CollideDamoclesSword(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
}
|
|
@ -20,7 +20,11 @@
|
|||
#include "Objects/TR1/Entity/tr1_winged_mutant.h"
|
||||
#include "Objects/Utils/object_helper.h"
|
||||
|
||||
// Traps
|
||||
#include "Objects/TR1/Trap/DamoclesSword.h"
|
||||
|
||||
using namespace TEN::Entities::Creatures::TR1;
|
||||
using namespace TEN::Entities::Traps::TR1;
|
||||
|
||||
static void StartEntity(ObjectInfo* obj)
|
||||
{
|
||||
|
@ -197,7 +201,6 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->SetBoneRotation(1, ROT_Y); // torso
|
||||
obj->SetBoneRotation(2, ROT_Y); // head
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void StartObject(ObjectInfo* obj)
|
||||
|
@ -213,7 +216,9 @@ static void StartObject(ObjectInfo* obj)
|
|||
|
||||
static void StartTrap(ObjectInfo* obj)
|
||||
{
|
||||
|
||||
obj = &Objects[ID_DAMOCLES_SWORD];
|
||||
if (obj->loaded)
|
||||
SetupDamoclesSword(obj);
|
||||
}
|
||||
|
||||
static void StartProjectiles(ObjectInfo* obj)
|
||||
|
|
|
@ -8,27 +8,27 @@
|
|||
|
||||
void SpringBoardControl(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto& item = g_Level.Items[itemNumber];
|
||||
|
||||
if (item->Animation.ActiveState == 0 && LaraItem->Pose.Position.y == item->Pose.Position.y &&
|
||||
LaraItem->Pose.Position.x / SECTOR(1) == item->Pose.Position.x / SECTOR(1) &&
|
||||
LaraItem->Pose.Position.z / SECTOR(1) == item->Pose.Position.z / SECTOR(1))
|
||||
if (item.Animation.ActiveState == 0 && LaraItem->Pose.Position.y == item.Pose.Position.y &&
|
||||
(LaraItem->Pose.Position.x / BLOCK(1)) == (item.Pose.Position.x / BLOCK(1)) &&
|
||||
(LaraItem->Pose.Position.z / BLOCK(1)) == (item.Pose.Position.z / BLOCK(1)))
|
||||
{
|
||||
if (LaraItem->HitPoints <= 0)
|
||||
return;
|
||||
|
||||
if (LaraItem->Animation.ActiveState == LS_WALK_BACK || LaraItem->Animation.ActiveState == LS_RUN_BACK)
|
||||
LaraItem->Animation.Velocity.z = -LaraItem->Animation.Velocity.z;
|
||||
if (LaraItem->Animation.ActiveState == LS_WALK_BACK ||
|
||||
LaraItem->Animation.ActiveState == LS_RUN_BACK)
|
||||
{
|
||||
LaraItem->Animation.Velocity.z *= -1;
|
||||
}
|
||||
|
||||
LaraItem->Animation.AnimNumber = LA_FALL_START;
|
||||
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
|
||||
LaraItem->Animation.ActiveState = LS_JUMP_FORWARD;
|
||||
LaraItem->Animation.TargetState = LS_JUMP_FORWARD;
|
||||
SetAnimation(LaraItem, LA_FALL_START);
|
||||
LaraItem->Animation.IsAirborne = true;
|
||||
LaraItem->Animation.Velocity.y = -240.0f;
|
||||
|
||||
item->Animation.TargetState = 1;
|
||||
item.Animation.TargetState = 1;
|
||||
}
|
||||
|
||||
AnimateItem(item);
|
||||
AnimateItem(&item);
|
||||
}
|
||||
|
|
|
@ -64,18 +64,14 @@ void RollingBallControl(short itemNumber)
|
|||
|
||||
if (item->Pose.Position.y > dh)
|
||||
{
|
||||
if (abs(item->Animation.Velocity.y) > 16)
|
||||
if (abs(item->Animation.Velocity.y) > 16.0f)
|
||||
{
|
||||
int distance = sqrt(
|
||||
pow(Camera.pos.x - item->Pose.Position.x, 2) +
|
||||
pow(Camera.pos.y - item->Pose.Position.y, 2) +
|
||||
pow(Camera.pos.z - item->Pose.Position.z, 2));
|
||||
|
||||
float distance = Vector3::Distance(item->Pose.Position.ToVector3(), Camera.pos.ToVector3());
|
||||
if (distance < 16384)
|
||||
{
|
||||
if ((item->TriggerFlags & 1) != 1) // Flag 1 = silent.
|
||||
{
|
||||
Camera.bounce = -(((16384 - distance) * abs(item->Animation.Velocity.y)) / 16384);
|
||||
Camera.bounce = -((BLOCK(16) - distance) * abs(item->Animation.Velocity.y)) / BLOCK(16);
|
||||
SoundEffect(SFX_TR4_BOULDER_FALL, &item->Pose);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -357,6 +357,7 @@ enum GAME_OBJECT_ID : short
|
|||
ID_PUSHABLE_OBJECT_CLIMBABLE10,
|
||||
ID_TRAIN,
|
||||
ID_EXPLOSION,
|
||||
ID_DAMOCLES_SWORD,
|
||||
|
||||
ID_PUZZLE_ITEM1 = 500,
|
||||
ID_PUZZLE_ITEM2,
|
||||
|
|
|
@ -166,6 +166,7 @@ CALL gen.bat</Command>
|
|||
<ClInclude Include="Game\debug\debug.h" />
|
||||
<ClInclude Include="Game\effects\lightning.h" />
|
||||
<ClInclude Include="Game\GuiObjects.h" />
|
||||
<ClInclude Include="Objects\TR1\Trap\DamoclesSword.h" />
|
||||
<ClInclude Include="Scripting\Internal\TEN\Objects\Lara\LaraObject.h" />
|
||||
<ClInclude Include="Specific\Input\InputAction.h" />
|
||||
<ClInclude Include="Game\itemdata\door_data.h" />
|
||||
|
@ -628,6 +629,7 @@ CALL gen.bat</Command>
|
|||
<ClCompile Include="Game\collision\floordata.cpp" />
|
||||
<ClCompile Include="Game\effects\footprint.cpp" />
|
||||
<ClCompile Include="Game\GuiObjects.cpp" />
|
||||
<ClCompile Include="Objects\TR1\Trap\DamoclesSword.cpp" />
|
||||
<ClCompile Include="Scripting\Internal\TEN\Objects\Lara\LaraObject.cpp" />
|
||||
<ClCompile Include="Specific\Input\InputAction.cpp" />
|
||||
<ClCompile Include="Game\itemdata\itemdata.cpp" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue