Merge branch 'dev' into model-ship-sync

This commit is contained in:
JohnCorby 2022-05-29 14:35:23 -07:00
commit 8c25a699ea
23 changed files with 492 additions and 186 deletions

View File

@ -34,7 +34,8 @@ internal class GhostPartyDirectorPatches : QSBPatch
var index = Random.Range(0, __instance._ghostsWaitingToAmbush.Count);
DebugLog.DebugWrite($"Unlocking ghost {index} for ambush.");
var ghost = __instance._ghostsWaitingToAmbush[index].GetWorldObject<QSBGhostBrain>();
(ghost.GetAction(GhostAction.Name.PartyHouse) as QSBPartyHouseAction).AllowChasePlayer();
// BUG: breaks on client cuz cast
((QSBPartyHouseAction)ghost.GetAction(GhostAction.Name.PartyHouse)).AllowChasePlayer();
ghost.HintPlayerLocation(ghost._data.players.MinBy(x => x.Value.playerLocation.distance).Key);
if (firstAmbush)
{
@ -68,11 +69,13 @@ internal class GhostPartyDirectorPatches : QSBPatch
__instance._ambushTriggered = true;
__instance._waitingToAmbushInitial = true;
__instance._ambushTriggerTime = Time.time + (__instance._ambushTriggeredThisLoop ? __instance._secondaryAmbushDelay : __instance._initialAmbushDelay);
(__instance._fireplaceGhost.GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.PartyHouse) as QSBPartyHouseAction).LookAtPlayer(0f, TurnSpeed.MEDIUM);
// BUG: breaks on client cuz cast
((QSBPartyHouseAction)__instance._fireplaceGhost.GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.PartyHouse)).LookAtPlayer(0f, TurnSpeed.MEDIUM);
for (int i = 0; i < __instance._ambushGhosts.Length; i++)
{
float delay = (float)i;
(__instance._ambushGhosts[i].GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.PartyHouse) as QSBPartyHouseAction).LookAtPlayer(delay, TurnSpeed.SLOWEST);
// BUG: breaks on client cuz cast
((QSBPartyHouseAction)__instance._ambushGhosts[i].GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.PartyHouse)).LookAtPlayer(delay, TurnSpeed.SLOWEST);
}
}
@ -100,7 +103,8 @@ internal class GhostPartyDirectorPatches : QSBPatch
__instance._ghostsWaitingToAmbush.AddRange(__instance._ambushGhosts);
for (int i = 0; i < __instance._directedGhosts.Length; i++)
{
(__instance._directedGhosts[i].GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.PartyHouse) as QSBPartyHouseAction).ResetAllowChasePlayer();
// BUG: breaks on client cuz cast
((QSBPartyHouseAction)__instance._directedGhosts[i].GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.PartyHouse)).ResetAllowChasePlayer();
}
}

View File

@ -60,7 +60,8 @@ internal class GhostPartyPathDirectorPatches : QSBPatch
var ghostBrain = __instance._dispatchedGhosts[i].GetWorldObject<QSBGhostBrain>();
if (ghostBrain.GetCurrentActionName() == GhostAction.Name.PartyPath)
{
var partyPathAction = ghostBrain.GetCurrentAction() as QSBPartyPathAction;
// BUG: breaks on client cuz cast
var partyPathAction = (QSBPartyPathAction)ghostBrain.GetCurrentAction();
if (partyPathAction.hasReachedEndOfPath)
{
if (!partyPathAction.isMovingToFinalPosition)
@ -101,7 +102,8 @@ internal class GhostPartyPathDirectorPatches : QSBPatch
var num = Random.Range(0, __instance._ghostSpawns.Length);
ghostBrain2.AttachedObject.transform.position = __instance._ghostSpawns[num].spawnTransform.position;
ghostBrain2.AttachedObject.transform.eulerAngles = Vector3.up * __instance._ghostSpawns[num].spawnTransform.eulerAngles.y;
(ghostBrain2.GetCurrentAction() as QSBPartyPathAction).StartFollowPath();
// BUG: breaks on client cuz cast
((QSBPartyPathAction)ghostBrain2.GetCurrentAction()).StartFollowPath();
__instance._ghostSpawns[num].spawnDoor.Open();
__instance._ghostSpawns[num].spawnDoorTimer = Time.timeSinceLevelLoad + 4f;
__instance._waitingGhosts.RemoveAt(0);

View File

@ -200,7 +200,8 @@ internal class GhostZone2DirectorPatches : QSBPatch
DebugLog.DebugWrite($"- fade light down");
QSBGhostZone2Director.ElevatorsStatus[j].elevatorPair.elevator.topLight.FadeTo(0f, 0.2f);
DebugLog.DebugWrite($"- get action");
QSBGhostZone2Director.ElevatorsStatus[j].elevatorAction = __instance._elevators[j].ghost.GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.ElevatorWalk) as QSBElevatorWalkAction;
// BUG: breaks on client cuz cast
QSBGhostZone2Director.ElevatorsStatus[j].elevatorAction = (QSBElevatorWalkAction)__instance._elevators[j].ghost.GetWorldObject<QSBGhostBrain>().GetAction(GhostAction.Name.ElevatorWalk);
DebugLog.DebugWrite($"- CallToUseElevator on action");
QSBGhostZone2Director.ElevatorsStatus[j].elevatorAction.CallToUseElevator();
DebugLog.DebugWrite($"- get ghost controller");

View File

@ -91,7 +91,7 @@ public class QSBGhostEffects : WorldObject<GhostEffects>, IGhostObject
var num2 = _data.isIlluminated ? 8f : 0.8f;
AttachedObject._eyeGlow = Mathf.MoveTowards(AttachedObject._eyeGlow, target, Time.deltaTime * num2);
var closestPlayer = QSBPlayerManager.GetClosestPlayerToWorldPoint(AttachedObject.transform.position, true);
var num3 = (closestPlayer.AssignedSimulationLantern.AttachedObject.GetLanternController().GetLight().GetFlickerScale() - 1f + 0.07f) / 0.14f;
var num3 = (closestPlayer?.AssignedSimulationLantern?.AttachedObject?.GetLanternController()?.GetLight()?.GetFlickerScale() - 1f + 0.07f) / 0.14f ?? 0;
num3 = Mathf.Lerp(0.7f, 1f, num3);
AttachedObject.SetEyeGlow(AttachedObject._eyeGlow * num3);

View File

@ -1,7 +1,6 @@
using HarmonyLib;
using QSB.EchoesOfTheEye.GrappleTotemSync.Messages;
using QSB.EchoesOfTheEye.GrappleTotemSync.WorldObjects;
using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
using QSB.Messaging;
using QSB.Patches;
using QSB.WorldSync;
@ -15,8 +14,8 @@ public class GrappleTotemPatches : QSBPatch
[HarmonyPrefix]
[HarmonyPatch(typeof(LanternZoomPoint), nameof(LanternZoomPoint.OnDetectLight))]
private static bool OnDetectLight(LanternZoomPoint __instance) =>
!QSBWorldSync.AllObjectsReady ||
__instance._lightSensor.GetWorldObject<QSBLightSensor>().LocallyIlluminated;
// only trigger with local player lantern
__instance._lightSensor.IsIlluminatedByLantern(Locator.GetDreamWorldController().GetPlayerLantern().GetLanternController());
[HarmonyPostfix]
[HarmonyPatch(typeof(LanternZoomPoint), nameof(LanternZoomPoint.StartZoomIn))]

View File

@ -12,11 +12,13 @@ internal class LightSensorManager : WorldObjectManager
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
public override bool DlcOnly => true;
public static bool IsPlayerLightSensor(LightSensor lightSensor) => lightSensor.name is "CameraDetector" or "REMOTE_CameraDetector";
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
{
// ignore player light sensors
var list = QSBWorldSync.GetUnityObjects<SingleLightSensor>()
.Where(x => x.name is not ("CameraDetector" or "REMOTE_CameraDetector"))
.Where(x => !IsPlayerLightSensor(x))
.SortDeterministic();
QSBWorldSync.Init<QSBLightSensor, SingleLightSensor>(list);
}

View File

@ -0,0 +1,26 @@
using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
using QSB.Messaging;
using System.Linq;
namespace QSB.EchoesOfTheEye.LightSensorSync.Messages;
/// <summary>
/// always sent by host
/// </summary>
internal class IlluminatedByMessage : QSBWorldObjectMessage<QSBLightSensor, uint[]>
{
public IlluminatedByMessage(uint[] illuminatedBy) : base(illuminatedBy) { }
public override void OnReceiveRemote()
{
foreach (var added in Data.Except(WorldObject._illuminatedBy))
{
WorldObject.SetIlluminated(added, true);
}
foreach (var removed in WorldObject._illuminatedBy.Except(Data))
{
WorldObject.SetIlluminated(removed, false);
}
}
}

View File

@ -0,0 +1,27 @@
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
using QSB.Messaging;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
namespace QSB.EchoesOfTheEye.LightSensorSync.Messages;
internal class IlluminatingLanternsMessage : QSBWorldObjectMessage<QSBLightSensor, int[]>
{
public IlluminatingLanternsMessage(IEnumerable<DreamLanternController> lanterns) :
base(lanterns.Select(x => x.GetWorldObject<QSBDreamLantern>().ObjectId).ToArray()) { }
public override void OnReceiveRemote()
{
if (WorldObject.AttachedObject.enabled)
{
// sensor is enabled, so this will already be synced
return;
}
WorldObject.AttachedObject._illuminatingDreamLanternList.Clear();
WorldObject.AttachedObject._illuminatingDreamLanternList.AddRange(
Data.Select(x => x.GetWorldObject<QSBDreamLantern>().AttachedObject));
}
}

View File

@ -0,0 +1,28 @@
using QSB.Messaging;
using QSB.Player;
using System.Linq;
namespace QSB.EchoesOfTheEye.LightSensorSync.Messages;
/// <summary>
/// always sent by host
/// </summary>
internal class PlayerIlluminatedByMessage : QSBMessage<(uint playerId, uint[] illuminatedBy)>
{
public PlayerIlluminatedByMessage(uint playerId, uint[] illuminatedBy) : base((playerId, illuminatedBy)) { }
public override void OnReceiveRemote()
{
var qsbPlayerLightSensor = QSBPlayerManager.GetPlayer(Data.playerId).QSBPlayerLightSensor;
foreach (var added in Data.illuminatedBy.Except(qsbPlayerLightSensor._illuminatedBy))
{
qsbPlayerLightSensor.SetIlluminated(added, true);
}
foreach (var removed in qsbPlayerLightSensor._illuminatedBy.Except(Data.illuminatedBy))
{
qsbPlayerLightSensor.SetIlluminated(removed, false);
}
}
}

View File

@ -0,0 +1,32 @@
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
namespace QSB.EchoesOfTheEye.LightSensorSync.Messages;
internal class PlayerIlluminatingLanternsMessage : QSBMessage<(uint playerId, int[] lanterns)>
{
public PlayerIlluminatingLanternsMessage(uint playerId, IEnumerable<DreamLanternController> lanterns) :
base((
playerId,
lanterns.Select(x => x.GetWorldObject<QSBDreamLantern>().ObjectId).ToArray()
)) { }
public override void OnReceiveRemote()
{
var lightSensor = (SingleLightSensor)QSBPlayerManager.GetPlayer(Data.playerId).LightSensor;
if (lightSensor.enabled)
{
// sensor is enabled, so this will already be synced
return;
}
lightSensor._illuminatingDreamLanternList.Clear();
lightSensor._illuminatingDreamLanternList.AddRange(
Data.lanterns.Select(x => x.GetWorldObject<QSBDreamLantern>().AttachedObject));
}
}

View File

@ -0,0 +1,13 @@
using QSB.Messaging;
using QSB.Player;
namespace QSB.EchoesOfTheEye.LightSensorSync.Messages;
internal class PlayerSetIlluminatedMessage : QSBMessage<(uint playerId, bool illuminated)>
{
public PlayerSetIlluminatedMessage(uint playerId, bool illuminated) : base((playerId, illuminated)) { }
public override void OnReceiveLocal() => OnReceiveRemote();
public override void OnReceiveRemote() =>
QSBPlayerManager.GetPlayer(Data.playerId).QSBPlayerLightSensor.SetIlluminated(From, Data.illuminated);
}

View File

@ -0,0 +1,11 @@
using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
using QSB.Messaging;
namespace QSB.EchoesOfTheEye.LightSensorSync.Messages;
internal class SetIlluminatedMessage : QSBWorldObjectMessage<QSBLightSensor, bool>
{
public SetIlluminatedMessage(bool illuminated) : base(illuminated) { }
public override void OnReceiveLocal() => OnReceiveRemote();
public override void OnReceiveRemote() => WorldObject.SetIlluminated(From, Data);
}

View File

@ -1,10 +1,11 @@
using HarmonyLib;
using QSB.EchoesOfTheEye.LightSensorSync.Messages;
using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
using QSB.Messaging;
using QSB.Patches;
using QSB.Player;
using QSB.Tools.FlashlightTool;
using QSB.Tools.ProbeTool;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace QSB.EchoesOfTheEye.LightSensorSync.Patches;
@ -15,163 +16,210 @@ internal class LightSensorPatches : QSBPatch
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPrefix]
[HarmonyPatch(nameof(SingleLightSensor.UpdateIllumination))]
private static bool UpdateIllumination(SingleLightSensor __instance)
[HarmonyPatch(nameof(SingleLightSensor.Start))]
private static bool Start(SingleLightSensor __instance)
{
if (!QSBWorldSync.AllObjectsReady)
{
return true;
}
if (!__instance.TryGetWorldObject(out QSBLightSensor qsbLightSensor))
var isPlayerLightSensor = LightSensorManager.IsPlayerLightSensor(__instance);
var qsbPlayerLightSensor = isPlayerLightSensor ? __instance.GetComponent<QSBPlayerLightSensor>() : null;
var qsbLightSensor = isPlayerLightSensor ? null : __instance.GetWorldObject<QSBLightSensor>();
if (__instance._lightDetector != null)
{
__instance._lightSources = new List<ILightSource>();
__instance._lightSourceMask = LightSourceType.VOLUME_ONLY;
if (__instance._detectFlashlight)
{
__instance._lightSourceMask |= LightSourceType.FLASHLIGHT;
}
if (__instance._detectProbe)
{
__instance._lightSourceMask |= LightSourceType.PROBE;
}
if (__instance._detectDreamLanterns)
{
__instance._lightSourceMask |= LightSourceType.DREAM_LANTERN;
}
if (__instance._detectSimpleLanterns)
{
__instance._lightSourceMask |= LightSourceType.SIMPLE_LANTERN;
}
__instance._lightDetector.OnLightVolumeEnter += __instance.OnLightSourceEnter;
__instance._lightDetector.OnLightVolumeExit += __instance.OnLightSourceExit;
}
else
{
Debug.LogError("LightSensor has no LightSourceDetector", __instance);
}
if (__instance._sector != null)
{
__instance.enabled = false;
__instance._lightDetector.GetShape().enabled = false;
if (__instance._startIlluminated)
{
if (isPlayerLightSensor)
{
qsbPlayerLightSensor._locallyIlluminated = true;
new PlayerSetIlluminatedMessage(qsbPlayerLightSensor.PlayerId, true).Send();
}
else
{
qsbLightSensor._locallyIlluminated = true;
qsbLightSensor.OnDetectLocalLight?.Invoke();
qsbLightSensor.SendMessage(new SetIlluminatedMessage(true));
}
}
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(SingleLightSensor.OnSectorOccupantsUpdated))]
private static bool OnSectorOccupantsUpdated(SingleLightSensor __instance)
{
if (!QSBWorldSync.AllObjectsReady)
{
return true;
}
var locallyIlluminated = qsbLightSensor.LocallyIlluminated;
qsbLightSensor.LocallyIlluminated = false;
var isPlayerLightSensor = LightSensorManager.IsPlayerLightSensor(__instance);
var qsbPlayerLightSensor = isPlayerLightSensor ? __instance.GetComponent<QSBPlayerLightSensor>() : null;
var qsbLightSensor = isPlayerLightSensor ? null : __instance.GetWorldObject<QSBLightSensor>();
__instance._illuminated = false;
__instance._illuminatingDreamLanternList?.Clear();
if (__instance._lightSources == null || __instance._lightSources.Count == 0)
var containsAnyOccupants = __instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe);
if (containsAnyOccupants && !__instance.enabled)
{
if (locallyIlluminated)
__instance.enabled = true;
__instance._lightDetector.GetShape().enabled = true;
if (__instance._preserveStateWhileDisabled)
{
__instance._fixedUpdateFrameDelayCount = 10;
}
}
else if (!containsAnyOccupants && __instance.enabled)
{
__instance.enabled = false;
__instance._lightDetector.GetShape().enabled = false;
if (!__instance._preserveStateWhileDisabled)
{
if (isPlayerLightSensor)
{
if (qsbPlayerLightSensor._locallyIlluminated)
{
qsbPlayerLightSensor._locallyIlluminated = false;
new PlayerSetIlluminatedMessage(qsbPlayerLightSensor.PlayerId, false).Send();
}
}
else
{
if (qsbLightSensor._locallyIlluminated)
{
qsbLightSensor._locallyIlluminated = false;
qsbLightSensor.OnDetectLocalDarkness?.Invoke();
qsbLightSensor.SendMessage(new SetIlluminatedMessage(false));
}
}
}
}
return false;
}
var vector = __instance.transform.TransformPoint(__instance._localSensorOffset);
var sensorWorldDir = Vector3.zero;
if (__instance._directionalSensor)
/// <summary>
/// to prevent allocating a new list every frame
/// </summary>
private static readonly List<DreamLanternController> _prevIlluminatingDreamLanternList = new();
[HarmonyPrefix]
[HarmonyPatch(nameof(SingleLightSensor.ManagedFixedUpdate))]
private static bool ManagedFixedUpdate(SingleLightSensor __instance)
{
sensorWorldDir = __instance.transform.TransformDirection(__instance._localDirection).normalized;
if (!QSBWorldSync.AllObjectsReady)
{
return true;
}
foreach (var lightSource in __instance._lightSources)
var isPlayerLightSensor = LightSensorManager.IsPlayerLightSensor(__instance);
var qsbPlayerLightSensor = isPlayerLightSensor ? __instance.GetComponent<QSBPlayerLightSensor>() : null;
var qsbLightSensor = isPlayerLightSensor ? null : __instance.GetWorldObject<QSBLightSensor>();
if (__instance._fixedUpdateFrameDelayCount > 0)
{
if ((__instance._lightSourceMask & lightSource.GetLightSourceType()) == lightSource.GetLightSourceType()
&& lightSource.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance))
{
switch (lightSource.GetLightSourceType())
{
case LightSourceType.UNDEFINED:
{
var owlight = lightSource as OWLight2;
var occludableLight = owlight.GetLight().shadows != LightShadows.None && owlight.GetLight().shadowStrength > 0.5f;
if (owlight.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
&& !__instance.CheckOcclusion(owlight.transform.position, vector, sensorWorldDir, occludableLight))
{
__instance._illuminated = true;
__instance._fixedUpdateFrameDelayCount--;
}
break;
}
case LightSourceType.FLASHLIGHT:
if (__instance._illuminatingDreamLanternList != null)
{
if (lightSource is Flashlight)
{
var position = Locator.GetPlayerCamera().transform.position;
var to = __instance.transform.position - position;
if (Vector3.Angle(Locator.GetPlayerCamera().transform.forward, to) <= __instance._maxSpotHalfAngle
&& !__instance.CheckOcclusion(position, vector, sensorWorldDir))
{
__instance._illuminated = true;
qsbLightSensor.LocallyIlluminated = true;
}
}
else if (lightSource is QSBFlashlight qsbFlashlight)
{
var playerCamera = qsbFlashlight.Player.Camera;
var position = playerCamera.transform.position;
var to = __instance.transform.position - position;
if (Vector3.Angle(playerCamera.transform.forward, to) <= __instance._maxSpotHalfAngle
&& !__instance.CheckOcclusion(position, vector, sensorWorldDir))
{
__instance._illuminated = true;
}
_prevIlluminatingDreamLanternList.Clear();
_prevIlluminatingDreamLanternList.AddRange(__instance._illuminatingDreamLanternList);
}
break;
var illuminated = __instance._illuminated;
__instance.UpdateIllumination();
bool locallyIlluminated;
if (isPlayerLightSensor)
{
locallyIlluminated = qsbPlayerLightSensor._locallyIlluminated;
qsbPlayerLightSensor._locallyIlluminated = __instance._illuminated;
}
case LightSourceType.PROBE:
else
{
if (lightSource is SurveyorProbe probe)
{
if (probe != null
&& probe.IsLaunched()
&& !probe.IsRetrieving()
&& probe.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
&& !__instance.CheckOcclusion(probe.GetLightSourcePosition(), vector, sensorWorldDir))
{
__instance._illuminated = true;
qsbLightSensor.LocallyIlluminated = true;
}
}
else if (lightSource is QSBProbe qsbProbe)
{
if (qsbProbe != null
&& qsbProbe.IsLaunched()
&& !qsbProbe.IsRetrieving()
&& qsbProbe.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
&& !__instance.CheckOcclusion(qsbProbe.GetLightSourcePosition(), vector, sensorWorldDir))
{
__instance._illuminated = true;
}
locallyIlluminated = qsbLightSensor._locallyIlluminated;
qsbLightSensor._locallyIlluminated = __instance._illuminated;
}
break;
}
case LightSourceType.DREAM_LANTERN:
{
var dreamLanternController = lightSource as DreamLanternController;
if (dreamLanternController.IsLit()
&& dreamLanternController.IsFocused(__instance._lanternFocusThreshold)
&& dreamLanternController.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance)
&& !__instance.CheckOcclusion(dreamLanternController.GetLightPosition(), vector, sensorWorldDir))
{
__instance._illuminatingDreamLanternList.Add(dreamLanternController);
__instance._illuminated = true;
var dreamLanternItem = dreamLanternController.GetComponent<DreamLanternItem>();
qsbLightSensor.LocallyIlluminated |= QSBPlayerManager.LocalPlayer.HeldItem?.AttachedObject == dreamLanternItem;
}
__instance._illuminated = illuminated;
break;
}
case LightSourceType.SIMPLE_LANTERN:
foreach (var owlight in lightSource.GetLights())
if (isPlayerLightSensor)
{
var occludableLight = owlight.GetLight().shadows != LightShadows.None && owlight.GetLight().shadowStrength > 0.5f;
var maxDistance = Mathf.Min(__instance._maxSimpleLanternDistance, __instance._maxDistance);
if (owlight.CheckIlluminationAtPoint(vector, __instance._sensorRadius, maxDistance)
&& !__instance.CheckOcclusion(owlight.transform.position, vector, sensorWorldDir, occludableLight))
if (!locallyIlluminated && qsbPlayerLightSensor._locallyIlluminated)
{
__instance._illuminated = true;
var simpleLanternItem = (SimpleLanternItem)lightSource;
qsbLightSensor.LocallyIlluminated |= QSBPlayerManager.LocalPlayer.HeldItem?.AttachedObject == simpleLanternItem;
break;
qsbPlayerLightSensor._locallyIlluminated = true;
new PlayerSetIlluminatedMessage(qsbPlayerLightSensor.PlayerId, true).Send();
}
}
break;
case LightSourceType.VOLUME_ONLY:
__instance._illuminated = true;
break;
}
}
}
if (!locallyIlluminated && qsbLightSensor.LocallyIlluminated)
else if (locallyIlluminated && !qsbPlayerLightSensor._locallyIlluminated)
{
qsbPlayerLightSensor._locallyIlluminated = false;
new PlayerSetIlluminatedMessage(qsbPlayerLightSensor.PlayerId, false).Send();
}
}
else
{
if (!locallyIlluminated && qsbLightSensor._locallyIlluminated)
{
qsbLightSensor._locallyIlluminated = true;
qsbLightSensor.OnDetectLocalLight?.Invoke();
qsbLightSensor.SendMessage(new SetIlluminatedMessage(true));
}
else if (locallyIlluminated && !qsbLightSensor.LocallyIlluminated)
else if (locallyIlluminated && !qsbLightSensor._locallyIlluminated)
{
qsbLightSensor._locallyIlluminated = false;
qsbLightSensor.OnDetectLocalDarkness?.Invoke();
qsbLightSensor.SendMessage(new SetIlluminatedMessage(false));
}
}
if (__instance._illuminatingDreamLanternList != null
&& !__instance._illuminatingDreamLanternList.SequenceEqual(_prevIlluminatingDreamLanternList))
{
if (isPlayerLightSensor)
{
new PlayerIlluminatingLanternsMessage(qsbPlayerLightSensor.PlayerId, __instance._illuminatingDreamLanternList).Send();
}
else
{
qsbLightSensor.SendMessage(new IlluminatingLanternsMessage(__instance._illuminatingDreamLanternList));
}
}
return false;

View File

@ -0,0 +1,75 @@
using QSB.EchoesOfTheEye.LightSensorSync.Messages;
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace QSB.EchoesOfTheEye.LightSensorSync;
/// <summary>
/// stores a bit of extra data needed for player light sensor sync
/// todo you might be able to remove when you simplify light sensor after the fake sector thingy
/// </summary>
[RequireComponent(typeof(SingleLightSensor))]
public class QSBPlayerLightSensor : MonoBehaviour
{
private SingleLightSensor _lightSensor;
[NonSerialized]
public uint PlayerId;
internal bool _locallyIlluminated;
internal readonly List<uint> _illuminatedBy = new();
private void Awake()
{
_lightSensor = GetComponent<SingleLightSensor>();
PlayerId = QSBPlayerManager.PlayerList.First(x => x.LightSensor == _lightSensor).PlayerId;
RequestInitialStatesMessage.SendInitialState += SendInitialState;
QSBPlayerManager.OnRemovePlayer += OnPlayerLeave;
}
private void OnDestroy()
{
RequestInitialStatesMessage.SendInitialState -= SendInitialState;
QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave;
}
private void SendInitialState(uint to)
{
new PlayerIlluminatedByMessage(PlayerId, _illuminatedBy.ToArray()) { To = to }.Send();
if (_lightSensor._illuminatingDreamLanternList != null)
{
new PlayerIlluminatingLanternsMessage(PlayerId, _lightSensor._illuminatingDreamLanternList) { To = to }.Send();
}
}
private void OnPlayerLeave(PlayerInfo player) => SetIlluminated(player.PlayerId, false);
public void SetIlluminated(uint playerId, bool locallyIlluminated)
{
var illuminated = _illuminatedBy.Count > 0;
if (locallyIlluminated)
{
_illuminatedBy.SafeAdd(playerId);
}
else
{
_illuminatedBy.QuickRemove(playerId);
}
if (!illuminated && _illuminatedBy.Count > 0)
{
_lightSensor._illuminated = true;
_lightSensor.OnDetectLight.Invoke();
}
else if (illuminated && _illuminatedBy.Count == 0)
{
_lightSensor._illuminated = false;
_lightSensor.OnDetectDarkness.Invoke();
}
}
}

View File

@ -1,14 +1,60 @@
using QSB.WorldSync;
using Cysharp.Threading.Tasks;
using QSB.EchoesOfTheEye.LightSensorSync.Messages;
using QSB.Messaging;
using QSB.Player;
using QSB.WorldSync;
using System;
using System.Collections.Generic;
using System.Threading;
namespace QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
/// <summary>
/// todo: simplify this after we do fake sectors (we dont need to store a list, we can just do it locally and then sync if it's disabled)
/// </summary>
internal class QSBLightSensor : WorldObject<SingleLightSensor>
{
public bool LocallyIlluminated;
internal bool _locallyIlluminated;
public Action OnDetectLocalLight;
public Action OnDetectLocalDarkness;
public override void SendInitialState(uint to) { }
internal readonly List<uint> _illuminatedBy = new();
public override void SendInitialState(uint to)
{
this.SendMessage(new IlluminatedByMessage(_illuminatedBy.ToArray()) { To = to });
if (AttachedObject._illuminatingDreamLanternList != null)
{
this.SendMessage(new IlluminatingLanternsMessage(AttachedObject._illuminatingDreamLanternList) { To = to });
}
}
public override async UniTask Init(CancellationToken ct) => QSBPlayerManager.OnRemovePlayer += OnPlayerLeave;
public override void OnRemoval() => QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave;
private void OnPlayerLeave(PlayerInfo player) => SetIlluminated(player.PlayerId, false);
public void SetIlluminated(uint playerId, bool locallyIlluminated)
{
var illuminated = _illuminatedBy.Count > 0;
if (locallyIlluminated)
{
_illuminatedBy.SafeAdd(playerId);
}
else
{
_illuminatedBy.QuickRemove(playerId);
}
if (!illuminated && _illuminatedBy.Count > 0)
{
AttachedObject._illuminated = true;
AttachedObject.OnDetectLight.Invoke();
}
else if (illuminated && _illuminatedBy.Count == 0)
{
AttachedObject._illuminated = false;
AttachedObject.OnDetectDarkness.Invoke();
}
}
}

View File

@ -1,4 +1,5 @@
using OWML.Common;
using QSB.EchoesOfTheEye.LightSensorSync;
using QSB.Utility;
using UnityEngine;
@ -73,6 +74,8 @@ public partial class PlayerInfo
}
}
public QSBPlayerLightSensor QSBPlayerLightSensor;
public Vector3 Velocity
{
get

View File

@ -1,4 +1,5 @@
using QSB.Messaging;
using QSB.EchoesOfTheEye.LightSensorSync;
using QSB.Messaging;
using QSB.Player;
using QSB.Player.Messages;
using QSB.SectorSync;
@ -37,6 +38,8 @@ public static class LocalPlayerCreation
player.CameraBody = cameraBody.gameObject;
visibleCameraRoot = cameraBody;
player.QSBPlayerLightSensor = player.LightSensor.gameObject.GetAddComponent<QSBPlayerLightSensor>();
PlayerToolsManager.InitLocal();
// stick

View File

@ -1,4 +1,5 @@
using QSB.Audio;
using QSB.EchoesOfTheEye.LightSensorSync;
using QSB.Player;
using QSB.RoastingSync;
using QSB.Tools;
@ -76,6 +77,8 @@ public static class RemotePlayerCreation
player.CameraBody = REMOTE_PlayerCamera;
visibleCameraRoot = REMOTE_PlayerCamera.transform;
player.QSBPlayerLightSensor = player.LightSensor.gameObject.GetAddComponent<QSBPlayerLightSensor>();
PlayerToolsManager.InitRemote(player);
/*

View File

@ -1,7 +1,6 @@
using QSB.ClientServerStateSync;
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
using QSB.Player;
using QSB.QuantumSync.WorldObjects;
using QSB.ShipSync;
using QSB.ShipSync.TransformSync;
using QSB.ShipSync.WorldObjects;
@ -156,7 +155,7 @@ internal class DebugGUI : MonoBehaviour, IAddComponentOnStart
if (player.IsReady && QSBWorldSync.AllObjectsReady)
{
WriteLine(2, $"Illuminated : {player.LightSensor.IsIlluminated()}");
var singleLightSensor = player.LightSensor as SingleLightSensor;
var singleLightSensor = (SingleLightSensor)player.LightSensor;
foreach (var item in singleLightSensor._lightSources)
{
WriteLine(2, $"- {item.GetLightSourceType()}");
@ -236,7 +235,7 @@ internal class DebugGUI : MonoBehaviour, IAddComponentOnStart
if (QSBWorldSync.AllObjectsReady)
{
var ghost = QSBWorldSync.GetWorldObjects<QSBGhostBrain>().First(x => x.AttachedObject._name == "Yubaba");
var ghost = QSBWorldSync.GetWorldObjects<QSBGhostBrain>().First(x => x.AttachedObject._name == "Kamaji");
WriteLine(4, ghost.AttachedObject._name);
WriteLine(4, $"Action:{ghost.GetCurrentActionName()}");
WriteLine(4, $"Threat Awareness:{ghost.GetThreatAwareness()}");

View File

@ -39,6 +39,10 @@ public class DebugSettings
private bool _drawQuantumVisibilityObjects;
public bool DrawQuantumVisibilityObjects => DebugMode && _drawQuantumVisibilityObjects;
[JsonProperty("drawGhostAI")]
private bool _drawGhostAI;
public bool DrawGhostAI => DebugMode && _drawGhostAI;
[JsonProperty("skipTitleScreen")]
private bool _skipTitleScreen;
public bool SkipTitleScreen => DebugMode && _skipTitleScreen;
@ -46,8 +50,4 @@ public class DebugSettings
[JsonProperty("greySkybox")]
private bool _greySkybox;
public bool GreySkybox => DebugMode && _greySkybox;
[JsonProperty("drawGhostAI")]
private bool _drawGhostAI;
public bool DrawGhostAI => DebugMode && _drawGhostAI;
}

View File

@ -269,26 +269,6 @@ public static class QSBWorldSync
return (TWorldObject)worldObject;
}
public static bool TryGetWorldObject<TWorldObject>(this MonoBehaviour unityObject, out TWorldObject worldObject)
where TWorldObject : IWorldObject
{
if (!unityObject)
{
DebugLog.ToConsole($"Error - Trying to run GetWorldFromUnity with a null unity object! TWorldObject:{typeof(TWorldObject).Name}, TUnityObject:NULL, Stacktrace:\r\n{Environment.StackTrace}", MessageType.Error);
worldObject = default;
return false;
}
if (!UnityObjectsToWorldObjects.TryGetValue(unityObject, out var iWorldObject))
{
worldObject = default;
return false;
}
worldObject = (TWorldObject)iWorldObject;
return true;
}
/// <summary>
/// not deterministic across platforms.
/// iterates thru all objects and throws error if there isn't exactly 1.

View File

@ -23,5 +23,9 @@ public class RequestInitialStatesMessage : QSBMessage
DebugLog.DebugWrite($"sent initial states to {to}");
}
/// <summary>
/// called on the host.
/// use this to send initial states to whoever is asking for it.
/// </summary>
public static event Action<uint> SendInitialState;
}

View File

@ -9,7 +9,7 @@
"drawLines": false,
"drawLabels": false,
"drawQuantumVisibilityObjects": false,
"drawGhostAI": false,
"skipTitleScreen": false,
"greySkybox": false,
"drawGhostAI": false
"greySkybox": false
}