TR1 hammer (#1588)

* First

* Implementation

* Add retract

* Fixing

* Finished

* Update CHANGELOG.md

* Fix Incorrect Death height

* Rename file

* Formatting; minor fixes

* Update Hammer.cpp

* Update Hammer.cpp

* Reset scale when enabling fly cheat

* Update tr1_objects.cpp

* Address comments.

* Fix Hammer code

---------

Co-authored-by: Sezz <sezzary@outlook.com>
This commit is contained in:
TrainWrack 2025-03-08 23:20:18 -05:00 committed by GitHub
parent e047da7257
commit 2f675b0069
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 252 additions and 20 deletions

View file

@ -27,6 +27,8 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
- You must update your Lara object: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Lara/TEN_Lara.wad2
* Added a particle based waterfall emitter object and associated sprite slots.
- You must use this version: https://github.com/TombEngine/Resources/raw/refs/heads/main/Wad2%20Objects/Interactables/TEN_Waterfall_Emitter.wad2
* Added TR1 hammer.
- You must use this version: <insert address here>
### Lua API changes

View file

@ -1,25 +1,6 @@
#include "framework.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_basic.h"
#include "Game/Lara/lara_cheat.h"
#include "Game/Lara/lara_climb.h"
#include "Game/Lara/lara_collide.h"
#include "Game/Lara/lara_crawl.h"
#include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_hang.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_initialise.h"
#include "Game/Lara/lara_jump.h"
#include "Game/Lara/lara_monkey.h"
#include "Game/Lara/lara_objects.h"
#include "Game/Lara/lara_one_gun.h"
#include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_slide.h"
#include "Game/Lara/lara_surface.h"
#include "Game/Lara/lara_swim.h"
#include "Game/Lara/lara_tests.h"
#include "Game/animation.h"
#include "Game/camera.h"
#include "Game/collision/collide_item.h"
@ -32,11 +13,24 @@
#include "Game/effects/tomb4fx.h"
#include "Game/Gui.h"
#include "Game/items.h"
#include "Game/Lara/lara_basic.h"
#include "Game/Lara/lara_cheat.h"
#include "Game/Lara/lara_climb.h"
#include "Game/Lara/lara_collide.h"
#include "Game/Lara/lara_crawl.h"
#include "Game/Lara/lara_fire.h"
#include "Game/Lara/lara_hang.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_initialise.h"
#include "Game/Lara/lara_jump.h"
#include "Game/Lara/lara_monkey.h"
#include "Game/Lara/lara_objects.h"
#include "Game/Lara/lara_one_gun.h"
#include "Game/Lara/lara_overhang.h"
#include "Game/Lara/lara_slide.h"
#include "Game/Lara/lara_surface.h"
#include "Game/Lara/lara_swim.h"
#include "Game/Lara/lara_tests.h"
#include "Game/Lara/PlayerStateMachine.h"
#include "Game/misc.h"
#include "Game/savegame.h"

View file

@ -802,6 +802,7 @@ void HandlePlayerFlyCheat(ItemInfo& item)
item.Animation.Velocity = Vector3::Zero;
item.Animation.IsAirborne = true;
item.Pose.Position.y -= CLICK(0.5f);
item.Pose.Scale = Vector3::One;
item.HitPoints = LARA_HEALTH_MAX;
player.Control.WaterStatus = WaterStatus::FlyCheat;

View file

@ -0,0 +1,197 @@
#include "framework.h"
#include "Objects/TR1/Trap/ThorHammer.h"
#include "Game/camera.h"
#include "Game/collision/collide_item.h"
#include "Game/collision/collide_room.h"
#include "Game/collision/Point.h"
#include "Game/collision/Sphere.h"
#include "Game/effects/effects.h"
#include "Game/Lara/lara.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Specific/level.h"
using namespace TEN::Collision::Point;
using namespace TEN::Collision::Sphere;
using namespace TEN::Math;
// NOTES:
// item.TriggerFlags = 0; Default TR1 behavior.
// item.TriggerFlags = 1; Retract after striking once.
// item.TriggerFlags = 2; Strike continuously.
namespace TEN::Entities::Traps
{
constexpr auto HAMMER_HIT_FRAME = 30;
enum HammerState
{
HAMMER_STATE_IDLE = 0,
HAMMER_STATE_UNSTABLE = 1,
HAMMER_STATE_FALL_START = 2,
HAMMER_STATE_FALL_END = 3,
HAMMER_STATE_RETRACT = 4
};
enum HammerAnim
{
HAMMER_ANIM_IDLE = 0,
HAMMER_ANIM_UNSTABLE = 1,
HAMMER_ANIM_FALL_START = 2,
HAMMER_ANIM_FALL_END = 3,
HAMMER_ANIM_RETRACT = 4
};
void InitializeThorHammer(short itemNumber)
{
auto& headItem = g_Level.Items[itemNumber];
int handleItemNumber = SpawnItem(headItem, ID_HAMMER_HEAD);
if (handleItemNumber == NO_VALUE)
{
TENLog("Failed to create hammer handle moveable.", LogLevel::Warning);
return;
}
auto& handleItem = g_Level.Items[handleItemNumber];
// Store hammer handle item number.
headItem.ItemFlags[0] = handleItemNumber;
handleItem.ItemFlags[0] = NO_VALUE;
}
static void SyncThorHammerHandle(ItemInfo& headItem)
{
int handleItemNumber = headItem.ItemFlags[0];
auto& handleItem = g_Level.Items[handleItemNumber];
// Sync item status.
handleItem.Status = headItem.Status;
//Sync item TriggerFlag
handleItem.TriggerFlags = headItem.TriggerFlags;
// Sync animation.
SetAnimation(handleItem, GetAnimNumber(headItem), GetFrameNumber(headItem));
// Sync position.
handleItem.Pose = headItem.Pose;
if (handleItem.RoomNumber != headItem.RoomNumber)
ItemNewRoom(handleItem.Index, headItem.RoomNumber);
}
void ControlThorHammer(short itemNumber)
{
auto& item = g_Level.Items[itemNumber];
const auto& playerItem = *LaraItem;
switch (item.Animation.ActiveState)
{
case HAMMER_STATE_IDLE:
if (TriggerActive(&item))
{
if (std::abs(item.TriggerFlags) == 1 && item.ItemFlags[1] == 1)
{
item.Status = ITEM_NOT_ACTIVE;
break;
}
if (std::abs(item.TriggerFlags) == 2)
{
item.Animation.TargetState = HAMMER_STATE_FALL_START;
break;
}
item.Animation.TargetState = HAMMER_STATE_UNSTABLE;
}
else
{
RemoveActiveItem(itemNumber);
item.Status = ITEM_NOT_ACTIVE;
}
break;
case HAMMER_STATE_UNSTABLE:
if (TriggerActive(&item))
{
item.Animation.TargetState = HAMMER_STATE_FALL_START;
}
else
{
item.Animation.TargetState = HAMMER_STATE_IDLE;
}
break;
case HAMMER_STATE_FALL_START:
break;
case HAMMER_STATE_FALL_END:
if (std::abs(item.TriggerFlags) > 0)
{
item.Animation.TargetState = HAMMER_STATE_RETRACT;
if (std::abs(item.TriggerFlags) == 1)
{
item.ItemFlags[1] = 1;
}
}
else
{
item.Status = ITEM_NOT_ACTIVE;
RemoveActiveItem(itemNumber);
}
break;
}
AnimateItem(&item);
SyncThorHammerHandle(item);
}
void CollideThorHammer(short itemNumber, ItemInfo* playerItem, CollisionInfo* coll)
{
auto& item = g_Level.Items[itemNumber];
if (!TestBoundsCollide(&item, playerItem, coll->Setup.Radius))
return;
if (!HandleItemSphereCollision(item, *playerItem))
return;
if (item.Animation.ActiveState == HAMMER_STATE_FALL_START && (item.Animation.FrameNumber - GetAnimData(item).frameBase) <= HAMMER_HIT_FRAME)
{
auto pointColl = GetPointCollision(*playerItem);
playerItem->Pose.Position.y = pointColl.GetFloorHeight();
playerItem->Animation.Velocity = Vector3::Zero;
playerItem->Animation.IsAirborne = false;
if (item.TriggerFlags < 0)
playerItem->Pose.Scale = Vector3(1.0f, 0.1f, 1.0f);
DoDamage(playerItem, INT_MAX);
SetAnimation(playerItem, LA_BOULDER_DEATH);
}
else if (playerItem->HitPoints > 0)
{
ItemPushItem(&item, playerItem, coll, false, 1);
}
}
void CollideThorHammerHandle(short itemNumber, ItemInfo* playerItem, CollisionInfo* coll)
{
auto& item = g_Level.Items[itemNumber];
if (!TestBoundsCollide(&item, playerItem, coll->Setup.Radius))
return;
if (coll->Setup.EnableObjectPush)
ItemPushItem(&item, playerItem, coll, false, 1);
}
}

View file

@ -0,0 +1,13 @@
#pragma once
struct CollisionInfo;
struct ItemInfo;
struct ObjectInfo;
namespace TEN::Entities::Traps
{
void InitializeThorHammer(short itemNumber);
void ControlThorHammer(short itemNumber);
void CollideThorHammer(short itemNumber, ItemInfo* playerItem, CollisionInfo* coll);
void CollideThorHammerHandle(short itemNumber, ItemInfo* playerItem, CollisionInfo* coll);
}

View file

@ -26,9 +26,10 @@
// Traps
#include "Objects/TR1/Trap/DamoclesSword.h"
#include "Objects/TR1/Trap/ElectricBall.h"
#include "Objects/TR1/Trap/ThorHammer.h"
#include "Objects/TR1/Trap/SlammingDoors.h"
#include "Objects/TR1/Trap/SwingingBlade.h"
#include "Objects/TR1/Trap/ElectricBall.h"
using namespace TEN::Entities::Creatures::TR1;
using namespace TEN::Entities::Traps;
@ -250,6 +251,26 @@ static void StartTrap(ObjectInfo* obj)
obj->shadowType = ShadowMode::All;
obj->SetHitEffect(true);
}
obj = &Objects[ID_HAMMER_HANDLE];
if (obj->loaded)
{
CheckIfSlotExists(ID_HAMMER_HEAD, "ID_HAMMER_HEAD");
obj->Initialize = InitializeThorHammer;
obj->collision = CollideThorHammerHandle;
obj->control = ControlThorHammer;
obj->shadowType = ShadowMode::All;
obj->SetHitEffect(true);
}
obj = &Objects[ID_HAMMER_HEAD];
if (obj->loaded)
{
CheckIfSlotExists(ID_HAMMER_HANDLE, "ID_HAMMER_HANDLE");
obj->collision = CollideThorHammer;
obj->shadowType = ShadowMode::All;
obj->SetHitEffect(true);
}
obj = &Objects[ID_SLAMMING_DOORS];
if (obj->loaded)

View file

@ -371,6 +371,8 @@ enum GAME_OBJECT_ID : short
ID_SWINGING_BLADE,
ID_ELECTRIC_BALL,
ID_ELECTRIC_BALL_IMPACT_POINT,
ID_HAMMER_HANDLE,
ID_HAMMER_HEAD,
ID_PUZZLE_ITEM1 = 500,
ID_PUZZLE_ITEM2,

View file

@ -605,6 +605,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings.
<ClInclude Include="Objects\TR1\Entity\WingedMutant.h" />
<ClInclude Include="Objects\TR1\Trap\DamoclesSword.h" />
<ClInclude Include="Objects\TR1\Trap\ElectricBall.h" />
<ClInclude Include="Objects\TR1\Trap\ThorHammer.h" />
<ClInclude Include="Objects\TR1\Trap\SlammingDoors.h" />
<ClInclude Include="Objects\TR1\tr1_objects.h" />
<ClInclude Include="Objects\TR1\Trap\SwingingBlade.h" />
@ -1140,6 +1141,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings.
<ClCompile Include="Objects\TR1\tr1_objects.cpp" />
<ClCompile Include="Objects\TR1\Trap\DamoclesSword.cpp" />
<ClCompile Include="Objects\TR1\Trap\ElectricBall.cpp" />
<ClCompile Include="Objects\TR1\Trap\ThorHammer.cpp" />
<ClCompile Include="Objects\TR1\Trap\SlammingDoors.cpp" />
<ClCompile Include="Objects\TR1\Trap\SwingingBlade.cpp" />
<ClCompile Include="Objects\TR2\Entity\Bartoli.cpp" />