Merge pull request #448 from misternebula/solanum-mask-sync

Solanum mask sync, persistent condition sync, better log sync, aaaaa
This commit is contained in:
_nebula 2022-01-14 22:10:05 +00:00 committed by GitHub
commit cdb2e96e5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 1252 additions and 667 deletions

View File

@ -16,10 +16,6 @@ namespace QSB.Animation.NPC
QSBWorldSync.Init<QSBSolanumAnimController, SolanumAnimController>();
QSBWorldSync.Init<QSBHearthianRecorderEffects, HearthianRecorderEffects>();
QSBWorldSync.Init<QSBTravelerEyeController, TravelerEyeController>();
//MOVE : this is the wrong place to put this... move it to Conversations?
QSBWorldSync.OldDialogueTrees.Clear();
QSBWorldSync.OldDialogueTrees.AddRange(QSBWorldSync.GetUnityObjects<CharacterDialogueTree>());
}
}
}

View File

@ -6,7 +6,7 @@ using QSB.ConversationSync;
using QSB.Messaging;
using QSB.Patches;
using QSB.Player;
using QSB.Player.Messages;
using QSB.TriggerSync.WorldObjects;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
@ -26,12 +26,18 @@ namespace QSB.Animation.NPC.Patches
{
if (!WorldObjectManager.AllObjectsReady || ConversationManager.Instance == null)
{
return false;
return true;
}
var playerId = ConversationManager.Instance.GetPlayerTalkingToTree(__instance._dialogueTree);
var player = QSBPlayerManager.GetPlayer(playerId);
var qsbObj = __instance.GetWorldObject<QSBCharacterAnimController>(); // OPTIMIZE : maybe cache this somewhere... or assess how slow this is
if (__instance.playerTrackingZone == null)
{
return true;
}
var qsbObj = __instance.playerTrackingZone.GetWorldObject<QSBCharacterTrigger>(); // OPTIMIZE : maybe cache this somewhere... or assess how slow this is
PlayerInfo playerToUse = null;
if (__instance._inConversation)
@ -48,9 +54,9 @@ namespace QSB.Animation.NPC.Patches
: player;
}
}
else if (!__instance.lookOnlyWhenTalking && qsbObj.GetPlayersInHeadZone().Count != 0) // IDEA : maybe this would be more fun if characters looked between players at random times? :P
else if (!__instance.lookOnlyWhenTalking && qsbObj.Occupants.Count != 0) // IDEA : maybe this would be more fun if characters looked between players at random times? :P
{
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(qsbObj.GetPlayersInHeadZone(), __instance.transform.position);
playerToUse = QSBPlayerManager.GetClosestPlayerToWorldPoint(qsbObj.Occupants, __instance.transform.position);
}
else if (QSBPlayerManager.PlayerList.Count != 0)
{
@ -65,16 +71,16 @@ namespace QSB.Animation.NPC.Patches
if (__instance.lookOnlyWhenTalking)
{
if (!__instance._inConversation
|| qsbObj.GetPlayersInHeadZone().Count == 0
|| !qsbObj.GetPlayersInHeadZone().Contains(playerToUse))
|| qsbObj.Occupants.Count == 0
|| !qsbObj.Occupants.Contains(playerToUse))
{
targetWeight *= 0;
}
}
else
{
if (qsbObj.GetPlayersInHeadZone().Count == 0
|| !qsbObj.GetPlayersInHeadZone().Contains(playerToUse))
if (qsbObj.Occupants.Count == 0
|| !qsbObj.Occupants.Contains(playerToUse))
{
targetWeight *= 0;
}
@ -88,32 +94,6 @@ namespace QSB.Animation.NPC.Patches
}
[HarmonyPrefix]
[HarmonyPatch(typeof(CharacterAnimController), nameof(CharacterAnimController.OnZoneExit))]
public static bool HeadZoneExit(CharacterAnimController __instance, GameObject input)
{
if (input.CompareTag("PlayerDetector"))
{
var qsbObj = __instance.GetWorldObject<QSBCharacterAnimController>();
new EnterLeaveMessage(EnterLeaveType.ExitNonNomaiHeadZone, qsbObj.ObjectId).Send();
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(CharacterAnimController), nameof(CharacterAnimController.OnZoneEntry))]
public static bool HeadZoneEntry(CharacterAnimController __instance, GameObject input)
{
if (input.CompareTag("PlayerDetector"))
{
var qsbObj = __instance.GetWorldObject<QSBCharacterAnimController>();
new EnterLeaveMessage(EnterLeaveType.EnterNonNomaiHeadZone, qsbObj.ObjectId).Send();
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(FacePlayerWhenTalking), nameof(FacePlayerWhenTalking.OnStartConversation))]
public static bool OnStartConversation(FacePlayerWhenTalking __instance)

View File

@ -1,9 +1,7 @@
using HarmonyLib;
using QSB.Animation.NPC.WorldObjects;
using QSB.Messaging;
using QSB.Patches;
using QSB.Player;
using QSB.Player.Messages;
using QSB.WorldSync;
using System.Linq;
using UnityEngine;
@ -26,7 +24,7 @@ namespace QSB.Animation.NPC.Patches
}
var qsbObj = __instance.GetWorldObject<QSBSolanumAnimController>();
var playersInHeadZone = qsbObj.GetPlayersInHeadZone();
var playersInHeadZone = qsbObj.Trigger.Occupants;
var targetCamera = playersInHeadZone == null || playersInHeadZone.Count == 0
? __instance._playerCameraTransform
@ -41,38 +39,12 @@ namespace QSB.Animation.NPC.Patches
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.OnEnterWatchVolume))]
public static bool EnterWatchZone(NomaiConversationManager __instance, GameObject hitObj)
{
if (hitObj.CompareTag("PlayerDetector"))
{
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
new EnterLeaveMessage(EnterLeaveType.EnterNomaiHeadZone, qsbObj.ObjectId).Send();
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.OnExitWatchVolume))]
public static bool ExitWatchZone(NomaiConversationManager __instance, GameObject hitObj)
{
if (hitObj.CompareTag("PlayerDetector"))
{
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
new EnterLeaveMessage(EnterLeaveType.ExitNomaiHeadZone, qsbObj.ObjectId).Send();
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(NomaiConversationManager), nameof(NomaiConversationManager.Update))]
public static bool ReplacementUpdate(NomaiConversationManager __instance)
{
var qsbObj = __instance._solanumAnimController.GetWorldObject<QSBSolanumAnimController>();
__instance._playerInWatchVolume = qsbObj.GetPlayersInHeadZone().Any();
__instance._playerInWatchVolume = qsbObj.Trigger.Occupants.Any();
if (!__instance._initialized)
{

View File

@ -1,35 +1,7 @@
using QSB.Player;
using System.Collections.Generic;
namespace QSB.Animation.NPC.WorldObjects
namespace QSB.Animation.NPC.WorldObjects
{
internal class QSBCharacterAnimController : NpcAnimController<CharacterAnimController>
{
private readonly List<PlayerInfo> _playersInHeadZone = new();
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._dialogueTree;

View File

@ -1,34 +1,12 @@
using QSB.Player;
using QSB.TriggerSync.WorldObjects;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
namespace QSB.Animation.NPC.WorldObjects
{
internal class QSBSolanumAnimController : WorldObject<SolanumAnimController>
{
private readonly List<PlayerInfo> _playersInHeadZone = new();
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);
}
private QSBSolanumTrigger _trigger;
public QSBSolanumTrigger Trigger => _trigger ??= QSBWorldSync.GetWorldObjects<QSBSolanumTrigger>().Single();
}
}

View File

@ -2,12 +2,12 @@
{
public enum AnimationType
{
Chert = 0,
Esker = 1,
Feldspar = 2,
Gabbro = 3,
PlayerSuited = 4,
PlayerUnsuited = 5,
Riebeck = 6
Chert,
Esker,
Feldspar,
Gabbro,
PlayerSuited,
PlayerUnsuited,
Riebeck
}
}

View File

@ -45,6 +45,7 @@ namespace QSB.Audio
bool randomize)
{
var go = new GameObject(name);
go.SetActive(false);
go.transform.parent = parent;
go.transform.localPosition = Vector3.zero;
go.transform.localScale = Vector3.one;
@ -66,6 +67,8 @@ namespace QSB.Audio
owAudioSource._track = track;
owAudioSource._randomizePlayheadOnAwake = randomize;
go.SetActive(true);
return owAudioSource;
}
}

View File

@ -7,7 +7,6 @@
AliveInSolarSystem,
DeadInSolarSystem,
AliveInEye,
WaitingForOthersToDie,
WaitingForOthersToBeReady,
WatchingLongCredits,
WatchingShortCredits

View File

@ -2,9 +2,9 @@
{
public enum ConversationType
{
Character = 0,
Player = 1,
CloseCharacter = 2,
ClosePlayer = 3
Character,
Player,
CloseCharacter,
ClosePlayer
}
}

View File

@ -1,5 +1,4 @@
using QSB.Messaging;
using QSB.Player.TransformSync;
using QSB.WorldSync;
using QuantumUNET.Transport;
@ -7,16 +6,6 @@ namespace QSB.ConversationSync.Messages
{
public class DialogueConditionMessage : QSBMessage
{
static DialogueConditionMessage() => GlobalMessenger<string, bool>.AddListener(OWEvents.DialogueConditionChanged, Handler);
private static void Handler(string name, bool state)
{
if (PlayerTransformSync.LocalInstance)
{
new DialogueConditionMessage(name, state).Send();
}
}
private string ConditionName;
private bool ConditionState;
@ -47,7 +36,32 @@ namespace QSB.ConversationSync.Messages
QSBWorldSync.SetDialogueCondition(ConditionName, ConditionState);
}
DialogueConditionManager.SharedInstance.SetConditionState(ConditionName, ConditionState);
var sharedInstance = DialogueConditionManager.SharedInstance;
var flag = true;
if (sharedInstance.ConditionExists(ConditionName))
{
if (sharedInstance._dictConditions[ConditionName] == ConditionState)
{
flag = false;
}
sharedInstance._dictConditions[ConditionName] = ConditionState;
}
else
{
sharedInstance.AddCondition(ConditionName, ConditionState);
}
if (flag)
{
GlobalMessenger<string, bool>.FireEvent("DialogueConditionChanged", ConditionName, ConditionState);
}
if (ConditionName == "LAUNCH_CODES_GIVEN")
{
PlayerData.LearnLaunchCodes();
}
}
public override void OnReceiveLocal()

View File

@ -0,0 +1,73 @@
using QSB.Messaging;
using QSB.Utility;
using QSB.WorldSync;
using QuantumUNET.Transport;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QSB.ConversationSync.Messages
{
internal class PersistentConditionMessage : QSBMessage
{
private string _conditionName;
private bool _conditionState;
public PersistentConditionMessage(string condition, bool state)
{
_conditionName = condition;
_conditionState = state;
}
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(_conditionName);
writer.Write(_conditionState);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
_conditionName = reader.ReadString();
_conditionState = reader.ReadBoolean();
}
public override void OnReceiveRemote()
{
if (QSBCore.IsHost)
{
QSBWorldSync.SetPersistentCondition(_conditionName, _conditionState);
}
var gameSave = PlayerData._currentGameSave;
if (gameSave.dictConditions.ContainsKey(_conditionName))
{
gameSave.dictConditions[_conditionName] = _conditionState;
}
else
{
gameSave.dictConditions.Add(_conditionName, _conditionState);
}
if (_conditionName
is not "LAUNCH_CODES_GIVEN"
and not "PLAYER_ENTERED_TIMELOOPCORE"
and not "PROBE_ENTERED_TIMELOOPCORE"
and not "PLAYER_ENTERED_TIMELOOPCORE_MULTIPLE")
{
PlayerData.SaveCurrentGame();
}
}
public override void OnReceiveLocal()
{
if (QSBCore.IsHost)
{
QSBWorldSync.SetPersistentCondition(_conditionName, _conditionState);
}
}
}
}

View File

@ -7,6 +7,7 @@ using QSB.Patches;
using QSB.Player;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
namespace QSB.ConversationSync.Patches
{
@ -15,6 +16,23 @@ namespace QSB.ConversationSync.Patches
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
public static readonly string[] PersistentConditionsToSync =
{
"MET_SOLANUM",
"MET_PRISONER",
"TALKED_TO_GABBRO",
"GABBRO_MERGE_TRIGGERED",
"KNOWS_MEDITATION"
};
[HarmonyPrefix]
[HarmonyPatch(typeof(DialogueConditionManager), nameof(DialogueConditionManager.SetConditionState))]
public static bool SetConditionState(string conditionName, bool conditionState)
{
new DialogueConditionMessage(conditionName, conditionState).Send();
return true;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(CharacterDialogueTree), nameof(CharacterDialogueTree.StartConversation))]
public static void CharacterDialogueTree_StartConversation(CharacterDialogueTree __instance)
@ -144,5 +162,15 @@ namespace QSB.ConversationSync.Patches
__result = true;
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameSave), nameof(GameSave.SetPersistentCondition))]
public static void SetPersistentCondition(string condition, bool state)
{
if (PersistentConditionsToSync.Contains(condition))
{
new PersistentConditionMessage(condition, state).Send();
}
}
}
}

View File

@ -28,7 +28,9 @@ namespace QSB.ElevatorSync.WorldObjects
_owAudioSourceOneShot = AttachedObject._owAudioSourceOneShot;
_owAudioSourceLP = AttachedObject._owAudioSourceLP;
var boxShape = AttachedObject.gameObject.AddComponent<BoxShape>();
// BUG : This won't work for the log lift! need to make a different trigger for that
var boxShape = AttachedObject.gameObject.GetAddComponent<BoxShape>();
boxShape.center = new Vector3(0, 1.75f, 0.25f);
boxShape.size = new Vector3(3, 3.5f, 3);
@ -74,5 +76,16 @@ namespace QSB.ElevatorSync.WorldObjects
_owAudioSourceLP.FadeIn(0.5f);
_interactVolume.DisableInteraction();
}
public override void DisplayLines()
{
var boxShape = (BoxShape)_elevatorTrigger._shape;
Popcron.Gizmos.Cube(
ShapeUtil.Box.CalcWorldSpaceCenter(boxShape),
boxShape.transform.rotation,
ShapeUtil.Box.CalcWorldSpaceSize(boxShape),
_elevatorTrigger.IsTrackingObject(Locator.GetPlayerDetector()) ? Color.green : Color.white
);
}
}
}

View File

@ -1,127 +0,0 @@
using QSB.Messaging;
using QSB.Player;
using QSB.Player.Messages;
using QSB.Utility;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace QSB.EyeOfTheUniverse.CosmicInflation
{
internal class InflationManager : WorldObjectManager
{
public static InflationManager Instance { get; private set; }
private readonly List<PlayerInfo> _playersInFog = new();
private CosmicInflationController _controller;
public override WorldObjectType WorldObjectType => WorldObjectType.Eye;
public override void Awake()
{
base.Awake();
Instance = this;
QSBPlayerManager.OnRemovePlayer += OnPlayerLeave;
}
private void OnPlayerLeave(uint id)
{
_playersInFog.Remove(QSBPlayerManager.GetPlayer(id));
// wait 1 frame for player to be removed
QSBCore.UnityEvents.FireOnNextUpdate(() =>
{
if (QSBCore.IsInMultiplayer && _playersInFog.Count == QSBPlayerManager.PlayerList.Count)
{
StartCollapse();
}
});
}
protected override void RebuildWorldObjects(OWScene scene)
{
_playersInFog.Clear();
if (_controller != null)
{
_controller._smokeSphereTrigger.OnEntry -= OnEntry;
}
_controller = QSBWorldSync.GetUnityObjects<CosmicInflationController>().First();
_controller._smokeSphereTrigger.OnEntry -= _controller.OnEnterFogSphere;
_controller._smokeSphereTrigger.OnEntry += OnEntry;
}
private void OnEntry(GameObject hitObj)
{
if (hitObj.CompareTag("PlayerCameraDetector") && _controller._state == CosmicInflationController.State.ReadyToCollapse)
{
_controller._smokeSphereTrigger.SetTriggerActivation(false);
_controller._probeDestroyTrigger.SetTriggerActivation(false);
new EnterLeaveMessage(EnterLeaveType.EnterCosmicFog).Send();
DebugLog.DebugWrite("disable input, wait for other players to enter");
var repelVolume = (WhiteHoleFluidVolume)_controller._repelVolume;
repelVolume._flowSpeed = -repelVolume._flowSpeed;
repelVolume._massiveFlowSpeed = -repelVolume._massiveFlowSpeed;
repelVolume.SetVolumeActivation(true);
QSBPlayerManager.HideAllPlayers();
ReticleController.Hide();
Locator.GetFlashlight().TurnOff(false);
Locator.GetPromptManager().SetPromptsVisible(false);
OWInput.ChangeInputMode(InputMode.None);
}
}
public void Enter(PlayerInfo player)
{
_playersInFog.Add(player);
if (player != QSBPlayerManager.LocalPlayer)
{
DebugLog.DebugWrite($"fade out player {player.PlayerId}");
player.DitheringAnimator.SetVisible(false, 3);
}
if (_playersInFog.Count == QSBPlayerManager.PlayerList.Count)
{
StartCollapse();
}
}
private void StartCollapse()
{
DebugLog.DebugWrite("fade in everyone, fog sphere collapse");
var repelVolume = (WhiteHoleFluidVolume)_controller._repelVolume;
repelVolume.SetVolumeActivation(false);
QSBPlayerManager.ShowAllPlayers();
_controller._state = CosmicInflationController.State.Collapsing;
_controller._stateChangeTime = Time.time;
_controller._collapseStartPos = _controller._possibilitySphereRoot.localPosition;
_controller._smokeSphereTrigger.SetTriggerActivation(false);
_controller._inflationLight.FadeTo(1f, 1f);
_controller._possibilitySphereController.OnCollapse();
if (_controller._campsiteController.GetUseAltPostCollapseSocket())
{
_controller._playerPostCollapseSocket = _controller._altPlayerPostCollapseSocket;
_controller._altTravelerToHidePostCollapse.SetActive(false);
}
Locator.GetPlayerBody().SetPosition(_controller._playerPostCollapseSocket.position);
Locator.GetPlayerBody().SetRotation(_controller._playerPostCollapseSocket.rotation);
Locator.GetPlayerBody().SetVelocity(-_controller._playerPostCollapseSocket.forward);
Locator.GetPlayerTransform().GetRequiredComponent<PlayerLockOnTargeting>().LockOn(_controller._possibilitySphereRoot, 2f);
foreach (var particles in _controller._smokeSphereParticles)
{
particles.Stop();
}
}
}
}

View File

@ -1,9 +0,0 @@
using QSB.Patches;
namespace QSB.EyeOfTheUniverse.CosmicInflation.Patches
{
internal class InflationPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
}
}

View File

@ -1,6 +1,5 @@
using QSB.Messaging;
using QSB.Player.TransformSync;
using QSB.Utility;
using QSB.WorldSync;
using QuantumUNET.Transport;
using System;
@ -10,10 +9,18 @@ namespace QSB.EyeOfTheUniverse.EyeStateSync.Messages
{
internal class FlickerMessage : QSBMessage
{
public static bool IgnoreNextMessage;
static FlickerMessage() => GlobalMessenger<float, float>.AddListener(OWEvents.FlickerOffAndOn, Handler);
private static void Handler(float offDuration, float onDuration)
{
if (IgnoreNextMessage)
{
IgnoreNextMessage = false;
return;
}
if (PlayerTransformSync.LocalInstance)
{
new FlickerMessage(offDuration, onDuration).Send();

View File

@ -5,6 +5,6 @@ namespace QSB.EyeOfTheUniverse.InstrumentSync.Messages
{
internal class GatherInstrumentMessage : QSBWorldObjectMessage<QSBQuantumInstrument>
{
public override void OnReceiveRemote() => WorldObject.AttachedObject.Gather();
public override void OnReceiveRemote() => WorldObject.Gather();
}
}

View File

@ -1,8 +1,21 @@
using QSB.WorldSync;
using QSB.Player;
using QSB.WorldSync;
using System.Linq;
namespace QSB.EyeOfTheUniverse.InstrumentSync.WorldObjects
{
internal class QSBQuantumInstrument : WorldObject<QuantumInstrument>
{
public void Gather()
{
AttachedObject.Gather();
var maskZoneController = QSBWorldSync.GetUnityObjects<MaskZoneController>().First();
if (maskZoneController._maskInstrument == AttachedObject)
{
// remote gathering solanum mask - make all players visible
QSBPlayerManager.ShowAllPlayers();
}
}
}
}

View File

@ -0,0 +1,46 @@
using QSB.EyeOfTheUniverse.EyeStateSync.Messages;
using QSB.Messaging;
using QSB.Player;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
using UnityEngine;
namespace QSB.EyeOfTheUniverse.MaskSync
{
internal class MaskManager : MonoBehaviour
{
private static bool _flickering;
private static float _flickerOutTime;
public void Awake() => QSBSceneManager.OnSceneLoaded += OnSceneLoaded;
private static void OnSceneLoaded(OWScene oldScene, OWScene newScene, bool inUniverse)
{
_flickering = false;
_flickerOutTime = 0f;
}
public static void FlickerOutShuttle()
{
FlickerMessage.IgnoreNextMessage = true;
GlobalMessenger<float, float>.FireEvent(OWEvents.FlickerOffAndOn, 0.5f, 0.5f);
_flickerOutTime = Time.time + 0.5f;
_flickering = true;
// hide all players in shuttle
QSBPlayerManager.PlayerList.Where(x => x.IsInEyeShuttle).ForEach(x => x.DitheringAnimator.SetVisibleImmediate(false));
}
private void Update()
{
if (_flickering && Time.time > _flickerOutTime)
{
var controller = QSBWorldSync.GetUnityObjects<EyeShuttleController>().First();
controller._shuttleObject.SetActive(false);
_flickering = false;
_flickerOutTime = 0f;
}
}
}
}

View File

@ -0,0 +1,25 @@
using HarmonyLib;
using QSB.Patches;
namespace QSB.EyeOfTheUniverse.MaskSync.Patches
{
internal class MaskPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPrefix]
[HarmonyPatch(typeof(EyeShuttleController), nameof(EyeShuttleController.OnLaunchSlotActivated))]
public static bool DontLaunch(EyeShuttleController __instance)
{
if (__instance._isPlayerInside)
{
return true;
}
MaskManager.FlickerOutShuttle();
__instance.enabled = false;
return false;
}
}
}

View File

@ -1,80 +0,0 @@
using QSB.Messaging;
using QSB.Player;
using QSB.Player.Messages;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace QSB.EyeOfTheUniverse.VesselSync
{
internal class VesselManager : WorldObjectManager
{
public static VesselManager Instance { get; private set; }
private readonly List<PlayerInfo> _playersInCage = new();
private VesselWarpController _warpController;
public override WorldObjectType WorldObjectType => WorldObjectType.Both;
public override void Awake()
{
base.Awake();
Instance = this;
}
protected override void RebuildWorldObjects(OWScene scene)
{
_playersInCage.Clear();
if (_warpController != null)
{
_warpController._cageTrigger.OnEntry -= OnEntry;
_warpController._cageTrigger.OnExit -= OnExit;
}
_warpController = QSBWorldSync.GetUnityObjects<VesselWarpController>().First();
_warpController._cageTrigger.OnExit -= _warpController.OnExitCageTrigger;
_warpController._cageTrigger.OnEntry += OnEntry;
_warpController._cageTrigger.OnExit += OnExit;
}
private static void OnEntry(GameObject hitObj)
{
if (hitObj.CompareTag("PlayerDetector"))
{
new EnterLeaveMessage(EnterLeaveType.EnterVesselCage).Send();
}
}
private static void OnExit(GameObject hitObj)
{
if (hitObj.CompareTag("PlayerDetector"))
{
new EnterLeaveMessage(EnterLeaveType.ExitVesselCage).Send();
}
}
public void Enter(PlayerInfo player)
{
_playersInCage.Add(player);
}
public void Exit(PlayerInfo player)
{
_playersInCage.Remove(player);
if (_playersInCage.Count == 0 && _warpController._hasPower)
{
var obj = _warpController;
obj._cageClosed = true;
obj._cageAnimator.TranslateToLocalPosition(new Vector3(0f, -8.1f, 0f), 5f);
obj._cageAnimator.RotateToLocalEulerAngles(new Vector3(0f, 180f, 0f), 5f);
obj._cageAnimator.OnTranslationComplete -= obj.OnCageAnimationComplete;
obj._cageAnimator.OnTranslationComplete += obj.OnCageAnimationComplete;
obj._cageLoopingAudio.FadeIn(1f);
}
}
}
}

View File

@ -2,7 +2,7 @@
{
public enum CameraMode
{
FirstPerson = 0,
ThirdPerson = 1
FirstPerson,
ThirdPerson
}
}

View File

@ -56,9 +56,9 @@ namespace QSB.ItemSync.WorldObjects.Items
public override void OnRemoval() => QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave;
private void OnPlayerLeave(uint player)
private void OnPlayerLeave(PlayerInfo player)
{
if (HoldingPlayer != player)
if (HoldingPlayer != player.PlayerId)
{
return;
}

View File

@ -5,6 +5,7 @@ using QSB.SaveSync.Messages;
using QSB.Utility;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
@ -151,6 +152,7 @@ namespace QSB.Menus
{
IPPopup = MenuApi.MakeInputFieldPopup("IP Address", "IP Address", "Connect", "Cancel");
IPPopup.OnPopupConfirm += Connect;
IPPopup.OnPopupValidate += Validate;
InfoPopup = MenuApi.MakeInfoPopup("", "");
InfoPopup.OnDeactivateMenu += OnCloseInfoPopup;
@ -222,15 +224,11 @@ namespace QSB.Menus
CreateCommonPopups();
ClientButton = MenuApi.TitleScreen_MakeMenuOpenButton("CONNECT TO MULTIPLAYER", _ClientButtonIndex, IPPopup);
var loadEye = MenuApi.TitleScreen_MakeSceneLoadButton("debug load eye", _ClientButtonIndex + 1, SubmitActionLoadScene.LoadableScenes.EYE);
_loadingText = ClientButton.transform.GetChild(0).GetChild(1).GetComponent<Text>();
ResumeGameButton = GameObject.Find("MainMenuLayoutGroup/Button-ResumeGame");
NewGameButton = GameObject.Find("MainMenuLayoutGroup/Button-NewGame");
SetButtonActive(loadEye, QSBCore.DebugMode);
if (QSBCore.IsInMultiplayer)
{
SetButtonActive(ClientButton, false);
@ -310,13 +308,16 @@ namespace QSB.Menus
DisconnectPopup._labelText.text = popupText;
}
private bool Validate()
{
var inputText = ((PopupInputMenu)IPPopup).GetInputText();
var regex = new Regex(@"\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z");
return inputText == "localhost" || regex.Match(inputText).Success;
}
private void Connect()
{
var address = string.Concat(((PopupInputMenu)IPPopup).GetInputText().Where(c => !char.IsWhiteSpace(c)));
if (address.Length == 0)
{
address = QSBCore.DefaultServerIP;
}
var address = ((PopupInputMenu)IPPopup).GetInputText();
QSBNetworkManager.Instance.networkAddress = address;
QSBNetworkManager.Instance.StartClient();

View File

@ -19,7 +19,6 @@
public const string EquipTranslator = nameof(EquipTranslator);
public const string UnequipTranslator = nameof(UnequipTranslator);
public const string WakeUp = nameof(WakeUp);
public const string DialogueConditionChanged = nameof(DialogueConditionChanged);
public const string PlayerEnterQuantumMoon = nameof(PlayerEnterQuantumMoon);
public const string PlayerExitQuantumMoon = nameof(PlayerExitQuantumMoon);
public const string EnterRoastingMode = nameof(EnterRoastingMode);

View File

@ -59,12 +59,14 @@ namespace QSB.OrbSync.TransformSync
_attachedBody = AttachedObject.GetAttachedOWRigidbody();
SetReferenceTransform(_attachedBody.GetOrigParent());
/*
if (_attachedBody.GetOrigParent() == Locator.GetRootTransform())
{
DebugLog.DebugWrite($"{LogName} with AttachedObject {AttachedObject.name} had it's original parent as SolarSystemRoot - Disabling...");
enabled = false;
return;
}
*/
if (QSBCore.IsHost)
{

View File

@ -2,9 +2,9 @@
{
public enum QSBPatchTypes
{
OnClientConnect = 0,
OnNonServerClientConnect = 1,
OnServerClientConnect = 2,
RespawnTime = 3
OnClientConnect,
OnNonServerClientConnect,
OnServerClientConnect,
RespawnTime
}
}

View File

@ -2,20 +2,11 @@
{
public enum EnterLeaveType
{
EnterMoon = 0,
ExitMoon = 1,
EnterShrine = 2,
ExitShrine = 3,
EnterPlatform = 4,
ExitPlatform = 5,
EnterNonNomaiHeadZone = 6,
ExitNonNomaiHeadZone = 7,
EnterShip = 8,
ExitShip = 9,
EnterNomaiHeadZone = 10,
ExitNomaiHeadZone = 11,
EnterVesselCage = 12,
ExitVesselCage = 13,
EnterCosmicFog = 14,
EnterMoon,
ExitMoon,
EnterPlatform,
ExitPlatform,
EnterShip,
ExitShip,
}
}

View File

@ -2,7 +2,6 @@
{
public enum GamePlatform
{
None,
Steam,
Epic
}

View File

@ -1,7 +1,4 @@
using OWML.Common;
using QSB.Animation.NPC.WorldObjects;
using QSB.EyeOfTheUniverse.CosmicInflation;
using QSB.EyeOfTheUniverse.VesselSync;
using QSB.Messaging;
using QSB.Player.TransformSync;
using QSB.PoolSync;
@ -65,12 +62,6 @@ namespace QSB.Player.Messages
case EnterLeaveType.ExitMoon:
player.IsInMoon = false;
break;
case EnterLeaveType.EnterShrine:
player.IsInShrine = true;
break;
case EnterLeaveType.ExitShrine:
player.IsInShrine = false;
break;
case EnterLeaveType.EnterPlatform:
CustomNomaiRemoteCameraPlatform.CustomPlatformList[ObjectId]
.OnRemotePlayerEnter(From);
@ -79,33 +70,12 @@ namespace QSB.Player.Messages
CustomNomaiRemoteCameraPlatform.CustomPlatformList[ObjectId]
.OnRemotePlayerExit(From);
break;
case EnterLeaveType.EnterNonNomaiHeadZone:
ObjectId.GetWorldObject<QSBCharacterAnimController>().AddPlayerToHeadZone(player);
break;
case EnterLeaveType.ExitNonNomaiHeadZone:
ObjectId.GetWorldObject<QSBCharacterAnimController>().RemovePlayerFromHeadZone(player);
break;
case EnterLeaveType.EnterNomaiHeadZone:
ObjectId.GetWorldObject<QSBSolanumAnimController>().AddPlayerToHeadZone(player);
break;
case EnterLeaveType.ExitNomaiHeadZone:
ObjectId.GetWorldObject<QSBSolanumAnimController>().RemovePlayerFromHeadZone(player);
break;
case EnterLeaveType.EnterShip:
ShipManager.Instance.AddPlayerToShip(player);
break;
case EnterLeaveType.ExitShip:
ShipManager.Instance.RemovePlayerFromShip(player);
break;
case EnterLeaveType.EnterVesselCage:
VesselManager.Instance.Enter(player);
break;
case EnterLeaveType.ExitVesselCage:
VesselManager.Instance.Exit(player);
break;
case EnterLeaveType.EnterCosmicFog:
InflationManager.Instance.Enter(player);
break;
default:
DebugLog.ToConsole($"Warning - Unknown EnterLeaveType : {Value}", MessageType.Warning);
break;

View File

@ -16,6 +16,8 @@ using QSB.Tools.TranslatorTool.TranslationSync.Messages;
using QSB.Tools.TranslatorTool.TranslationSync.WorldObjects;
using QSB.TornadoSync.Messages;
using QSB.TornadoSync.WorldObjects;
using QSB.TriggerSync.Messages;
using QSB.TriggerSync.WorldObjects;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
@ -126,6 +128,9 @@ namespace QSB.Player.Messages
QSBWorldSync.GetWorldObjects<QSBTornado>().ForEach(tornado
=> tornado.SendMessage(new TornadoFormStateMessage(tornado.FormState) { To = From }));
QSBWorldSync.GetWorldObjects<IQSBTrigger>().ForEach(trigger
=> trigger.SendMessage(new TriggerResyncMessage(trigger.Occupants) { To = From }));
}
/// <summary>

View File

@ -39,6 +39,7 @@ namespace QSB.Player
public bool IsReady { get; set; }
public bool IsInMoon { get; set; }
public bool IsInShrine { get; set; }
public bool IsInEyeShuttle { get; set; }
public IQSBQuantumObject EntangledObject { get; set; }
public QSBPlayerAudioController AudioController { get; set; }
public DitheringAnimator DitheringAnimator { get; set; }
@ -97,7 +98,7 @@ namespace QSB.Player
// Tools
public GameObject ProbeBody { get; set; }
public QSBProbe Probe { get; set; }
public QSBFlashlight FlashLight => CameraBody?.GetComponentInChildren<QSBFlashlight>();
public QSBFlashlight FlashLight => CameraBody == null ? null : CameraBody.GetComponentInChildren<QSBFlashlight>();
public QSBTool Signalscope => GetToolByType(ToolType.Signalscope);
public QSBTool Translator => GetToolByType(ToolType.Translator);
public QSBProbeLauncherTool ProbeLauncher => (QSBProbeLauncherTool)GetToolByType(ToolType.ProbeLauncher);

View File

@ -32,11 +32,11 @@ namespace QSB.Player
/// <summary>
/// called right after player is added
/// </summary>
public static Action<uint> OnAddPlayer;
public static Action<PlayerInfo> OnAddPlayer;
/// <summary>
/// called right before player is removed
/// </summary>
public static Action<uint> OnRemovePlayer;
public static Action<PlayerInfo> OnRemovePlayer;
public static readonly List<PlayerInfo> PlayerList = new();

View File

@ -50,7 +50,7 @@ namespace QSB.Player.TransformSync
var player = new PlayerInfo(this);
QSBPlayerManager.PlayerList.SafeAdd(player);
base.Start();
QSBPlayerManager.OnAddPlayer?.Invoke(Player.PlayerId);
QSBPlayerManager.OnAddPlayer?.Invoke(Player);
DebugLog.DebugWrite($"Create Player : id<{Player.PlayerId}>", MessageType.Info);
}
@ -87,7 +87,7 @@ namespace QSB.Player.TransformSync
protected override void OnDestroy()
{
// TODO : Maybe move this to a leave event...? Would ensure everything could finish up before removing the player
QSBPlayerManager.OnRemovePlayer?.Invoke(Player.PlayerId);
QSBPlayerManager.OnRemovePlayer?.Invoke(Player);
base.OnDestroy();
Player.HudMarker?.Remove();
QSBPlayerManager.PlayerList.Remove(Player);
@ -169,6 +169,12 @@ namespace QSB.Player.TransformSync
REMOTE_Player_Body.AddComponent<PlayerHUDMarker>().Init(Player);
REMOTE_Player_Body.AddComponent<PlayerMapMarker>().PlayerName = Player.Name;
Player.DitheringAnimator = REMOTE_Player_Body.AddComponent<DitheringAnimator>();
// get inactive renderers too
QSBCore.UnityEvents.FireOnNextUpdate(() =>
Player.DitheringAnimator._renderers = Player.DitheringAnimator
.GetComponentsInChildren<Renderer>(true)
.Select(x => x.gameObject.GetAddComponent<OWRenderer>())
.ToArray());
Player.AudioController = PlayerAudioManager.InitRemote(REMOTE_Player_Body.transform);
/*

View File

@ -673,14 +673,13 @@ namespace QSB.PoolSync
public bool IsPlatformActive() => _platformActive;
public void OnRemovePlayer(uint id)
public void OnRemovePlayer(PlayerInfo player)
{
if (id == QSBPlayerManager.LocalPlayerId)
if (player == QSBPlayerManager.LocalPlayer)
{
return;
}
var player = QSBPlayerManager.GetPlayer(id);
if (!_playerToHologram.Any(x => x.Key == player))
{
return;
@ -689,7 +688,7 @@ namespace QSB.PoolSync
var hologram = _playerToHologram.First(x => x.Key == player).Value;
if (hologram.activeSelf)
{
OnRemotePlayerExit(id);
OnRemotePlayerExit(player.PlayerId);
}
_playerToHologram.Remove(player);

View File

@ -63,10 +63,10 @@ copy /y "$(OwmlDir)\OWML.Abstractions.dll" "$(UnityAssetsDir)"
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="OuterWildsGameLibs" Version="*" IncludeAssets="compile" />
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.125" IncludeAssets="compile" />
<ProjectReference Include="..\QuantumUNET\QuantumUNET.csproj" />
<PackageReference Include="HarmonyX" Version="2.*" IncludeAssets="compile" />
<PackageReference Include="OWML" Version="2.*" IncludeAssets="compile" />
<PackageReference Include="HarmonyX" Version="2.8.0" IncludeAssets="compile" />
<PackageReference Include="OWML" Version="2.3.1" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup>
<None Update="AssetBundles\*">

View File

@ -2,6 +2,7 @@
using OWML.ModHelper;
using OWML.ModHelper.Input;
using QSB.EyeOfTheUniverse.GalaxyMap;
using QSB.EyeOfTheUniverse.MaskSync;
using QSB.Inputs;
using QSB.Menus;
using QSB.Patches;
@ -109,6 +110,7 @@ namespace QSB
gameObject.AddComponent<StatueManager>();
gameObject.AddComponent<GalaxyMapManager>();
gameObject.AddComponent<DebugCameraSettings>();
gameObject.AddComponent<MaskManager>();
// WorldObject managers
foreach (var type in typeof(WorldObjectManager).GetDerivedTypes())

View File

@ -174,6 +174,7 @@ namespace QSB
if (QSBSceneManager.IsInUniverse)
{
WorldObjectManager.Rebuild(QSBSceneManager.CurrentScene);
QSBWorldSync.Init();
}
var specificType = QNetworkServer.active ? QSBPatchTypes.OnServerClientConnect : QSBPatchTypes.OnNonServerClientConnect;
@ -205,9 +206,7 @@ namespace QSB
QSBPlayerManager.PlayerList.ForEach(player => player.HudMarker?.Remove());
RemoveWorldObjects();
QSBWorldSync.DialogueConditions.Clear();
QSBWorldSync.OldDialogueTrees.Clear();
QSBWorldSync.ShipLogFacts.Clear();
QSBWorldSync.Reset();
if (WakeUpSync.LocalInstance != null)
{
@ -236,10 +235,10 @@ namespace QSB
DebugLog.DebugWrite("OnServerDisconnect", MessageType.Info);
// revert authority from ship
if (ShipTransformSync.LocalInstance)
if (ShipTransformSync.LocalInstance != null)
{
var identity = ShipTransformSync.LocalInstance.NetIdentity;
if (identity.ClientAuthorityOwner == conn)
if (identity != null && identity.ClientAuthorityOwner == conn)
{
identity.SetAuthority(QSBPlayerManager.LocalPlayerId);
}
@ -248,6 +247,12 @@ namespace QSB
// stop dragging for the orbs this player was dragging
foreach (var qsbOrb in QSBWorldSync.GetWorldObjects<QSBOrb>())
{
if (qsbOrb.TransformSync == null)
{
DebugLog.ToConsole($"{qsbOrb.LogName} TransformSync == null??????????", MessageType.Warning);
continue;
}
if (!qsbOrb.TransformSync.enabled)
{
continue;

View File

@ -28,6 +28,8 @@ namespace QSB
{
// So objects have time to be deleted, made, whatever
QSBCore.UnityEvents.FireOnNextUpdate(() => WorldObjectManager.Rebuild(newScene));
QSBWorldSync.Init();
}
OnSceneLoaded?.SafeInvoke(oldScene, newScene, universe);

View File

@ -0,0 +1,27 @@
using QSB.Messaging;
using QSB.QuantumSync.WorldObjects;
using QuantumUNET.Transport;
namespace QSB.QuantumSync.Messages
{
internal class MoveSkeletonMessage : QSBWorldObjectMessage<QSBQuantumSkeletonTower>
{
private int _index;
public MoveSkeletonMessage(int index) => _index = index;
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(_index);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
_index = reader.ReadInt32();
}
public override void OnReceiveRemote() => WorldObject.MoveSkeleton(_index);
}
}

View File

@ -0,0 +1,12 @@
using QSB.Messaging;
using QSB.QuantumSync.WorldObjects;
namespace QSB.QuantumSync.Messages
{
public class SetIsQuantumMessage : QSBBoolWorldObjectMessage<IQSBQuantumObject>
{
public SetIsQuantumMessage(bool isQuantum) => Value = isQuantum;
public override void OnReceiveRemote() => WorldObject.SetIsQuantum(Value);
}
}

View File

@ -3,7 +3,6 @@ using OWML.Common;
using QSB.Messaging;
using QSB.Patches;
using QSB.Player;
using QSB.Player.Messages;
using QSB.QuantumSync.Messages;
using QSB.QuantumSync.WorldObjects;
using QSB.Utility;
@ -28,6 +27,16 @@ namespace QSB.QuantumSync.Patches
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(QuantumObject), nameof(QuantumObject.SetIsQuantum))]
public static void QuantumObject_SetIsQuantum(QuantumObject __instance)
{
if (WorldObjectManager.AllObjectsReady)
{
__instance.GetWorldObject<IQSBQuantumObject>().SendMessage(new SetIsQuantumMessage(__instance.IsQuantum()));
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(SocketedQuantumObject), nameof(SocketedQuantumObject.ChangeQuantumState))]
public static bool SocketedQuantumObject_ChangeQuantumState(
@ -354,48 +363,6 @@ namespace QSB.QuantumSync.Patches
return isInControl;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(QuantumShrine), nameof(QuantumShrine.OnEntry))]
public static bool QuantumShrine_OnEntry(
QuantumShrine __instance,
GameObject hitObj)
{
if (hitObj.CompareTag("PlayerDetector"))
{
__instance._isPlayerInside = true;
__instance._fading = true;
__instance._exteriorLightController.FadeTo(0f, 1f);
new EnterLeaveMessage(EnterLeaveType.EnterShrine).Send();
}
else if (hitObj.CompareTag("ProbeDetector"))
{
__instance._isProbeInside = true;
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(QuantumShrine), nameof(QuantumShrine.OnExit))]
public static bool QuantumShrine_OnExit(
QuantumShrine __instance,
GameObject hitObj)
{
if (hitObj.CompareTag("PlayerDetector"))
{
__instance._isPlayerInside = false;
__instance._fading = true;
__instance._exteriorLightController.FadeTo(1f, 1f);
new EnterLeaveMessage(EnterLeaveType.ExitShrine).Send();
}
else if (hitObj.CompareTag("ProbeDetector"))
{
__instance._isProbeInside = false;
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(QuantumMoon), nameof(QuantumMoon.CheckPlayerFogProximity))]
public static bool QuantumMoon_CheckPlayerFogProximity(QuantumMoon __instance)
@ -462,5 +429,50 @@ namespace QSB.QuantumSync.Patches
__instance._shipLandingCamFogBubble.SetFogAlpha(fogAlpha);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(QuantumSkeletonTower), nameof(QuantumSkeletonTower.ChangeQuantumState))]
public static bool QuantumSkeletonTower_ChangeQuantumState(QuantumSkeletonTower __instance, ref bool __result)
{
if (!WorldObjectManager.AllObjectsReady)
{
return true;
}
var qsbQuantumSkeletonTower = __instance.GetWorldObject<QSBQuantumSkeletonTower>();
if (qsbQuantumSkeletonTower.ControllingPlayer != QSBPlayerManager.LocalPlayerId)
{
return false;
}
if (__instance._waitForPlayerToLookAtTower)
{
__result = false;
return false;
}
if (__instance._index < __instance._towerSkeletons.Length)
{
for (var i = 0; i < __instance._pointingSkeletons.Length; i++)
{
if (__instance._pointingSkeletons[i].gameObject.activeInHierarchy &&
(!__instance._pointingSkeletons[i].IsVisible() || !__instance._pointingSkeletons[i].IsIlluminated()))
{
__instance._pointingSkeletons[i].gameObject.SetActive(false);
__instance._towerSkeletons[__instance._index].SetActive(true);
__instance._index++;
__instance._waitForPlayerToLookAtTower = true;
qsbQuantumSkeletonTower.SendMessage(new MoveSkeletonMessage(i));
__result = true;
return false;
}
}
}
__result = false;
return false;
}
}
}

View File

@ -40,13 +40,14 @@ namespace QSB.QuantumSync
QSBWorldSync.Init<QSBQuantumShuffleObject, QuantumShuffleObject>();
QSBWorldSync.Init<QSBQuantumMoon, QuantumMoon>();
QSBWorldSync.Init<QSBEyeProxyQuantumMoon, EyeProxyQuantumMoon>();
QSBWorldSync.Init<QSBQuantumSkeletonTower, QuantumSkeletonTower>();
if (scene == OWScene.SolarSystem)
{
Shrine = QSBWorldSync.GetUnityObjects<QuantumShrine>().First();
}
}
public void PlayerLeave(uint playerId)
public void PlayerLeave(PlayerInfo player)
{
if (!QSBCore.IsHost)
{
@ -55,7 +56,7 @@ namespace QSB.QuantumSync
foreach (var obj in QSBWorldSync.GetWorldObjects<IQSBQuantumObject>())
{
if (obj.ControllingPlayer == playerId)
if (obj.ControllingPlayer == player.PlayerId)
{
obj.SendMessage(new QuantumAuthorityMessage(obj.IsEnabled ? QSBPlayerManager.LocalPlayerId : 0u));
}
@ -64,7 +65,7 @@ namespace QSB.QuantumSync
public void OnRenderObject()
{
if (!QSBCore.ShowLinesInDebug || !AllObjectsReady)
if (!QSBCore.ShowLinesInDebug)
{
return;
}
@ -73,25 +74,6 @@ namespace QSB.QuantumSync
{
Popcron.Gizmos.Sphere(Shrine.transform.position, 10f, Color.magenta);
}
foreach (var quantumObject in QSBWorldSync.GetWorldObjects<IQSBQuantumObject>())
{
if (quantumObject.ControllingPlayer == 0)
{
if (quantumObject.IsEnabled)
{
Popcron.Gizmos.Line(quantumObject.ReturnObject().transform.position,
QSBPlayerManager.LocalPlayer.Body.transform.position,
Color.magenta * 0.25f);
}
continue;
}
Popcron.Gizmos.Line(quantumObject.ReturnObject().transform.position,
QSBPlayerManager.GetPlayer(quantumObject.ControllingPlayer).Body.transform.position,
Color.magenta);
}
}
public static Tuple<bool, List<PlayerInfo>> IsVisibleUsingCameraFrustum(ShapeVisibilityTracker tracker, bool ignoreLocalCamera)

View File

@ -6,9 +6,10 @@ namespace QSB.QuantumSync.WorldObjects
public interface IQSBQuantumObject : IWorldObject
{
uint ControllingPlayer { get; set; }
bool IsEnabled { get; set; }
bool IsEnabled { get; }
List<ShapeVisibilityTracker> GetVisibilityTrackers();
List<Shape> GetAttachedShapes();
void SetIsQuantum(bool isQuantum);
}
}

View File

@ -1,15 +1,7 @@
using QSB.Player;
using System.Linq;
namespace QSB.QuantumSync.WorldObjects
namespace QSB.QuantumSync.WorldObjects
{
internal class QSBEyeProxyQuantumMoon : QSBQuantumObject<EyeProxyQuantumMoon>
{
public override void Init()
{
// smallest player id is the host
ControllingPlayer = QSBPlayerManager.PlayerList.Min(x => x.PlayerId);
base.Init();
}
protected override bool HostControls => true;
}
}

View File

@ -1,15 +1,7 @@
using QSB.Player;
using System.Linq;
namespace QSB.QuantumSync.WorldObjects
namespace QSB.QuantumSync.WorldObjects
{
internal class QSBQuantumMoon : QSBQuantumObject<QuantumMoon>
{
public override void Init()
{
// smallest player id is the host
ControllingPlayer = QSBPlayerManager.PlayerList.Min(x => x.PlayerId);
base.Init();
}
protected override bool HostControls => true;
}
}

View File

@ -7,17 +7,28 @@ using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace QSB.QuantumSync.WorldObjects
{
internal abstract class QSBQuantumObject<T> : WorldObject<T>, IQSBQuantumObject
where T : QuantumObject
{
/// <summary>
/// whether the controlling player is always the host <br/>
/// also means this object is considered always enabled
/// </summary>
protected virtual bool HostControls => false;
public uint ControllingPlayer { get; set; }
public bool IsEnabled { get; set; }
public bool IsEnabled { get; private set; }
public override void OnRemoval()
{
if (HostControls)
{
return;
}
foreach (var shape in GetAttachedShapes())
{
shape.OnShapeActivated -= OnEnable;
@ -25,9 +36,9 @@ namespace QSB.QuantumSync.WorldObjects
}
}
public override bool ShouldDisplayLabel() => ControllingPlayer != 0;
public override void Init()
{
if (QSBCore.ShowQuantumVisibilityObjects)
{
var debugBundle = QSBCore.DebugAssetBundle;
var sphere = debugBundle.LoadAsset<GameObject>("Assets/Prefabs/Sphere.prefab");
@ -50,13 +61,6 @@ namespace QSB.QuantumSync.WorldObjects
}
foreach (var shape in GetAttachedShapes())
{
if (shape == null)
{
break;
}
if (QSBCore.ShowQuantumVisibilityObjects)
{
if (shape is BoxShape boxShape)
{
@ -92,20 +96,28 @@ namespace QSB.QuantumSync.WorldObjects
private void LateInit()
{
FinishDelayedReady();
foreach (var shape in GetAttachedShapes())
if (HostControls)
{
shape.OnShapeActivated += OnEnable;
shape.OnShapeDeactivated += OnDisable;
// smallest player id is the host
ControllingPlayer = QSBPlayerManager.PlayerList.Min(x => x.PlayerId);
IsEnabled = true;
return;
}
var attachedShapes = GetAttachedShapes();
if (attachedShapes.Count == 0)
{
IsEnabled = false;
return;
}
foreach (var shape in attachedShapes)
{
shape.OnShapeActivated += OnEnable;
shape.OnShapeDeactivated += OnDisable;
}
if (attachedShapes.All(x => x.enabled && x.gameObject.activeInHierarchy && x.active))
{
IsEnabled = true;
@ -117,11 +129,6 @@ namespace QSB.QuantumSync.WorldObjects
}
}
public List<ShapeVisibilityTracker> GetVisibilityTrackers()
=> AttachedObject?._visibilityTrackers == null
? new()
: AttachedObject._visibilityTrackers.Select(x => (ShapeVisibilityTracker)x).ToList();
public List<Shape> GetAttachedShapes()
{
if (AttachedObject == null)
@ -150,13 +157,16 @@ namespace QSB.QuantumSync.WorldObjects
continue;
}
var shapes = tracker._shapes;
totalShapes.AddRange(shapes);
// if the tracker is not active, this won't have been set, so just do it ourselves
tracker._shapes ??= tracker.GetComponents<Shape>();
totalShapes.AddRange(tracker._shapes.Where(x => x != null));
}
return totalShapes;
}
public void SetIsQuantum(bool isQuantum) => AttachedObject._isQuantum = isQuantum;
private void OnEnable(Shape s)
{
if (IsEnabled)
@ -209,5 +219,57 @@ namespace QSB.QuantumSync.WorldObjects
// send event to other players that we're releasing authority
((IQSBQuantumObject)this).SendMessage(new QuantumAuthorityMessage(0u));
});
public override void DisplayLines()
{
if (AttachedObject == null)
{
return;
}
var localPlayer = QSBPlayerManager.LocalPlayer;
if (localPlayer == null)
{
return;
}
var body = localPlayer.Body;
if (body == null)
{
return;
}
if (ControllingPlayer == 0)
{
if (IsEnabled)
{
Popcron.Gizmos.Line(AttachedObject.transform.position,
body.transform.position,
Color.magenta * 0.25f);
}
return;
}
var player = QSBPlayerManager.GetPlayer(ControllingPlayer);
if (player == null)
{
return;
}
var playerBody = player.Body;
if (playerBody == null)
{
return;
}
Popcron.Gizmos.Line(AttachedObject.transform.position,
playerBody.transform.position,
Color.magenta);
}
}
}

View File

@ -0,0 +1,18 @@
namespace QSB.QuantumSync.WorldObjects
{
internal class QSBQuantumSkeletonTower : QSBQuantumObject<QuantumSkeletonTower>
{
public override string ReturnLabel() => $"{base.ReturnLabel()}\n"
+ $"{AttachedObject._index} {AttachedObject._waitForPlayerToLookAtTower}\n"
+ $"{AttachedObject._waitForFlicker} {AttachedObject._flickering}";
public void MoveSkeleton(int index)
{
AttachedObject._pointingSkeletons[index].gameObject.SetActive(false);
AttachedObject._towerSkeletons[AttachedObject._index].SetActive(true);
AttachedObject._index++;
AttachedObject._waitForPlayerToLookAtTower = true;
}
}
}

View File

@ -4,5 +4,6 @@ namespace QSB.QuantumSync.WorldObjects
{
internal class QSBQuantumSocket : WorldObject<QuantumSocket>
{
public override bool ShouldDisplayDebug() => false;
}
}

View File

@ -11,5 +11,7 @@ namespace QSB.QuantumSync.WorldObjects
IsMeantToBeEnabled = visible;
AttachedObject.SetVisible(visible);
}
public override bool ShouldDisplayDebug() => false;
}
}

View File

@ -31,6 +31,7 @@ namespace QSB.SaveSync.Messages
KnownSignals = gameSave.knownSignals;
}
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
@ -46,10 +47,10 @@ namespace QSB.SaveSync.Messages
}
writer.Write(KnownSignals.Count);
foreach (var (key, value) in KnownSignals)
foreach (var (name, discovered) in KnownSignals)
{
writer.Write(key);
writer.Write(value);
writer.Write(name);
writer.Write(discovered);
}
}

View File

@ -1,4 +1,7 @@
using QSB.Messaging;
using QSB.ConversationSync.Messages;
using QSB.ConversationSync.Patches;
using QSB.Messaging;
using System.Linq;
namespace QSB.SaveSync.Messages
{
@ -9,6 +12,24 @@ namespace QSB.SaveSync.Messages
{
public RequestGameStateMessage() => To = 0;
public override void OnReceiveRemote() => new GameStateMessage(From).Send();
public override void OnReceiveRemote()
{
new GameStateMessage(From).Send();
var gameSave = StandaloneProfileManager.SharedInstance.currentProfileGameSave;
var factSaves = gameSave.shipLogFactSaves;
foreach (var item in factSaves)
{
new ShipLogFactSaveMessage(item.Value).Send();
}
var dictConditions = gameSave.dictConditions;
var dictConditionsToSend = dictConditions.Where(x => ConversationPatches.PersistentConditionsToSync.Contains(x.Key));
foreach (var item in dictConditionsToSend)
{
new PersistentConditionMessage(item.Key, item.Value).Send();
}
}
}
}

View File

@ -0,0 +1,54 @@
using QSB.Messaging;
using QuantumUNET.Transport;
namespace QSB.SaveSync.Messages
{
internal class ShipLogFactSaveMessage : QSBMessage
{
private string _id;
private int _revealOrder;
private bool _read;
private bool _newlyRevealed;
public ShipLogFactSaveMessage(ShipLogFactSave save)
{
_id = save.id;
_revealOrder = save.revealOrder;
_read = save.read;
_newlyRevealed = save.newlyRevealed;
}
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(_id);
writer.Write(_revealOrder);
writer.Write(_read);
writer.Write(_newlyRevealed);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
_id = reader.ReadString();
_revealOrder = reader.ReadInt32();
_read = reader.ReadBoolean();
_newlyRevealed = reader.ReadBoolean();
}
public override void OnReceiveRemote()
{
var save = PlayerData.GetShipLogFactSave(_id);
if (save == null)
{
save = new ShipLogFactSave(_id);
PlayerData.AddShipLogFactSave(save);
}
save.revealOrder = _revealOrder;
save.read = _read;
save.newlyRevealed = _newlyRevealed;
}
}
}

View File

@ -1,10 +0,0 @@
namespace QSB.ShipSync
{
public enum ComponentType
{
None = 0,
Hull = 1,
Component = 2,
SatelliteNode = 3
}
}

View File

@ -8,7 +8,6 @@ using QSB.ShipSync.TransformSync;
using QSB.ShipSync.WorldObjects;
using QSB.Utility;
using QSB.WorldSync;
using System;
using UnityEngine;
namespace QSB.ShipSync.Patches
@ -60,7 +59,7 @@ namespace QSB.ShipSync.Patches
[HarmonyReversePatch]
[HarmonyPatch(typeof(SingleInteractionVolume), nameof(SingleInteractionVolume.UpdateInteractVolume))]
public static void SingleInteractionVolume_UpdateInteractVolume_Stub(object instance) => throw new NotImplementedException();
public static void SingleInteractionVolume_UpdateInteractVolume_Stub(object instance) { }
[HarmonyPrefix]
[HarmonyPatch(typeof(InteractZone), nameof(InteractZone.UpdateInteractVolume))]
@ -96,7 +95,7 @@ namespace QSB.ShipSync.Patches
[HarmonyReversePatch]
[HarmonyPatch(typeof(ShipComponent), nameof(ShipComponent.OnEnterShip))]
public static void ShipComponent_OnEnterShip_Stub(object instance) => throw new NotImplementedException();
public static void ShipComponent_OnEnterShip_Stub(object instance) { }
[HarmonyPrefix]
[HarmonyPatch(typeof(ShipElectricalComponent), nameof(ShipElectricalComponent.OnEnterShip))]
@ -109,7 +108,7 @@ namespace QSB.ShipSync.Patches
[HarmonyReversePatch]
[HarmonyPatch(typeof(ShipComponent), nameof(ShipComponent.OnExitShip))]
public static void ShipComponent_OnExitShip_Stub(object instance) => throw new NotImplementedException();
public static void ShipComponent_OnExitShip_Stub(object instance) { }
[HarmonyPrefix]
[HarmonyPatch(typeof(ShipElectricalComponent), nameof(ShipElectricalComponent.OnExitShip))]

View File

@ -1,5 +1,4 @@
using OWML.Common;
using QSB.Player;
using QSB.SectorSync;
using QSB.SectorSync.WorldObjects;
using QSB.Utility;
@ -127,6 +126,13 @@ namespace QSB.Syncs.Sectored
{
if (IsPlayerObject)
{
if (Player == null)
{
// happens once right when you start hosting
writer.Write(-1);
return;
}
if (!Player.IsReady)
{
writer.Write(-1);

View File

@ -57,7 +57,7 @@ namespace QSB.Syncs
public T AttachedObject { get; set; }
public Transform ReferenceTransform { get; set; }
public string LogName => $"{(IsPlayerObject ? Player.PlayerId : "<non player object>")}.{NetId.Value}:{GetType().Name}";
public string LogName => (IsPlayerObject ? $"{Player.PlayerId}." : string.Empty) + $"{NetId.Value}:{GetType().Name}";
protected virtual float DistanceLeeway { get; } = 5f;
private float _previousDistance;
protected const float SmoothTime = 0.1f;
@ -157,12 +157,20 @@ namespace QSB.Syncs
return;
}
if (ReferenceTransform != null && ReferenceTransform.position == Vector3.zero)
if (!AttachedObject.gameObject.activeInHierarchy && !IgnoreDisabledAttachedObject)
{
base.Update();
return;
}
else
{
if (ReferenceTransform != null && ReferenceTransform.position == Vector3.zero && ReferenceTransform != Locator.GetRootTransform())
{
DebugLog.ToConsole($"Warning - {LogName}'s ReferenceTransform is at (0,0,0). ReferenceTransform:{ReferenceTransform.name}, AttachedObject:{AttachedObject.name}", MessageType.Warning);
}
}
if (!AttachedObject.gameObject.activeInHierarchy && !IgnoreDisabledAttachedObject)
if (ReferenceTransform == Locator.GetRootTransform())
{
base.Update();
return;

View File

@ -2,7 +2,6 @@
{
public enum FastForwardReason
{
None,
TooFarBehind
}
}

View File

@ -2,7 +2,6 @@
{
public enum PauseReason
{
None,
TooFarAhead,
ServerNotStarted,
WaitingForAllPlayersToDie,

View File

@ -2,8 +2,8 @@
{
public enum TimeSyncType
{
None = 0,
Pausing = 1,
Fastforwarding = 2
None,
Pausing,
FastForwarding
}
}

View File

@ -90,7 +90,7 @@ namespace QSB.TimeSync
var text = "";
switch (_currentType)
{
case TimeSyncType.Fastforwarding:
case TimeSyncType.FastForwarding:
switch ((FastForwardReason)_currentReason)
{
case FastForwardReason.TooFarBehind:

View File

@ -192,7 +192,7 @@ namespace QSB.TimeSync
OWTime.SetMaxDeltaTime(0.033333335f);
OWTime.SetFixedTimestep(0.033333335f);
TimeSyncUI.TargetTime = _serverTime;
TimeSyncUI.Start(TimeSyncType.Fastforwarding, FastForwardReason.TooFarBehind);
TimeSyncUI.Start(TimeSyncType.FastForwarding, FastForwardReason.TooFarBehind);
}
private void StartPausing(PauseReason reason)

View File

@ -2,11 +2,11 @@
{
public enum ProbeEvent
{
Invalid = 0,
Launch = 1,
Anchor = 2,
Unanchor = 3,
Retrieve = 4,
Destroy = 5
Invalid,
Launch,
Anchor,
Unanchor,
Retrieve,
Destroy
}
}

View File

@ -2,8 +2,8 @@
{
public enum ToolType
{
Signalscope = 0,
ProbeLauncher = 1,
Translator = 2
Signalscope,
ProbeLauncher,
Translator
}
}

View File

@ -0,0 +1,26 @@
using QSB.Messaging;
using QSB.Player;
using QSB.TriggerSync.WorldObjects;
namespace QSB.TriggerSync.Messages
{
public class TriggerMessage : QSBBoolWorldObjectMessage<IQSBTrigger>
{
public TriggerMessage(bool entered) => Value = entered;
public override void OnReceiveLocal() => OnReceiveRemote();
public override void OnReceiveRemote()
{
var player = QSBPlayerManager.GetPlayer(From);
if (Value)
{
WorldObject.Enter(player);
}
else
{
WorldObject.Exit(player);
}
}
}
}

View File

@ -0,0 +1,52 @@
using QSB.Messaging;
using QSB.Player;
using QSB.TriggerSync.WorldObjects;
using QSB.Utility;
using QuantumUNET.Transport;
using System.Collections.Generic;
using System.Linq;
namespace QSB.TriggerSync.Messages
{
/// <summary>
/// always sent by host
/// </summary>
public class TriggerResyncMessage : QSBWorldObjectMessage<IQSBTrigger>
{
private uint[] _playerIds;
public TriggerResyncMessage(IEnumerable<PlayerInfo> occupants) =>
_playerIds = occupants.Select(x => x.PlayerId).ToArray();
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(_playerIds.Length);
_playerIds.ForEach(writer.Write);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
_playerIds = new uint[reader.ReadInt32()];
for (var i = 0; i < _playerIds.Length; i++)
{
_playerIds[i] = reader.ReadUInt32();
}
}
public override void OnReceiveRemote()
{
var serverOccupants = _playerIds.Select(QSBPlayerManager.GetPlayer).ToList();
foreach (var added in serverOccupants.Except(WorldObject.Occupants))
{
WorldObject.Enter(added);
}
foreach (var removed in WorldObject.Occupants.Except(serverOccupants))
{
WorldObject.Exit(removed);
}
}
}
}

View File

@ -0,0 +1,20 @@
using QSB.TriggerSync.WorldObjects;
using QSB.WorldSync;
namespace QSB.TriggerSync
{
public class TriggerManager : WorldObjectManager
{
public override WorldObjectType WorldObjectType => WorldObjectType.Both;
protected override void RebuildWorldObjects(OWScene scene)
{
QSBWorldSync.Init<QSBCharacterTrigger, CharacterAnimController>(x => x.playerTrackingZone);
QSBWorldSync.Init<QSBSolanumTrigger, NomaiConversationManager>(x => x._watchPlayerVolume);
QSBWorldSync.Init<QSBShrineTrigger, QuantumShrine>(x => x._triggerVolume);
QSBWorldSync.Init<QSBVesselCageTrigger, VesselWarpController>(x => x._cageTrigger);
QSBWorldSync.Init<QSBInflationTrigger, CosmicInflationController>(x => x._smokeSphereTrigger);
QSBWorldSync.Init<QSBMaskZoneTrigger, MaskZoneController>(x => x._maskZoneTrigger);
}
}
}

View File

@ -0,0 +1,12 @@
namespace QSB.TriggerSync.WorldObjects
{
public class QSBCharacterTrigger : QSBTrigger<CharacterAnimController>
{
public override void Init()
{
base.Init();
AttachedObject.OnEntry -= TriggerOwner.OnZoneEntry;
AttachedObject.OnExit -= TriggerOwner.OnZoneExit;
}
}
}

View File

@ -0,0 +1,20 @@
using QSB.Player;
namespace QSB.TriggerSync.WorldObjects
{
public class QSBEyeShuttleTrigger : QSBTrigger<EyeShuttleController>
{
public override void Init()
{
base.Init();
AttachedObject.OnEntry -= TriggerOwner.OnEnterShuttle;
AttachedObject.OnExit -= TriggerOwner.OnExitShuttle;
}
protected override void OnEnter(PlayerInfo player)
=> player.IsInEyeShuttle = true;
protected override void OnExit(PlayerInfo player)
=> player.IsInEyeShuttle = false;
}
}

View File

@ -0,0 +1,98 @@
using QSB.Player;
using QSB.Utility;
using UnityEngine;
namespace QSB.TriggerSync.WorldObjects
{
public class QSBInflationTrigger : QSBTrigger<CosmicInflationController>
{
protected override string CompareTag => "PlayerCameraDetector";
public override void Init()
{
base.Init();
AttachedObject.OnEntry -= TriggerOwner.OnEnterFogSphere;
}
protected override void OnEnter(PlayerInfo player)
{
if (TriggerOwner._state != CosmicInflationController.State.ReadyToCollapse)
{
return;
}
if (player == QSBPlayerManager.LocalPlayer)
{
AttachedObject.OnEntry -= OnEnterEvent;
AttachedObject.SetTriggerActivation(false);
TriggerOwner._probeDestroyTrigger.SetTriggerActivation(false);
DebugLog.DebugWrite("disable input, wait for other players to enter");
var repelVolume = (WhiteHoleFluidVolume)TriggerOwner._repelVolume;
repelVolume._flowSpeed = -repelVolume._flowSpeed;
repelVolume._massiveFlowSpeed = -repelVolume._massiveFlowSpeed;
repelVolume.SetVolumeActivation(true);
QSBPlayerManager.HideAllPlayers();
ReticleController.Hide();
Locator.GetFlashlight().TurnOff(false);
Locator.GetPromptManager().SetPromptsVisible(false);
OWInput.ChangeInputMode(InputMode.None);
}
else
{
DebugLog.DebugWrite($"fade out player {player.PlayerId}");
player.DitheringAnimator.SetVisible(false, 3);
}
if (Occupants.Count == QSBPlayerManager.PlayerList.Count)
{
StartCollapse();
}
}
protected override void OnExit(PlayerInfo player)
{
// wait 1 frame for player to be removed
QSBCore.UnityEvents.FireOnNextUpdate(() =>
{
if (QSBCore.IsInMultiplayer && Occupants.Count == QSBPlayerManager.PlayerList.Count)
{
StartCollapse();
}
});
}
private void StartCollapse()
{
DebugLog.DebugWrite("fade in everyone, fog sphere collapse");
var repelVolume = (WhiteHoleFluidVolume)TriggerOwner._repelVolume;
repelVolume.SetVolumeActivation(false);
QSBPlayerManager.ShowAllPlayers();
TriggerOwner._state = CosmicInflationController.State.Collapsing;
TriggerOwner._stateChangeTime = Time.time;
TriggerOwner._collapseStartPos = TriggerOwner._possibilitySphereRoot.localPosition;
AttachedObject.SetTriggerActivation(false);
TriggerOwner._inflationLight.FadeTo(1f, 1f);
TriggerOwner._possibilitySphereController.OnCollapse();
if (TriggerOwner._campsiteController.GetUseAltPostCollapseSocket())
{
TriggerOwner._playerPostCollapseSocket = TriggerOwner._altPlayerPostCollapseSocket;
TriggerOwner._altTravelerToHidePostCollapse.SetActive(false);
}
Locator.GetPlayerBody().SetPosition(TriggerOwner._playerPostCollapseSocket.position);
Locator.GetPlayerBody().SetRotation(TriggerOwner._playerPostCollapseSocket.rotation);
Locator.GetPlayerBody().SetVelocity(-TriggerOwner._playerPostCollapseSocket.forward);
Locator.GetPlayerTransform().GetRequiredComponent<PlayerLockOnTargeting>().LockOn(TriggerOwner._possibilitySphereRoot, 2f);
foreach (var particles in TriggerOwner._smokeSphereParticles)
{
particles.Stop();
}
}
}
}

View File

@ -0,0 +1,38 @@
using QSB.Player;
namespace QSB.TriggerSync.WorldObjects
{
public class QSBMaskZoneTrigger : QSBTrigger<MaskZoneController>
{
public override void Init()
{
base.Init();
AttachedObject.OnEntry -= TriggerOwner.OnEnterMaskZone;
AttachedObject.OnExit -= TriggerOwner.OnExitMaskZone;
}
protected override void OnEnter(PlayerInfo player)
{
if (Occupants.Count == 1)
{
TriggerOwner._whiteSphere.SetActive(true);
TriggerOwner._groundSignal.SetSignalActivation(false);
TriggerOwner._skySignal.SetSignalActivation(true);
TriggerOwner._skeletonTower.SetIsQuantum(TriggerOwner._hasPlayerLookedAtSky);
TriggerOwner.enabled = true;
}
}
protected override void OnExit(PlayerInfo player)
{
if (Occupants.Count == 0 && !TriggerOwner._shuttle.HasLaunched())
{
TriggerOwner._whiteSphere.SetActive(false);
TriggerOwner._skeletonTower.SetIsQuantum(false);
TriggerOwner._groundSignal.SetSignalActivation(true);
TriggerOwner._skySignal.SetSignalActivation(false);
TriggerOwner.enabled = false;
}
}
}
}

View File

@ -0,0 +1,11 @@
using QSB.Player;
namespace QSB.TriggerSync.WorldObjects
{
public class QSBShrineTrigger : QSBTrigger<QuantumShrine>
{
protected override void OnEnter(PlayerInfo player) => player.IsInShrine = true;
protected override void OnExit(PlayerInfo player) => player.IsInShrine = false;
}
}

View File

@ -0,0 +1,12 @@
namespace QSB.TriggerSync.WorldObjects
{
public class QSBSolanumTrigger : QSBTrigger<NomaiConversationManager>
{
public override void Init()
{
base.Init();
AttachedObject.OnEntry -= TriggerOwner.OnEnterWatchVolume;
AttachedObject.OnExit -= TriggerOwner.OnExitWatchVolume;
}
}
}

View File

@ -0,0 +1,106 @@
using QSB.Messaging;
using QSB.Player;
using QSB.TriggerSync.Messages;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace QSB.TriggerSync.WorldObjects
{
public interface IQSBTrigger : IWorldObject
{
List<PlayerInfo> Occupants { get; }
void Enter(PlayerInfo player);
void Exit(PlayerInfo player);
}
public abstract class QSBTrigger<TO> : WorldObject<OWTriggerVolume>, IQSBTrigger
{
public TO TriggerOwner { get; init; }
public List<PlayerInfo> Occupants { get; } = new();
protected virtual string CompareTag => "PlayerDetector";
public override void Init()
{
AttachedObject.OnEntry += OnEnterEvent;
AttachedObject.OnExit += OnExitEvent;
QSBPlayerManager.OnRemovePlayer += OnPlayerLeave;
QSBCore.UnityEvents.RunWhen(() => WorldObjectManager.AllObjectsReady, () =>
{
if (AttachedObject._trackedObjects != null && AttachedObject._trackedObjects.Any(x => x.CompareTag(CompareTag)))
{
((IQSBTrigger)this).SendMessage(new TriggerMessage(true));
}
});
}
public override void OnRemoval()
{
AttachedObject.OnEntry -= OnEnterEvent;
AttachedObject.OnExit -= OnExitEvent;
QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave;
}
protected void OnEnterEvent(GameObject hitObj)
{
if (hitObj.CompareTag(CompareTag))
{
((IQSBTrigger)this).SendMessage(new TriggerMessage(true));
}
}
protected void OnExitEvent(GameObject hitObj)
{
if (hitObj.CompareTag(CompareTag))
{
((IQSBTrigger)this).SendMessage(new TriggerMessage(false));
}
}
private void OnPlayerLeave(PlayerInfo player)
{
if (Occupants.Contains(player))
{
Exit(player);
}
}
public void Enter(PlayerInfo player)
{
if (!Occupants.SafeAdd(player))
{
return;
}
OnEnter(player);
}
public void Exit(PlayerInfo player)
{
if (!Occupants.QuickRemove(player))
{
return;
}
OnExit(player);
}
/// <summary>
/// called when a player enters this trigger
/// </summary>
protected virtual void OnEnter(PlayerInfo player) { }
/// <summary>
/// called when a player exits this trigger or leaves the game
/// </summary>
protected virtual void OnExit(PlayerInfo player) { }
}
}

View File

@ -0,0 +1,27 @@
using QSB.Player;
using UnityEngine;
namespace QSB.TriggerSync.WorldObjects
{
public class QSBVesselCageTrigger : QSBTrigger<VesselWarpController>
{
public override void Init()
{
base.Init();
AttachedObject.OnExit -= TriggerOwner.OnExitCageTrigger;
}
protected override void OnExit(PlayerInfo player)
{
if (Occupants.Count == 0 && TriggerOwner._hasPower)
{
TriggerOwner._cageClosed = true;
TriggerOwner._cageAnimator.TranslateToLocalPosition(new Vector3(0f, -8.1f, 0f), 5f);
TriggerOwner._cageAnimator.RotateToLocalEulerAngles(new Vector3(0f, 180f, 0f), 5f);
TriggerOwner._cageAnimator.OnTranslationComplete -= TriggerOwner.OnCageAnimationComplete;
TriggerOwner._cageAnimator.OnTranslationComplete += TriggerOwner.OnCageAnimationComplete;
TriggerOwner._cageLoopingAudio.FadeIn(1f);
}
}
}
}

View File

@ -47,7 +47,7 @@ namespace QSB.Utility
* 3 - Destroy probe
* 4 - Damage ship electricals
* 5 - Trigger supernova
* 6 -
* 6 - Set MET_SOLANUM
* 7 - Warp to vessel
* 8 - Place warp core into vessel
* 9 - Load eye scene
@ -83,6 +83,14 @@ namespace QSB.Utility
new DebugTriggerSupernovaMessage().Send();
}
if (Keyboard.current[Key.Numpad6].wasPressedThisFrame)
{
PlayerData.SetPersistentCondition("MET_SOLANUM", true);
PlayerData.SetPersistentCondition("MET_PRISONER", true);
DialogueConditionManager.SharedInstance.SetConditionState("MET_SOLANUM", true);
DialogueConditionManager.SharedInstance.SetConditionState("MET_PRISONER", true);
}
if (Keyboard.current[Key.Numpad7].wasPressedThisFrame)
{
GoToVessel();
@ -94,10 +102,19 @@ namespace QSB.Utility
}
if (Keyboard.current[Key.Numpad9].wasPressedThisFrame)
{
if (Keyboard.current[Key.LeftShift].isPressed)
{
PlayerData._currentGameSave.warpedToTheEye = false;
PlayerData.SaveCurrentGame();
LoadManager.LoadSceneAsync(OWScene.SolarSystem, true, LoadManager.FadeType.ToBlack);
}
else
{
PlayerData.SaveWarpedToTheEye(60);
LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, true, LoadManager.FadeType.ToWhite);
}
}
}
}
}

View File

@ -251,6 +251,8 @@ namespace QSB.Utility
DrawWorldObjectLabels();
}
public void OnRenderObject() => DrawWorldObjectLines();
private static void DrawWorldObjectLabels()
{
if (!QSBCore.ShowDebugLabels)
@ -265,13 +267,34 @@ namespace QSB.Utility
return;
}
if (obj.ShouldDisplayLabel())
if (obj.ShouldDisplayDebug())
{
DrawLabel(obj.ReturnObject().transform, obj.ReturnLabel());
}
}
}
private static void DrawWorldObjectLines()
{
if (!QSBCore.ShowLinesInDebug)
{
return;
}
foreach (var obj in QSBWorldSync.GetWorldObjects())
{
if (obj.ReturnObject() == null)
{
return;
}
if (obj.ShouldDisplayDebug())
{
obj.DisplayLines();
}
}
}
public static void DrawLabel(Transform obj, string label)
{
var camera = Locator.GetPlayerCamera();

View File

@ -37,6 +37,6 @@ namespace QSB.Utility
private static string GetCallingType(StackTrace frame) =>
frame.GetFrames()!
.Select(x => x.GetMethod().DeclaringType!.Name)
.First(x => x == nameof(DebugLog));
.First(x => x != nameof(DebugLog));
}
}

View File

@ -9,7 +9,8 @@ namespace QSB.WorldSync
void OnRemoval();
MonoBehaviour ReturnObject();
bool ShouldDisplayLabel();
bool ShouldDisplayDebug();
string ReturnLabel();
void DisplayLines();
}
}

View File

@ -1,5 +1,7 @@
using OWML.Common;
using QSB.ConversationSync.Patches;
using QSB.LogSync;
using QSB.TriggerSync.WorldObjects;
using QSB.Utility;
using System;
using System.Collections.Generic;
@ -10,13 +12,53 @@ namespace QSB.WorldSync
{
public static class QSBWorldSync
{
public static readonly List<CharacterDialogueTree> OldDialogueTrees = new();
public static readonly Dictionary<string, bool> DialogueConditions = new();
public static readonly List<FactReveal> ShipLogFacts = new();
public static List<CharacterDialogueTree> OldDialogueTrees { get; private set; } = new();
public static Dictionary<string, bool> DialogueConditions { get; private set; } = new();
public static Dictionary<string, bool> PersistentConditions { get; private set; } = new();
public static List<FactReveal> ShipLogFacts { get; private set; } = new();
private static readonly List<IWorldObject> WorldObjects = new();
private static readonly Dictionary<MonoBehaviour, IWorldObject> WorldObjectsToUnityObjects = new();
public static void Init()
{
DebugLog.DebugWrite($"Init QSBWorldSync", MessageType.Info);
OldDialogueTrees.Clear();
OldDialogueTrees.AddRange(GetUnityObjects<CharacterDialogueTree>());
if (!QSBCore.IsHost)
{
return;
}
DebugLog.DebugWrite($"DIALOGUE CONDITIONS :");
DialogueConditions = (Dictionary<string, bool>)DialogueConditionManager.SharedInstance._dictConditions;
foreach (var item in DialogueConditions)
{
DebugLog.DebugWrite($"- {item.Key}, {item.Value}");
}
DebugLog.DebugWrite($"PERSISTENT CONDITIONS :");
var dictConditions = PlayerData._currentGameSave.dictConditions;
var syncedConditions = dictConditions.Where(x => ConversationPatches.PersistentConditionsToSync.Contains(x.Key));
PersistentConditions = syncedConditions.ToDictionary(x => x.Key, x => x.Value);
foreach (var item in PersistentConditions)
{
DebugLog.DebugWrite($"- {item.Key}, {item.Value}");
}
}
public static void Reset()
{
DebugLog.DebugWrite($"Reset QSBWorldSync", MessageType.Info);
OldDialogueTrees.Clear();
DialogueConditions.Clear();
PersistentConditions.Clear();
ShipLogFacts.Clear();
}
public static IEnumerable<IWorldObject> GetWorldObjects() => WorldObjects;
public static IEnumerable<TWorldObject> GetWorldObjects<TWorldObject>()
@ -122,11 +164,33 @@ namespace QSB.WorldSync
{
//DebugLog.DebugWrite($"{typeof(TWorldObject).Name} init : {listToInitFrom.Count()} instances.", MessageType.Info);
foreach (var item in listToInitFrom)
{
var obj = new TWorldObject
{
AttachedObject = item,
ObjectId = WorldObjects.Count
};
obj.Init();
WorldObjects.Add(obj);
WorldObjectsToUnityObjects.Add(item, obj);
}
}
public static void Init<TWorldObject, TUnityObject>(Func<TUnityObject, OWTriggerVolume> triggerSelector)
where TWorldObject : QSBTrigger<TUnityObject>, new()
where TUnityObject : MonoBehaviour
{
var list = GetUnityObjects<TUnityObject>()
.Select(x => (triggerSelector(x), x))
.Where(x => x.Item1);
foreach (var (item, owner) in list)
{
var obj = new TWorldObject
{
AttachedObject = item,
ObjectId = WorldObjects.Count,
TriggerOwner = owner
};
obj.Init();
@ -139,13 +203,24 @@ namespace QSB.WorldSync
{
if (!QSBCore.IsHost)
{
DebugLog.ToConsole("Warning - Cannot write to condition dict when not server!", MessageType.Warning);
DebugLog.ToConsole("Warning - Cannot write to dialogue condition dict when not server!", MessageType.Warning);
return;
}
DialogueConditions[name] = state;
}
public static void SetPersistentCondition(string name, bool state)
{
if (!QSBCore.IsHost)
{
DebugLog.ToConsole("Warning - Cannot write to persistent condition dict when not server!", MessageType.Warning);
return;
}
PersistentConditions[name] = state;
}
public static void AddFactReveal(string id, bool saveGame)
{
if (!QSBCore.IsHost)

View File

@ -9,13 +9,14 @@ namespace QSB.WorldSync
public int ObjectId { get; init; }
public T AttachedObject { get; init; }
public string Name => AttachedObject == null ? "<NullObject!>" : AttachedObject.name;
public string LogName => $"{QSBPlayerManager.LocalPlayerId}.{ObjectId}:{GetType().Name}";
public string LogName => $"{QSBPlayerManager.LocalPlayerId}.{ObjectId}:{GetType().Name} ({Name})";
public virtual void Init() { }
public virtual void OnRemoval() { }
public MonoBehaviour ReturnObject() => AttachedObject;
public virtual bool ShouldDisplayLabel() => true;
public virtual bool ShouldDisplayDebug() => AttachedObject != null && AttachedObject.gameObject.activeInHierarchy;
public virtual string ReturnLabel() => LogName;
public virtual void DisplayLines() { }
/// indicates that this won't become ready immediately
protected void StartDelayedReady() => WorldObjectManager._numObjectsReadying++;

View File

@ -1,5 +1,6 @@
using OWML.Common;
using QSB.Player;
using QSB.Player.TransformSync;
using QSB.Utility;
using System;
using System.Collections.Generic;
@ -66,10 +67,10 @@ namespace QSB.WorldSync
return;
}
if (QSBPlayerManager.LocalPlayerId == uint.MaxValue)
if (PlayerTransformSync.LocalInstance == null)
{
DebugLog.ToConsole($"Warning - Tried to rebuild WorldObjects when LocalPlayer is not ready! Building when ready...", MessageType.Warning);
QSBCore.UnityEvents.RunWhen(() => QSBPlayerManager.LocalPlayerId != uint.MaxValue, () => Rebuild(scene));
QSBCore.UnityEvents.RunWhen(() => PlayerTransformSync.LocalInstance, () => Rebuild(scene));
return;
}

View File

@ -8,6 +8,6 @@
},
"uniqueName": "Raicuparta.QuantumSpaceBuddies",
"version": "0.15.0",
"owmlVersion": "2.2.0",
"owmlVersion": "2.3.1",
"dependencies": [ "_nebula.MenuFramework" ]
}

View File

@ -2,6 +2,7 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using MonoMod.Cil;
using QSB.Messaging;
using QSB.Utility;
using System;
@ -34,7 +35,7 @@ namespace QSBTests
foreach (var field in fields)
{
if (!field.Eq(fromField) && !field.Eq(toField) && !field.Eq(objectIdField))
if (!field.GenericEq(fromField) && !field.GenericEq(toField) && !field.GenericEq(objectIdField))
{
constructor.CheckUses(field, Util.UseType.Store);
}
@ -46,60 +47,45 @@ namespace QSBTests
}
}
internal static class Util
public static partial class Util
{
public const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
public static bool Eq(this MemberReference a, MemberReference b) =>
/// <summary>
/// ignores open vs closed generic type
/// </summary>
public static bool GenericEq(this MemberReference a, MemberReference b) =>
a.DeclaringType.Namespace == b.DeclaringType.Namespace &&
a.DeclaringType.Name == b.DeclaringType.Name &&
a.Name == b.Name;
public static bool IsOp<T>(this Instruction instruction, OpCode opCode, out T operand)
{
if (instruction.OpCode == opCode)
{
operand = (T)instruction.Operand;
return true;
}
operand = default;
return false;
}
public enum UseType { Store, Load };
public enum UseType { Store, Load }
public static void CheckUses(this MethodDefinition method, FieldReference field, UseType useType)
{
var opCode = useType switch
Func<Instruction, bool> matches = useType switch
{
UseType.Store => OpCodes.Stfld,
UseType.Load => OpCodes.Ldfld,
UseType.Store => x => x.MatchStfld(out var f) && f.GenericEq(field),
UseType.Load => x => x.MatchLdfld(out var f) && f.GenericEq(field),
_ => throw new ArgumentOutOfRangeException(nameof(useType), useType, null)
};
while (true)
{
var il = method.Body.Instructions;
var uses = il.Any(x =>
x.IsOp(opCode, out FieldReference f) &&
f.Eq(field)
);
var uses = il.Any(matches);
if (uses)
{
return;
}
var baseMethod = method.GetBaseMethod();
if (baseMethod.Eq(method))
if (baseMethod == method)
{
break;
}
var callsBase = il.Any(x =>
x.IsOp(OpCodes.Call, out MethodReference m) &&
m.Eq(baseMethod)
);
var callsBase = il.Any(x => x.MatchCall(out var m) && m.GenericEq(baseMethod));
if (!callsBase)
{
break;

View File

@ -19,12 +19,12 @@
</PropertyGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
<PackageReference Include="OuterWildsGameLibs" Version="*" />
<PackageReference Include="OWML" Version="2.*" />
<PackageReference Include="HarmonyX" Version="2.*" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.125" />
<PackageReference Include="OWML" Version="2.3.1" />
<PackageReference Include="HarmonyX" Version="2.8.0" />
<ProjectReference Include="..\QSB\QSB.csproj" />
</ItemGroup>
</Project>

View File

@ -153,10 +153,6 @@ namespace QuantumUNET
do
{
networkEventType = NetworkTransport.ReceiveFromHost(serverHostId, out var connectionId, out var channelId, messageBuffer, messageBuffer.Length, out var receivedSize, out var b);
if (networkEventType != NetworkEventType.Nothing)
{
Debug.Log($"Server event: host={serverHostId} event={networkEventType} error={b}");
}
switch (networkEventType)
{

View File

@ -13,7 +13,7 @@
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OuterWildsGameLibs" Version="*" IncludeAssets="compile" />
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.125" IncludeAssets="compile" />
<Reference Include="System.Data.DataSetExtensions" />
</ItemGroup>
</Project>