mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-01 05:49:23 +00:00
397 lines
No EOL
12 KiB
C#
397 lines
No EOL
12 KiB
C#
using System.Collections;
|
|
using ABI_RC.Core.IO;
|
|
using ABI_RC.Core.Player;
|
|
using ABI_RC.Core.Player.AvatarTracking;
|
|
using ABI_RC.Core.UI;
|
|
using ABI_RC.Systems.GameEventSystem;
|
|
using NAK.AvatarScaleMod.Components;
|
|
using NAK.AvatarScaleMod.Networking;
|
|
using UnityEngine;
|
|
|
|
namespace NAK.AvatarScaleMod.AvatarScaling;
|
|
|
|
public class AvatarScaleManager : MonoBehaviour
|
|
{
|
|
public static AvatarScaleManager Instance { get; private set; }
|
|
|
|
private LocalScaler _localAvatarScaler;
|
|
private Dictionary<string, NetworkScaler> _networkedScalers;
|
|
|
|
private Coroutine _heightUpdateCoroutine;
|
|
private readonly YieldInstruction _heightUpdateYield = new WaitForEndOfFrame();
|
|
|
|
#region Universal Scaling Limits
|
|
|
|
// ReSharper disable MemberCanBePrivate.Global
|
|
// To match AvatarScaleTool: https://github.com/NotAKidoS/AvatarScaleTool/tree/main
|
|
public const float DefaultMinHeight = 0.25f;
|
|
public const float DefaultMaxHeight = 2.50f;
|
|
// ReSharper restore MemberCanBePrivate.Global
|
|
|
|
// Universal Scaling Limits
|
|
public static float MinHeight { get; private set; } = DefaultMinHeight;
|
|
public static float MaxHeight { get; private set; } = DefaultMaxHeight;
|
|
|
|
#endregion
|
|
|
|
#region Settings
|
|
|
|
private bool _settingUniversalScaling;
|
|
public bool Setting_UniversalScaling
|
|
{
|
|
get => _settingUniversalScaling;
|
|
set
|
|
{
|
|
if (_settingUniversalScaling == value)
|
|
return;
|
|
|
|
_settingUniversalScaling = value;
|
|
|
|
if (_localAvatarScaler != null)
|
|
_localAvatarScaler.UseTargetHeight = value;
|
|
}
|
|
}
|
|
|
|
private bool _settingAnimationClipScalingOverride;
|
|
public bool Setting_AnimationClipScalingOverride
|
|
{
|
|
get => _settingAnimationClipScalingOverride;
|
|
set
|
|
{
|
|
if (_settingAnimationClipScalingOverride == value)
|
|
return;
|
|
|
|
_settingAnimationClipScalingOverride = value;
|
|
|
|
if (_localAvatarScaler != null)
|
|
_localAvatarScaler.overrideAnimationHeight = value;
|
|
}
|
|
}
|
|
|
|
private bool _settingPersistentHeight;
|
|
public bool Setting_PersistentHeight
|
|
{
|
|
get => _settingPersistentHeight;
|
|
set
|
|
{
|
|
if (_settingPersistentHeight == value)
|
|
return;
|
|
|
|
_settingPersistentHeight = value;
|
|
|
|
// if (_localAvatarScaler != null)
|
|
// _localAvatarScaler.persistHeight = value;
|
|
}
|
|
}
|
|
|
|
private float _lastTargetHeight = -1f;
|
|
public float LastTargetHeight
|
|
{
|
|
get => _lastTargetHeight;
|
|
set
|
|
{
|
|
_lastTargetHeight = value;
|
|
ModSettings.EntryHiddenAvatarHeight.Value = _lastTargetHeight;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Unity Events
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance != null
|
|
&& Instance != this)
|
|
{
|
|
DestroyImmediate(this);
|
|
return;
|
|
}
|
|
|
|
Instance = this;
|
|
_networkedScalers = new Dictionary<string, NetworkScaler>();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
_localAvatarScaler = PlayerSetup.Instance.gameObject.AddComponent<LocalScaler>();
|
|
_localAvatarScaler.Initialize();
|
|
|
|
_settingUniversalScaling = ModSettings.EntryUseUniversalScaling.Value;
|
|
Setting_AnimationClipScalingOverride = ModSettings.EntryAnimationScalingOverride.Value;
|
|
Setting_PersistentHeight = ModSettings.EntryPersistentHeight.Value;
|
|
_lastTargetHeight = ModSettings.EntryPersistThroughRestart.Value
|
|
? ModSettings.EntryHiddenAvatarHeight.Value : -1f; // -1f is default
|
|
|
|
// listen for events
|
|
_localAvatarScaler.OnAnimatedHeightOverride += OnAnimationHeightOverride;
|
|
|
|
CVRGameEventSystem.Instance.OnConnected.AddListener(OnInstanceConnected);
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (_heightUpdateCoroutine != null) StopCoroutine(_heightUpdateCoroutine);
|
|
_heightUpdateCoroutine = StartCoroutine(HeightUpdateCoroutine());
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (_heightUpdateCoroutine != null) StopCoroutine(_heightUpdateCoroutine);
|
|
_heightUpdateCoroutine = null;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
_heightUpdateCoroutine = null;
|
|
CVRGameEventSystem.Instance.OnConnected.RemoveListener(OnInstanceConnected);
|
|
|
|
if (_localAvatarScaler != null) Destroy(_localAvatarScaler);
|
|
_localAvatarScaler = null;
|
|
|
|
foreach (NetworkScaler scaler in _networkedScalers.Values) Destroy(scaler);
|
|
_networkedScalers.Clear();
|
|
|
|
if (Instance == this)
|
|
Instance = null;
|
|
}
|
|
|
|
// only update the height per scaler once per frame, to prevent spam & jitter
|
|
// this is to ensure that the height is also set at correct time during frame, no matter when it is called
|
|
private IEnumerator HeightUpdateCoroutine()
|
|
{
|
|
while (enabled) yield return _heightUpdateYield;
|
|
// ReSharper disable once IteratorNeverReturns
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Game Events
|
|
|
|
public void OnInstanceConnected(string instanceId)
|
|
{
|
|
// TODO: need to know if this causes issues when in a reconnection loop
|
|
SchedulerSystem.AddJob(ModNetwork.RequestHeightSync, 2f, 1f, 1);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Local Methods
|
|
|
|
public void OnAvatarInstantiated(PlayerSetup playerSetup)
|
|
{
|
|
if (playerSetup._avatar == null)
|
|
return;
|
|
|
|
_localAvatarScaler.OnAvatarInstantiated(playerSetup._avatar, playerSetup._initialAvatarHeight,
|
|
playerSetup.initialScale);
|
|
|
|
if (!_settingUniversalScaling)
|
|
return;
|
|
|
|
SetTargetHeight(_lastTargetHeight);
|
|
}
|
|
|
|
public void OnAvatarDestroyed(PlayerSetup playerSetup)
|
|
{
|
|
if (_localAvatarScaler != null)
|
|
_localAvatarScaler.OnAvatarDestroyed();
|
|
}
|
|
|
|
public void SetTargetHeight(float targetHeight)
|
|
{
|
|
_lastTargetHeight = targetHeight; // save for persistent height
|
|
|
|
if (_localAvatarScaler == null)
|
|
return;
|
|
|
|
_localAvatarScaler.TargetHeight = _lastTargetHeight;
|
|
}
|
|
|
|
public float GetHeight()
|
|
{
|
|
if (_localAvatarScaler == null)
|
|
return PlayerAvatarPoint.DefaultAvatarHeight;
|
|
|
|
if (!_localAvatarScaler.IsForcingHeight())
|
|
return PlayerSetup.Instance.GetAvatarHeight();
|
|
|
|
return _localAvatarScaler.GetTargetHeight();
|
|
}
|
|
|
|
public float GetAnimationClipHeight()
|
|
{
|
|
if (_localAvatarScaler == null)
|
|
return PlayerAvatarPoint.DefaultAvatarHeight;
|
|
|
|
if (!_localAvatarScaler.IsForcingHeight())
|
|
return PlayerSetup.Instance.GetAvatarHeight();
|
|
|
|
return _localAvatarScaler.GetAnimatedHeight();
|
|
}
|
|
|
|
public float GetHeightForNetwork()
|
|
{
|
|
if (!_settingUniversalScaling)
|
|
return -1f;
|
|
|
|
if (_localAvatarScaler == null)
|
|
return -1f;
|
|
|
|
if (!_localAvatarScaler.IsForcingHeight())
|
|
return -1f;
|
|
|
|
return _localAvatarScaler.GetTargetHeight();
|
|
}
|
|
|
|
public float GetInitialHeight()
|
|
{
|
|
if (_localAvatarScaler == null)
|
|
return -1f;
|
|
|
|
return _localAvatarScaler.GetInitialHeight();
|
|
}
|
|
|
|
public bool IsHeightAdjustedFromInitial()
|
|
{
|
|
return _localAvatarScaler != null && _localAvatarScaler.IsForcingHeight();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Network Methods
|
|
|
|
public bool DoesNetworkHeightScalerExist(string playerId)
|
|
=> _networkedScalers.ContainsKey(playerId);
|
|
|
|
public int GetNetworkHeightScalerCount()
|
|
=> _networkedScalers.Count;
|
|
|
|
public float GetNetworkHeight(string playerId)
|
|
{
|
|
if (_networkedScalers.TryGetValue(playerId, out NetworkScaler scaler))
|
|
if (scaler.IsForcingHeight()) return scaler.GetTargetHeight();
|
|
|
|
//doesn't have mod or has no custom height, get from player avatar directly
|
|
CVRPlayerEntity playerEntity = CVRPlayerManager.Instance.NetworkPlayers.Find((players) => players.Uuid == playerId);
|
|
if (playerEntity != null && playerEntity.PuppetMaster != null)
|
|
return playerEntity.PuppetMaster.netIkController.GetRemoteHeight();
|
|
|
|
// player is invalid???
|
|
return -1f;
|
|
}
|
|
|
|
// we will create a Universal Scaler for only users that send a height update
|
|
// this is sent at a rate of 10s locally!
|
|
|
|
internal void OnNetworkHeightUpdateReceived(string playerId, float targetHeight)
|
|
{
|
|
if (_networkedScalers.TryGetValue(playerId, out NetworkScaler scaler))
|
|
scaler.TargetHeight = targetHeight;
|
|
else
|
|
SetupHeightScalerForNetwork(playerId, targetHeight);
|
|
}
|
|
|
|
internal void OnNetworkAvatarInstantiated(PuppetMaster puppetMaster)
|
|
{
|
|
var playerId = puppetMaster._playerDescriptor.ownerId;
|
|
if (_networkedScalers.TryGetValue(playerId, out NetworkScaler scaler))
|
|
scaler.OnAvatarInstantiated(puppetMaster.avatarObject, puppetMaster.netIkController._initialHeight,
|
|
puppetMaster.netIkController._initialScale);
|
|
}
|
|
|
|
internal void OnNetworkAvatarDestroyed(PuppetMaster puppetMaster)
|
|
{
|
|
// on disconnect
|
|
if (puppetMaster == null || puppetMaster._playerDescriptor == null)
|
|
return;
|
|
|
|
var playerId = puppetMaster._playerDescriptor.ownerId;
|
|
if (_networkedScalers.TryGetValue(playerId, out NetworkScaler scaler))
|
|
scaler.OnAvatarDestroyed();
|
|
}
|
|
|
|
internal void RemoveNetworkHeightScaler(string playerId)
|
|
{
|
|
if (_networkedScalers.ContainsKey(playerId))
|
|
{
|
|
AvatarScaleMod.Logger.Msg(
|
|
$"Removed user height scaler! This is hopefully due to a disconnect or block. : {playerId}");
|
|
|
|
_networkedScalers.Remove(playerId);
|
|
return;
|
|
}
|
|
|
|
AvatarScaleMod.Logger.Msg(
|
|
$"Failed to remove a user height scaler! This shouldn't happen. : {playerId}");
|
|
}
|
|
|
|
private void SetupHeightScalerForNetwork(string playerId, float targetHeight)
|
|
{
|
|
CVRPlayerEntity playerEntity =
|
|
CVRPlayerManager.Instance.NetworkPlayers.Find(players => players.Uuid == playerId);
|
|
|
|
PuppetMaster puppetMaster = playerEntity?.PuppetMaster;
|
|
|
|
if (playerEntity == null || puppetMaster == null)
|
|
{
|
|
AvatarScaleMod.Logger.Error(
|
|
$"Attempted to set up height scaler for user which does not exist! : {playerId}");
|
|
return;
|
|
}
|
|
|
|
AvatarScaleMod.Logger.Msg(
|
|
$"Setting up new height scaler for user which has sent a height update! : {playerId}");
|
|
|
|
if (_networkedScalers.ContainsKey(playerId))
|
|
_networkedScalers.Remove(playerId); // ??
|
|
|
|
NetworkScaler scaler = puppetMaster.gameObject.AddComponent<NetworkScaler>();
|
|
scaler.Initialize(playerId);
|
|
|
|
scaler.OnAvatarInstantiated(puppetMaster.avatarObject, puppetMaster.netIkController._initialHeight,
|
|
puppetMaster.netIkController._initialScale);
|
|
|
|
_networkedScalers[playerId] = scaler;
|
|
|
|
scaler.TargetHeight = targetHeight;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Manager Methods
|
|
|
|
// sometimes fun to play with via UE
|
|
public void SetUniversalScalingLimit(float min, float max)
|
|
{
|
|
const float HardCodedMinLimit = 0.01f;
|
|
const float HardCodedMaxLimit = 100f;
|
|
|
|
MinHeight = Mathf.Clamp(min, HardCodedMinLimit, HardCodedMaxLimit);
|
|
MaxHeight = Mathf.Clamp(max, HardCodedMinLimit, HardCodedMaxLimit);
|
|
|
|
AvatarScaleMod.Logger.Msg($"Universal Scaling Limits changed: {min} - {max}");
|
|
AvatarScaleMod.Logger.Warning("This will not network to other users unless they also have the same limits set!");
|
|
}
|
|
|
|
public void ResetUniversalScalingLimit()
|
|
{
|
|
MinHeight = DefaultMinHeight;
|
|
MaxHeight = DefaultMaxHeight;
|
|
|
|
AvatarScaleMod.Logger.Msg("Universal Scaling Limits reset to default!");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Event Listeners
|
|
|
|
private static void OnAnimationHeightOverride(BaseScaler scaler)
|
|
{
|
|
AvatarScaleMod.Logger.Msg("AnimationClip-based avatar scaling detected. Disabling Universal Scaling.");
|
|
CohtmlHud.Instance.ViewDropTextImmediate("(Local) AvatarScaleMod", "Avatar Scale Changed!",
|
|
"Universal Scaling is now disabled in favor of built-in avatar scaling.");
|
|
}
|
|
|
|
#endregion
|
|
} |