Merge pull request #289 from misternebula/animations

rework animations, fix many worldobject things
This commit is contained in:
_nebula 2021-05-02 09:44:03 +01:00 committed by GitHub
commit affcc9bef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 527 additions and 268 deletions

View File

@ -1,11 +0,0 @@
using QSB.Animation.Character.WorldObjects;
using QSB.WorldSync;
namespace QSB.Animation.Character
{
internal class CharacterAnimManager : WorldObjectManager
{
protected override void RebuildWorldObjects(OWScene scene)
=> QSBWorldSync.Init<QSBCharacterAnimController, CharacterAnimController>();
}
}

View File

@ -1,111 +0,0 @@
using OWML.Common;
using QSB.Animation.Character.WorldObjects;
using QSB.Events;
using QSB.Patches;
using QSB.Player;
using QSB.Utility;
using QSB.WorldSync;
using UnityEngine;
namespace QSB.ConversationSync.Patches
{
public class CharacterAnimationPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
public override void DoPatches()
{
QSBCore.HarmonyHelper.AddPrefix<CharacterAnimController>("OnAnimatorIK", typeof(CharacterAnimationPatches), nameof(AnimController_OnAnimatorIK));
QSBCore.HarmonyHelper.AddPrefix<CharacterAnimController>("OnZoneEntry", typeof(CharacterAnimationPatches), nameof(AnimController_OnZoneEntry));
QSBCore.HarmonyHelper.AddPrefix<CharacterAnimController>("OnZoneExit", typeof(CharacterAnimationPatches), nameof(AnimController_OnZoneExit));
}
public override void DoUnpatches()
{
QSBCore.HarmonyHelper.Unpatch<CharacterAnimController>("OnAnimatorIK");
QSBCore.HarmonyHelper.Unpatch<CharacterAnimController>("OnZoneExit");
}
public static bool AnimController_OnAnimatorIK(
CharacterAnimController __instance,
float ___headTrackingWeight,
bool ___lookOnlyWhenTalking,
bool ____playerInHeadZone,
bool ____inConversation,
ref float ____currentLookWeight,
ref Vector3 ____currentLookTarget,
DampedSpring3D ___lookSpring,
Animator ____animator,
CharacterDialogueTree ____dialogueTree)
{
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(____dialogueTree);
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(__instance); // TODO : maybe cache this somewhere... or assess how slow this is
Vector3 position;
if (____inConversation)
{
if (playerId == uint.MaxValue)
{
DebugLog.DebugWrite($"Error - {__instance.name} is in conversation with a null player! Defaulting to active camera.", MessageType.Error);
position = Locator.GetActiveCamera().transform.position;
}
else
{
var player = QSBPlayerManager.GetPlayer(playerId);
position = player.CameraBody == null
? Locator.GetActiveCamera().transform.position
: player.CameraBody.transform.position;
}
}
else if (!___lookOnlyWhenTalking && qsbObj.GetPlayersInHeadZone().Count != 0)
{
position = QSBPlayerManager.GetClosestPlayerToWorldPoint(qsbObj.GetPlayersInHeadZone(), __instance.transform.position).CameraBody.transform.position;
}
else
{
position = QSBPlayerManager.GetClosestPlayerToWorldPoint(__instance.transform.position, true).CameraBody.transform.position;
}
var localPosition = ____animator.transform.InverseTransformPoint(position);
var targetWeight = ___headTrackingWeight;
if (___lookOnlyWhenTalking)
{
if (!____inConversation || qsbObj.GetPlayersInHeadZone().Count == 0)
{
targetWeight *= 0;
}
}
else
{
if (qsbObj.GetPlayersInHeadZone().Count == 0)
{
targetWeight *= 0;
}
}
____currentLookWeight = Mathf.Lerp(____currentLookWeight, targetWeight, Time.deltaTime * 2f);
____currentLookTarget = ___lookSpring.Update(____currentLookTarget, localPosition, Time.deltaTime);
____animator.SetLookAtPosition(____animator.transform.TransformPoint(____currentLookTarget));
____animator.SetLookAtWeight(____currentLookWeight);
return false;
}
public static bool AnimController_OnZoneExit(CharacterAnimController __instance)
{
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(__instance);
QSBEventManager.FireEvent(EventNames.QSBExitHeadZone, qsbObj.ObjectId);
return false;
}
public static bool AnimController_OnZoneEntry(CharacterAnimController __instance)
{
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(__instance);
QSBEventManager.FireEvent(EventNames.QSBEnterHeadZone, qsbObj.ObjectId);
return false;
}
}
}

View File

@ -1,51 +0,0 @@
using OWML.Utils;
using QSB.Player;
using QSB.WorldSync;
using System.Collections.Generic;
namespace QSB.Animation.Character.WorldObjects
{
internal class QSBCharacterAnimController : WorldObject<CharacterAnimController>
{
private readonly List<PlayerInfo> _playersInHeadZone = new List<PlayerInfo>();
public override void Init(CharacterAnimController controller, int id)
{
ObjectId = id;
AttachedObject = controller;
}
public List<PlayerInfo> GetPlayersInHeadZone()
=> _playersInHeadZone;
public void AddPlayerToHeadZone(PlayerInfo player)
{
if (_playersInHeadZone.Contains(player))
{
return;
}
_playersInHeadZone.Add(player);
}
public void RemovePlayerFromHeadZone(PlayerInfo player)
{
if (!_playersInHeadZone.Contains(player))
{
return;
}
_playersInHeadZone.Remove(player);
}
public void StartConversation()
{
AttachedObject.SetValue("_inConversation", true);
QSBWorldSync.RaiseEvent(AttachedObject, "OnStartConversation");
}
public void EndConversation()
{
AttachedObject.SetValue("_inConversation", false);
QSBWorldSync.RaiseEvent(AttachedObject, "OnEndConversation");
}
}
}

View File

@ -0,0 +1,8 @@
namespace QSB.Animation.NPC
{
internal enum AnimationEvent
{
StartConversation,
EndConversation
}
}

View File

@ -0,0 +1,20 @@
using QSB.Animation.NPC.WorldObjects;
using QSB.WorldSync;
using System.Linq;
using UnityEngine;
namespace QSB.Animation.NPC
{
internal class CharacterAnimManager : WorldObjectManager
{
protected override void RebuildWorldObjects(OWScene scene)
{
QSBWorldSync.Init<QSBCharacterAnimController, CharacterAnimController>();
QSBWorldSync.Init<QSBTravelerController, TravelerController>();
//TODO : this is the wrong place to put this... move it to Conversations?
QSBWorldSync.OldDialogueTrees.Clear();
QSBWorldSync.OldDialogueTrees = Resources.FindObjectsOfTypeAll<CharacterDialogueTree>().ToList();
}
}
}

View File

@ -0,0 +1,37 @@
using QSB.Animation.NPC.WorldObjects;
using QSB.Events;
using QSB.WorldSync;
namespace QSB.Animation.NPC.Events
{
internal class NpcAnimationEvent : QSBEvent<NpcAnimationMessage>
{
public override EventType Type => EventType.NpcAnimEvent;
public override void SetupListener() => GlobalMessenger<AnimationEvent, int>.AddListener(EventNames.QSBNpcAnimEvent, Handler);
public override void CloseListener() => GlobalMessenger<AnimationEvent, int>.RemoveListener(EventNames.QSBNpcAnimEvent, Handler);
private void Handler(AnimationEvent animEvent, int index) => SendEvent(CreateMessage(animEvent, index));
private NpcAnimationMessage CreateMessage(AnimationEvent animEvent, int index) => new NpcAnimationMessage
{
AboutId = LocalPlayerId,
AnimationEvent = animEvent,
AnimControllerIndex = index
};
public override void OnReceiveRemote(bool server, NpcAnimationMessage message)
{
var qsbObj = QSBWorldSync.GetWorldFromId<INpcAnimController>(message.AnimControllerIndex);
switch (message.AnimationEvent)
{
case AnimationEvent.StartConversation:
qsbObj.StartConversation();
break;
case AnimationEvent.EndConversation:
qsbObj.EndConversation();
break;
}
}
}
}

View File

@ -0,0 +1,25 @@
using QSB.Messaging;
using QuantumUNET.Transport;
namespace QSB.Animation.NPC.Events
{
internal class NpcAnimationMessage : PlayerMessage
{
public AnimationEvent AnimationEvent { get; set; }
public int AnimControllerIndex { get; set; }
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
AnimationEvent = (AnimationEvent)reader.ReadInt32();
AnimControllerIndex = reader.ReadInt32();
}
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write((int)AnimationEvent);
writer.Write(AnimControllerIndex);
}
}
}

View File

@ -0,0 +1,196 @@
using OWML.Common;
using QSB.Animation.NPC.WorldObjects;
using QSB.ConversationSync;
using QSB.Events;
using QSB.Patches;
using QSB.Player;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace QSB.Animation.NPC.Patches
{
public class CharacterAnimationPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
public override void DoPatches()
{
QSBCore.HarmonyHelper.AddPrefix<CharacterAnimController>("OnAnimatorIK", typeof(CharacterAnimationPatches), nameof(AnimController_OnAnimatorIK));
QSBCore.HarmonyHelper.AddPrefix<CharacterAnimController>("OnZoneEntry", typeof(CharacterAnimationPatches), nameof(AnimController_OnZoneEntry));
QSBCore.HarmonyHelper.AddPrefix<CharacterAnimController>("OnZoneExit", typeof(CharacterAnimationPatches), nameof(AnimController_OnZoneExit));
QSBCore.HarmonyHelper.AddPrefix<FacePlayerWhenTalking>("OnStartConversation", typeof(CharacterAnimationPatches), nameof(FacePlayerWhenTalking_OnStartConversation));
QSBCore.HarmonyHelper.AddPrefix<CharacterDialogueTree>("StartConversation", typeof(CharacterAnimationPatches), nameof(CharacterDialogueTree_StartConversation));
QSBCore.HarmonyHelper.AddPrefix<CharacterDialogueTree>("EndConversation", typeof(CharacterAnimationPatches), nameof(CharacterDialogueTree_EndConversation));
QSBCore.HarmonyHelper.AddPrefix<KidRockController>("Update", typeof(CharacterAnimationPatches), nameof(KidRockController_Update));
}
public override void DoUnpatches()
{
QSBCore.HarmonyHelper.Unpatch<CharacterAnimController>("OnAnimatorIK");
QSBCore.HarmonyHelper.Unpatch<CharacterAnimController>("OnZoneEntry");
QSBCore.HarmonyHelper.Unpatch<CharacterAnimController>("OnZoneExit");
QSBCore.HarmonyHelper.Unpatch<FacePlayerWhenTalking>("OnStartConversation");
QSBCore.HarmonyHelper.Unpatch<CharacterDialogueTree>("StartConversation");
QSBCore.HarmonyHelper.Unpatch<CharacterDialogueTree>("EndConversation");
QSBCore.HarmonyHelper.Unpatch<KidRockController>("Update");
}
public static bool AnimController_OnAnimatorIK(
CharacterAnimController __instance,
float ___headTrackingWeight,
bool ___lookOnlyWhenTalking,
bool ____playerInHeadZone,
bool ____inConversation,
ref float ____currentLookWeight,
ref Vector3 ____currentLookTarget,
DampedSpring3D ___lookSpring,
Animator ____animator,
CharacterDialogueTree ____dialogueTree)
{
if (!WorldObjectManager.AllReady)
{
return false;
}
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(____dialogueTree);
var player = QSBPlayerManager.GetPlayer(playerId);
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(__instance); // TODO : maybe cache this somewhere... or assess how slow this is
PlayerInfo playerToUse;
if (____inConversation)
{
if (playerId == uint.MaxValue)
{
DebugLog.DebugWrite($"Error - {__instance.name} is in conversation with a null player! Defaulting to active camera.", MessageType.Error);
playerToUse = QSBPlayerManager.LocalPlayer;
}
else
{
playerToUse = player.CameraBody == null
? QSBPlayerManager.LocalPlayer
: player;
}
}
else if (!___lookOnlyWhenTalking && qsbObj.GetPlayersInHeadZone().Count != 0) // TODO : maybe this would be more fun if characters looked between players at random times? :P
{
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(qsbObj.GetPlayersInHeadZone(), __instance.transform.position);
}
else
{
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(__instance.transform.position, true);
}
var localPosition = ____animator.transform.InverseTransformPoint(playerToUse.CameraBody.transform.position);
var targetWeight = ___headTrackingWeight;
if (___lookOnlyWhenTalking)
{
if (!____inConversation
|| qsbObj.GetPlayersInHeadZone().Count == 0
|| !qsbObj.GetPlayersInHeadZone().Contains(playerToUse))
{
targetWeight *= 0;
}
}
else
{
if (qsbObj.GetPlayersInHeadZone().Count == 0
|| !qsbObj.GetPlayersInHeadZone().Contains(playerToUse))
{
targetWeight *= 0;
}
}
____currentLookWeight = Mathf.Lerp(____currentLookWeight, targetWeight, Time.deltaTime * 2f);
____currentLookTarget = ___lookSpring.Update(____currentLookTarget, localPosition, Time.deltaTime);
____animator.SetLookAtPosition(____animator.transform.TransformPoint(____currentLookTarget));
____animator.SetLookAtWeight(____currentLookWeight);
return false;
}
public static bool AnimController_OnZoneExit(CharacterAnimController __instance)
{
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(__instance);
QSBEventManager.FireEvent(EventNames.QSBExitHeadZone, qsbObj.ObjectId);
return false;
}
public static bool AnimController_OnZoneEntry(CharacterAnimController __instance)
{
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(__instance);
QSBEventManager.FireEvent(EventNames.QSBEnterHeadZone, qsbObj.ObjectId);
return false;
}
public static bool FacePlayerWhenTalking_OnStartConversation(
FacePlayerWhenTalking __instance,
CharacterDialogueTree ____dialogueTree)
{
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(____dialogueTree);
if (playerId == uint.MaxValue)
{
DebugLog.ToConsole($"Error - No player talking to {____dialogueTree.name}!", MessageType.Error);
return false;
}
var player = QSBPlayerManager.GetPlayer(playerId);
var distance = player.Body.transform.position - __instance.transform.position;
var vector2 = distance - Vector3.Project(distance, __instance.transform.up);
var angle = Vector3.Angle(__instance.transform.forward, vector2) * Mathf.Sign(Vector3.Dot(vector2, __instance.transform.right));
var axis = __instance.transform.parent.InverseTransformDirection(__instance.transform.up);
var lhs = Quaternion.AngleAxis(angle, axis);
__instance.GetType().GetMethod("FaceLocalRotation", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, new object[] { lhs * __instance.transform.localRotation });
return false;
}
public static bool CharacterDialogueTree_StartConversation(CharacterDialogueTree __instance)
{
var allNpcAnimControllers = QSBWorldSync.GetWorldObjects<INpcAnimController>();
var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance);
if (ownerOfThis == default)
{
return true;
}
var id = QSBWorldSync.GetIdFromTypeSubset(ownerOfThis);
QSBEventManager.FireEvent(EventNames.QSBNpcAnimEvent, AnimationEvent.StartConversation, id);
return true;
}
public static bool CharacterDialogueTree_EndConversation(CharacterDialogueTree __instance)
{
var allNpcAnimControllers = QSBWorldSync.GetWorldObjects<INpcAnimController>();
var ownerOfThis = allNpcAnimControllers.FirstOrDefault(x => x.GetDialogueTree() == __instance);
if (ownerOfThis == default)
{
return true;
}
var id = QSBWorldSync.GetIdFromTypeSubset(ownerOfThis);
QSBEventManager.FireEvent(EventNames.QSBNpcAnimEvent, AnimationEvent.EndConversation, id);
return true;
}
public static bool KidRockController_Update(
KidRockController __instance,
bool ____throwingRock,
CharacterDialogueTree ____dialogueTree,
float ____nextThrowTime)
{
if (!WorldObjectManager.AllReady)
{
return true;
}
var qsbObj = QSBWorldSync.GetWorldObjects<QSBCharacterAnimController>().First(x => x.GetDialogueTree() == ____dialogueTree);
if (!____throwingRock && !qsbObj.InConversation() && Time.time > ____nextThrowTime)
{
__instance.GetType().GetMethod("StartRockThrow", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(__instance, null);
}
return false;
}
}
}

View File

@ -0,0 +1,10 @@
namespace QSB.Animation.NPC.WorldObjects
{
public interface INpcAnimController
{
CharacterDialogueTree GetDialogueTree();
void StartConversation();
void EndConversation();
bool InConversation();
}
}

View File

@ -0,0 +1,25 @@
using QSB.WorldSync;
using UnityEngine;
namespace QSB.Animation.NPC.WorldObjects
{
internal abstract class NpcAnimController<T> : WorldObject<T>, INpcAnimController
where T : MonoBehaviour
{
public override void Init(T controller, int id)
{
ObjectId = id;
AttachedObject = controller;
}
public abstract CharacterDialogueTree GetDialogueTree();
public virtual void StartConversation()
=> QSBWorldSync.RaiseEvent(GetDialogueTree(), "OnStartConversation");
public virtual void EndConversation()
=> QSBWorldSync.RaiseEvent(GetDialogueTree(), "OnEndConversation");
public abstract bool InConversation();
}
}

View File

@ -0,0 +1,38 @@
using OWML.Utils;
using QSB.Player;
using System.Collections.Generic;
namespace QSB.Animation.NPC.WorldObjects
{
internal class QSBCharacterAnimController : NpcAnimController<CharacterAnimController>
{
private readonly List<PlayerInfo> _playersInHeadZone = new List<PlayerInfo>();
public List<PlayerInfo> GetPlayersInHeadZone()
=> _playersInHeadZone;
public void AddPlayerToHeadZone(PlayerInfo player)
{
if (_playersInHeadZone.Contains(player))
{
return;
}
_playersInHeadZone.Add(player);
}
public void RemovePlayerFromHeadZone(PlayerInfo player)
{
if (!_playersInHeadZone.Contains(player))
{
return;
}
_playersInHeadZone.Remove(player);
}
public override CharacterDialogueTree GetDialogueTree()
=> AttachedObject.GetValue<CharacterDialogueTree>("_dialogueTree");
public override bool InConversation()
=> AttachedObject.GetValue<bool>("_inConversation");
}
}

View File

@ -0,0 +1,13 @@
using OWML.Utils;
namespace QSB.Animation.NPC.WorldObjects
{
internal class QSBTravelerController : NpcAnimController<TravelerController>
{
public override CharacterDialogueTree GetDialogueTree()
=> AttachedObject.GetValue<CharacterDialogueTree>("_dialogueSystem");
public override bool InConversation()
=> AttachedObject.GetValue<bool>("_talking");
}
}

View File

@ -37,9 +37,9 @@ namespace QSB.ConversationSync
public uint GetPlayerTalkingToTree(CharacterDialogueTree tree)
{
var treeIndex = QSBWorldSync.OldDialogueTrees.IndexOf(tree);
return QSBPlayerManager.PlayerList.All(x => x.CurrentDialogueID != treeIndex)
return QSBPlayerManager.PlayerList.All(x => x.CurrentCharacterDialogueTreeId != treeIndex)
? uint.MaxValue
: QSBPlayerManager.PlayerList.First(x => x.CurrentDialogueID == treeIndex).PlayerId;
: QSBPlayerManager.PlayerList.First(x => x.CurrentCharacterDialogueTreeId == treeIndex).PlayerId;
}
public void SendPlayerOption(string text) =>
@ -122,6 +122,7 @@ namespace QSB.ConversationSync
lookAt.SetValue("_localFacingVector", Vector3.back);
lookAt.SetValue("_localRotationAxis", Vector3.up);
newBox.GetComponent<Text>().text = text;
newBox.AddComponent<ZOverride>();
newBox.SetActive(true);
return newBox;
}

View File

@ -1,12 +1,8 @@
using OWML.Common;
using OWML.Utils;
using QSB.Animation.Character.WorldObjects;
using QSB.Events;
using QSB.Player;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
using UnityEngine;
namespace QSB.ConversationSync.Events
{
@ -17,19 +13,19 @@ namespace QSB.ConversationSync.Events
public override void SetupListener() => GlobalMessenger<int, uint, bool>.AddListener(EventNames.QSBConversationStartEnd, Handler);
public override void CloseListener() => GlobalMessenger<int, uint, bool>.RemoveListener(EventNames.QSBConversationStartEnd, Handler);
private void Handler(int charId, uint playerId, bool state) => SendEvent(CreateMessage(charId, playerId, state));
private void Handler(int objId, uint playerId, bool state) => SendEvent(CreateMessage(objId, playerId, state));
private ConversationStartEndMessage CreateMessage(int charId, uint playerId, bool state) => new ConversationStartEndMessage
private ConversationStartEndMessage CreateMessage(int objId, uint playerId, bool state) => new ConversationStartEndMessage
{
AboutId = LocalPlayerId,
CharacterId = charId,
TreeId = objId,
PlayerId = playerId,
State = state
};
public override void OnReceiveRemote(bool server, ConversationStartEndMessage message)
{
if (message.CharacterId == -1)
if (message.TreeId == -1)
{
DebugLog.ToConsole("Warning - Received conv. start/end event with char id -1.", MessageType.Warning);
return;
@ -40,44 +36,32 @@ namespace QSB.ConversationSync.Events
return;
}
var dialogueTree = QSBWorldSync.OldDialogueTrees[message.CharacterId];
var animController = Resources.FindObjectsOfTypeAll<CharacterAnimController>().FirstOrDefault(x => x.GetValue<CharacterDialogueTree>("_dialogueTree") == dialogueTree);
if (animController == default(CharacterAnimController))
{
return;
}
var dialogueTree = QSBWorldSync.OldDialogueTrees[message.TreeId];
if (message.State)
{
StartConversation(message.PlayerId, message.CharacterId, animController, dialogueTree);
StartConversation(message.PlayerId, message.TreeId, dialogueTree);
}
else
{
EndConversation(message.PlayerId, animController, dialogueTree);
EndConversation(message.PlayerId, dialogueTree);
}
}
private void StartConversation(
uint playerId,
int characterId,
CharacterAnimController controller,
int dialogueTreeId,
CharacterDialogueTree tree)
{
QSBPlayerManager.GetPlayer(playerId).CurrentDialogueID = characterId;
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(controller);
qsbObj.StartConversation();
QSBPlayerManager.GetPlayer(playerId).CurrentCharacterDialogueTreeId = dialogueTreeId;
tree.GetInteractVolume().DisableInteraction();
}
private void EndConversation(
uint playerId,
CharacterAnimController controller,
CharacterDialogueTree tree)
{
QSBPlayerManager.GetPlayer(playerId).CurrentDialogueID = -1;
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBCharacterAnimController, CharacterAnimController>(controller);
qsbObj.EndConversation();
QSBPlayerManager.GetPlayer(playerId).CurrentCharacterDialogueTreeId = -1;
tree.GetInteractVolume().EnableInteraction();
}
}

View File

@ -5,14 +5,14 @@ namespace QSB.ConversationSync.Events
{
public class ConversationStartEndMessage : PlayerMessage
{
public int CharacterId { get; set; }
public int TreeId { get; set; }
public uint PlayerId { get; set; }
public bool State { get; set; }
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
CharacterId = reader.ReadInt32();
TreeId = reader.ReadInt32();
PlayerId = reader.ReadUInt32();
State = reader.ReadBoolean();
}
@ -20,7 +20,7 @@ namespace QSB.ConversationSync.Events
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(CharacterId);
writer.Write(TreeId);
writer.Write(PlayerId);
writer.Write(State);
}

View File

@ -15,7 +15,7 @@ namespace QSB.ConversationSync.Patches
{
QSBCore.HarmonyHelper.AddPostfix<DialogueNode>("GetNextPage", typeof(ConversationPatches), nameof(Node_GetNextPage));
QSBCore.HarmonyHelper.AddPrefix<CharacterDialogueTree>("InputDialogueOption", typeof(ConversationPatches), nameof(Tree_InputDialogueOption));
QSBCore.HarmonyHelper.AddPostfix<CharacterDialogueTree>("StartConversation", typeof(ConversationPatches), nameof(Tree_StartConversation));
QSBCore.HarmonyHelper.AddPrefix<CharacterDialogueTree>("StartConversation", typeof(ConversationPatches), nameof(Tree_StartConversation));
QSBCore.HarmonyHelper.AddPrefix<CharacterDialogueTree>("EndConversation", typeof(ConversationPatches), nameof(Tree_EndConversation));
}
@ -34,7 +34,7 @@ namespace QSB.ConversationSync.Patches
{
DebugLog.ToConsole($"Warning - Index for tree {__instance.name} was -1.", MessageType.Warning);
}
QSBPlayerManager.LocalPlayer.CurrentDialogueID = index;
QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId = index;
ConversationManager.Instance.SendConvState(index, true);
}
@ -44,14 +44,14 @@ namespace QSB.ConversationSync.Patches
{
return false;
}
if (QSBPlayerManager.LocalPlayer.CurrentDialogueID == -1)
if (QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId == -1)
{
DebugLog.ToConsole($"Warning - Ending conversation with CurrentDialogueId of -1! Called from {__instance.name}", MessageType.Warning);
return true;
}
ConversationManager.Instance.SendConvState(QSBPlayerManager.LocalPlayer.CurrentDialogueID, false);
ConversationManager.Instance.CloseBoxCharacter(QSBPlayerManager.LocalPlayer.CurrentDialogueID);
QSBPlayerManager.LocalPlayer.CurrentDialogueID = -1;
ConversationManager.Instance.SendConvState(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId, false);
ConversationManager.Instance.CloseBoxCharacter(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId);
QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId = -1;
ConversationManager.Instance.CloseBoxPlayer();
return true;
}
@ -74,8 +74,8 @@ namespace QSB.ConversationSync.Patches
{
var key = ____name + ____listPagesToDisplay[____currentPage];
// Sending key so translation can be done on client side - should make different language-d clients compatible
QSBCore.UnityEvents.RunWhen(() => QSBPlayerManager.LocalPlayer.CurrentDialogueID != -1,
() => ConversationManager.Instance.SendCharacterDialogue(QSBPlayerManager.LocalPlayer.CurrentDialogueID, key));
QSBCore.UnityEvents.RunWhen(() => QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId != -1,
() => ConversationManager.Instance.SendCharacterDialogue(QSBPlayerManager.LocalPlayer.CurrentCharacterDialogueTreeId, key));
}
}
}

View File

@ -63,5 +63,6 @@
public static string QSBAnimTrigger = "QSBAnimTrigger";
public static string QSBEnterHeadZone = "QSBEnterHeadZone";
public static string QSBExitHeadZone = "QSBExitHeadZone";
public static string QSBNpcAnimEvent = "QSBNpcAnimEvent";
}
}

View File

@ -42,6 +42,7 @@
CampfireState,
Roasting,
MarshmallowEvent,
AnimTrigger
AnimTrigger,
NpcAnimEvent
}
}

View File

@ -1,4 +1,5 @@
using OWML.Common;
using QSB.Animation.NPC.Events;
using QSB.Animation.Player.Events;
using QSB.CampfireSync.Events;
using QSB.ConversationSync.Events;
@ -75,7 +76,8 @@ namespace QSB.Events
new DialogueConditionEvent(),
new RevealFactEvent(),
new IdentifyFrequencyEvent(),
new IdentifySignalEvent()
new IdentifySignalEvent(),
new NpcAnimationEvent()
};
if (UnitTestDetector.IsInUnitTest)

View File

@ -79,7 +79,6 @@ namespace QSB.OrbSync.Events
DebugLog.ToConsole($"Error - Orb identity is null. (ID {message.ObjectId})", MessageType.Error);
return;
}
DebugLog.DebugWrite($"Orb {message.ObjectId} to owner {message.FromId}");
if (orbIdentity.ClientAuthorityOwner != null && orbIdentity.ClientAuthorityOwner != fromPlayer)
{
orbIdentity.RemoveClientAuthority(orbIdentity.ClientAuthorityOwner);
@ -105,7 +104,6 @@ namespace QSB.OrbSync.Events
DebugLog.ToConsole($"Error - No NomaiOrbTransformSync has AttachedOrb with objectId {message.ObjectId}!");
return;
}
DebugLog.DebugWrite($"Orb {message.ObjectId} to owner {message.FromId}");
var orb = NomaiOrbTransformSync.OrbTransformSyncs
.First(x => x.AttachedObject == QSBWorldSync.OldOrbList[message.ObjectId].gameObject);
orb.enabled = true;

View File

@ -22,7 +22,7 @@ namespace QSB.OrbSync.TransformSync
private void OnReady()
{
if (QSBWorldSync.OldOrbList == null || QSBWorldSync.OldOrbList.Count < _index)
if (QSBWorldSync.OldOrbList == null || QSBWorldSync.OldOrbList.Count <= _index)
{
DebugLog.ToConsole($"Error - OldOrbList is null or does not contain index {_index}.", OWML.Common.MessageType.Error);
return;
@ -33,7 +33,7 @@ namespace QSB.OrbSync.TransformSync
protected override void OnDestroy()
{
OrbTransformSyncs.Remove(this);
base.OnDestroy();
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
}
protected override void Init()

View File

@ -1,4 +1,5 @@
using OWML.Common;
using QSB.Animation.NPC.Patches;
using QSB.Animation.Patches;
using QSB.CampfireSync.Patches;
using QSB.ConversationSync.Patches;

View File

@ -1,4 +1,4 @@
using QSB.Animation.Character.WorldObjects;
using QSB.Animation.NPC.WorldObjects;
using QSB.Events;
using QSB.PoolSync;
using QSB.Utility;

View File

@ -40,7 +40,7 @@ namespace QSB.Player
public QSBCampfire Campfire { get; set; }
// Conversation
public int CurrentDialogueID { get; set; }
public int CurrentCharacterDialogueTreeId { get; set; }
public GameObject CurrentDialogueBox { get; set; }
// Animation
@ -56,7 +56,7 @@ namespace QSB.Player
public PlayerInfo(uint id)
{
PlayerId = id;
CurrentDialogueID = -1;
CurrentCharacterDialogueTreeId = -1;
}
public void UpdateStateObjects()

View File

@ -102,9 +102,15 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Animation\Character\CharacterAnimManager.cs" />
<Compile Include="Animation\Character\Patches\CharacterAnimationPatches.cs" />
<Compile Include="Animation\Character\WorldObjects\QSBCharacterAnimController.cs" />
<Compile Include="Animation\NPC\AnimationEvent.cs" />
<Compile Include="Animation\NPC\CharacterAnimManager.cs" />
<Compile Include="Animation\NPC\Events\NpcAnimationEvent.cs" />
<Compile Include="Animation\NPC\Events\NpcAnimationMessage.cs" />
<Compile Include="Animation\NPC\Patches\CharacterAnimationPatches.cs" />
<Compile Include="Animation\NPC\WorldObjects\INpcAnimController.cs" />
<Compile Include="Animation\NPC\WorldObjects\NpcAnimController.cs" />
<Compile Include="Animation\NPC\WorldObjects\QSBCharacterAnimController.cs" />
<Compile Include="Animation\NPC\WorldObjects\QSBTravelerController.cs" />
<Compile Include="Animation\Player\AnimationSync.cs" />
<Compile Include="Animation\Player\AnimationType.cs" />
<Compile Include="Animation\Player\Events\AnimationTriggerEvent.cs" />
@ -273,7 +279,7 @@
<Compile Include="SectorSync\SectorSync.cs" />
<Compile Include="Utility\CustomCallbacks.cs" />
<Compile Include="Utility\DebugBoxManager.cs" />
<Compile Include="Utility\DebugZOverride.cs" />
<Compile Include="Utility\ZOverride.cs" />
<Compile Include="Utility\Extensions.cs" />
<Compile Include="Utility\GlobalMessenger4Args.cs" />
<Compile Include="Utility\GlobalMessenger5Args.cs" />

View File

@ -1,7 +1,7 @@
using OWML.Common;
using OWML.ModHelper;
using OWML.Utils;
using QSB.Animation.Character;
using QSB.Animation.NPC;
using QSB.CampfireSync;
using QSB.ConversationSync;
using QSB.ElevatorSync;
@ -179,7 +179,9 @@ namespace QSB
foreach (var player in QSBPlayerManager.PlayerList.Where(x => x.PlayerStates.IsReady))
{
var networkTransform = player.TransformSync;
GUI.Label(new Rect(220, offset, 400f, 20f), $"- {player.PlayerId} : {networkTransform.transform.localPosition} from {networkTransform.ReferenceSector.Name}");
var sector = networkTransform.ReferenceSector;
GUI.Label(new Rect(220, offset, 400f, 20f), $"- {player.PlayerId} : {networkTransform.transform.localPosition} from {(sector == null ? "NULL" : sector.Name)}");
offset += _debugLineSpacing;
}
@ -244,10 +246,6 @@ namespace QSB
QSBNetworkManager.Instance.networkPort = Port;
}
DebugMode = config.GetSettingsValue<bool>("debugMode");
if (!DebugMode)
{
FindObjectsOfType<DebugZOverride>().ToList().ForEach(x => Destroy(x.gameObject));
}
ShowLinesInDebug = config.GetSettingsValue<bool>("showLinesInDebug");
SocketedObjToDebug = config.GetSettingsValue<int>("socketedObjToDebug");
}

View File

@ -91,7 +91,6 @@ namespace QSB
spawnPrefabs.Add(_stickPrefab);
ConfigureNetworkManager();
QSBSceneManager.OnUniverseSceneLoaded += OnSceneLoaded;
}
private void SetupNetworkId(GameObject go)
@ -114,15 +113,6 @@ namespace QSB
Destroy(go.GetComponent<NetworkIdentity>());
}
public void OnDestroy() =>
QSBSceneManager.OnUniverseSceneLoaded -= OnSceneLoaded;
private void OnSceneLoaded(OWScene scene)
{
QSBWorldSync.OldDialogueTrees.Clear();
QSBWorldSync.OldDialogueTrees = Resources.FindObjectsOfTypeAll<CharacterDialogueTree>().ToList();
}
private void ConfigureNetworkManager()
{
networkAddress = QSBCore.DefaultServerIP;

View File

@ -25,7 +25,8 @@ namespace QSB
DebugLog.DebugWrite($"COMPLETE SCENE LOAD ({oldScene} -> {newScene})", MessageType.Info);
if (QSBCore.IsInMultiplayer)
{
WorldObjectManager.Rebuild(newScene);
// So objects have time to be deleted, made, whatever
QSBCore.UnityEvents.FireOnNextUpdate(() => WorldObjectManager.Rebuild(newScene));
}
var universe = InUniverse(newScene);
OnSceneLoaded?.SafeInvoke(newScene, universe);

View File

@ -22,7 +22,7 @@ namespace QSB.QuantumSync.Events
public override bool CheckMessage(bool isServer, QuantumAuthorityMessage message)
{
if (!QuantumManager.Instance.IsReady)
if (!WorldObjectManager.AllReady)
{
return false;
}

View File

@ -63,10 +63,13 @@ namespace QSB.QuantumSync.Patches
ref QuantumSocket ____recentlyObscuredSocket,
QuantumSocket ____occupiedSocket)
{
var socketedWorldObject = QSBWorldSync.GetWorldFromUnity<QSBSocketedQuantumObject, SocketedQuantumObject>(__instance);
if (socketedWorldObject.ControllingPlayer != QSBPlayerManager.LocalPlayerId)
if (WorldObjectManager.AllReady)
{
return false;
var socketedWorldObject = QSBWorldSync.GetWorldFromUnity<QSBSocketedQuantumObject, SocketedQuantumObject>(__instance);
if (socketedWorldObject.ControllingPlayer != QSBPlayerManager.LocalPlayerId)
{
return false;
}
}
foreach (var socket in ____childSockets)
@ -160,6 +163,11 @@ namespace QSB.QuantumSync.Patches
public static void Socketed_MoveToSocket(SocketedQuantumObject __instance, QuantumSocket socket)
{
if (!WorldObjectManager.AllReady)
{
return;
}
if (socket == null)
{
DebugLog.ToConsole($"Error - Trying to move {__instance.name} to a null socket!", MessageType.Error);
@ -193,10 +201,14 @@ namespace QSB.QuantumSync.Patches
ref Transform[] ____shuffledObjects,
ref bool __result)
{
var shuffleWorldObject = QSBWorldSync.GetWorldFromUnity<QSBQuantumShuffleObject, QuantumShuffleObject>(__instance);
if (shuffleWorldObject.ControllingPlayer != QSBPlayerManager.LocalPlayerId)
QSBQuantumShuffleObject shuffleWorldObject = default;
if (WorldObjectManager.AllReady)
{
return false;
shuffleWorldObject = QSBWorldSync.GetWorldFromUnity<QSBQuantumShuffleObject, QuantumShuffleObject>(__instance);
if (shuffleWorldObject.ControllingPlayer != QSBPlayerManager.LocalPlayerId)
{
return false;
}
}
____indexList.Clear();
@ -208,20 +220,32 @@ namespace QSB.QuantumSync.Patches
____indexList[i] = ____indexList[random];
____indexList[random] = temp;
}
for (var j = 0; j < ____shuffledObjects.Length; j++)
{
____shuffledObjects[j].localPosition = ____localPositions[____indexList[j]];
}
QSBEventManager.FireEvent(
if (WorldObjectManager.AllReady)
{
QSBEventManager.FireEvent(
EventNames.QSBQuantumShuffle,
shuffleWorldObject.ObjectId,
____indexList.ToArray());
__result = true;
__result = true;
}
return false;
}
public static bool MultiState_Start(MultiStateQuantumObject __instance, Sector ____sector, bool ____collapseOnStart)
{
if (!WorldObjectManager.AllReady)
{
return true;
}
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBMultiStateQuantumObject, MultiStateQuantumObject>(__instance);
if (qsbObj.ControllingPlayer == 0)
{
@ -250,6 +274,11 @@ namespace QSB.QuantumSync.Patches
public static bool MultiState_ChangeQuantumState(MultiStateQuantumObject __instance)
{
if (!WorldObjectManager.AllReady)
{
return true;
}
var qsbObj = QSBWorldSync.GetWorldFromUnity<QSBMultiStateQuantumObject, MultiStateQuantumObject>(__instance);
if (qsbObj.ControllingPlayer == 0 && qsbObj.CurrentState == -1)
{
@ -261,6 +290,11 @@ namespace QSB.QuantumSync.Patches
public static void QuantumState_SetVisible(QuantumState __instance, bool visible)
{
if (!WorldObjectManager.AllReady)
{
return;
}
if (!visible)
{
return;

View File

@ -40,7 +40,7 @@ namespace QSB.QuantumSync.Patches
GameObject[] ____deactivateAtEye
)
{
if (QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, skipInstantVisibilityCheck) && !QuantumManager.Instance.Shrine.IsPlayerInDarkness())
if (QuantumManager.IsVisibleUsingCameraFrustum((ShapeVisibilityTracker)____visibilityTracker, skipInstantVisibilityCheck) && !QuantumManager.Shrine.IsPlayerInDarkness())
{
if (!skipInstantVisibilityCheck)
{

View File

@ -13,9 +13,8 @@ namespace QSB.QuantumSync
{
internal class QuantumManager : WorldObjectManager
{
public static QuantumShrine Shrine { get; private set; }
public static QuantumManager Instance { get; private set; }
public QuantumShrine Shrine;
public bool IsReady;
public override void Awake()
{
@ -44,7 +43,6 @@ namespace QSB.QuantumSync
{
Shrine = Resources.FindObjectsOfTypeAll<QuantumShrine>().First();
}
IsReady = true;
}
public void PlayerLeave(uint playerId)
@ -117,6 +115,10 @@ namespace QSB.QuantumSync
public static IEnumerable<PlayerInfo> GetEntangledPlayers(QuantumObject obj)
{
if (!WorldObjectManager.AllReady)
{
return Enumerable.Empty<PlayerInfo>();
}
var worldObj = GetObject(obj);
return QSBPlayerManager.PlayerList.Where(x => x.EntangledObject == worldObj);
}

View File

@ -59,7 +59,7 @@ namespace QSB.QuantumSync.WorldObjects
component.MoveToRelativeLocation(location, AttachedObject.transform);
}
if (QuantumManager.Instance.Shrine != AttachedObject)
if (QuantumManager.Shrine != AttachedObject)
{
AttachedObject.transform.localRotation = message.LocalRotation;
}

View File

@ -29,7 +29,7 @@ namespace QSB.TransformSync
protected abstract GameObject InitLocalTransform();
protected abstract GameObject InitRemoteTransform();
private bool _isInitialized;
protected bool _isInitialized;
private const float SmoothTime = 0.1f;
protected virtual float DistanceLeeway { get; } = 5f;
private float _previousDistance;
@ -62,6 +62,10 @@ namespace QSB.TransformSync
protected virtual void Init()
{
if (!HasAuthority && AttachedObject != null)
{
Destroy(AttachedObject);
}
AttachedObject = HasAuthority ? InitLocalTransform() : InitRemoteTransform();
_isInitialized = true;
}

View File

@ -46,15 +46,30 @@ namespace QSB.TransformSync
/// Sets the position of the INVISIBLE transform to be correct, according to the reference sector and the position of the VISIBLE transform.
/// </summary>
/// <param name="worldPosition">The world position of the VISIBLE transform.</param>
public void EncodePosition(Vector3 worldPosition)
=> SetPosition(_referenceTransform.InverseTransformPoint(worldPosition));
public void EncodePosition(Vector3 worldPosition)
{
if (_referenceTransform == null)
{
DebugLog.DebugWrite($"Error - _referenceTransform has not been set for {_attachedTransform.name}", OWML.Common.MessageType.Error);
return;
}
SetPosition(_referenceTransform.InverseTransformPoint(worldPosition));
}
/// <summary>
/// Sets the rotation of the INVISIBLE transform to be correct, according to the reference sector and the rotation of the VISIBLE transform.
/// </summary>
/// <param name="worldPosition">The world rotation of the VISIBLE transform.</param>
public void EncodeRotation(Quaternion worldRotation)
=> SetRotation(_referenceTransform.InverseTransformRotation(worldRotation));
{
if (_referenceTransform == null)
{
DebugLog.DebugWrite($"Error - _referenceTransform has not been set for {_attachedTransform.name}", OWML.Common.MessageType.Error);
return;
}
SetRotation(_referenceTransform.InverseTransformRotation(worldRotation));
}
/// <summary>
/// Returns the local position the VISIBLE transform should be set to, from the INVISIBLE transform.

View File

@ -31,7 +31,15 @@ namespace QSB.TransformSync
protected override void Init()
{
base.Init();
SetReferenceTransform(SectorSync.GetClosestSector(AttachedObject.transform).Transform);
var closestSector = SectorSync.GetClosestSector(AttachedObject.transform);
if (closestSector != null)
{
SetReferenceTransform(closestSector.Transform);
}
else
{
_isInitialized = false;
}
}
public override void SerializeTransform(QNetworkWriter writer)

View File

@ -52,7 +52,7 @@ namespace QSB.TransformSync
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(OWScene scene, bool isInUniverse) =>
protected void OnSceneLoaded(OWScene scene, bool isInUniverse) =>
_isInitialized = false;
protected virtual void Init()

View File

@ -33,7 +33,7 @@ namespace QSB.Utility
lookAt.SetValue("_localFacingVector", Vector3.back);
lookAt.SetValue("_localRotationAxis", Vector3.up);
newBox.GetComponent<Text>().text = text;
newBox.AddComponent<DebugZOverride>();
newBox.AddComponent<ZOverride>();
newBox.SetActive(true);
return newBox;
}

View File

@ -4,7 +4,7 @@ using UnityEngine.UI;
namespace QSB.Utility
{
public class DebugZOverride : MonoBehaviour
public class ZOverride : MonoBehaviour
{
private const string shaderTestMode = "unity_GUIZTestMode";
private readonly UnityEngine.Rendering.CompareFunction desiredUIComparison = UnityEngine.Rendering.CompareFunction.Always;

View File

@ -4,6 +4,7 @@ using QSB.OrbSync.WorldObjects;
using QSB.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using UnityEngine;
@ -54,7 +55,7 @@ namespace QSB.WorldSync
}
if (!WorldObjectsToUnityObjects.ContainsKey(unityObject))
{
DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"!", MessageType.Error);
DebugLog.ToConsole($"Error - WorldObjectsToUnityObjects does not contain \"{unityObject.name}\"! Called from {new StackTrace().GetFrame(1).GetMethod().Name}", MessageType.Error);
return default;
}
return WorldObjectsToUnityObjects[unityObject] as TWorldObject;

View File

@ -7,11 +7,22 @@ namespace QSB.WorldSync
{
private static readonly List<WorldObjectManager> _managers = new List<WorldObjectManager>();
public static bool AllReady { get; private set; }
public virtual void Awake()
=> _managers.Add(this);
{
QSBSceneManager.OnSceneLoaded += OnSceneLoaded;
_managers.Add(this);
}
public virtual void OnDestroy()
=> _managers.Remove(this);
{
QSBSceneManager.OnSceneLoaded -= OnSceneLoaded;
_managers.Remove(this);
}
private void OnSceneLoaded(OWScene scene, bool inUniverse)
=> AllReady = false;
public static void Rebuild(OWScene scene)
{
@ -19,6 +30,7 @@ namespace QSB.WorldSync
{
manager.RebuildWorldObjects(scene);
}
AllReady = true;
}
protected abstract void RebuildWorldObjects(OWScene scene);

View File

@ -80,7 +80,7 @@ QSB relies on exact orders of objects found using Resources.FindObjectsOfTypeAll
| Marshmallow roasting | Yes |
| Meteors | No |
| Museum statue | Yes |
| NPC animations | Kind of |
| NPC animations | Yes |
| Nomai orbs | Yes |
| Nomai shuttle | Kind of |
| Orbital Probe Cannon (direction) | No |
@ -90,6 +90,7 @@ QSB relies on exact orders of objects found using Resources.FindObjectsOfTypeAll
| Projection pools | Yes |
| Quantum objects | Yes |
| Ship log | Yes |
| Solanum | No |
| Timber Hearth satellite | No |
| Tornadoes | No |