Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

11 changed files with 89 additions and 152 deletions

3
.gitignore vendored
View File

@ -2,8 +2,7 @@
project/References/EFT_Managed/*
# Allow these files, despite the rules above
!project/References/EFT_Managed/.keep
!project/References/SPT/.keep
project/References/EFT_Managed/.keep
# ---> VisualStudio
## Ignore Visual Studio temporary files, build results, and

View File

@ -1,6 +1,6 @@
# Freecam
A BepInEx plugin for SPT-AKI that allows you to detach the camera and fly around freely in Escape From Tarkov.
An SPT-AKI module mod that allows you to detach the camera and fly around freely in Escape From Tarkov.
### Controls
@ -21,9 +21,10 @@ Alternatively, you can find the `com.terkoiz.freecam.cfg` file in your `BepInEx/
### Known issues
1. When teleporting to camera position, the camera rotation gets copied exactly, potentially causing the player model to tilt
2. Game version UI element is not hidden when toggling UI
3. When flying to distant parts of the map in freecam mode, LODs are not triggered (these seem to follow the player)
1. Your weapon doesn't turn invisible when you enter freecam mode
2. When teleporting to camera position, the camera rotation gets copied exactly, potentially causing the player model to tilt
3. Game version UI element is not hidden when toggling UI
4. When flying to distant parts of the map in freecam mode, LODs are not triggered (these seem to follow the player)
### How to build from source

Binary file not shown.

View File

@ -1,44 +0,0 @@
using System.Reflection;
using SPT.Reflection.Patching;
using EFT.HealthSystem;
using HarmonyLib;
namespace Terkoiz.Freecam
{
public class FallDamagePatch : ModulePatch
{
internal static bool HasTeleported;
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(ActiveHealthController), nameof(ActiveHealthController.HandleFall));
}
[PatchPrefix]
public static bool PatchPrefix(ActiveHealthController __instance, float height)
{
// WARNING: The 'HandleFall' method gets called every frame for every player and AI in a raid. Be very careful with logging or expensive operations in this prefix patch!
// Check if it's our own player, or if a Player property even exists
if (__instance.Player?.IsAI ?? true)
{
return true; // Run original method
}
// Global fall damage flag overrides everything
if (FreecamPlugin.GlobalDisableFallDamage.Value)
{
return false; // Prevent original method from running
}
// If smart fall damage flag is enabled, check if we've recently teleported and if the fall height value was positive
if (FreecamPlugin.SmartDisableFallDamage.Value && HasTeleported && height > 0)
{
HasTeleported = false;
return false; // Prevent original method from running
}
return true; // Run original method
}
}
}

View File

@ -6,7 +6,7 @@ namespace Terkoiz.Freecam
/// <summary>
/// A simple free camera to be added to a Unity game object.
///
/// Full credit to Ashley Davis on GitHub for the initial code:
/// Full credit to Ashley Davis on GitHub for the inital code:
/// https://gist.github.com/ashleydavis/f025c03a9221bc840a2b
///
/// </summary>

View File

@ -13,46 +13,37 @@ namespace Terkoiz.Freecam
private GameObject _mainCamera;
private Freecam _freeCamScript;
private EftBattleUIScreen _playerUi;
private BattleUIScreen _playerUi;
private bool _uiHidden;
private bool _fallDamageToggle = false;
private GamePlayerOwner _gamePlayerOwner;
private Vector3? _lastPosition;
private Quaternion? _lastRotation;
// TODO:
// Hide version number UI element
// Button to toggle between camera and player movement
// Independent FoV setting for Freecam mode (_mainCamera.GetComponent<Camera>().fieldOfView = ...)
[UsedImplicitly]
public void Start()
{
// Get Main Camera
_mainCamera = GetLocalPlayerFromWorld().GetComponent<PlayerCameraController>().Camera.gameObject;
// Find Main Camera
_mainCamera = GameObject.Find("FPS Camera");
if (_mainCamera == null)
{
FreecamPlugin.Logger.LogError("Failed to locate main camera");
return;
}
// Get Player UI
_playerUi = Singleton<CommonUI>.Instance.EftBattleUIScreen;
if (_playerUi == null)
{
FreecamPlugin.Logger.LogError("Failed to locate player UI");
return;
}
// Add Freecam script to main camera in scene
_freeCamScript = _mainCamera.AddComponent<Freecam>();
if (_freeCamScript == null)
{
FreecamPlugin.Logger.LogError("Failed to add Freecam script to Camera");
}
// Get GamePlayerOwner component
_gamePlayerOwner = GetLocalPlayerFromWorld().GetComponentInChildren<GamePlayerOwner>();
if (_gamePlayerOwner == null)
{
FreecamPlugin.Logger.LogError("Failed to locate GamePlayerOwner");
}
}
[UsedImplicitly]
@ -68,15 +59,33 @@ namespace Terkoiz.Freecam
ToggleCamera();
}
if (FreecamPlugin.ToggleFreecamControls.Value.IsDown())
{
ToggleCameraControls();
}
if (FreecamPlugin.TeleportToCamera.Value.IsDown())
{
MovePlayerToCamera();
}
if (_fallDamageToggle != FreecamPlugin.DisableFallDamage.Value)
{
_fallDamageToggle = ToggleFallDamage(FreecamPlugin.DisableFallDamage.Value);
}
}
private bool ToggleFallDamage(bool config)
{
var localPlayer = GetLocalPlayerFromWorld();
if (localPlayer == null)
return false;
// Set Safe fall height to 99999
if (config)
{
localPlayer.ActiveHealthController.FallSafeHeight = 99999;
return true;
}
// Set Safe fall height to 3
localPlayer.ActiveHealthController.FallSafeHeight = 3;
return false;
}
/// <summary>
@ -111,9 +120,6 @@ namespace Terkoiz.Freecam
// Move the player to the camera's current position and switch to First Person mode
if (_freeCamScript.IsActive)
{
// Tell the fall damage patch that we just teleported. Used for the "smart" fall damage prevention feature
FallDamagePatch.HasTeleported = true;
// We grab the camera's position, but we subtract a bit off the Y axis, because the players coordinate origin is at the feet
var position = new Vector3(_mainCamera.transform.position.x, _mainCamera.transform.position.y - 1.8f, _mainCamera.transform.position.z);
localPlayer.gameObject.transform.SetPositionAndRotation(position, Quaternion.Euler(0, _mainCamera.transform.rotation.y, 0));
@ -132,6 +138,18 @@ namespace Terkoiz.Freecam
if (GetLocalPlayerFromWorld() == null)
return;
// If we don't have the UI Component cached, go look for it in the scene
if (_playerUi == null)
{
_playerUi = GameObject.Find("BattleUIScreen").GetComponent<BattleUIScreen>();
if (_playerUi == null)
{
FreecamPlugin.Logger.LogError("Failed to locate player UI");
return;
}
}
_playerUi.gameObject.SetActive(_uiHidden);
_uiHidden = !_uiHidden;
}
@ -197,25 +215,6 @@ namespace Terkoiz.Freecam
localPlayer.PointOfView = EPointOfView.FirstPerson;
}
/// <summary>
/// A helper method to toggle the Freecam Camera Controls
/// </summary>
private void ToggleCameraControls()
{
if (_freeCamScript.IsActive)
{
_freeCamScript.IsActive = false;
_gamePlayerOwner.enabled = true;
}
else
{
_freeCamScript.IsActive = true;
_gamePlayerOwner.enabled = false;
}
}
/// <summary>
/// Gets the current <see cref="Player"/> instance if it's available
/// </summary>
@ -232,10 +231,11 @@ namespace Terkoiz.Freecam
}
[UsedImplicitly]
public void OnDestroy()
private void OnDestroy()
{
// Destroy FreeCamScript before FreeCamController if exists
Destroy(_freeCamScript);
Destroy(this);
}
}
}

View File

@ -1,8 +1,7 @@
using System.Reflection;
using SPT.Reflection.Patching;
using Aki.Reflection.Patching;
using Comfort.Common;
using EFT;
using HarmonyLib;
namespace Terkoiz.Freecam
{
@ -10,19 +9,19 @@ namespace Terkoiz.Freecam
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(GameWorld), nameof(GameWorld.OnGameStarted));
return typeof(GameWorld).GetMethod("OnGameStarted", BindingFlags.Public | BindingFlags.Instance);
}
[PatchPostfix]
public static void PatchPostFix()
{
var gameWorld = Singleton<GameWorld>.Instance;
var gameworld = Singleton<GameWorld>.Instance;
if (gameWorld == null)
if (gameworld == null)
return;
// Add FreecamController to GameWorld GameObject
gameWorld.gameObject.AddComponent<FreecamController>();
// Add FreeCamController to GameWorld GameObject
gameworld.gameObject.AddComponent<FreecamController>();
}
}
}

View File

@ -7,25 +7,19 @@ using KeyboardShortcut = BepInEx.Configuration.KeyboardShortcut;
namespace Terkoiz.Freecam
{
[BepInPlugin("com.terkoiz.freecam", "Terkoiz.Freecam", "1.4.4")]
[BepInPlugin("com.terkoiz.freecam", "Terkoiz.Freecam", "1.3.0")]
public class FreecamPlugin : BaseUnityPlugin
{
internal new static ManualLogSource Logger { get; private set; }
// Fall damage config entries
private const string FallDamageSectionName = "Fall Damage";
internal static ConfigEntry<bool> GlobalDisableFallDamage;
internal static ConfigEntry<bool> SmartDisableFallDamage;
// Keyboard shortcut config entries
private const string KeybindSectionName = "Keybinds";
internal static ConfigEntry<KeyboardShortcut> ToggleFreecamMode;
internal static ConfigEntry<KeyboardShortcut> ToggleFreecamControls;
internal static ConfigEntry<KeyboardShortcut> TeleportToCamera;
internal static ConfigEntry<KeyboardShortcut> ToggleUi;
// Camera settings config entries
private const string CameraSettingsSectionName = "Camera Settings";
private const string CameraSettingsSectionName = "CameraSettings";
internal static ConfigEntry<float> CameraMoveSpeed;
internal static ConfigEntry<float> CameraFastMoveSpeed;
internal static ConfigEntry<float> CameraLookSensitivity;
@ -37,6 +31,7 @@ namespace Terkoiz.Freecam
internal static ConfigEntry<bool> CameraHeightMovement;
internal static ConfigEntry<bool> CameraMousewheelZoom;
internal static ConfigEntry<bool> CameraRememberLastPosition;
internal static ConfigEntry<bool> DisableFallDamage;
[UsedImplicitly]
internal void Start()
@ -45,50 +40,31 @@ namespace Terkoiz.Freecam
InitConfiguration();
new FreecamPatch().Enable();
new FallDamagePatch().Enable();
}
private void InitConfiguration()
{
GlobalDisableFallDamage = Config.Bind(
FallDamageSectionName,
"Globally Disable Fall Damage",
false,
"Completely disables fall damage. This is the safest option for using freecam. Will fully override the 'Smart Fall Damage Prevention' setting.");
SmartDisableFallDamage = Config.Bind(
FallDamageSectionName,
"Smart Fall Damage Prevention",
true,
"Fall damage will only be disabled after using teleport, until your player lands. Less cheat-y way to save yourself from fall damage, but might sometimes be unreliable.");
ToggleFreecamMode = Config.Bind(
KeybindSectionName,
"Toggle Freecam",
"ToggleCamera",
new KeyboardShortcut(KeyCode.KeypadPlus),
"The keyboard shortcut that toggles Freecam");
ToggleFreecamControls = Config.Bind(
KeybindSectionName,
"Toggle Freecam Controls",
new KeyboardShortcut(KeyCode.KeypadPeriod),
"The keyboard shortcut that toggles Freecam Controls");
TeleportToCamera = Config.Bind(
KeybindSectionName,
"Teleport To Camera",
"TeleportToCamera",
new KeyboardShortcut(KeyCode.KeypadEnter),
"The keyboard shortcut that teleports the player to camera position");
ToggleUi = Config.Bind(
KeybindSectionName,
"Toggle UI",
"ToggleUi",
new KeyboardShortcut(KeyCode.KeypadMultiply),
"The keyboard shortcut that toggles the game UI");
CameraMoveSpeed = Config.Bind(
CameraSettingsSectionName,
"Camera Speed",
"CameraMoveSpeed",
10f,
new ConfigDescription(
"The speed at which the camera will move normally",
@ -96,7 +72,7 @@ namespace Terkoiz.Freecam
CameraFastMoveSpeed = Config.Bind(
CameraSettingsSectionName,
"Camera Sprint Speed",
"CameraFastMoveSpeed",
100f,
new ConfigDescription(
"The speed at which the camera will move when the Shift key is held down",
@ -104,7 +80,7 @@ namespace Terkoiz.Freecam
CameraLookSensitivity = Config.Bind(
CameraSettingsSectionName,
"Camera Mouse Sensitivity",
"CameraLookSensitivity",
3f,
new ConfigDescription(
"Camera free look mouse sensitivity",
@ -112,7 +88,7 @@ namespace Terkoiz.Freecam
CameraZoomSpeed = Config.Bind(
CameraSettingsSectionName,
"Camera Zoom Speed",
"CameraMousewheelZoomSpeed",
10f,
new ConfigDescription(
"Amount to zoom the camera when using the mouse wheel",
@ -120,7 +96,7 @@ namespace Terkoiz.Freecam
CameraFastZoomSpeed = Config.Bind(
CameraSettingsSectionName,
"Camera Zoom Sprint Speed",
"CameraMousewheelFastZoomSpeed",
50f,
new ConfigDescription(
"Amount to zoom the camera when using the mouse wheel while holding Shift",
@ -128,22 +104,28 @@ namespace Terkoiz.Freecam
CameraHeightMovement = Config.Bind(
TogglesSectionName,
"Camera Height Movement Keys",
"CameraHeightMovementKeys",
true,
"Enables or disables the camera height movement keys, which default to Q, E, R, F." +
" \nUseful to disable if you want to let your character lean in Freecam mode");
CameraMousewheelZoom = Config.Bind(
TogglesSectionName,
"Camera Mousewheel Zoom",
"CameraMousewheelZoom",
true,
"Enables or disables camera movement on mousewheel scroll. Just in case you find it annoying and want that disabled.");
CameraRememberLastPosition = Config.Bind(
TogglesSectionName,
"Remember Last Camera Position",
"CameraRememberLastPosition",
false,
"If enabled, returning to Freecam mode will put the camera to it's last position which is saved when exiting Freecam mode.");
"If enabled, returning to Freecam mode will put the camera to it's last position which was saved when exiting Freecam mode.");
DisableFallDamage = Config.Bind(
TogglesSectionName,
"DisableFallDamage",
true,
"If enabled, will prevent fall damage for the player. Highly recommended to leave this enabled if using TeleportToCamera.");
}
}
}

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<Version>1.4.4</Version>
<TargetFramework>net472</TargetFramework>
<Version>1.3.0</Version>
<Authors>Terkoiz, Kobrakon, CWX</Authors>
<RepositoryUrl>https://dev.sp-tarkov.com/Terkoiz/Freecam</RepositoryUrl>
<PackageLicenseExpression>NCSA</PackageLicenseExpression>
@ -16,11 +16,11 @@
<ItemGroup>
<Reference Include="Aki.Reflection">
<HintPath>..\References\SPT\spt-reflection.dll</HintPath>
<HintPath>..\References\EFT_Managed\Aki.Reflection.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\References\EFT_Managed\Assembly-CSharp.dll</HintPath>
<HintPath>..\References\Hollowed\Assembly-CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Comfort">