diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index c10e95409..4d98e9af4 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -59,6 +59,7 @@ GameVector LookCamPosition; GameVector LookCamTarget; Vector3i CamOldPos; CAMERA_INFO Camera; +OBJ_CAMERA_INFO ItemCamera; GameVector ForcedFixedCamera; int UseForcedFixedCamera; @@ -324,6 +325,99 @@ void MoveCamera(GameVector* ideal, int speed) } } +void ObjCamera(ItemInfo* camSlotId, int camMeshId, ItemInfo* targetItem, int targetMeshId, bool cond) +{ + //camSlotId and targetItem stay the same object until I know how to expand targetItem to another object. + //activates code below -> void CalculateCamera(). + ItemCamera.ItemCameraOn = cond; + + UpdateCameraElevation(); + + //get mesh 0 coordinates. + auto pos = GetJointPosition(camSlotId, 0, Vector3i::Zero); + auto dest = Vector3(pos.x, pos.y, pos.z); + + GameVector from = GameVector(dest, camSlotId->RoomNumber); + Camera.fixedCamera = true; + + MoveObjCamera(&from, camSlotId, camMeshId, targetItem, targetMeshId); + Camera.timer = -1; +} + + +void MoveObjCamera(GameVector* ideal, ItemInfo* camSlotId, int camMeshId, ItemInfo* targetItem, int targetMeshId) +{ + int speed = 1; + //Get mesh1 to attach camera to + //Vector3i pos = Vector3i::Zero; + auto pos = GetJointPosition(camSlotId, camMeshId, Vector3i::Zero); + //Get mesh2 to attach target to + //Vector3i pos2 = Vector3i::Zero; + auto pos2 = GetJointPosition(targetItem, targetMeshId, Vector3i::Zero); + + if (OldCam.pos.Position != pos || + OldCam.targetDistance != Camera.targetDistance || + OldCam.targetElevation != Camera.targetElevation || + OldCam.actualElevation != Camera.actualElevation || + OldCam.actualAngle != Camera.actualAngle || + OldCam.target != Camera.target.ToVector3i() || + Camera.oldType != Camera.type || + BinocularOn) + { + OldCam.pos.Position = pos; + OldCam.targetDistance = Camera.targetDistance; + OldCam.targetElevation = Camera.targetElevation; + OldCam.actualElevation = Camera.actualElevation; + OldCam.actualAngle = Camera.actualAngle; + OldCam.target = Camera.target.ToVector3i(); + LastIdeal = pos; + LastIdeal.RoomNumber = ideal->RoomNumber; + LastTarget = pos2; + } + else + { + pos = LastIdeal.ToVector3i(); + pos2 = LastTarget.ToVector3i(); + ideal->RoomNumber = LastIdeal.RoomNumber; + } + + Camera.pos += (ideal->ToVector3i() - Camera.pos.ToVector3i()) / speed; + Camera.pos.RoomNumber = GetCollision(Camera.pos.x, Camera.pos.y, Camera.pos.z, Camera.pos.RoomNumber).RoomNumber; + LookAt(&Camera, 0); + + auto angle = Camera.target.ToVector3i() - Camera.pos.ToVector3i(); + auto position = Vector3i(Camera.target.ToVector3i() - Camera.pos.ToVector3i()); + + // write last frame camera angle to LastAngle to compare if next frame camera angle has a bigger step than 100. + // To make camera movement smoother a speed of 2 is used. + // While for big camera angle steps (cuts) - + // the speed is set to 1 to make the cut immediatelly. + constexpr int angleThresholdDegrees = 100; + + if (LastTarget.x - Camera.target.x > angleThresholdDegrees || + LastTarget.y - Camera.target.y > angleThresholdDegrees || + LastTarget.z - Camera.target.z > angleThresholdDegrees) + { + speed = 1; + } + else + { + speed = 2; + } + + //actual movement of the target. + Camera.target.x += (pos2.x - Camera.target.x) / speed; + Camera.target.y += (pos2.y - Camera.target.y) / speed; + Camera.target.z += (pos2.z - Camera.target.z) / speed; + + if (ItemCamera.LastAngle != position) + { + ItemCamera.LastAngle = Vector3i(ItemCamera.LastAngle.x = angle.x, + ItemCamera.LastAngle.y = angle.y, + ItemCamera.LastAngle.z = angle.z); + } +} + void ChaseCamera(ItemInfo* item) { if (!Camera.targetElevation) @@ -1262,6 +1356,11 @@ void CalculateCamera() return; } + if (ItemCamera.ItemCameraOn) + { + return; + } + if (UseForcedFixedCamera != 0) { Camera.type = CameraType::Fixed; diff --git a/TombEngine/Game/camera.h b/TombEngine/Game/camera.h index 78c66b3ba..c6e6a2e89 100644 --- a/TombEngine/Game/camera.h +++ b/TombEngine/Game/camera.h @@ -8,7 +8,8 @@ enum class CameraType Fixed, Look, Combat, - Heavy + Heavy, + Object }; struct CAMERA_INFO @@ -41,6 +42,12 @@ struct CAMERA_INFO Vector3i mikePos; // size=12, offset=108 }; +struct OBJ_CAMERA_INFO +{ + GameVector LastAngle; + bool ItemCameraOn; +}; + enum CAMERA_FLAGS { CF_NONE = 0, @@ -93,6 +100,8 @@ void RumbleScreen(); bool TestBoundsCollideCamera(const GameBoundingBox& bounds, const Pose& pose, short radius); void ItemPushCamera(GameBoundingBox* bounds, Pose* pos, short radius); void ItemsCollideCamera(); +void ObjCamera(ItemInfo* camSlotId, int camMeshID, ItemInfo* targetItem, int targetMeshID, bool cond); +void MoveObjCamera(GameVector* ideal, ItemInfo* camSlotId, int camMeshID, ItemInfo* targetItem, int targetMeshID); void SetScreenFadeOut(float speed); void SetScreenFadeIn(float speed); diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index 5c582b13a..1189cd996 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -102,6 +102,8 @@ static constexpr char ScriptReserved_SetOnCollidedWithObject[] = "SetOnCollidedW static constexpr char ScriptReserved_GetOnCollidedWithRoom[] = "GetOnCollidedWithRoom"; static constexpr char ScriptReserved_SetOnCollidedWithRoom[] = "SetOnCollidedWithRoom"; static constexpr char ScriptReserved_ToLength[] = "ToLength"; +static constexpr char ScriptReserved_AttachObjCamera[] = "AttachObjCamera"; +static constexpr char ScriptReserved_AnimFromObject[] = "AnimFromObject"; // Flow Functions static constexpr char ScriptReserved_AddLevel[] = "AddLevel"; @@ -131,6 +133,8 @@ static constexpr char ScriptReserved_RotationAxis[] = "RotationAxis"; static constexpr char ScriptReserved_ItemAction[] = "ItemAction"; static constexpr char ScriptReserved_ErrorMode[] = "ErrorMode"; static constexpr char ScriptReserved_InventoryItem[] = "InventoryItem"; +static constexpr char ScriptReserved_LaraWeaponType[] = "LaraWeaponType"; +static constexpr char ScriptReserved_HandStatus[] = "HandStatus"; // Functions static constexpr char ScriptReserved_ShowString[] = "ShowString"; @@ -186,6 +190,13 @@ static constexpr char ScriptReserved_KeyClear[] = "KeyClear"; static constexpr char ScriptReserved_FlipMap[] = "FlipMap"; static constexpr char ScriptReserved_PlayFlyBy[] = "PlayFlyBy"; +static constexpr char ScriptReserved_ResetObjCamera[] = "ResetObjCamera"; +static constexpr char ScriptReserved_UndrawWeapons[] = "UndrawWeapons"; +static constexpr char ScriptReserved_GetHandStatus[] = "GetHandStatus"; +static constexpr char ScriptReserved_GetWeaponType[] = "GetWeaponType"; +static constexpr char ScriptReserved_ThrowAwayTorch[] = "ThrowAwayTorch"; +static constexpr char ScriptReserved_SetWeaponType[] = "SetWeaponType"; + // Enums static constexpr char ScriptReserved_ObjID[] = "ObjID"; static constexpr char ScriptReserved_BlendID[] = "BlendID"; diff --git a/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp b/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp index f1902e163..8a04e01ed 100644 --- a/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp +++ b/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp @@ -6,12 +6,14 @@ #include "Game/effects/explosion.h" #include "Game/effects/tomb4fx.h" #include "Game/effects/weather.h" +#include "Game/Lara/lara.h" #include "Game/room.h" #include "Game/spotcam.h" #include "ReservedScriptNames.h" #include "ScriptUtil.h" #include "Sound/sound.h" #include "Specific/configuration.h" +#include "Specific/level.h" #include "Specific/Input/Input.h" #include "Vec3/Vec3.h" #include "ScriptAssert.h" @@ -286,6 +288,13 @@ namespace Misc return std::make_tuple(resX, resY); } + /// Reset object camera back to Lara and deactivate object camera. + //@function ResetObjCamera + static void ResetObjCamera() + { + ObjCamera(LaraItem, 0, LaraItem, 0, false); + } + void Register(sol::state * state, sol::table & parent) { sol::table table_misc{ state->lua_state(), sol::create }; @@ -346,5 +355,6 @@ namespace Misc table_misc.set_function(ScriptReserved_PlayFlyBy, &PlayFlyBy); + table_misc.set_function(ScriptReserved_ResetObjCamera, &ResetObjCamera); } } diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.cpp index bf34a469e..46cb9c1c8 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.cpp @@ -1,9 +1,11 @@ #include "framework.h" #include "LaraObject.h" +#include "Game/camera.h" #include "Game/Lara/lara.h" #include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_struct.h" +#include "Objects/Generic/Object/burning_torch.h" #include "Game/effects/lara_fx.h" #include "ReservedScriptNames.h" @@ -18,6 +20,7 @@ In addition, LaraObject inherits all the functions of @{Objects.Moveable|Moveabl */ constexpr auto LUA_CLASS_NAME{ ScriptReserved_LaraObject }; +using namespace TEN::Entities::Generic; /// Set Lara on fire // @function LaraObject:SetOnFire @@ -98,6 +101,93 @@ int LaraObject::GetAir() const return lara->Air; } +/// Lara will undraw her weapons if they are drawn, throw away flare if she helds one in her hand. +// @function LaraObject:UndrawWeapons +// @usage +// Lara:UndrawWeapons() +void LaraObject::UndrawWeapons() +{ + auto* lara = GetLaraInfo(m_item); + + if (lara->Control.HandStatus != HandStatus::Free || + lara->Control.Weapon.GunType == LaraWeaponType::Flare) + { + lara->Control.HandStatus = HandStatus::WeaponUndraw; + } +} + +/// Lara will throw away the torch if she helds one in her hand. +// @function LaraObject:ThrowAwayTorch +// @usage +// Lara:ThrowAwayTorch() +void LaraObject::ThrowAwayTorch() +{ + auto* lara = GetLaraInfo(m_item); + + if (lara->Control.Weapon.GunType == LaraWeaponType::Torch) + { + Lara.Torch.State = TorchState::Dropping; + } +} + +/// Get actual hand status of Lara +// @function LaraObject:GetHandStatus +// @usage +// Lara:GetHandStatus() +// @treturn 0=HandsFree, 1=Busy(climbing,etc), 2=WeaponDraw, 3=WeaponUndraw, 4=WeaponInHand. +HandStatus LaraObject::GetHandStatus() const +{ + auto* lara = GetLaraInfo(m_item); + return HandStatus{ lara->Control.HandStatus }; +} + +/// Get actual weapon type of Lara +// @function LaraObject:GetWeaponType +// @usage +// Lara:GetWeaponType() +// @treturn 0=None, 1=Pistols, 2=Revolver, 3=Uzi, 4=Shotgun, 5=HK, 6=Crossbow, 7=Flare, 8=Torch, 9=GrenadeLauncher, 10=Harpoon, 11=RocketLauncher. +LaraWeaponType LaraObject::GetWeaponType() const +{ + auto* lara = GetLaraInfo(m_item); + return LaraWeaponType{ lara->Control.Weapon.GunType }; +} + +/// Set Lara weapon type +// @function LaraObject:SetWeaponType +// @usage +// Lara:SetWeaponType(LaraWeaponType.WEAPONNAME, true/false) +// @tparam LaraWeaponType NONE, PISTOLS, REVOLVER, UZI, SHOTGUN, HK, CROSSBOW, FLARE, TORCH, GRENADELAUNCHER, HARPOONGUN, ROCKETLAUNCHER. +// @tparam bool activate true = let her also draw the weapons, set torch lit. false = let Laras new weapons holstered until you draw them, set torch unlit. +void LaraObject::SetWeaponType(LaraWeaponType weaponType, bool activate) +{ + auto* lara = GetLaraInfo(m_item); + + switch (weaponType) + { + case LaraWeaponType::Flare: + lara->Control.Weapon.RequestGunType = LaraWeaponType::Flare; + break; + case LaraWeaponType::Torch: + if (activate == false) + { + GetFlameTorch(); + lara->Torch.IsLit = false; + } + else + { + GetFlameTorch(); + lara->Torch.IsLit = true; + } + break; + default: + if (activate == false) + lara->Control.Weapon.LastGunType = weaponType; + else + lara->Control.Weapon.RequestGunType = weaponType; + break; + } +} + void LaraObject::Register(sol::table& parent) { parent.new_usertype(LUA_CLASS_NAME, @@ -107,6 +197,11 @@ void LaraObject::Register(sol::table& parent) ScriptReserved_GetPoison, &LaraObject::GetPoison, ScriptReserved_SetAir, &LaraObject::SetAir, ScriptReserved_GetAir, &LaraObject::GetAir, + ScriptReserved_UndrawWeapons, &LaraObject::UndrawWeapons, + ScriptReserved_ThrowAwayTorch, &LaraObject::ThrowAwayTorch, + ScriptReserved_GetHandStatus, &LaraObject::GetHandStatus, + ScriptReserved_GetWeaponType, &LaraObject::GetWeaponType, + ScriptReserved_SetWeaponType, &LaraObject::SetWeaponType, sol::base_classes, sol::bases() ); } diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.h b/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.h index 68224f1dc..93409bb7f 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.h @@ -1,6 +1,36 @@ #pragma once +#include "Game/Lara/lara_struct.h" #include "Objects/Moveable/MoveableObject.h" +static const std::unordered_map LaraWeaponTypeMap +{ + {"NONE", LaraWeaponType::None}, + {"PISTOLS", LaraWeaponType::Pistol}, + {"REVOLVER", LaraWeaponType::Revolver}, + {"UZI", LaraWeaponType::Uzi}, + {"SHOTGUN", LaraWeaponType::Shotgun}, + {"HK", LaraWeaponType::HK}, + {"CROSSBOW", LaraWeaponType::Crossbow}, + {"FLARE", LaraWeaponType::Flare}, + {"TORCH", LaraWeaponType::Torch}, + {"GRENADELAUNCHER", LaraWeaponType::GrenadeLauncher}, + {"HARPOONGUN", LaraWeaponType::HarpoonGun}, + {"ROCKETLAUNCHER", LaraWeaponType::RocketLauncher}, + {"SNOWMOBILE", LaraWeaponType::Snowmobile}, + {"NUMWEAPONS", LaraWeaponType::NumWeapons} +}; + +static const std::unordered_map HandStatusMap +{ + {"FREE", HandStatus::Free}, + {"BUSY", HandStatus::Busy}, + {"WEAPONDRAW", HandStatus::WeaponDraw}, + {"WEAPONUNDRAW", HandStatus::WeaponUndraw}, + {"WEAPONREADY", HandStatus::WeaponReady}, + {"SPECIAL", HandStatus::Special}, +}; + + class LaraObject : public Moveable { public: @@ -10,6 +40,11 @@ public: int GetPoison() const; void SetAir(sol::optional air); int GetAir() const; + void UndrawWeapons(); + void ThrowAwayTorch(); + HandStatus GetHandStatus() const; + LaraWeaponType GetWeaponType() const; + void SetWeaponType(LaraWeaponType weaponType, bool activate); static void Register(sol::table & parent); using Moveable::Moveable; }; diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp index 346c635bd..fdae77c22 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp @@ -434,7 +434,15 @@ ScriptReserved_GetSlotHP, & Moveable::GetSlotHP, /// Destroy the moveable. This will mean it can no longer be used, except to re-initialise it with another object. // @function Moveable:Destroy - ScriptReserved_Destroy, &Moveable::Destroy); + ScriptReserved_Destroy, &Moveable::Destroy, + +/// Attach camera to an object. +// @function Moveable:mesh 1 for camera, mesh 2 for target + ScriptReserved_AttachObjCamera, & Moveable::AttachObjCamera, + +/// Borrow animation from an object +// @function GAME_OBJECT_ID ObjectID to take animation and stateID from, int animationnumber from object, int stateID from object + ScriptReserved_AnimFromObject, & Moveable::AnimFromObject); } @@ -996,3 +1004,21 @@ bool Moveable::MeshExists(int index) const return true; } + +//Attach camera and camera target to a mesh of an object. +void Moveable::AttachObjCamera(short camMeshId, Moveable& mov, short targetMeshId) +{ + if ((m_item->Active || m_item->IsLara()) && (mov.m_item->Active || mov.m_item->IsLara())) + { + ObjCamera(m_item, camMeshId, mov.m_item, targetMeshId, true); + } +} + +//Borrow an animtaion and state id from an object. +void Moveable::AnimFromObject(GAME_OBJECT_ID object, int animNumber, int stateID) +{ + m_item->Animation.AnimNumber = Objects[object].animIndex + animNumber; + m_item->Animation.ActiveState = stateID; + m_item->Animation.FrameNumber = g_Level.Anims[m_item->Animation.AnimNumber].frameBase; + AnimateItem(m_item); +} diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h index 1b976d42e..ecf8dd05e 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h @@ -96,6 +96,9 @@ public: [[nodiscard]] short GetRoom() const; void SetRoom(short room); + void AttachObjCamera(short camMeshId, Moveable& mov, short targetMeshId); + void AnimFromObject(GAME_OBJECT_ID object, int animNumber, int stateID); + void EnableItem(); void DisableItem(); void MakeInvisible(); diff --git a/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.cpp b/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.cpp index bb4a69a28..5a4e490dc 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/ObjectsHandler.cpp @@ -127,6 +127,8 @@ ObjectsHandler::ObjectsHandler(sol::state* lua, sol::table & parent) : ); m_handler.MakeReadOnlyTable(m_table_objects, ScriptReserved_ObjID, kObjIDs); + m_handler.MakeReadOnlyTable(m_table_objects, ScriptReserved_LaraWeaponType, LaraWeaponTypeMap); + m_handler.MakeReadOnlyTable(m_table_objects, ScriptReserved_HandStatus, HandStatusMap); } void ObjectsHandler::TestCollidingObjects()