mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-02-11 06:40:39 +00:00
Merge branch 'dev' into model-ship-sync
This commit is contained in:
commit
8c25a699ea
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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))]
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var locallyIlluminated = qsbLightSensor.LocallyIlluminated;
|
||||
qsbLightSensor.LocallyIlluminated = false;
|
||||
|
||||
__instance._illuminated = false;
|
||||
__instance._illuminatingDreamLanternList?.Clear();
|
||||
|
||||
if (__instance._lightSources == null || __instance._lightSources.Count == 0)
|
||||
{
|
||||
if (locallyIlluminated)
|
||||
__instance._lightSources = new List<ILightSource>();
|
||||
__instance._lightSourceMask = LightSourceType.VOLUME_ONLY;
|
||||
if (__instance._detectFlashlight)
|
||||
{
|
||||
qsbLightSensor.OnDetectLocalDarkness?.Invoke();
|
||||
__instance._lightSourceMask |= LightSourceType.FLASHLIGHT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var vector = __instance.transform.TransformPoint(__instance._localSensorOffset);
|
||||
var sensorWorldDir = Vector3.zero;
|
||||
if (__instance._directionalSensor)
|
||||
{
|
||||
sensorWorldDir = __instance.transform.TransformDirection(__instance._localDirection).normalized;
|
||||
}
|
||||
|
||||
foreach (var lightSource in __instance._lightSources)
|
||||
{
|
||||
if ((__instance._lightSourceMask & lightSource.GetLightSourceType()) == lightSource.GetLightSourceType()
|
||||
&& lightSource.CheckIlluminationAtPoint(vector, __instance._sensorRadius, __instance._maxDistance))
|
||||
if (__instance._detectProbe)
|
||||
{
|
||||
switch (lightSource.GetLightSourceType())
|
||||
__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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.FLASHLIGHT:
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.PROBE:
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LightSourceType.SIMPLE_LANTERN:
|
||||
foreach (var owlight in lightSource.GetLights())
|
||||
{
|
||||
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))
|
||||
{
|
||||
__instance._illuminated = true;
|
||||
var simpleLanternItem = (SimpleLanternItem)lightSource;
|
||||
qsbLightSensor.LocallyIlluminated |= QSBPlayerManager.LocalPlayer.HeldItem?.AttachedObject == simpleLanternItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case LightSourceType.VOLUME_ONLY:
|
||||
__instance._illuminated = true;
|
||||
break;
|
||||
qsbPlayerLightSensor._locallyIlluminated = true;
|
||||
new PlayerSetIlluminatedMessage(qsbPlayerLightSensor.PlayerId, true).Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
qsbLightSensor._locallyIlluminated = true;
|
||||
qsbLightSensor.OnDetectLocalLight?.Invoke();
|
||||
qsbLightSensor.SendMessage(new SetIlluminatedMessage(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!locallyIlluminated && qsbLightSensor.LocallyIlluminated)
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(SingleLightSensor.OnSectorOccupantsUpdated))]
|
||||
private static bool OnSectorOccupantsUpdated(SingleLightSensor __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
qsbLightSensor.OnDetectLocalLight?.Invoke();
|
||||
return true;
|
||||
}
|
||||
else if (locallyIlluminated && !qsbLightSensor.LocallyIlluminated)
|
||||
|
||||
var isPlayerLightSensor = LightSensorManager.IsPlayerLightSensor(__instance);
|
||||
var qsbPlayerLightSensor = isPlayerLightSensor ? __instance.GetComponent<QSBPlayerLightSensor>() : null;
|
||||
var qsbLightSensor = isPlayerLightSensor ? null : __instance.GetWorldObject<QSBLightSensor>();
|
||||
|
||||
var containsAnyOccupants = __instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe);
|
||||
if (containsAnyOccupants && !__instance.enabled)
|
||||
{
|
||||
qsbLightSensor.OnDetectLocalDarkness?.Invoke();
|
||||
__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;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var isPlayerLightSensor = LightSensorManager.IsPlayerLightSensor(__instance);
|
||||
var qsbPlayerLightSensor = isPlayerLightSensor ? __instance.GetComponent<QSBPlayerLightSensor>() : null;
|
||||
var qsbLightSensor = isPlayerLightSensor ? null : __instance.GetWorldObject<QSBLightSensor>();
|
||||
|
||||
if (__instance._fixedUpdateFrameDelayCount > 0)
|
||||
{
|
||||
__instance._fixedUpdateFrameDelayCount--;
|
||||
}
|
||||
|
||||
if (__instance._illuminatingDreamLanternList != null)
|
||||
{
|
||||
_prevIlluminatingDreamLanternList.Clear();
|
||||
_prevIlluminatingDreamLanternList.AddRange(__instance._illuminatingDreamLanternList);
|
||||
}
|
||||
|
||||
var illuminated = __instance._illuminated;
|
||||
__instance.UpdateIllumination();
|
||||
bool locallyIlluminated;
|
||||
if (isPlayerLightSensor)
|
||||
{
|
||||
locallyIlluminated = qsbPlayerLightSensor._locallyIlluminated;
|
||||
qsbPlayerLightSensor._locallyIlluminated = __instance._illuminated;
|
||||
}
|
||||
else
|
||||
{
|
||||
locallyIlluminated = qsbLightSensor._locallyIlluminated;
|
||||
qsbLightSensor._locallyIlluminated = __instance._illuminated;
|
||||
}
|
||||
|
||||
__instance._illuminated = illuminated;
|
||||
|
||||
if (isPlayerLightSensor)
|
||||
{
|
||||
if (!locallyIlluminated && qsbPlayerLightSensor._locallyIlluminated)
|
||||
{
|
||||
qsbPlayerLightSensor._locallyIlluminated = true;
|
||||
new PlayerSetIlluminatedMessage(qsbPlayerLightSensor.PlayerId, true).Send();
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
75
QSB/EchoesOfTheEye/LightSensorSync/QSBPlayerLightSensor.cs
Normal file
75
QSB/EchoesOfTheEye/LightSensorSync/QSBPlayerLightSensor.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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()}");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
"drawLines": false,
|
||||
"drawLabels": false,
|
||||
"drawQuantumVisibilityObjects": false,
|
||||
"drawGhostAI": false,
|
||||
"skipTitleScreen": false,
|
||||
"greySkybox": false,
|
||||
"drawGhostAI": false
|
||||
"greySkybox": false
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user