first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
using System.Collections.Generic;
using NitroxClient.GameLogic;
using UnityEngine;
namespace NitroxClient.MonoBehaviours.CinematicController;
public class MultiplayerCinematicController : MonoBehaviour
{
private readonly Dictionary<ushort, RemotePlayerCinematicController> controllerByPlayerId = new();
/// <summary>
/// MCCs with the same Animator to reset state if needed.
/// </summary>
private readonly List<MultiplayerCinematicController> multiplayerControllerSameAnimator = new();
private CinematicControllerPrefab controllerPrefab;
private PlayerCinematicController playerController;
public void CallStartCinematicMode(RemotePlayer player)
{
if (!playerController.cinematicModeActive) // Check if local player is occupying the animator.
{
GetController(player).StartCinematicMode(player);
}
}
public void CallCinematicModeEnd(RemotePlayer player)
{
if (!playerController.cinematicModeActive) // Check if local player is occupying the animator.
{
GetController(player).OnPlayerCinematicModeEnd();
}
}
public void CallAllCinematicModeEnd()
{
foreach (RemotePlayerCinematicController remoteController in controllerByPlayerId.Values)
{
remoteController.EndCinematicMode(true);
}
foreach (MultiplayerCinematicController controller in multiplayerControllerSameAnimator)
{
foreach (RemotePlayerCinematicController remoteController in controller.controllerByPlayerId.Values)
{
remoteController.EndCinematicMode(true);
}
}
}
private RemotePlayerCinematicController GetController(RemotePlayer player)
{
if (controllerByPlayerId.TryGetValue(player.PlayerId, out RemotePlayerCinematicController controller))
{
return controller;
}
player.PlayerDisconnectEvent.AddHandler(gameObject, OnPlayerDisconnect);
controller = CreateNewControllerForPlayer();
controllerByPlayerId.Add(player.PlayerId, controller);
return controller;
}
public void OnPlayerDisconnect(RemotePlayer player)
{
if (controllerByPlayerId.TryGetValue(player.PlayerId, out RemotePlayerCinematicController controller))
{
Destroy(controller);
controllerByPlayerId.Remove(player.PlayerId);
}
}
private RemotePlayerCinematicController CreateNewControllerForPlayer()
{
RemotePlayerCinematicController controller = gameObject.AddComponent<RemotePlayerCinematicController>();
controllerPrefab.PopulateRemoteController(controller);
return controller;
}
public void AddOtherControllers(IEnumerable<MultiplayerCinematicController> otherControllers)
{
foreach (MultiplayerCinematicController controller in otherControllers)
{
if (controller.playerController.animator == playerController.animator)
{
multiplayerControllerSameAnimator.Add(controller);
}
}
}
public static MultiplayerCinematicController Initialize(PlayerCinematicController playerController)
{
MultiplayerCinematicController mcp = playerController.gameObject.AddComponent<MultiplayerCinematicController>();
mcp.controllerPrefab = new CinematicControllerPrefab(playerController);
mcp.playerController = playerController;
return mcp;
}
}
public readonly struct CinematicControllerPrefab
{
private readonly Transform animatedTransform;
private readonly Transform endTransform;
private readonly bool onlyUseEndTransformInVr;
private readonly bool playInVr;
private readonly string playerViewAnimationName;
private readonly string playerViewInterpolateAnimParam;
private readonly string animParam;
private readonly string interpolateAnimParam;
private readonly float interpolationTime;
private readonly float interpolationTimeOut;
private readonly string receiversAnimParam;
private readonly GameObject[] animParamReceivers;
private readonly bool interpolateDuringAnimation;
private readonly Animator animator;
// Currently we don't sync playerController.informGameObject but no problem could be found while testing.
public CinematicControllerPrefab(PlayerCinematicController playerController)
{
animatedTransform = playerController.animatedTransform;
endTransform = playerController.endTransform;
onlyUseEndTransformInVr = playerController.onlyUseEndTransformInVr;
playInVr = playerController.playInVr;
playerViewAnimationName = playerController.playerViewAnimationName;
playerViewInterpolateAnimParam = playerController.playerViewInterpolateAnimParam;
animParam = playerController.animParam;
interpolateAnimParam = playerController.interpolateAnimParam;
interpolationTime = playerController.interpolationTime;
interpolationTimeOut = playerController.interpolationTimeOut;
receiversAnimParam = playerController.receiversAnimParam;
animParamReceivers = playerController.animParamReceivers;
interpolateDuringAnimation = playerController.interpolateDuringAnimation;
animator = playerController.animator;
}
public void PopulateRemoteController(RemotePlayerCinematicController remoteController)
{
remoteController.animatedTransform = animatedTransform;
remoteController.informGameObject = null;
remoteController.endTransform = endTransform;
remoteController.onlyUseEndTransformInVr = onlyUseEndTransformInVr;
remoteController.playInVr = playInVr;
remoteController.playerViewAnimationName = playerViewAnimationName;
remoteController.playerViewInterpolateAnimParam = playerViewInterpolateAnimParam;
remoteController.animParam = animParam;
remoteController.interpolateAnimParam = interpolateAnimParam;
remoteController.interpolationTime = interpolationTime;
remoteController.interpolationTimeOut = interpolationTimeOut;
remoteController.receiversAnimParam = receiversAnimParam;
remoteController.animParamReceivers = animParamReceivers;
remoteController.interpolateDuringAnimation = interpolateDuringAnimation;
remoteController.animator = animator;
}
}

View File

@@ -0,0 +1,81 @@
using System.Collections.Generic;
using System.Linq;
using NitroxClient.GameLogic;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic;
using UnityEngine;
namespace NitroxClient.MonoBehaviours.CinematicController;
public class MultiplayerCinematicReference : MonoBehaviour
{
private readonly Dictionary<string, Dictionary<int, MultiplayerCinematicController>> controllerByKey = new();
private bool isEscapePod;
private void Start()
{
// TODO: Currently only single EscapePod is supported, therefor EscapePod.main. Can probably be removed after we use one pod per intro sequence
isEscapePod = gameObject == EscapePod.main.gameObject;
}
public void CallStartCinematicMode(string key, int identifier, RemotePlayer player)
{
if(isEscapePod && this.Resolve<LocalPlayer>().IntroCinematicMode is IntroCinematicMode.PLAYING or IntroCinematicMode.SINGLEPLAYER) return;
if (!controllerByKey.TryGetValue(key, out Dictionary<int, MultiplayerCinematicController> controllers))
{
throw new KeyNotFoundException($"There was no entry for the key {key} at {gameObject.GetFullHierarchyPath()}");
}
if (!controllers.TryGetValue(identifier, out MultiplayerCinematicController controller))
{
throw new KeyNotFoundException($"There was no entry for the identifier {identifier} at {gameObject.GetFullHierarchyPath()}");
}
controller.CallStartCinematicMode(player);
}
public void CallCinematicModeEnd(string key, int identifier, RemotePlayer player)
{
if(isEscapePod && this.Resolve<LocalPlayer>().IntroCinematicMode is IntroCinematicMode.PLAYING or IntroCinematicMode.SINGLEPLAYER) return;
if (!controllerByKey.TryGetValue(key, out Dictionary<int, MultiplayerCinematicController> controllers))
{
throw new KeyNotFoundException($"There was no entry for the key {key} at {gameObject.GetFullHierarchyPath()}");
}
if (!controllers.TryGetValue(identifier, out MultiplayerCinematicController controller))
{
throw new KeyNotFoundException($"There was no entry for the identifier {identifier} at {gameObject.GetFullHierarchyPath()}");
}
controller.CallCinematicModeEnd(player);
}
public static int GetCinematicControllerIdentifier(GameObject controller, GameObject reference) => controller.gameObject.GetHierarchyPath(reference).GetHashCode();
public void AddController(PlayerCinematicController playerController)
{
MultiplayerCinematicController[] allControllers = controllerByKey.SelectMany(n => n.Value.Select(x => x.Value)).ToArray();
if (!controllerByKey.TryGetValue(playerController.playerViewAnimationName, out Dictionary<int, MultiplayerCinematicController> controllers))
{
controllers = new Dictionary<int, MultiplayerCinematicController>();
controllerByKey.Add(playerController.playerViewAnimationName, controllers);
}
int identifier = GetCinematicControllerIdentifier(playerController.gameObject, gameObject);
if (controllers.ContainsKey(identifier))
{
return;
}
MultiplayerCinematicController controller = MultiplayerCinematicController.Initialize(playerController);
controller.AddOtherControllers(allControllers);
allControllers.ForEach(x => x.AddOtherControllers(new[] { controller }));
controllers.Add(identifier, controller);
}
}

View File

@@ -0,0 +1,471 @@
using System;
using NitroxClient.GameLogic;
using UnityEngine;
using static PlayerCinematicController;
namespace NitroxClient.MonoBehaviours.CinematicController;
/// <summary>
/// Override for <see cref="PlayerCinematicController"/>
/// </summary>
public class RemotePlayerCinematicController : MonoBehaviour, IManagedUpdateBehaviour, IManagedLateUpdateBehaviour
{
[AssertNotNull] public Transform animatedTransform;
public GameObject informGameObject;
public Transform endTransform;
public bool onlyUseEndTransformInVr;
public bool playInVr;
public string playerViewAnimationName = "";
public string playerViewInterpolateAnimParam = "";
public string animParam = "cinematicMode";
public string interpolateAnimParam = "";
public float interpolationTime = 0.25f;
public float interpolationTimeOut = 0.25f;
public string receiversAnimParam = "";
public GameObject[] animParamReceivers;
public bool interpolateDuringAnimation;
public bool debug;
public Animator animator;
[NonSerialized] public bool cinematicModeActive;
private Vector3 playerFromPosition = Vector3.zero;
private Quaternion playerFromRotation = Quaternion.identity;
private bool onCinematicModeEndCall;
private float timeStateChanged;
private State _state;
private RemotePlayer player;
private bool subscribed;
private bool _animState;
public static int cinematicModeCount { get; private set; }
public static float cinematicActivityStart { get; private set; }
private State state
{
get => _state;
set
{
timeStateChanged = Time.time;
_state = value;
}
}
public bool animState
{
get => _animState;
private set
{
if (value == _animState)
{
return;
}
if (debug)
{
Debug.Log($"setting cinematic controller {gameObject.name} to: {value}");
}
_animState = value;
if (animParam.Length > 0)
{
SafeAnimator.SetBool(animator, animParam, value);
}
if (receiversAnimParam.Length > 0)
{
for (int i = 0; i < animParamReceivers.Length; i++)
{
animParamReceivers[i].GetComponent<IAnimParamReceiver>()?.ForwardAnimationParameterBool(receiversAnimParam, value);
}
}
if (playerViewAnimationName.Length > 0 && player != null)
{
Animator componentInChildren = player.Body.GetComponentInChildren<Animator>();
if (componentInChildren != null && componentInChildren.gameObject.activeInHierarchy)
{
SafeAnimator.SetBool(componentInChildren, playerViewAnimationName, value);
}
}
SetVrActiveParam();
}
}
public int managedUpdateIndex { get; set; }
public int managedLateUpdateIndex { get; set; }
public string GetProfileTag()
{
return nameof(RemotePlayerCinematicController);
}
public void SetPlayer(RemotePlayer setplayer)
{
if (subscribed && player != setplayer)
{
Subscribe(player, false);
Subscribe(setplayer, true);
}
player = setplayer;
}
public RemotePlayer GetPlayer()
{
return player;
}
private void AddToUpdateManager()
{
BehaviourUpdateUtils.Register((IManagedUpdateBehaviour)this);
BehaviourUpdateUtils.Register((IManagedLateUpdateBehaviour)this);
}
private void RemoveFromUpdateManager()
{
BehaviourUpdateUtils.Deregister((IManagedUpdateBehaviour)this);
BehaviourUpdateUtils.Deregister((IManagedLateUpdateBehaviour)this);
}
private void OnEnable()
{
AddToUpdateManager();
}
private void OnDestroy()
{
RemoveFromUpdateManager();
}
private void Start()
{
SetVrActiveParam();
}
private void SetVrActiveParam()
{
string paramaterName = "vr_active";
bool vrAnimationMode = GameOptions.GetVrAnimationMode();
if (animator != null)
{
animator.SetBool(paramaterName, vrAnimationMode);
}
foreach (GameObject animatedObject in animParamReceivers)
{
animatedObject.GetComponent<IAnimParamReceiver>()?.ForwardAnimationParameterBool(paramaterName, vrAnimationMode);
}
}
private bool UseEndTransform()
{
if (endTransform == null)
{
return false;
}
if (onlyUseEndTransformInVr)
{
return GameOptions.GetVrAnimationMode();
}
return true;
}
private void SkipCinematic(RemotePlayer player)
{
this.player = player;
if (player != null)
{
Transform component = player.Body.GetComponent<Transform>();
if (UseEndTransform())
{
component.position = endTransform.position;
component.rotation = endTransform.rotation;
}
}
if (informGameObject != null)
{
informGameObject.SendMessage(nameof(CinematicModeTriggerBase.OnPlayerCinematicModeEnd), this, SendMessageOptions.DontRequireReceiver);
}
}
public void StartCinematicMode(RemotePlayer setplayer)
{
if (debug)
{
Debug.Log($"{gameObject.name}.StartCinematicMode");
}
if (!cinematicModeActive)
{
player = null;
if (!playInVr && GameOptions.GetVrAnimationMode())
{
if (debug)
{
Debug.Log($"{gameObject.name} skip cinematic");
}
SkipCinematic(setplayer);
return;
}
animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
cinematicModeActive = true;
if (setplayer != null)
{
SetPlayer(setplayer);
Subscribe(player, true);
}
state = State.In;
if (informGameObject != null)
{
informGameObject.SendMessage(nameof(DockedVehicleHandTarget.OnPlayerCinematicModeStart), this, SendMessageOptions.DontRequireReceiver);
}
if (player != null)
{
Transform component = player.Body.GetComponent<Transform>();
playerFromPosition = component.position;
playerFromRotation = component.rotation;
if (playerViewInterpolateAnimParam.Length > 0)
{
SafeAnimator.SetBool(player.Body.GetComponentInChildren<Animator>(), playerViewInterpolateAnimParam, true);
}
}
if (interpolateAnimParam.Length > 0)
{
SafeAnimator.SetBool(animator, interpolateAnimParam, true);
}
if (interpolateDuringAnimation)
{
animState = true;
}
if (debug)
{
Debug.Log($"{gameObject.name} successfully started cinematic");
}
if (cinematicModeCount == 0)
{
cinematicActivityStart = Time.time;
}
cinematicModeCount++;
}
else if (debug)
{
Debug.Log($"{gameObject.name} cinematic already active!");
}
}
public void EndCinematicMode(bool reset = false)
{
if (cinematicModeActive)
{
if (reset) // Added by us
{
animator.Rebind();
animator.Update(0f);
}
animator.cullingMode = AnimatorCullingMode.CullCompletely;
animState = false;
state = State.None;
cinematicModeActive = false;
cinematicModeCount--;
}
}
public void OnPlayerCinematicModeEnd()
{
if (!cinematicModeActive || onCinematicModeEndCall)
{
return;
}
if (player != null)
{
UpdatePlayerPosition();
}
animState = false;
if (UseEndTransform())
{
state = State.Out;
if (player != null)
{
Transform component = player.Body.GetComponent<Transform>();
playerFromPosition = component.position;
playerFromRotation = component.rotation;
}
}
else
{
EndCinematicMode();
}
if (informGameObject != null)
{
onCinematicModeEndCall = true;
informGameObject.SendMessage(nameof(DockedVehicleHandTarget.OnPlayerCinematicModeEnd), this, SendMessageOptions.DontRequireReceiver);
onCinematicModeEndCall = false;
}
}
private void UpdatePlayerPosition()
{
Transform component = player.Body.GetComponent<Transform>();
component.position = animatedTransform.position;
component.rotation = animatedTransform.rotation;
}
public void ManagedLateUpdate()
{
if (!cinematicModeActive)
{
return;
}
float num = Time.time - timeStateChanged;
float timedOutScalar;
Transform transform = null;
if (player != null && player.Body)
{
transform = player.Body.GetComponent<Transform>();
}
bool isVrAnimationMode = !GameOptions.GetVrAnimationMode();
switch (state)
{
case State.In:
timedOutScalar = interpolationTime != 0f && isVrAnimationMode ? Mathf.Clamp01(num / interpolationTime) : 1f;
if (player != null && transform)
{
transform.position = Vector3.Lerp(playerFromPosition, animatedTransform.position, timedOutScalar);
transform.rotation = Quaternion.Slerp(playerFromRotation, animatedTransform.rotation, timedOutScalar);
}
if (timedOutScalar == 1f)
{
state = State.Update;
animState = true;
if (interpolateAnimParam.Length > 0)
{
SafeAnimator.SetBool(animator, interpolateAnimParam, false);
}
if (playerViewInterpolateAnimParam.Length > 0 && player != null)
{
SafeAnimator.SetBool(player.Body.GetComponentInChildren<Animator>(), playerViewInterpolateAnimParam, false);
}
}
break;
case State.Update:
if (player != null)
{
UpdatePlayerPosition();
}
break;
case State.Out:
timedOutScalar = interpolationTimeOut != 0f && isVrAnimationMode ? Mathf.Clamp01(num / interpolationTimeOut) : 1f;
if (player != null && transform)
{
transform.position = Vector3.Lerp(playerFromPosition, endTransform.position, timedOutScalar);
transform.rotation = Quaternion.Slerp(playerFromRotation, endTransform.rotation, timedOutScalar);
}
if (timedOutScalar == 1f)
{
EndCinematicMode();
}
break;
}
}
public bool IsCinematicModeActive()
{
return cinematicModeActive;
}
private void OnDisable()
{
RemoveFromUpdateManager();
if (subscribed)
{
Subscribe(player, false);
}
EndCinematicMode();
}
private void OnPlayerDeath(RemotePlayer player)
{
EndCinematicMode();
animator.Rebind();
}
private void Subscribe(RemotePlayer player, bool state)
{
if (player == null)
{
subscribed = false;
}
else if (subscribed != state)
{
if (state)
{
player.PlayerDeathEvent.AddHandler(gameObject, OnPlayerDeath);
}
else
{
player.PlayerDeathEvent.RemoveHandler(gameObject, OnPlayerDeath);
}
subscribed = state;
}
}
public void ManagedUpdate()
{
if (!cinematicModeActive && subscribed)
{
Subscribe(player, false);
}
}
}