mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-03-12 22:14:37 +00:00
Merge pull request #510 from misternebula/eote-ghost-ai-multiple-players
Ghost AI
This commit is contained in:
commit
1d17a96500
@ -28,7 +28,7 @@ public class QSBAngler : LinkedWorldObject<AnglerfishController, AnglerTransform
|
||||
return;
|
||||
}
|
||||
|
||||
TargetVelocity = TargetTransform.position - _lastTargetPosition;
|
||||
TargetVelocity = (TargetTransform.position - _lastTargetPosition) / Time.fixedDeltaTime;
|
||||
_lastTargetPosition = TargetTransform.position;
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -1,7 +0,0 @@
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern;
|
||||
|
||||
public enum DreamLanternActionType
|
||||
{
|
||||
FOCUS,
|
||||
CONCEAL
|
||||
}
|
15
QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs
Normal file
15
QSB/EchoesOfTheEye/DreamLantern/DreamLanternManager.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern;
|
||||
|
||||
public class DreamLanternManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
public override bool DlcOnly => true;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) =>
|
||||
QSBWorldSync.Init<QSBDreamLantern, DreamLanternController>();
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
|
||||
internal class DreamLanternStateMessage : QSBMessage<(DreamLanternActionType Type, bool BoolValue, float FloatValue)>
|
||||
{
|
||||
public DreamLanternStateMessage(DreamLanternActionType actionType, bool boolValue = false, float floatValue = 0f)
|
||||
: base((actionType, boolValue, floatValue)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var heldItem = QSBPlayerManager.GetPlayer(From).HeldItem;
|
||||
|
||||
if (heldItem is not QSBDreamLanternItem lantern)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Got DreamLanternStateMessage from player {From}, but they are not holding a QSBDreamLanternItem!");
|
||||
return;
|
||||
}
|
||||
|
||||
var controller = lantern.AttachedObject._lanternController;
|
||||
|
||||
switch (Data.Type)
|
||||
{
|
||||
case DreamLanternActionType.CONCEAL:
|
||||
controller.SetConcealed(Data.BoolValue);
|
||||
break;
|
||||
case DreamLanternActionType.FOCUS:
|
||||
controller.SetFocus(Data.FloatValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
|
||||
internal class SetConcealedMessage : QSBWorldObjectMessage<QSBDreamLantern, bool>
|
||||
{
|
||||
public SetConcealedMessage(bool concealed) : base(concealed) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetConcealed(Data));
|
||||
}
|
13
QSB/EchoesOfTheEye/DreamLantern/Messages/SetFocusMessage.cs
Normal file
13
QSB/EchoesOfTheEye/DreamLantern/Messages/SetFocusMessage.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
|
||||
internal class SetFocusMessage : QSBWorldObjectMessage<QSBDreamLantern, float>
|
||||
{
|
||||
public SetFocusMessage(float focus) : base(focus) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetFocus(Data));
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
|
||||
internal class DreamLanternLitMessage : QSBWorldObjectMessage<QSBDreamLanternItem, bool>
|
||||
internal class SetLitMessage : QSBWorldObjectMessage<QSBDreamLantern, bool>
|
||||
{
|
||||
public DreamLanternLitMessage(bool lit) : base(lit) { }
|
||||
public SetLitMessage(bool lit) : base(lit) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetLit(Data));
|
13
QSB/EchoesOfTheEye/DreamLantern/Messages/SetRangeMessage.cs
Normal file
13
QSB/EchoesOfTheEye/DreamLantern/Messages/SetRangeMessage.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
|
||||
internal class SetRangeMessage : QSBWorldObjectMessage<QSBDreamLantern, (float minRange, float maxRange)>
|
||||
{
|
||||
public SetRangeMessage(float minRange, float maxRange) : base((minRange, maxRange)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> QSBPatch.RemoteCall(() => WorldObject.AttachedObject.SetRange(Data.minRange, Data.maxRange));
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.WorldSync;
|
||||
@ -13,73 +13,15 @@ internal class DreamLanternPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DreamLanternItem), nameof(DreamLanternItem.Update))]
|
||||
public static bool UpdateReplacement(DreamLanternItem __instance)
|
||||
{
|
||||
var isHoldingItem = Locator.GetToolModeSwapper().IsInToolMode(ToolMode.Item);
|
||||
|
||||
__instance._wasFocusing = __instance._focusing;
|
||||
__instance._focusing = OWInput.IsPressed(InputLibrary.toolActionPrimary, InputMode.Character) && Time.time > __instance._forceUnfocusTime + 1f && isHoldingItem;
|
||||
|
||||
var concealActionPressed = OWInput.IsPressed(InputLibrary.toolActionSecondary, InputMode.Character) && isHoldingItem;
|
||||
if (concealActionPressed && !__instance._lanternController.IsConcealed())
|
||||
{
|
||||
Locator.GetPlayerAudioController().OnArtifactConceal();
|
||||
__instance._lanternController.SetConcealed(true);
|
||||
new DreamLanternStateMessage(DreamLanternActionType.CONCEAL, true).Send();
|
||||
}
|
||||
else if (!concealActionPressed && __instance._lanternController.IsConcealed())
|
||||
{
|
||||
Locator.GetPlayerAudioController().OnArtifactUnconceal();
|
||||
__instance._lanternController.SetConcealed(false);
|
||||
new DreamLanternStateMessage(DreamLanternActionType.CONCEAL).Send();
|
||||
}
|
||||
|
||||
if (__instance._focusing != __instance._wasFocusing)
|
||||
{
|
||||
if (__instance._focusing)
|
||||
{
|
||||
Locator.GetPlayerAudioController().OnArtifactFocus();
|
||||
}
|
||||
else
|
||||
{
|
||||
Locator.GetPlayerAudioController().OnArtifactUnfocus();
|
||||
}
|
||||
}
|
||||
|
||||
__instance.UpdateFocus();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DreamLanternController), nameof(DreamLanternController.MoveTowardFocus))]
|
||||
public static bool UpdateFocusReplacement(DreamLanternController __instance, float targetFocus, float rate)
|
||||
{
|
||||
var value = Mathf.MoveTowards(__instance._focus, targetFocus, rate * Time.deltaTime);
|
||||
|
||||
if (__instance._focus == value)
|
||||
{
|
||||
__instance.SetFocus(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance.SetFocus(value);
|
||||
new DreamLanternStateMessage(DreamLanternActionType.FOCUS, floatValue: value).Send();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DreamLanternItem), nameof(DreamLanternItem.SetLit))]
|
||||
public static void SetLit(DreamLanternItem __instance, bool lit)
|
||||
[HarmonyPatch(typeof(DreamLanternController), nameof(DreamLanternController.SetLit))]
|
||||
public static void SetLit(DreamLanternController __instance, bool lit)
|
||||
{
|
||||
if (Remote)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (__instance._lanternController.IsLit() == lit)
|
||||
if (__instance._lit == lit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -89,6 +31,73 @@ internal class DreamLanternPatches : QSBPatch
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBDreamLanternItem>().SendMessage(new DreamLanternLitMessage(lit));
|
||||
__instance.GetWorldObject<QSBDreamLantern>().SendMessage(new SetLitMessage(lit));
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DreamLanternController), nameof(DreamLanternController.SetConcealed))]
|
||||
public static void SetConcealed(DreamLanternController __instance, bool concealed)
|
||||
{
|
||||
if (Remote)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (__instance._concealed == concealed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBDreamLantern>().SendMessage(new SetConcealedMessage(concealed));
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DreamLanternController), nameof(DreamLanternController.SetFocus))]
|
||||
public static void SetFocus(DreamLanternController __instance, float focus)
|
||||
{
|
||||
if (Remote)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
focus = Mathf.Clamp01(focus);
|
||||
if (OWMath.ApproxEquals(__instance._focus, focus))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBDreamLantern>().SendMessage(new SetFocusMessage(focus));
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(DreamLanternController), nameof(DreamLanternController.SetRange))]
|
||||
public static void SetRange(DreamLanternController __instance, float minRange, float maxRange)
|
||||
{
|
||||
if (Remote)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (OWMath.ApproxEquals(__instance._minRange, minRange) && OWMath.ApproxEquals(__instance._maxRange, maxRange))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBDreamLantern>().SendMessage(new SetRangeMessage(minRange, maxRange));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
using QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
|
||||
public class QSBDreamLantern : WorldObject<DreamLanternController>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
this.SendMessage(new SetLitMessage(AttachedObject._lit) { To = to });
|
||||
this.SendMessage(new SetConcealedMessage(AttachedObject._concealed) { To = to });
|
||||
this.SendMessage(new SetFocusMessage(AttachedObject._focus) { To = to });
|
||||
this.SendMessage(new SetRangeMessage(AttachedObject._minRange, AttachedObject._maxRange) { To = to });
|
||||
}
|
||||
}
|
79
QSB/EchoesOfTheEye/Ghosts/Actions/QSBChaseAction.cs
Normal file
79
QSB/EchoesOfTheEye/Ghosts/Actions/QSBChaseAction.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
|
||||
internal class QSBChaseAction : QSBGhostAction
|
||||
{
|
||||
private float _lastScreamTime;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.Chase;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_data.threatAwareness < GhostData.ThreatAwareness.IntruderConfirmed || (PlayerData.GetReducedFrights() && !_data.reducedFrights_allowChase))
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
var canSeePlayer = _data.interestedPlayer.sensor.isPlayerVisible
|
||||
|| _data.interestedPlayer.sensor.isPlayerHeldLanternVisible
|
||||
|| _data.interestedPlayer.sensor.inContactWithPlayer;
|
||||
|
||||
if ((_running
|
||||
&& _data.interestedPlayer.timeSincePlayerLocationKnown < 5f)
|
||||
|| (canSeePlayer && _data.interestedPlayer.playerLocation.distance < _data.interestedPlayer.playerMinLanternRange + 0.5f))
|
||||
{
|
||||
return 95f;
|
||||
}
|
||||
|
||||
return -100f;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Chase);
|
||||
if (Time.time > _lastScreamTime + 10f && !PlayerData.GetReducedFrights())
|
||||
{
|
||||
_effects.PlayVoiceAudioNear(global::AudioType.Ghost_Chase, 1f);
|
||||
_lastScreamTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (_data.interestedPlayer.playerLocation.distance > 10f
|
||||
&& !_controller.AttachedObject.GetNodeMap().CheckLocalPointInBounds(_data.interestedPlayer.lastKnownPlayerLocation.localPosition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var worldPos = _controller.AttachedObject.LocalToWorldPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition);
|
||||
_controller.PathfindToLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, MoveType.CHASE);
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, TurnSpeed.FAST);
|
||||
return true;
|
||||
}
|
||||
}
|
91
QSB/EchoesOfTheEye/Ghosts/Actions/QSBElevatorWalkAction.cs
Normal file
91
QSB/EchoesOfTheEye/Ghosts/Actions/QSBElevatorWalkAction.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using GhostEnums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
|
||||
public class QSBElevatorWalkAction : QSBGhostAction
|
||||
{
|
||||
private bool _reachedEndOfPath;
|
||||
|
||||
private bool _calledToElevator;
|
||||
|
||||
private bool _hasUsedElevator;
|
||||
|
||||
private GhostNode _elevatorNode;
|
||||
|
||||
public bool reachedEndOfPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._reachedEndOfPath;
|
||||
}
|
||||
}
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.ElevatorWalk;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (this._calledToElevator && !this._hasUsedElevator && (_data.interestedPlayer == null || !this._data.interestedPlayer.isPlayerLocationKnown))
|
||||
{
|
||||
return 100f;
|
||||
}
|
||||
|
||||
if (this._calledToElevator && !this._hasUsedElevator)
|
||||
{
|
||||
return 70f;
|
||||
}
|
||||
|
||||
return -100f;
|
||||
}
|
||||
|
||||
public void UseElevator()
|
||||
{
|
||||
this._hasUsedElevator = true;
|
||||
}
|
||||
|
||||
public void CallToUseElevator()
|
||||
{
|
||||
this._calledToElevator = true;
|
||||
if (this._controller.AttachedObject.GetNodeMap().GetPathNodes().Length > 1)
|
||||
{
|
||||
this._elevatorNode = this._controller.AttachedObject.GetNodeMap().GetPathNodes()[1];
|
||||
this._controller.PathfindToNode(this._elevatorNode, MoveType.PATROL);
|
||||
return;
|
||||
}
|
||||
Debug.LogError("MissingElevatorNode");
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
this._controller.SetLanternConcealed(true, true);
|
||||
this._controller.FaceVelocity();
|
||||
this._effects.PlayDefaultAnimation();
|
||||
this._effects.SetMovementStyle(GhostEffects.MovementStyle.Normal);
|
||||
if (this._elevatorNode != null)
|
||||
{
|
||||
this._controller.PathfindToNode(this._elevatorNode, MoveType.PATROL);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
this._reachedEndOfPath = true;
|
||||
}
|
||||
}
|
48
QSB/EchoesOfTheEye/Ghosts/Actions/QSBGhostActionStub.cs
Normal file
48
QSB/EchoesOfTheEye/Ghosts/Actions/QSBGhostActionStub.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
|
||||
public class QSBGhostActionStub : QSBGhostAction
|
||||
{
|
||||
public GhostAction.Name Name;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool IsInterruptible()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void FixedUpdate_Action()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
107
QSB/EchoesOfTheEye/Ghosts/Actions/QSBGrabAction.cs
Normal file
107
QSB/EchoesOfTheEye/Ghosts/Actions/QSBGrabAction.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using GhostEnums;
|
||||
using QSB.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
|
||||
internal class QSBGrabAction : QSBGhostAction
|
||||
{
|
||||
private bool _playerIsGrabbed;
|
||||
|
||||
private bool _grabAnimComplete;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.Grab;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (PlayerState.IsAttached() || !_data.interestedPlayer.sensor.inContactWithPlayer)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
return 100f;
|
||||
}
|
||||
|
||||
public override bool IsInterruptible()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Chase);
|
||||
_effects.PlayGrabAnimation();
|
||||
_effects.AttachedObject.OnGrabComplete += OnGrabComplete;
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
_controller.ChangeLanternFocus(0f, 2f);
|
||||
if (_data.previousAction != GhostAction.Name.Chase)
|
||||
{
|
||||
_effects.PlayVoiceAudioNear((_data.interestedPlayer.sensor.isPlayerVisible || PlayerData.GetReducedFrights()) ? AudioType.Ghost_Grab_Shout : AudioType.Ghost_Grab_Scream, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
_effects.PlayDefaultAnimation();
|
||||
_playerIsGrabbed = false;
|
||||
_grabAnimComplete = false;
|
||||
_effects.AttachedObject.OnGrabComplete -= OnGrabComplete;
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (_playerIsGrabbed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_data.interestedPlayer.playerLocation.distanceXZ > 1.7f)
|
||||
{
|
||||
_controller.MoveToLocalPosition(_data.interestedPlayer.playerLocation.localPosition, MoveType.GRAB);
|
||||
}
|
||||
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.playerLocation.localPosition, TurnSpeed.FASTEST);
|
||||
if (_sensors.CanGrabPlayer(_data.interestedPlayer))
|
||||
{
|
||||
DebugLog.DebugWrite($"Grab player {_data.interestedPlayer.player.PlayerId}!");
|
||||
GrabPlayer();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.DebugWrite($"can't grab player {_data.interestedPlayer.player.PlayerId}");
|
||||
}
|
||||
|
||||
return !_grabAnimComplete;
|
||||
}
|
||||
|
||||
private void GrabPlayer()
|
||||
{
|
||||
_playerIsGrabbed = true;
|
||||
_controller.StopMovingInstantly();
|
||||
_controller.StopFacing();
|
||||
_controller.SetLanternConcealed(true, false);
|
||||
_controller.AttachedObject.GetGrabController().GrabPlayer(1f);
|
||||
}
|
||||
|
||||
private void OnGrabComplete()
|
||||
{
|
||||
_grabAnimComplete = true;
|
||||
}
|
||||
|
||||
public bool isPlayerGrabbed()
|
||||
{
|
||||
return _playerIsGrabbed;
|
||||
}
|
||||
}
|
159
QSB/EchoesOfTheEye/Ghosts/Actions/QSBGuardAction.cs
Normal file
159
QSB/EchoesOfTheEye/Ghosts/Actions/QSBGuardAction.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class QSBGuardAction : QSBGhostAction
|
||||
{
|
||||
private GhostNode _targetSearchNode;
|
||||
|
||||
private bool _searchingAtNode;
|
||||
|
||||
private bool _watchingPlayer;
|
||||
|
||||
private float _searchStartTime;
|
||||
|
||||
private float _lastSawPlayer;
|
||||
|
||||
private GhostNode[] _searchNodes;
|
||||
|
||||
private bool _hasReachedAnySearchNode;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.Guard;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (!_controller.AttachedObject.GetNodeMap().HasSearchNodes(_controller.AttachedObject.GetNodeLayer()))
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_data.threatAwareness < GhostData.ThreatAwareness.SomeoneIsInHere)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_data.reduceGuardUtility)
|
||||
{
|
||||
return 60f;
|
||||
}
|
||||
|
||||
return 90f;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
_sensors.AttachedObject.SetContactEdgeCatcherWidth(5f);
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Normal);
|
||||
ContinueSearch();
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
if (_searchingAtNode)
|
||||
{
|
||||
_controller.FaceVelocity();
|
||||
}
|
||||
|
||||
_sensors.AttachedObject.ResetContactEdgeCatcherWidth();
|
||||
_targetSearchNode = null;
|
||||
_searchingAtNode = false;
|
||||
_watchingPlayer = false;
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (_searchingAtNode && Time.time > _searchStartTime + 4f)
|
||||
{
|
||||
_controller.FaceVelocity();
|
||||
_targetSearchNode.searchData.lastSearchTime = Time.time;
|
||||
ContinueSearch();
|
||||
}
|
||||
|
||||
var anyPlayerVisible = _data.players.Values.Any(x => x.sensor.isPlayerVisible);
|
||||
var anyPlayerLanternVisible = _data.players.Values.Any(x => x.sensor.isPlayerHeldLanternVisible);
|
||||
|
||||
var flag = _hasReachedAnySearchNode && (anyPlayerVisible || anyPlayerLanternVisible);
|
||||
if (flag)
|
||||
{
|
||||
_lastSawPlayer = Time.time;
|
||||
}
|
||||
|
||||
if (!_watchingPlayer && flag)
|
||||
{
|
||||
_watchingPlayer = true;
|
||||
_searchingAtNode = false;
|
||||
_controller.StopMoving();
|
||||
_controller.FacePlayer(_data.interestedPlayer.player, TurnSpeed.MEDIUM);
|
||||
}
|
||||
else if (_watchingPlayer && !flag && Time.time - _lastSawPlayer > 1f)
|
||||
{
|
||||
_watchingPlayer = false;
|
||||
ContinueSearch();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
if (_searchNodes != null && _searchNodes.Length == 1)
|
||||
{
|
||||
_controller.FaceLocalPosition(_targetSearchNode.localPosition + (_targetSearchNode.localForward * 10f), TurnSpeed.MEDIUM);
|
||||
}
|
||||
else
|
||||
{
|
||||
_controller.AttachedObject.Spin(TurnSpeed.MEDIUM);
|
||||
}
|
||||
|
||||
_searchingAtNode = true;
|
||||
_hasReachedAnySearchNode = true;
|
||||
_searchStartTime = Time.time;
|
||||
}
|
||||
|
||||
private void ContinueSearch()
|
||||
{
|
||||
_searchingAtNode = false;
|
||||
_targetSearchNode = GetHighestPriorityNodeToSearch();
|
||||
if (_targetSearchNode == null)
|
||||
{
|
||||
Debug.LogError("Failed to find any nodes to search! Did we exhaust our existing options?", _controller.AttachedObject);
|
||||
Debug.Break();
|
||||
}
|
||||
|
||||
_controller.PathfindToNode(_targetSearchNode, MoveType.SEARCH);
|
||||
_controller.FaceVelocity();
|
||||
}
|
||||
|
||||
private GhostNode GetHighestPriorityNodeToSearch()
|
||||
{
|
||||
_searchNodes = _controller.AttachedObject.GetNodeMap().GetSearchNodesOnLayer(_controller.AttachedObject.GetNodeLayer());
|
||||
var num = 0f;
|
||||
var time = Time.time;
|
||||
for (var i = 0; i < _searchNodes.Length; i++)
|
||||
{
|
||||
var num2 = time - _searchNodes[i].searchData.lastSearchTime;
|
||||
num += num2;
|
||||
}
|
||||
|
||||
num /= (float)_searchNodes.Length;
|
||||
GhostNode ghostNode = null;
|
||||
for (var j = 0; j < 5; j++)
|
||||
{
|
||||
ghostNode = _searchNodes[Random.Range(0, _searchNodes.Length)];
|
||||
if (time - ghostNode.searchData.lastSearchTime > num)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ghostNode;
|
||||
}
|
||||
}
|
299
QSB/EchoesOfTheEye/Ghosts/Actions/QSBHuntAction.cs
Normal file
299
QSB/EchoesOfTheEye/Ghosts/Actions/QSBHuntAction.cs
Normal file
@ -0,0 +1,299 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
public class QSBHuntAction : QSBGhostAction
|
||||
{
|
||||
private int _numNodesToSearch;
|
||||
private GhostNodeMap.NodeSearchData[] _nodesToSearch;
|
||||
private int _currentNodeIndex;
|
||||
private GhostNode _closestNode;
|
||||
private bool _startAtClosestNode;
|
||||
private bool _huntStarted;
|
||||
private float _huntStartTime;
|
||||
private bool _huntFailed;
|
||||
private float _huntFailTime;
|
||||
private List<int> _spotlightIndexList = new List<int>(16);
|
||||
private int _spotlightIndex = -1;
|
||||
|
||||
public override void Initialize(QSBGhostBrain brain)
|
||||
{
|
||||
base.Initialize(brain);
|
||||
_numNodesToSearch = 0;
|
||||
_nodesToSearch = new GhostNodeMap.NodeSearchData[_controller.AttachedObject.GetNodeMap().GetNodeCount()];
|
||||
_currentNodeIndex = 0;
|
||||
_huntStarted = false;
|
||||
_huntStartTime = 0f;
|
||||
_huntFailed = false;
|
||||
_huntFailTime = 0f;
|
||||
_controller.AttachedObject.OnNodeMapChanged += new OWEvent.OWCallback(OnNodeMapChanged);
|
||||
}
|
||||
|
||||
private void OnNodeMapChanged()
|
||||
{
|
||||
if (_running)
|
||||
{
|
||||
Debug.LogError("Changing node maps while the Hunt action is running is almost definitely not supported!");
|
||||
_huntFailed = true;
|
||||
}
|
||||
|
||||
_numNodesToSearch = 0;
|
||||
_nodesToSearch = new GhostNodeMap.NodeSearchData[_controller.AttachedObject.GetNodeMap().GetNodeCount()];
|
||||
_currentNodeIndex = 0;
|
||||
}
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.Hunt;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_data.threatAwareness < GhostData.ThreatAwareness.IntruderConfirmed)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_huntFailed && _huntFailTime > _data.interestedPlayer.timeLastSawPlayer)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_running || _data.interestedPlayer.timeSincePlayerLocationKnown < 60f)
|
||||
{
|
||||
return 80f;
|
||||
}
|
||||
|
||||
return -100f;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
_controller.FaceVelocity();
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Normal);
|
||||
if (!_huntStarted || _data.interestedPlayer.timeLastSawPlayer > _huntStartTime)
|
||||
{
|
||||
var knownPlayerVelocity = _data.interestedPlayer.lastKnownSensor.knowsPlayerVelocity ? _data.interestedPlayer.lastKnownPlayerLocation.localVelocity : Vector3.zero;
|
||||
_numNodesToSearch = _controller.AttachedObject.GetNodeMap().FindPossiblePlayerNodes(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, knownPlayerVelocity, 30f, _nodesToSearch, true, null, null, null);
|
||||
_currentNodeIndex = 0;
|
||||
_startAtClosestNode = false;
|
||||
_closestNode = null;
|
||||
_huntStarted = true;
|
||||
_huntStartTime = Time.time;
|
||||
_huntFailed = false;
|
||||
if (_numNodesToSearch == 0)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} : Failed to find nodes to hunt player.", OWML.Common.MessageType.Error);
|
||||
_huntFailed = true;
|
||||
_huntFailTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_huntFailed)
|
||||
{
|
||||
_closestNode = _controller.AttachedObject.GetNodeMap().FindClosestNode(_controller.AttachedObject.GetLocalFeetPosition());
|
||||
for (var i = 0; i < _closestNode.visibleNodes.Count; i++)
|
||||
{
|
||||
for (var j = 0; j < _numNodesToSearch; j++)
|
||||
{
|
||||
if (_closestNode.visibleNodes[i] == _nodesToSearch[j].node.index)
|
||||
{
|
||||
_startAtClosestNode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_startAtClosestNode)
|
||||
{
|
||||
_controller.PathfindToNode(_closestNode, MoveType.SEARCH);
|
||||
}
|
||||
else
|
||||
{
|
||||
_controller.PathfindToNode(_nodesToSearch[_currentNodeIndex].node, MoveType.SEARCH);
|
||||
}
|
||||
|
||||
_effects.PlayVoiceAudioNear(AudioType.Ghost_Hunt, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
if (_huntFailed && !_data.interestedPlayer.isPlayerLocationKnown)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} : Hunt failed. :(");
|
||||
_effects.PlayVoiceAudioNear(AudioType.Ghost_HuntFail, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
return !_huntFailed && !_data.interestedPlayer.isPlayerLocationKnown;
|
||||
}
|
||||
|
||||
public override void FixedUpdate_Action()
|
||||
{
|
||||
if (_huntStarted && !_huntFailed && _spotlightIndexList.Count > 0 && !_controller.AttachedObject.GetDreamLanternController().IsConcealed())
|
||||
{
|
||||
for (var i = 0; i < _spotlightIndexList.Count; i++)
|
||||
{
|
||||
if (!_nodesToSearch[_spotlightIndexList[i]].searched)
|
||||
{
|
||||
var from = _nodesToSearch[_spotlightIndexList[i]].node.localPosition - _controller.AttachedObject.GetLocalFeetPosition();
|
||||
var light = _controller.AttachedObject.GetDreamLanternController().GetLight();
|
||||
var to = _controller.AttachedObject.WorldToLocalDirection(light.transform.forward);
|
||||
if (Vector3.Angle(from, to) < (light.GetLight().spotAngle * 0.5f) - 5f && from.sqrMagnitude < light.range * light.range)
|
||||
{
|
||||
_nodesToSearch[_spotlightIndexList[i]].searched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnTraversePathNode(GhostNode node)
|
||||
{
|
||||
for (var i = 0; i < _numNodesToSearch; i++)
|
||||
{
|
||||
if (node == _nodesToSearch[i].node)
|
||||
{
|
||||
_nodesToSearch[i].searched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
GhostNode node;
|
||||
if (_startAtClosestNode)
|
||||
{
|
||||
_startAtClosestNode = false;
|
||||
node = _closestNode;
|
||||
for (var i = 0; i < _numNodesToSearch; i++)
|
||||
{
|
||||
if (_closestNode == _nodesToSearch[i].node)
|
||||
{
|
||||
_nodesToSearch[i].searched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node = _nodesToSearch[_currentNodeIndex].node;
|
||||
_nodesToSearch[_currentNodeIndex].searched = true;
|
||||
}
|
||||
|
||||
GenerateSpotlightList(node);
|
||||
if (_spotlightIndexList.Count > 0)
|
||||
{
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
SpotlightNextNode();
|
||||
return;
|
||||
}
|
||||
|
||||
TryContinueSearch();
|
||||
}
|
||||
|
||||
public override void OnFaceNode(GhostNode node)
|
||||
{
|
||||
var num = _spotlightIndexList[_spotlightIndex];
|
||||
if (node != _nodesToSearch[num].node)
|
||||
{
|
||||
Debug.LogError("Why are we facing this node??? " + node.name);
|
||||
Debug.Break();
|
||||
return;
|
||||
}
|
||||
|
||||
_nodesToSearch[num].searched = true;
|
||||
for (var i = _spotlightIndexList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_nodesToSearch[_spotlightIndexList[i]].searched)
|
||||
{
|
||||
_spotlightIndexList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (_spotlightIndexList.Count > 0)
|
||||
{
|
||||
SpotlightNextNode();
|
||||
return;
|
||||
}
|
||||
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
_controller.FaceVelocity();
|
||||
TryContinueSearch();
|
||||
}
|
||||
|
||||
private void SpotlightNextNode()
|
||||
{
|
||||
_spotlightIndex = 0;
|
||||
var num = _spotlightIndexList[_spotlightIndex];
|
||||
_controller.FaceNode(_nodesToSearch[num].node, TurnSpeed.MEDIUM, 1f, true);
|
||||
}
|
||||
|
||||
private void TryContinueSearch()
|
||||
{
|
||||
if (Time.time > _enterTime + 60f)
|
||||
{
|
||||
_huntFailed = true;
|
||||
_huntFailTime = Time.time;
|
||||
return;
|
||||
}
|
||||
|
||||
while (_nodesToSearch[_currentNodeIndex].searched && _currentNodeIndex < _numNodesToSearch)
|
||||
{
|
||||
_currentNodeIndex++;
|
||||
}
|
||||
|
||||
if (_currentNodeIndex < _numNodesToSearch)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} : Moving to hunt at new node.");
|
||||
_controller.PathfindToNode(_nodesToSearch[_currentNodeIndex].node, MoveType.SEARCH);
|
||||
return;
|
||||
}
|
||||
|
||||
_huntFailed = true;
|
||||
_huntFailTime = Time.time;
|
||||
}
|
||||
|
||||
private void GenerateSpotlightList(GhostNode node)
|
||||
{
|
||||
_spotlightIndexList.Clear();
|
||||
for (var i = 0; i < node.visibleNodes.Count; i++)
|
||||
{
|
||||
for (var j = 0; j < _numNodesToSearch; j++)
|
||||
{
|
||||
if (!_nodesToSearch[j].searched && node.visibleNodes[i] == _nodesToSearch[j].node.index)
|
||||
{
|
||||
_spotlightIndexList.Add(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawGizmos(bool isGhostSelected)
|
||||
{
|
||||
if (isGhostSelected)
|
||||
{
|
||||
for (var i = 0; i < _numNodesToSearch; i++)
|
||||
{
|
||||
var t = Mathf.Abs(_nodesToSearch[i].score) / 180f;
|
||||
Popcron.Gizmos.Sphere(
|
||||
_controller.AttachedObject.LocalToWorldPosition(_nodesToSearch[i].node.localPosition),
|
||||
(i < _currentNodeIndex) ? 0.5f : 2f,
|
||||
_nodesToSearch[i].searched ? Color.black : Color.HSVToRGB(Mathf.Lerp(0.5f, 0f, t), 1f, 1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
298
QSB/EchoesOfTheEye/Ghosts/Actions/QSBIdentifyIntruderAction.cs
Normal file
298
QSB/EchoesOfTheEye/Ghosts/Actions/QSBIdentifyIntruderAction.cs
Normal file
@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using GhostEnums;
|
||||
using QSB;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
using QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
public class QSBIdentifyIntruderAction : QSBGhostAction
|
||||
{
|
||||
private const int AGGRO_THRESHOLD = 2;
|
||||
|
||||
private static GhostNode[] s_nodesToSpotlight = new GhostNode[8];
|
||||
|
||||
private int _numTimesIlluminatedByPlayer;
|
||||
|
||||
private bool _allowFocusBeam;
|
||||
|
||||
private bool _sawPlayerOccluded;
|
||||
|
||||
private bool _movingToSearchLocation;
|
||||
|
||||
private bool _arrivedAtTargetSearchPosition;
|
||||
|
||||
private bool _searchNodesNearTarget;
|
||||
|
||||
private bool _searchNodesComplete;
|
||||
|
||||
private bool _checkingTargetLocation;
|
||||
|
||||
private Vector3 _searchStartPosition;
|
||||
|
||||
private Vector3 _searchPosition;
|
||||
|
||||
private GhostNode _searchNode;
|
||||
|
||||
private float _checkTimer;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.IdentifyIntruder;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_data.threatAwareness >= GhostData.ThreatAwareness.IntruderConfirmed)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_running || (_data.interestedPlayer.sensor.isPlayerHeldLanternVisible && (_data.threatAwareness > GhostData.ThreatAwareness.EverythingIsNormal || _data.interestedPlayer.playerLocation.distance < 20f)) || _data.interestedPlayer.sensor.isIlluminatedByPlayer)
|
||||
{
|
||||
return 80f;
|
||||
}
|
||||
|
||||
return -100f;
|
||||
}
|
||||
|
||||
public override float GetActionDelay()
|
||||
{
|
||||
if (_data.interestedPlayer.playerLocation.distance < 8f)
|
||||
{
|
||||
return 0.1f;
|
||||
}
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
public override void OnSetAsPending()
|
||||
{
|
||||
if (_data.interestedPlayer.sensor.isIlluminatedByPlayer)
|
||||
{
|
||||
_numTimesIlluminatedByPlayer++;
|
||||
_allowFocusBeam = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
_sawPlayerOccluded = false;
|
||||
_movingToSearchLocation = false;
|
||||
_arrivedAtTargetSearchPosition = false;
|
||||
_searchNodesNearTarget = false;
|
||||
_searchNodesComplete = false;
|
||||
_checkingTargetLocation = false;
|
||||
_checkTimer = 0f;
|
||||
_effects.PlayVoiceAudioNear((_numTimesIlluminatedByPlayer <= 2) ? AudioType.Ghost_Identify_Curious : AudioType.Ghost_Identify_Irritated, 1f);
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
_controller.FaceVelocity();
|
||||
_allowFocusBeam = false;
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (_checkingTargetLocation && !_data.interestedPlayer.isPlayerLocationKnown && _controller.AttachedObject.GetSpeed() < 0.1f)
|
||||
{
|
||||
_checkTimer += Time.deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
_checkTimer = 0f;
|
||||
}
|
||||
|
||||
if ((_searchNodesNearTarget && _checkTimer > 1f) || _checkTimer > 3f)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} : Couldn't identify target. :(");
|
||||
_effects.PlayVoiceAudioNear(AudioType.Ghost_Identify_Fail, 1f);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FixedUpdate_Action()
|
||||
{
|
||||
_checkingTargetLocation = false;
|
||||
if (!_data.interestedPlayer.wasPlayerLocationKnown && _data.interestedPlayer.isPlayerLocationKnown && _data.interestedPlayer.sensor.isIlluminatedByPlayer)
|
||||
{
|
||||
_numTimesIlluminatedByPlayer++;
|
||||
}
|
||||
|
||||
if (!_allowFocusBeam && _data.interestedPlayer.sensor.isIlluminatedByPlayer)
|
||||
{
|
||||
_allowFocusBeam = true;
|
||||
}
|
||||
|
||||
var flag = !_data.interestedPlayer.lastKnownSensor.isPlayerVisible && _data.interestedPlayer.lastKnownSensor.isIlluminatedByPlayer && _numTimesIlluminatedByPlayer > 2;
|
||||
if (_data.interestedPlayer.isPlayerLocationKnown)
|
||||
{
|
||||
_sawPlayerOccluded = false;
|
||||
_movingToSearchLocation = false;
|
||||
_arrivedAtTargetSearchPosition = false;
|
||||
_searchNodesNearTarget = false;
|
||||
_searchNodesComplete = false;
|
||||
}
|
||||
else if (!_movingToSearchLocation && (_data.interestedPlayer.lostPlayerDueToOcclusion || flag))
|
||||
{
|
||||
_movingToSearchLocation = true;
|
||||
_sawPlayerOccluded = _data.interestedPlayer.lostPlayerDueToOcclusion;
|
||||
_controller.ChangeLanternFocus(0f, 2f);
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
if (_allowFocusBeam)
|
||||
{
|
||||
_searchNodesNearTarget = true;
|
||||
_searchStartPosition = _controller.AttachedObject.GetLocalFeetPosition();
|
||||
_searchNode = _controller.AttachedObject.GetNodeMap().FindClosestNode(_data.interestedPlayer.lastKnownPlayerLocation.localPosition);
|
||||
_controller.PathfindToLocalPosition(_searchNode.localPosition, MoveType.INVESTIGATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
_searchNodesNearTarget = false;
|
||||
_controller.PathfindToLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, MoveType.INVESTIGATE);
|
||||
}
|
||||
|
||||
_controller.FaceVelocity();
|
||||
}
|
||||
|
||||
if (_movingToSearchLocation)
|
||||
{
|
||||
if (_arrivedAtTargetSearchPosition)
|
||||
{
|
||||
if (_searchNodesNearTarget)
|
||||
{
|
||||
_checkingTargetLocation = _searchNodesComplete;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_controller.AttachedObject.GetAngleToLocalPosition(_searchPosition) < 5f)
|
||||
{
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
_checkingTargetLocation = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var playerLocationToCheck = _data.interestedPlayer.lastKnownPlayerLocation.localPosition + new Vector3(0f, 1.8f, 0f);
|
||||
var canSeePlayerCheckLocation = _sensors.AttachedObject.CheckPositionOccluded(_controller.AttachedObject.LocalToWorldPosition(playerLocationToCheck));
|
||||
var lanternRange = _allowFocusBeam
|
||||
? (_controller.AttachedObject.GetFocusedLanternRange() - 3f)
|
||||
: (_controller.AttachedObject.GetUnfocusedLanternRange() - 1f);
|
||||
var isLastKnownLocationInRange = _data.interestedPlayer.lastKnownPlayerLocation.distance < _controller.AttachedObject.GetUnfocusedLanternRange();
|
||||
if (_data.interestedPlayer.sensor.isPlayerIlluminatedByUs)
|
||||
{
|
||||
_allowFocusBeam = true;
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, TurnSpeed.MEDIUM);
|
||||
if (isLastKnownLocationInRange == _controller.AttachedObject.IsLanternFocused())
|
||||
{
|
||||
_controller.ChangeLanternFocus(isLastKnownLocationInRange ? 0f : 1f, 2f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (_data.interestedPlayer.lastKnownPlayerLocation.distance < lanternRange && !canSeePlayerCheckLocation)
|
||||
{
|
||||
if (_allowFocusBeam || !_data.interestedPlayer.isPlayerLocationKnown)
|
||||
{
|
||||
_controller.StopMoving();
|
||||
}
|
||||
|
||||
if (_data.interestedPlayer.lastKnownPlayerLocation.degreesToPositionXZ < 5f && (isLastKnownLocationInRange || _controller.AttachedObject.IsLanternFocused()))
|
||||
{
|
||||
_checkingTargetLocation = true;
|
||||
}
|
||||
|
||||
if (isLastKnownLocationInRange)
|
||||
{
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, TurnSpeed.FASTEST);
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
_controller.ChangeLanternFocus(0f, 2f);
|
||||
return;
|
||||
}
|
||||
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, TurnSpeed.MEDIUM);
|
||||
if (_data.interestedPlayer.lastKnownPlayerLocation.degreesToPositionXZ < 5f)
|
||||
{
|
||||
_controller.ChangeLanternFocus(1f, 2f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_controller.ChangeLanternFocus(0f, 2f);
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_controller.PathfindToLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, MoveType.INVESTIGATE);
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, TurnSpeed.MEDIUM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
if (_movingToSearchLocation)
|
||||
{
|
||||
_arrivedAtTargetSearchPosition = true;
|
||||
if (_searchNodesNearTarget)
|
||||
{
|
||||
var num = GenerateSpotlightList(_searchNode, _searchStartPosition - _searchNode.localPosition);
|
||||
if (num > 0)
|
||||
{
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
_controller.ChangeLanternFocus(1f, 2f);
|
||||
_controller.FaceNodeList(IdentifyIntruderAction.s_nodesToSpotlight, num, TurnSpeed.MEDIUM, 1f, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_sawPlayerOccluded)
|
||||
{
|
||||
_searchPosition = _data.interestedPlayer.lastKnownPlayerLocation.localPosition + _data.interestedPlayer.lastKnownPlayerLocation.localVelocity;
|
||||
_controller.FaceLocalPosition(_searchPosition, TurnSpeed.MEDIUM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFinishFaceNodeList()
|
||||
{
|
||||
_searchNodesComplete = true;
|
||||
}
|
||||
|
||||
private int GenerateSpotlightList(GhostNode node, Vector3 ignoreDirection)
|
||||
{
|
||||
var num = 0;
|
||||
var localPosition = node.localPosition;
|
||||
for (var i = 0; i < node.neighbors.Count; i++)
|
||||
{
|
||||
if (Vector3.Angle(node.neighbors[i].localPosition - localPosition, ignoreDirection) >= 45f)
|
||||
{
|
||||
IdentifyIntruderAction.s_nodesToSpotlight[num] = node.neighbors[i];
|
||||
num++;
|
||||
if (num == IdentifyIntruderAction.s_nodesToSpotlight.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
115
QSB/EchoesOfTheEye/Ghosts/Actions/QSBPartyHouseAction.cs
Normal file
115
QSB/EchoesOfTheEye/Ghosts/Actions/QSBPartyHouseAction.cs
Normal file
@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using UnityEngine;
|
||||
|
||||
public class QSBPartyHouseAction : QSBGhostAction
|
||||
{
|
||||
private Vector3 _initialLocalPosition;
|
||||
|
||||
private Vector3 _initialLocalDirection;
|
||||
|
||||
private bool _allowChasePlayer;
|
||||
|
||||
private bool _waitingToLookAtPlayer;
|
||||
|
||||
private bool _lookingAtPlayer;
|
||||
|
||||
private float _lookAtPlayerTime;
|
||||
|
||||
private TurnSpeed _lookSpeed;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.PartyHouse;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (!this._allowChasePlayer)
|
||||
{
|
||||
return 99f;
|
||||
}
|
||||
return 94f;
|
||||
}
|
||||
|
||||
public override bool IsInterruptible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetAllowChasePlayer()
|
||||
{
|
||||
this._allowChasePlayer = false;
|
||||
}
|
||||
|
||||
public void AllowChasePlayer()
|
||||
{
|
||||
this._allowChasePlayer = true;
|
||||
this._controller.SetLanternConcealed(true, true);
|
||||
this._controller.FacePlayer(_data.interestedPlayer.player, TurnSpeed.MEDIUM);
|
||||
this._effects.SetMovementStyle(GhostEffects.MovementStyle.Stalk);
|
||||
}
|
||||
|
||||
public void LookAtPlayer(float delay, TurnSpeed lookSpeed = TurnSpeed.SLOWEST)
|
||||
{
|
||||
this._waitingToLookAtPlayer = true;
|
||||
this._lookAtPlayerTime = Time.time + delay;
|
||||
this._lookSpeed = lookSpeed;
|
||||
}
|
||||
|
||||
public override void Initialize(QSBGhostBrain brain)
|
||||
{
|
||||
base.Initialize(brain);
|
||||
this._initialLocalPosition = this._controller.AttachedObject.GetLocalFeetPosition();
|
||||
this._initialLocalDirection = this._controller.AttachedObject.GetLocalForward();
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
this._controller.MoveToLocalPosition(this._initialLocalPosition, MoveType.PATROL);
|
||||
this._controller.FaceLocalPosition(this._initialLocalPosition + this._initialLocalDirection, TurnSpeed.MEDIUM);
|
||||
this._controller.SetLanternConcealed(true, true);
|
||||
this._effects.SetMovementStyle(GhostEffects.MovementStyle.Normal);
|
||||
this._waitingToLookAtPlayer = false;
|
||||
this._lookingAtPlayer = false;
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
this._allowChasePlayer = true;
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (!this._lookingAtPlayer)
|
||||
{
|
||||
bool isIlluminatedByPlayer = this._data.IsIlluminatedByAnyPlayer;
|
||||
if ((this._waitingToLookAtPlayer && Time.time > this._lookAtPlayerTime) || isIlluminatedByPlayer)
|
||||
{
|
||||
this._controller.FacePlayer(_data.interestedPlayer.player, isIlluminatedByPlayer ? TurnSpeed.SLOW : this._lookSpeed);
|
||||
this._waitingToLookAtPlayer = false;
|
||||
this._lookingAtPlayer = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FixedUpdate_Action()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._allowChasePlayer)
|
||||
{
|
||||
if (this._controller.AttachedObject.GetNodeMap().CheckLocalPointInBounds(this._data.interestedPlayer.playerLocation.localPosition))
|
||||
{
|
||||
this._controller.PathfindToLocalPosition(this._data.interestedPlayer.playerLocation.localPosition, MoveType.SEARCH);
|
||||
}
|
||||
this._controller.FaceLocalPosition(this._data.interestedPlayer.playerLocation.localPosition, TurnSpeed.MEDIUM);
|
||||
}
|
||||
}
|
||||
}
|
136
QSB/EchoesOfTheEye/Ghosts/Actions/QSBPartyPathAction.cs
Normal file
136
QSB/EchoesOfTheEye/Ghosts/Actions/QSBPartyPathAction.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
using UnityEngine;
|
||||
|
||||
public class QSBPartyPathAction : QSBGhostAction
|
||||
{
|
||||
private int _pathIndex;
|
||||
|
||||
private bool _allowFollowPath;
|
||||
|
||||
private bool _reachedEndOfPath;
|
||||
|
||||
private bool _isMovingToFinalPosition;
|
||||
|
||||
private Vector3 _finalPosition;
|
||||
|
||||
public int currentPathIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._pathIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public bool hasReachedEndOfPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._reachedEndOfPath;
|
||||
}
|
||||
}
|
||||
|
||||
public bool isMovingToFinalPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._isMovingToFinalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public bool allowHearGhostCall
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._allowFollowPath && !this._isMovingToFinalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.PartyPath;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (this._controller.AttachedObject.GetNodeMap().GetPathNodes().Length == 0)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
return 10f;
|
||||
}
|
||||
|
||||
public void ResetPath()
|
||||
{
|
||||
this._pathIndex = 0;
|
||||
this._allowFollowPath = false;
|
||||
this._reachedEndOfPath = false;
|
||||
this._isMovingToFinalPosition = false;
|
||||
this._controller.StopMoving();
|
||||
this._controller.SetLanternConcealed(true, false);
|
||||
}
|
||||
|
||||
public void StartFollowPath()
|
||||
{
|
||||
this._allowFollowPath = true;
|
||||
this._controller.PathfindToNode(this._controller.AttachedObject.GetNodeMap().GetPathNodes()[this._pathIndex], MoveType.PATROL);
|
||||
this._controller.SetLanternConcealed(false, false);
|
||||
}
|
||||
|
||||
public void MoveToFinalPosition(Vector3 worldPosition)
|
||||
{
|
||||
this._isMovingToFinalPosition = true;
|
||||
this._finalPosition = this._controller.AttachedObject.WorldToLocalPosition(worldPosition);
|
||||
this._controller.PathfindToLocalPosition(this._finalPosition, MoveType.PATROL);
|
||||
this._controller.SetLanternConcealed(true, true);
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
this._controller.FaceVelocity();
|
||||
this._effects.PlayDefaultAnimation();
|
||||
this._effects.SetMovementStyle(GhostEffects.MovementStyle.Normal);
|
||||
if (this._allowFollowPath)
|
||||
{
|
||||
if (this._isMovingToFinalPosition)
|
||||
{
|
||||
this._controller.PathfindToLocalPosition(this._finalPosition, MoveType.PATROL);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._controller.PathfindToNode(this._controller.AttachedObject.GetNodeMap().GetPathNodes()[this._pathIndex], MoveType.PATROL);
|
||||
}
|
||||
this._controller.SetLanternConcealed(this._isMovingToFinalPosition, true);
|
||||
this._controller.ChangeLanternFocus(0f, 2f);
|
||||
return;
|
||||
}
|
||||
this._controller.SetLanternConcealed(true, false);
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
this._reachedEndOfPath = false;
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
if (this._isMovingToFinalPosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GhostNode[] pathNodes = this._controller.AttachedObject.GetNodeMap().GetPathNodes();
|
||||
if (this._pathIndex + 1 > pathNodes.Length || pathNodes[this._pathIndex].pathData.isEndOfPath)
|
||||
{
|
||||
this._reachedEndOfPath = true;
|
||||
return;
|
||||
}
|
||||
this._pathIndex++;
|
||||
this._controller.PathfindToNode(pathNodes[this._pathIndex], MoveType.PATROL);
|
||||
}
|
||||
}
|
109
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSearchAction.cs
Normal file
109
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSearchAction.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using GhostEnums;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
|
||||
public class QSBSearchAction : QSBGhostAction
|
||||
{
|
||||
private GhostNode _targetSearchNode;
|
||||
|
||||
private bool _searchingAtNode;
|
||||
|
||||
private float _searchStartTime;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.SearchForIntruder;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (!_controller.AttachedObject.GetNodeMap().HasSearchNodes(_controller.AttachedObject.GetNodeLayer()))
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_data.threatAwareness >= GhostData.ThreatAwareness.SomeoneIsInHere)
|
||||
{
|
||||
return 50f;
|
||||
}
|
||||
|
||||
return -100f;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Normal);
|
||||
ContinueSearch();
|
||||
}
|
||||
|
||||
protected override void OnExitAction()
|
||||
{
|
||||
if (_searchingAtNode)
|
||||
{
|
||||
_controller.FaceVelocity();
|
||||
}
|
||||
|
||||
_targetSearchNode = null;
|
||||
_searchingAtNode = false;
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (_searchingAtNode && Time.time > _searchStartTime + 4f)
|
||||
{
|
||||
_controller.FaceVelocity();
|
||||
_targetSearchNode.searchData.lastSearchTime = Time.time;
|
||||
ContinueSearch();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
_controller.AttachedObject.Spin(TurnSpeed.MEDIUM);
|
||||
_searchingAtNode = true;
|
||||
_searchStartTime = Time.time;
|
||||
}
|
||||
|
||||
private void ContinueSearch()
|
||||
{
|
||||
_searchingAtNode = false;
|
||||
_targetSearchNode = GetHighestPriorityNodeToSearch();
|
||||
if (_targetSearchNode == null)
|
||||
{
|
||||
Debug.LogError("Failed to find any nodes to search! Did we exhaust our existing options?", _controller.AttachedObject);
|
||||
Debug.Break();
|
||||
}
|
||||
|
||||
_controller.PathfindToNode(_targetSearchNode, MoveType.SEARCH);
|
||||
_controller.FaceVelocity();
|
||||
}
|
||||
|
||||
private GhostNode GetHighestPriorityNodeToSearch()
|
||||
{
|
||||
var searchNodesOnLayer = _controller.AttachedObject.GetNodeMap().GetSearchNodesOnLayer(_controller.AttachedObject.GetNodeLayer());
|
||||
var num = 0f;
|
||||
var time = Time.time;
|
||||
for (var i = 0; i < searchNodesOnLayer.Length; i++)
|
||||
{
|
||||
var num2 = time - searchNodesOnLayer[i].searchData.lastSearchTime;
|
||||
num += num2;
|
||||
}
|
||||
|
||||
num /= (float)searchNodesOnLayer.Length;
|
||||
GhostNode ghostNode = null;
|
||||
for (var j = 0; j < 5; j++)
|
||||
{
|
||||
ghostNode = searchNodesOnLayer[Random.Range(0, searchNodesOnLayer.Length)];
|
||||
if (time - ghostNode.searchData.lastSearchTime > num)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ghostNode;
|
||||
}
|
||||
}
|
80
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSentryAction.cs
Normal file
80
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSentryAction.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
using QSB.Utility;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class QSBSentryAction : QSBGhostAction
|
||||
{
|
||||
private GhostNode _targetSentryNode;
|
||||
|
||||
private bool _spotlighting;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.Sentry;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (_data.threatAwareness >= GhostData.ThreatAwareness.SomeoneIsInHere)
|
||||
{
|
||||
return 50f;
|
||||
}
|
||||
|
||||
return -100f;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
DebugLog.DebugWrite($"ON ENTER ACTION");
|
||||
_spotlighting = false;
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Stalk);
|
||||
var searchNodesOnLayer = _controller.AttachedObject.GetNodeMap().GetSearchNodesOnLayer(_controller.AttachedObject.GetNodeLayer());
|
||||
_targetSentryNode = searchNodesOnLayer[0];
|
||||
_controller.PathfindToNode(_targetSentryNode, MoveType.PATROL);
|
||||
_controller.FaceVelocity();
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FixedUpdate_Action()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_data.interestedPlayer.isPlayerLocationKnown && !_spotlighting)
|
||||
{
|
||||
DebugLog.DebugWrite($"Spotlighting player...");
|
||||
_spotlighting = true;
|
||||
_controller.ChangeLanternFocus(1f, 2f);
|
||||
}
|
||||
|
||||
if (_spotlighting)
|
||||
{
|
||||
if (_data.interestedPlayer.timeSincePlayerLocationKnown > 3f)
|
||||
{
|
||||
DebugLog.DebugWrite($"Give up on spotlighting player");
|
||||
_spotlighting = false;
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
_controller.FaceLocalPosition(_targetSentryNode.localPosition + _targetSentryNode.localForward * 10f, TurnSpeed.MEDIUM);
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"Facing last known position...");
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, TurnSpeed.FAST);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
{
|
||||
_controller.FaceLocalPosition(_targetSentryNode.localPosition + _targetSentryNode.localForward * 10f, TurnSpeed.MEDIUM);
|
||||
}
|
||||
}
|
56
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSleepAction.cs
Normal file
56
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSleepAction.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
|
||||
public class QSBSleepAction : QSBGhostAction
|
||||
{
|
||||
private SleepAction.WakeState _state;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
=> GhostAction.Name.Sleep;
|
||||
|
||||
public override float CalculateUtility()
|
||||
=> !_data.hasWokenUp
|
||||
? 100f
|
||||
: -100f;
|
||||
|
||||
public override bool IsInterruptible()
|
||||
=> false;
|
||||
|
||||
protected override void OnEnterAction()
|
||||
=> EnterSleepState();
|
||||
|
||||
protected override void OnExitAction() { }
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (_state == SleepAction.WakeState.Sleeping)
|
||||
{
|
||||
if (_data.hasWokenUp || _data.IsIlluminatedByAnyPlayer)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} : Who dares awaken me?");
|
||||
_state = SleepAction.WakeState.Awake;
|
||||
_effects.PlayDefaultAnimation();
|
||||
}
|
||||
}
|
||||
else if (_state is not SleepAction.WakeState.WakingUp and SleepAction.WakeState.Awake)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void EnterSleepState()
|
||||
{
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
_effects.PlaySleepAnimation();
|
||||
_state = SleepAction.WakeState.Sleeping;
|
||||
}
|
||||
|
||||
private enum WakeState
|
||||
{
|
||||
Sleeping,
|
||||
Awake
|
||||
}
|
||||
}
|
31
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSleepwalkAction.cs
Normal file
31
QSB/EchoesOfTheEye/Ghosts/Actions/QSBSleepwalkAction.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
public class QSBSleepwalkAction : QSBGhostAction
|
||||
{
|
||||
public override GhostAction.Name GetName() => GhostAction.Name.Sleepwalk;
|
||||
|
||||
public override float CalculateUtility()
|
||||
=> !_data.hasWokenUp
|
||||
? 100f
|
||||
: -100f;
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
MoveToRandomPatrolNode();
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Normal);
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
=> true;
|
||||
|
||||
public override void OnArriveAtPosition()
|
||||
=> MoveToRandomPatrolNode();
|
||||
|
||||
private void MoveToRandomPatrolNode()
|
||||
{
|
||||
_controller.PathfindToNode(_controller.AttachedObject.GetNodeMap().GetRandomPatrolNode(), MoveType.PATROL);
|
||||
_controller.FaceVelocity();
|
||||
}
|
||||
}
|
111
QSB/EchoesOfTheEye/Ghosts/Actions/QSBStalkAction.cs
Normal file
111
QSB/EchoesOfTheEye/Ghosts/Actions/QSBStalkAction.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
public class QSBStalkAction : QSBGhostAction
|
||||
{
|
||||
private bool _wasPlayerLanternConcealed;
|
||||
private bool _isFocusingLight;
|
||||
private bool _shouldFocusLightOnPlayer;
|
||||
private float _changeFocusTime;
|
||||
|
||||
public override GhostAction.Name GetName()
|
||||
=> GhostAction.Name.Stalk;
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if (_data.threatAwareness < GhostData.ThreatAwareness.IntruderConfirmed)
|
||||
{
|
||||
return -100f;
|
||||
}
|
||||
|
||||
if ((_running && _data.interestedPlayer.timeSincePlayerLocationKnown < 4f) || _data.interestedPlayer.isPlayerLocationKnown)
|
||||
{
|
||||
return 85f;
|
||||
}
|
||||
|
||||
return -100f;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
var flag = Locator.GetDreamWorldController().GetPlayerLantern().GetLanternController().IsConcealed();
|
||||
_wasPlayerLanternConcealed = flag;
|
||||
_isFocusingLight = flag;
|
||||
_shouldFocusLightOnPlayer = flag;
|
||||
_changeFocusTime = 0f;
|
||||
_controller.ChangeLanternFocus(_isFocusingLight ? 1f : 0f, 2f);
|
||||
_controller.SetLanternConcealed(!_isFocusingLight, true);
|
||||
_controller.FaceVelocity();
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Stalk);
|
||||
_effects.PlayVoiceAudioNear(_data.fastStalkUnlocked ? AudioType.Ghost_Stalk_Fast : AudioType.Ghost_Stalk, 1f);
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
if (!_data.fastStalkUnlocked && _data.illuminatedByPlayerMeter > 4f)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} Fast stalk unlocked.");
|
||||
_data.fastStalkUnlocked = true;
|
||||
_effects.PlayVoiceAudioNear(AudioType.Ghost_Stalk_Fast, 1f);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FixedUpdate_Action()
|
||||
{
|
||||
var stalkSpeed = GhostConstants.GetMoveSpeed(MoveType.SEARCH);
|
||||
if (_data.fastStalkUnlocked)
|
||||
{
|
||||
stalkSpeed += 1.5f;
|
||||
}
|
||||
|
||||
if (_controller.AttachedObject.GetNodeMap().CheckLocalPointInBounds(_data.interestedPlayer.lastKnownPlayerLocation.localPosition))
|
||||
{
|
||||
_controller.PathfindToLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, stalkSpeed, GhostConstants.GetMoveAcceleration(MoveType.SEARCH));
|
||||
}
|
||||
|
||||
_controller.FaceLocalPosition(_data.interestedPlayer.lastKnownPlayerLocation.localPosition, TurnSpeed.MEDIUM);
|
||||
|
||||
var isPlayerLanternConcealed = Locator.GetDreamWorldController().GetPlayerLantern().GetLanternController().IsConcealed();
|
||||
var sawPlayerLanternConceal = !_wasPlayerLanternConcealed
|
||||
&& isPlayerLanternConcealed
|
||||
&& _data.interestedPlayer.wasPlayerLocationKnown;
|
||||
|
||||
_wasPlayerLanternConcealed = isPlayerLanternConcealed;
|
||||
if (sawPlayerLanternConceal && !_shouldFocusLightOnPlayer)
|
||||
{
|
||||
_shouldFocusLightOnPlayer = true;
|
||||
_changeFocusTime = Time.time + 1f;
|
||||
}
|
||||
else if (_data.interestedPlayer.sensor.isPlayerHeldLanternVisible && _shouldFocusLightOnPlayer)
|
||||
{
|
||||
_shouldFocusLightOnPlayer = false;
|
||||
_changeFocusTime = Time.time + 1f;
|
||||
}
|
||||
|
||||
if (_isFocusingLight != _shouldFocusLightOnPlayer && Time.time > _changeFocusTime)
|
||||
{
|
||||
if (_shouldFocusLightOnPlayer)
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} : Un-concealing lantern and focusing on player.");
|
||||
_controller.SetLanternConcealed(false, true);
|
||||
_controller.ChangeLanternFocus(1f, 2f);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.DebugWrite($"{_brain.AttachedObject._name} : Concealing lantern.");
|
||||
_controller.SetLanternConcealed(true, true);
|
||||
}
|
||||
|
||||
_isFocusingLight = _shouldFocusLightOnPlayer;
|
||||
}
|
||||
}
|
||||
}
|
58
QSB/EchoesOfTheEye/Ghosts/Actions/QSBWaitAction.cs
Normal file
58
QSB/EchoesOfTheEye/Ghosts/Actions/QSBWaitAction.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using GhostEnums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
|
||||
public class QSBWaitAction : QSBGhostAction
|
||||
{
|
||||
public override GhostAction.Name GetName()
|
||||
{
|
||||
return GhostAction.Name.Wait;
|
||||
}
|
||||
|
||||
public override float CalculateUtility()
|
||||
{
|
||||
if (_data.interestedPlayer == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
if (PlayerState.IsGrabbedByGhost() && (_running || _data.interestedPlayer.timeSincePlayerLocationKnown < 4f))
|
||||
{
|
||||
return 666f;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
protected override void OnEnterAction()
|
||||
{
|
||||
if (!PlayerState.IsGrabbedByGhost())
|
||||
{
|
||||
_controller.StopMoving();
|
||||
_controller.StopFacing();
|
||||
return;
|
||||
}
|
||||
|
||||
_effects.SetMovementStyle(GhostEffects.MovementStyle.Stalk);
|
||||
_controller.FacePlayer(_data.interestedPlayer.player, TurnSpeed.MEDIUM);
|
||||
if (_data.interestedPlayer.playerLocation.distanceXZ < 3f)
|
||||
{
|
||||
Vector3 toPositionXZ = _data.interestedPlayer.playerLocation.toPositionXZ;
|
||||
_controller.MoveToLocalPosition(_controller.AttachedObject.GetLocalFeetPosition() - toPositionXZ * 3f, MoveType.SEARCH);
|
||||
return;
|
||||
}
|
||||
|
||||
_controller.StopMoving();
|
||||
}
|
||||
|
||||
public override bool Update_Action()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
11
QSB/EchoesOfTheEye/Ghosts/GhostAnimationType.cs
Normal file
11
QSB/EchoesOfTheEye/Ghosts/GhostAnimationType.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
public enum GhostAnimationType
|
||||
{
|
||||
Sleep,
|
||||
Default,
|
||||
Grab,
|
||||
BlowOutLanternNormal,
|
||||
BlowOutLanternFast,
|
||||
SnapNeck
|
||||
}
|
106
QSB/EchoesOfTheEye/Ghosts/GhostManager.cs
Normal file
106
QSB/EchoesOfTheEye/Ghosts/GhostManager.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
internal class GhostManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
public override bool DlcOnly => true;
|
||||
|
||||
private static GhostHotelDirector _hotelDirector;
|
||||
private static GhostPartyPathDirector _partyPathDirector;
|
||||
private static GhostZone2Director _zone2Director;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
QSBWorldSync.Init<QSBGhostController, GhostController>();
|
||||
QSBWorldSync.Init<QSBGhostEffects, GhostEffects>();
|
||||
QSBWorldSync.Init<QSBGhostSensors, GhostSensors>();
|
||||
QSBWorldSync.Init<QSBGhostNodeMap, GhostNodeMap>();
|
||||
QSBWorldSync.Init<QSBGhostBrain, GhostBrain>(QSBWorldSync.GetUnityObjects<GhostBrain>().Where(x => x.gameObject.activeSelf).SortDeterministic());
|
||||
|
||||
_hotelDirector = QSBWorldSync.GetUnityObjects<GhostHotelDirector>().First();
|
||||
_partyPathDirector = QSBWorldSync.GetUnityObjects<GhostPartyPathDirector>().First();
|
||||
_zone2Director = QSBWorldSync.GetUnityObjects<GhostZone2Director>().First();
|
||||
|
||||
for (int i = 0; i < _hotelDirector._hotelDepthsGhosts.Length; i++)
|
||||
{
|
||||
_hotelDirector._hotelDepthsGhosts[i].OnIdentifyIntruder -= _hotelDirector.OnHotelDepthsGhostsIdentifiedIntruder;
|
||||
_hotelDirector._hotelDepthsGhosts[i].GetWorldObject<QSBGhostBrain>().OnIdentifyIntruder += CustomOnHotelDepthsGhostsIdentifiedIntruder;
|
||||
}
|
||||
|
||||
for (var j = 0; j < _partyPathDirector._directedGhosts.Length; j++)
|
||||
{
|
||||
_partyPathDirector._directedGhosts[j].OnIdentifyIntruder -= _partyPathDirector.OnGhostIdentifyIntruder;
|
||||
_partyPathDirector._directedGhosts[j].GetWorldObject<QSBGhostBrain>().OnIdentifyIntruder += CustomOnGhostIdentifyIntruder;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _zone2Director._cityGhosts.Length; i++)
|
||||
{
|
||||
_zone2Director._cityGhosts[i].OnIdentifyIntruder -= _zone2Director.OnCityGhostsIdentifiedIntruder;
|
||||
_zone2Director._cityGhosts[i].GetWorldObject<QSBGhostBrain>().OnIdentifyIntruder += CustomOnCityGhostsIdentifiedIntruder;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CustomOnHotelDepthsGhostsIdentifiedIntruder(GhostBrain ghostBrain, QSBGhostData ghostData)
|
||||
{
|
||||
if (_hotelDirector._playerIdentifiedInDepths)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var num = Random.Range(2f, 3f);
|
||||
for (var i = 0; i < _hotelDirector._hotelDepthsGhosts.Length; i++)
|
||||
{
|
||||
if (!(_hotelDirector._hotelDepthsGhosts[i] == ghostBrain) && _hotelDirector._hotelDepthsGhosts[i].HearGhostCall(ghostData.interestedPlayer.playerLocation.localPosition, num, false))
|
||||
{
|
||||
num += Random.Range(2f, 3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CustomOnGhostIdentifyIntruder(GhostBrain ghostBrain, QSBGhostData ghostData)
|
||||
{
|
||||
float num = Random.Range(2f, 3f);
|
||||
for (int i = 0; i < _partyPathDirector._directedGhosts.Length; i++)
|
||||
{
|
||||
if (!(_partyPathDirector._directedGhosts[i] == ghostBrain))
|
||||
{
|
||||
bool flag = _partyPathDirector._directedGhosts[i].GetCurrentActionName() != GhostAction.Name.PartyPath || ((QSBPartyPathAction)_partyPathDirector._directedGhosts[i].GetWorldObject<QSBGhostBrain>().GetCurrentAction()).allowHearGhostCall;
|
||||
float num2 = Vector3.Distance(ghostBrain.transform.position, _partyPathDirector._directedGhosts[i].transform.position);
|
||||
if (flag && num2 < 50f && _partyPathDirector._directedGhosts[i].HearGhostCall(ghostData.interestedPlayer.playerLocation.localPosition, num, true))
|
||||
{
|
||||
_partyPathDirector._directedGhosts[i].GetWorldObject<QSBGhostBrain>().HintPlayerLocation(ghostData.interestedPlayer.player);
|
||||
num += Random.Range(2f, 3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CustomOnCityGhostsIdentifiedIntruder(GhostBrain ghostBrain, QSBGhostData ghostData)
|
||||
{
|
||||
if (_zone2Director._playerIdentifiedInCity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_zone2Director._playerIdentifiedInCity = true;
|
||||
float num = Random.Range(2f, 3f);
|
||||
for (int i = 0; i < _zone2Director._cityGhosts.Length; i++)
|
||||
{
|
||||
if (!(_zone2Director._cityGhosts[i] == ghostBrain) && _zone2Director._cityGhosts[i].HearGhostCall(ghostData.interestedPlayer.playerLocation.localPosition, num, false))
|
||||
{
|
||||
num += Random.Range(2f, 3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
QSB/EchoesOfTheEye/Ghosts/GhostPlayer.cs
Normal file
27
QSB/EchoesOfTheEye/Ghosts/GhostPlayer.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using QSB.Player;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
public class GhostPlayer
|
||||
{
|
||||
public PlayerInfo player;
|
||||
public QSBGhostSensorData sensor = new();
|
||||
public GhostLocationData playerLocation = new();
|
||||
public GhostLocationData lastKnownPlayerLocation = new();
|
||||
public QSBGhostSensorData lastKnownSensor = new();
|
||||
public QSBGhostSensorData firstUnknownSensor = new();
|
||||
public bool isPlayerLocationKnown;
|
||||
public bool wasPlayerLocationKnown;
|
||||
public float timeLastSawPlayer;
|
||||
public float timeSincePlayerLocationKnown = float.PositiveInfinity;
|
||||
public float playerMinLanternRange;
|
||||
public bool lostPlayerDueToOcclusion
|
||||
=> !isPlayerLocationKnown
|
||||
&& !lastKnownSensor.isPlayerOccluded
|
||||
&& firstUnknownSensor.isPlayerOccluded;
|
||||
}
|
22
QSB/EchoesOfTheEye/Ghosts/Messages/ChangeActionMessage.cs
Normal file
22
QSB/EchoesOfTheEye/Ghosts/Messages/ChangeActionMessage.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class ChangeActionMessage : QSBWorldObjectMessage<QSBGhostBrain, GhostAction.Name>
|
||||
{
|
||||
public ChangeActionMessage(GhostAction.Name name) : base(name) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received ChangeActionMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"{WorldObject.AttachedObject._name} Change action to {Data}");
|
||||
WorldObject.ChangeAction(WorldObject.GetAction(Data), true);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class ChangeInterestedPlayerMessage : QSBWorldObjectMessage<QSBGhostSensors, uint>
|
||||
{
|
||||
public ChangeInterestedPlayerMessage(uint playerId) : base(playerId) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
DebugLog.DebugWrite($"{WorldObject.AttachedObject.name} Set interested player {Data}");
|
||||
WorldObject._data.interestedPlayer = WorldObject._data.players[QSBPlayerManager.GetPlayer(Data)];
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class FaceLocalDirectionMessage : QSBWorldObjectMessage<QSBGhostController, (Vector3 localDirection, float degreesPerSecond, float turnAcceleration)>
|
||||
{
|
||||
public FaceLocalDirectionMessage(Vector3 localDirection, float degreesPerSecond, float turnAcceleration) : base((localDirection, degreesPerSecond, turnAcceleration)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received FaceLocalDirectionMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.FaceLocalDirection(Data.localDirection, Data.degreesPerSecond, Data.turnAcceleration, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class FaceLocalPositionMessage : QSBWorldObjectMessage<QSBGhostController, (Vector3 localPosition, float degreesPerSecond, float turnAcceleration)>
|
||||
{
|
||||
public FaceLocalPositionMessage(Vector3 localPos, float degPerSecond, float turnAccel) : base((localPos, degPerSecond, turnAccel)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received FaceLocalPositionMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.FaceLocalPosition(Data.localPosition, Data.degreesPerSecond, Data.turnAcceleration, true);
|
||||
}
|
||||
}
|
72
QSB/EchoesOfTheEye/Ghosts/Messages/FaceNodeListMessage.cs
Normal file
72
QSB/EchoesOfTheEye/Ghosts/Messages/FaceNodeListMessage.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class FaceNodeListMessage : QSBWorldObjectMessage<QSBGhostController, (int mapId, int[] nodeIndexes, int numNodes, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern)>
|
||||
{
|
||||
public FaceNodeListMessage(GhostNode[] nodeList, int numNodes, TurnSpeed turnSpeed, float nodeDelay, bool autoFocus) : base(Process(nodeList, numNodes, turnSpeed, nodeDelay, autoFocus)) { }
|
||||
|
||||
private static (int mapId, int[] nodeIndexes, int numNodes, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern) Process(GhostNode[] nodeList, int numNodes, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern)
|
||||
{
|
||||
(int mapId, int[] nodeIndexes, int numNodes, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern) ret = new();
|
||||
|
||||
ret.numNodes = numNodes;
|
||||
ret.turnSpeed = turnSpeed;
|
||||
ret.nodeDelay = nodeDelay;
|
||||
ret.autoFocusLantern = autoFocusLantern;
|
||||
|
||||
if (numNodes == 0)
|
||||
{
|
||||
ret.mapId = -1;
|
||||
ret.nodeIndexes = new int[numNodes];
|
||||
return ret;
|
||||
}
|
||||
|
||||
var nodeMaps = QSBWorldSync.GetWorldObjects<QSBGhostNodeMap>();
|
||||
var owner = nodeMaps.First(x => x.AttachedObject._nodes.Contains(nodeList[0]));
|
||||
|
||||
var hasAll = nodeList.All(owner.AttachedObject._nodes.Contains);
|
||||
|
||||
if (!hasAll)
|
||||
{
|
||||
DebugLog.ToConsole($"Warning - owner.nodes does not contain all of nodelist! Trying to find a correct owner...", OWML.Common.MessageType.Warning);
|
||||
|
||||
owner = nodeMaps.FirstOrDefault(x => nodeList.All(x.AttachedObject._nodes.Contains));
|
||||
|
||||
if (owner == default)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Failed to find correct owner for nodelist.", OWML.Common.MessageType.Error);
|
||||
ret.mapId = -1;
|
||||
ret.nodeIndexes = new int[numNodes];
|
||||
}
|
||||
}
|
||||
|
||||
ret.mapId = owner.ObjectId;
|
||||
ret.nodeIndexes = nodeList.Select(x => Array.IndexOf(owner.AttachedObject._nodes, x)).ToArray();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received FaceNodeListMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var map = Data.mapId.GetWorldObject<QSBGhostNodeMap>();
|
||||
var nodeList = Data.nodeIndexes.Select(x => map.AttachedObject._nodes[x]).ToArray();
|
||||
|
||||
WorldObject.FaceNodeList(nodeList, Data.numNodes, Data.turnSpeed, Data.nodeDelay, Data.autoFocusLantern, true);
|
||||
}
|
||||
}
|
45
QSB/EchoesOfTheEye/Ghosts/Messages/FaceNodeMessage.cs
Normal file
45
QSB/EchoesOfTheEye/Ghosts/Messages/FaceNodeMessage.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class FaceNodeMessage : QSBWorldObjectMessage<QSBGhostController, (int mapId, int nodeIndex, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern)>
|
||||
{
|
||||
public FaceNodeMessage(GhostNode node, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern) : base(Process(node, turnSpeed, nodeDelay, autoFocusLantern)) { }
|
||||
|
||||
private static (int mapId, int nodeIndex, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern) Process(GhostNode node, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern)
|
||||
{
|
||||
(int mapId, int nodeIndex, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern) ret = new();
|
||||
|
||||
ret.turnSpeed = turnSpeed;
|
||||
ret.nodeDelay = nodeDelay;
|
||||
ret.autoFocusLantern = autoFocusLantern;
|
||||
|
||||
var nodeMaps = QSBWorldSync.GetWorldObjects<QSBGhostNodeMap>();
|
||||
var owner = nodeMaps.First(x => x.AttachedObject._nodes.Contains(node));
|
||||
|
||||
ret.mapId = owner.ObjectId;
|
||||
ret.nodeIndex = Array.IndexOf(owner.AttachedObject._nodes, node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received FaceNodeMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var map = Data.mapId.GetWorldObject<QSBGhostNodeMap>();
|
||||
var node = map.AttachedObject._nodes[Data.nodeIndex];
|
||||
|
||||
WorldObject.FaceNode(node, Data.turnSpeed, Data.nodeIndex, Data.autoFocusLantern, true);
|
||||
}
|
||||
}
|
28
QSB/EchoesOfTheEye/Ghosts/Messages/FacePlayerMessage.cs
Normal file
28
QSB/EchoesOfTheEye/Ghosts/Messages/FacePlayerMessage.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class FacePlayerMessage : QSBWorldObjectMessage<QSBGhostController, (uint playerId, TurnSpeed turnSpeed)>
|
||||
{
|
||||
public FacePlayerMessage(uint playerId, TurnSpeed turnSpeed) : base((playerId, turnSpeed)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received FacePlayerMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.FacePlayer(QSBPlayerManager.GetPlayer(Data.playerId), Data.turnSpeed, true);
|
||||
}
|
||||
}
|
19
QSB/EchoesOfTheEye/Ghosts/Messages/FaceVelocityMessage.cs
Normal file
19
QSB/EchoesOfTheEye/Ghosts/Messages/FaceVelocityMessage.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class FaceVelocityMessage : QSBWorldObjectMessage<QSBGhostController>
|
||||
{
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received FaceVelocityMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.FaceVelocity(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class GhostAnimationTriggerMessage : QSBWorldObjectMessage<QSBGhostEffects, GhostAnimationType>
|
||||
{
|
||||
public GhostAnimationTriggerMessage(GhostAnimationType type) : base(type) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
switch (Data)
|
||||
{
|
||||
case GhostAnimationType.Sleep:
|
||||
WorldObject.PlaySleepAnimation(true);
|
||||
break;
|
||||
case GhostAnimationType.Default:
|
||||
WorldObject.PlayDefaultAnimation(true);
|
||||
break;
|
||||
case GhostAnimationType.Grab:
|
||||
WorldObject.PlayGrabAnimation(true);
|
||||
break;
|
||||
case GhostAnimationType.BlowOutLanternNormal:
|
||||
WorldObject.PlayBlowOutLanternAnimation(false, true);
|
||||
break;
|
||||
case GhostAnimationType.BlowOutLanternFast:
|
||||
WorldObject.PlayBlowOutLanternAnimation(true, true);
|
||||
break;
|
||||
case GhostAnimationType.SnapNeck:
|
||||
WorldObject.PlaySnapNeckAnimation(true);
|
||||
break;
|
||||
default:
|
||||
DebugLog.ToConsole($"Warning - Received unknown animation type of {Data} for QSBGhostEffects {WorldObject.ObjectId}", OWML.Common.MessageType.Warning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class MoveToLocalPositionMessage : QSBWorldObjectMessage<QSBGhostController, (Vector3 localPosition, float speed, float acceleration)>
|
||||
{
|
||||
public MoveToLocalPositionMessage(Vector3 localPos, float speed, float accel) : base((localPos, speed, accel)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received MoveToLocalPosition on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.MoveToLocalPosition(Data.localPosition, Data.speed, Data.acceleration, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class PathfindLocalPositionMessage : QSBWorldObjectMessage<QSBGhostController,
|
||||
(Vector3 localPosition, float speed, float acceleration)>
|
||||
{
|
||||
public PathfindLocalPositionMessage(Vector3 localPosition, float speed, float acceleration) :
|
||||
base((localPosition, speed, acceleration)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received PathfindLocalPositionMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.PathfindToLocalPosition(Data.localPosition, Data.speed, Data.acceleration, true);
|
||||
}
|
||||
}
|
43
QSB/EchoesOfTheEye/Ghosts/Messages/PathfindNodeMessage.cs
Normal file
43
QSB/EchoesOfTheEye/Ghosts/Messages/PathfindNodeMessage.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class PathfindNodeMessage : QSBWorldObjectMessage<QSBGhostController, (int mapId, int nodeIndex, float speed, float acceleration)>
|
||||
{
|
||||
public PathfindNodeMessage(GhostNode node, float speed, float acceleration) : base(Process(node, speed, acceleration)) { }
|
||||
|
||||
private static (int mapId, int nodeIndex, float speed, float acceleration) Process(GhostNode node, float speed, float acceleration)
|
||||
{
|
||||
(int mapId, int nodeId, float speed, float acceleration) ret = new();
|
||||
|
||||
ret.speed = speed;
|
||||
ret.acceleration = acceleration;
|
||||
|
||||
var nodeMaps = QSBWorldSync.GetWorldObjects<QSBGhostNodeMap>();
|
||||
var owner = nodeMaps.First(x => x.AttachedObject._nodes.Contains(node));
|
||||
|
||||
ret.mapId = owner.ObjectId;
|
||||
ret.nodeId = Array.IndexOf(owner.AttachedObject._nodes, node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received PathfindNodeMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var map = Data.mapId.GetWorldObject<QSBGhostNodeMap>();
|
||||
var node = map.AttachedObject._nodes[Data.nodeIndex];
|
||||
|
||||
WorldObject.PathfindToNode(node, Data.speed, Data.acceleration, true);
|
||||
}
|
||||
}
|
20
QSB/EchoesOfTheEye/Ghosts/Messages/PlayVoiceAudioMessage.cs
Normal file
20
QSB/EchoesOfTheEye/Ghosts/Messages/PlayVoiceAudioMessage.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class PlayVoiceAudioMessage : QSBWorldObjectMessage<QSBGhostEffects, (AudioType audioType, float volumeScale, bool near)>
|
||||
{
|
||||
public PlayVoiceAudioMessage(AudioType audioType, float volumeScale, bool near) : base((audioType, volumeScale, near)) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (Data.near)
|
||||
{
|
||||
WorldObject.PlayVoiceAudioNear(Data.audioType, Data.volumeScale, true);
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.PlayVoiceAudioFar(Data.audioType, Data.volumeScale, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class SetMovementStyleMessage : QSBWorldObjectMessage<QSBGhostEffects, GhostEffects.MovementStyle>
|
||||
{
|
||||
public SetMovementStyleMessage(GhostEffects.MovementStyle style) : base(style) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> WorldObject.SetMovementStyle(Data, true);
|
||||
}
|
17
QSB/EchoesOfTheEye/Ghosts/Messages/StopFacingMessage.cs
Normal file
17
QSB/EchoesOfTheEye/Ghosts/Messages/StopFacingMessage.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class StopFacingMessage : QSBWorldObjectMessage<QSBGhostController>
|
||||
{
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
WorldObject.StopFacing(true);
|
||||
}
|
||||
}
|
36
QSB/EchoesOfTheEye/Ghosts/Messages/StopMovingMessage.cs
Normal file
36
QSB/EchoesOfTheEye/Ghosts/Messages/StopMovingMessage.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
|
||||
internal class StopMovingMessage : QSBWorldObjectMessage<QSBGhostController, bool>
|
||||
{
|
||||
public StopMovingMessage(bool instant) : base(instant) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
DebugLog.ToConsole("Error - Received StopMovingMessage on host. Something has gone horribly wrong!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Data)
|
||||
{
|
||||
WorldObject.AttachedObject._velocity = Vector3.zero;
|
||||
// rest of code gets handled by the second StopMovingMessage
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.AttachedObject._moveToTargetPosition = false;
|
||||
WorldObject.AttachedObject._followNodePath = false;
|
||||
WorldObject.AttachedObject._hasFinalPathPosition = false;
|
||||
}
|
||||
}
|
436
QSB/EchoesOfTheEye/Ghosts/Patches/GhostBrainPatches.cs
Normal file
436
QSB/EchoesOfTheEye/Ghosts/Patches/GhostBrainPatches.cs
Normal file
@ -0,0 +1,436 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(GhostBrain))]
|
||||
internal class GhostBrainPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.GetCurrentActionName))]
|
||||
public static bool GetCurrentActionName(GhostBrain __instance, ref GhostAction.Name __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__result = __instance.GetWorldObject<QSBGhostBrain>().GetCurrentActionName();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.GetCurrentAction))]
|
||||
public static bool GetCurrentAction(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.GetAction))]
|
||||
public static bool GetAction(GhostBrain __instance, GhostAction.Name actionName)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.GetThreatAwareness))]
|
||||
public static bool GetThreatAwareness(GhostBrain __instance, ref GhostData.ThreatAwareness __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__result = __instance.GetWorldObject<QSBGhostBrain>().GetThreatAwareness();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.GetEffects))]
|
||||
public static bool GetEffects(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.CheckDreadAudioConditions))]
|
||||
public static bool CheckDreadAudioConditions(GhostBrain __instance, ref bool __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__result = __instance.GetWorldObject<QSBGhostBrain>().CheckDreadAudioConditions();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.CheckFearAudioConditions))]
|
||||
public static bool CheckFearAudioConditions(GhostBrain __instance, bool fearAudioAlreadyPlaying, ref bool __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__result = __instance.GetWorldObject<QSBGhostBrain>().CheckFearAudioConditions(fearAudioAlreadyPlaying);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.Awake))]
|
||||
public static bool Awake(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().Awake();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.Start))]
|
||||
public static bool Start(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().Start();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnDestroy))]
|
||||
public static bool OnDestroy(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnDestroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.TabulaRasa))]
|
||||
public static bool TabulaRasa(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().TabulaRasa();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.Die))]
|
||||
public static bool Die(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().Die();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.EscalateThreatAwareness))]
|
||||
public static bool EscalateThreatAwareness(GhostBrain __instance, GhostData.ThreatAwareness newThreatAwareness)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().EscalateThreatAwareness(newThreatAwareness);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.WakeUp))]
|
||||
public static bool WakeUp(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().WakeUp();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.HearGhostCall))]
|
||||
public static bool HearGhostCall(GhostBrain __instance, Vector3 playerLocalPosition, float reactDelay, bool playResponseAudio, ref bool __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__result = __instance.GetWorldObject<QSBGhostBrain>().HearGhostCall(playerLocalPosition, reactDelay, playResponseAudio);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.HearCallForHelp))]
|
||||
public static bool HearCallForHelp(GhostBrain __instance, Vector3 playerLocalPosition, float reactDelay, ref bool __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.HintPlayerLocation), new Type[] { })]
|
||||
public static bool HintPlayerLocation(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.HintPlayerLocation), typeof(Vector3), typeof(float))]
|
||||
public static bool HintPlayerLocation(GhostBrain __instance, Vector3 localPosition, float informationTime)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.FixedUpdate))]
|
||||
public static bool FixedUpdate(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().FixedUpdate();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.Update))]
|
||||
public static bool Update(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().Update();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.FixedUpdate_ThreatAwareness))]
|
||||
public static bool FixedUpdate_ThreatAwareness(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().FixedUpdate_ThreatAwareness();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.EvaluateActions))]
|
||||
public static bool EvaluateActions(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().EvaluateActions();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.ChangeAction), typeof(GhostAction.Name))]
|
||||
public static bool ChangeAction(GhostBrain __instance, GhostAction.Name actionName)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.ChangeAction), typeof(GhostAction))]
|
||||
public static bool ChangeAction(GhostBrain __instance, GhostAction action)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.ClearPendingAction))]
|
||||
public static bool ClearPendingAction(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().ClearPendingAction();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnArriveAtPosition))]
|
||||
public static bool OnArriveAtPosition(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnArriveAtPosition();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnTraversePathNode))]
|
||||
public static bool OnTraversePathNode(GhostBrain __instance, GhostNode node)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnTraversePathNode(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnFaceNode))]
|
||||
public static bool OnFaceNode(GhostBrain __instance, GhostNode node)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnFaceNode(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnFinishFaceNodeList))]
|
||||
public static bool OnFinishFaceNodeList(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnFinishFaceNodeList();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnCallForHelp))]
|
||||
public static bool OnCallForHelp(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnCallForHelp();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnEnterDreamWorld))]
|
||||
public static bool OnEnterDreamWorld(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnEnterDreamWorld();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostBrain.OnExitDreamWorld))]
|
||||
public static bool OnExitDreamWorld(GhostBrain __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostBrain>().OnExitDreamWorld();
|
||||
return false;
|
||||
}
|
||||
}
|
72
QSB/EchoesOfTheEye/Ghosts/Patches/GhostControllerPatches.cs
Normal file
72
QSB/EchoesOfTheEye/Ghosts/Patches/GhostControllerPatches.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using GhostEnums;
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(GhostController))]
|
||||
internal class GhostControllerPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostController.Initialize))]
|
||||
public static bool Initialize(GhostController __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostController.SetLanternConcealed))]
|
||||
public static bool SetLanternConcealed(GhostController __instance, bool concealed, bool playAudio)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
|
||||
}
|
||||
__instance.GetWorldObject<QSBGhostController>().SetLanternConcealed(concealed, playAudio);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostController.ChangeLanternFocus))]
|
||||
public static bool ChangeLanternFocus(GhostController __instance, float focus, float focusRate)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostController>().ChangeLanternFocus(focus, focusRate);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostController.FacePlayer))]
|
||||
public static bool FacePlayer(GhostController __instance, TurnSpeed turnSpeed)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
72
QSB/EchoesOfTheEye/Ghosts/Patches/GhostEffectsPatches.cs
Normal file
72
QSB/EchoesOfTheEye/Ghosts/Patches/GhostEffectsPatches.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(GhostEffects))]
|
||||
internal class GhostEffectsPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostEffects.Initialize))]
|
||||
public static bool Initialize(GhostEffects __instance, Transform nodeRoot, GhostController controller, GhostData data)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostEffects.AllowFootstepAudio))]
|
||||
public static bool AllowFootstepAudio(GhostEffects __instance, bool usingTimer, ref bool __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__result = __instance.GetWorldObject<QSBGhostEffects>().AllowFootstepAudio(usingTimer);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostEffects.PlayLanternAudio))]
|
||||
public static bool PlayLanternAudio(GhostEffects __instance, AudioType audioType)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostEffects>().PlayLanternAudio(audioType);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostEffects.Update_Effects))]
|
||||
public static bool Update_Effects(GhostEffects __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostEffects>().Update_Effects();
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(GhostHotelDirector))]
|
||||
internal class GhostHotelDirectorPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyReversePatch]
|
||||
[HarmonyPatch(typeof(GhostDirector), nameof(GhostDirector.OnDestroy))]
|
||||
public static void GhostDirector_OnDestroy_Stub(object instance) { }
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostHotelDirector.OnDestroy))]
|
||||
public static bool OnDestroy(GhostHotelDirector __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GhostDirector_OnDestroy_Stub(__instance);
|
||||
|
||||
__instance._hotelProjector.OnProjectorExtinguished -= __instance.OnHotelProjectorExtinguished;
|
||||
__instance._bridgeProjector.OnProjectorLit -= __instance.OnBridgeProjectorLit;
|
||||
__instance._depthsVolume.OnEntry -= __instance.OnEnterDepths;
|
||||
__instance._depthsVolume.OnExit -= __instance.OnExitDepths;
|
||||
for (var i = 0; i < __instance._hotelDepthsGhosts.Length; i++)
|
||||
{
|
||||
__instance._hotelDepthsGhosts[i].GetWorldObject<QSBGhostBrain>().OnIdentifyIntruder -= GhostManager.CustomOnHotelDepthsGhostsIdentifiedIntruder;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* I have no idea why, but for some reason unknown to the damned souls that walk this mortal plane,
|
||||
* this method only runs when this patch is here. What the absolute fuck.
|
||||
*/
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GhostDirector), nameof(GhostDirector.WakeGhosts))]
|
||||
public static bool WakeGhosts()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
109
QSB/EchoesOfTheEye/Ghosts/Patches/GhostPartyDirectorPatches.cs
Normal file
109
QSB/EchoesOfTheEye/Ghosts/Patches/GhostPartyDirectorPatches.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using GhostEnums;
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(GhostPartyDirector))]
|
||||
internal class GhostPartyDirectorPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostPartyDirector.UnlockGhostForAmbush))]
|
||||
public static bool UnlockGhostForAmbush(GhostPartyDirector __instance, bool firstAmbush)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._ghostsWaitingToAmbush.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
ghost.HintPlayerLocation(ghost._data.players.MinBy(x => x.Value.playerLocation.distance).Key);
|
||||
if (firstAmbush)
|
||||
{
|
||||
ghost.GetEffects().GetWorldObject<QSBGhostEffects>().PlayVoiceAudioNear(global::AudioType.Ghost_Stalk, 1f);
|
||||
}
|
||||
|
||||
__instance._ghostsWaitingToAmbush.QuickRemoveAt(index);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostPartyDirector.OnEnterAmbushTrigger))]
|
||||
public static bool OnEnterAmbushTrigger(GhostPartyDirector __instance, GameObject hitObj)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._ambushTriggered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"OnEnterAmbushTrigger");
|
||||
|
||||
if (hitObj.CompareTag("PlayerDetector"))
|
||||
{
|
||||
__instance._ambushTriggeredThisLoop = true;
|
||||
__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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostPartyDirector.OnEnterSector))]
|
||||
public static bool OnEnterSector(GhostPartyDirector __instance, SectorDetector detector)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._connectedDreamCampfire != null && __instance._connectedDreamCampfire.GetState() == Campfire.State.UNLIT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (detector.GetOccupantType() == DynamicOccupant.Player)
|
||||
{
|
||||
__instance._partyMusicController.FadeIn(3f);
|
||||
__instance._ghostsWaitingToAmbush.Clear();
|
||||
__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();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(GhostPartyPathDirector))]
|
||||
internal class GhostPartyPathDirectorPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyReversePatch]
|
||||
[HarmonyPatch(typeof(GhostDirector), nameof(GhostDirector.OnDestroy))]
|
||||
public static void GhostDirector_OnDestroy_Stub(object instance) { }
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostPartyPathDirector.OnDestroy))]
|
||||
public static bool OnDestroy(GhostPartyPathDirector __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GhostDirector_OnDestroy_Stub(__instance);
|
||||
|
||||
for (var i = 0; i < __instance._directedGhosts.Length; i++)
|
||||
{
|
||||
__instance._directedGhosts[i].GetWorldObject<QSBGhostBrain>().OnIdentifyIntruder -= GhostManager.CustomOnGhostIdentifyIntruder;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostPartyPathDirector.Update))]
|
||||
public static bool Update(GhostPartyPathDirector __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance._connectedCampfireExtinguished)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = __instance._dispatchedGhosts.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var ghostBrain = __instance._dispatchedGhosts[i].GetWorldObject<QSBGhostBrain>();
|
||||
if (ghostBrain.GetCurrentActionName() == GhostAction.Name.PartyPath)
|
||||
{
|
||||
var partyPathAction = ghostBrain.GetCurrentAction() as QSBPartyPathAction;
|
||||
if (partyPathAction.hasReachedEndOfPath)
|
||||
{
|
||||
if (!partyPathAction.isMovingToFinalPosition)
|
||||
{
|
||||
var transform = __instance._numArrivedGhosts < __instance._ghostFinalDestinations.Length
|
||||
? __instance._ghostFinalDestinations[__instance._numArrivedGhosts].destinationTransform
|
||||
: __instance._ghostOverflowFinalDestinations[__instance._numArrivedGhosts % __instance._ghostOverflowFinalDestinations.Length].transform;
|
||||
partyPathAction.MoveToFinalPosition(transform.position);
|
||||
__instance._numArrivedGhosts++;
|
||||
}
|
||||
|
||||
if (!__instance._respawnBlockTrigger.IsTrackingObject(Locator.GetPlayerDetector()))
|
||||
{
|
||||
__instance._dispatchedGhosts.QuickRemoveAt(i);
|
||||
ghostBrain.AttachedObject.transform.position = __instance._ghostSpawns[Random.Range(0, __instance._ghostSpawns.Length)].spawnTransform.position;
|
||||
ghostBrain.AttachedObject.transform.eulerAngles = Vector3.up * __instance._ghostSpawns[Random.Range(0, __instance._ghostSpawns.Length)].spawnTransform.eulerAngles.y;
|
||||
ghostBrain.TabulaRasa();
|
||||
partyPathAction.ResetPath();
|
||||
if (__instance._numEnabledGhostProxies < __instance._ghostFinalDestinations.Length && __instance._ghostFinalDestinations[__instance._numEnabledGhostProxies].proxyGhost != null)
|
||||
{
|
||||
__instance._ghostFinalDestinations[__instance._numEnabledGhostProxies].proxyGhost.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
__instance._numEnabledGhostProxies++;
|
||||
__instance._waitingGhosts.Add(ghostBrain.AttachedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (__instance._waitingGhosts.Count > 0
|
||||
&& __instance._waitingGhosts[0].GetCurrentActionName() == GhostAction.Name.PartyPath
|
||||
&& (__instance._dispatchedGhosts.Count == 0 || Time.timeSinceLevelLoad > __instance._nextGhostDispatchTime))
|
||||
{
|
||||
DebugLog.DebugWrite($"Dispatch new ghost!");
|
||||
var ghostBrain2 = __instance._waitingGhosts[0].GetWorldObject<QSBGhostBrain>();
|
||||
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();
|
||||
__instance._ghostSpawns[num].spawnDoor.Open();
|
||||
__instance._ghostSpawns[num].spawnDoorTimer = Time.timeSinceLevelLoad + 4f;
|
||||
__instance._waitingGhosts.RemoveAt(0);
|
||||
__instance._lastDispatchedGhost = ghostBrain2.AttachedObject;
|
||||
__instance._dispatchedGhosts.Add(ghostBrain2.AttachedObject);
|
||||
__instance._nextGhostDispatchTime = Time.timeSinceLevelLoad + Random.Range(__instance._minGhostDispatchDelay, __instance._maxGhostDispatchDelay);
|
||||
}
|
||||
|
||||
for (var j = 0; j < __instance._ghostSpawns.Length; j++)
|
||||
{
|
||||
if (__instance._ghostSpawns[j].spawnDoor.IsOpen() && Time.timeSinceLevelLoad > __instance._ghostSpawns[j].spawnDoorTimer)
|
||||
{
|
||||
__instance._ghostSpawns[j].spawnDoor.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostPartyPathDirector.OnGhostIdentifyIntruder))]
|
||||
public static bool OnGhostIdentifyIntruder(GhostPartyPathDirector __instance, GhostBrain ghostBrain, GhostData ghostData)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
85
QSB/EchoesOfTheEye/Ghosts/Patches/GhostSensorsPatches.cs
Normal file
85
QSB/EchoesOfTheEye/Ghosts/Patches/GhostSensorsPatches.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(GhostSensors))]
|
||||
internal class GhostSensorsPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostSensors.Initialize))]
|
||||
public static bool Initialize(GhostSensors __instance, GhostData data, OWTriggerVolume guardVolume)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostSensors.CanGrabPlayer))]
|
||||
public static bool CanGrabPlayer(GhostSensors __instance, ref bool __result)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.ToConsole($"Error - {MethodBase.GetCurrentMethod().Name} not supported!", OWML.Common.MessageType.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostSensors.FixedUpdate_Sensors))]
|
||||
public static bool FixedUpdate_Sensors(GhostSensors __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostSensors>().FixedUpdate_Sensors();
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostSensors.OnEnterContactTrigger))]
|
||||
public static bool OnEnterContactTrigger(GhostSensors __instance, GameObject hitObj)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostSensors>().OnEnterContactTrigger(hitObj);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GhostSensors.OnExitContactTrigger))]
|
||||
public static bool OnExitContactTrigger(GhostSensors __instance, GameObject hitObj)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.GetWorldObject<QSBGhostSensors>().OnExitContactTrigger(hitObj);
|
||||
return false;
|
||||
}
|
||||
}
|
181
QSB/EchoesOfTheEye/Ghosts/Patches/GhostZone2DirectorPatches.cs
Normal file
181
QSB/EchoesOfTheEye/Ghosts/Patches/GhostZone2DirectorPatches.cs
Normal file
@ -0,0 +1,181 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.Patches;
|
||||
|
||||
internal class GhostZone2DirectorPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyReversePatch]
|
||||
[HarmonyPatch(typeof(GhostDirector), nameof(GhostDirector.Awake))]
|
||||
public static void GhostDirector_Awake_Stub(object instance) { }
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GhostZone2Director), nameof(GhostZone2Director.Awake))]
|
||||
public static bool Awake(GhostZone2Director __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GhostDirector_Awake_Stub(__instance);
|
||||
|
||||
QSBGhostZone2Director.ElevatorsStatus = new QSBGhostZone2Director.ElevatorStatus[__instance._elevators.Length];
|
||||
for (var j = 0; j < __instance._elevators.Length; j++)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].elevatorPair = __instance._elevators[j];
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].activated = false;
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].deactivated = false;
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].lightsDeactivated = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyReversePatch]
|
||||
[HarmonyPatch(typeof(GhostDirector), nameof(GhostDirector.OnDestroy))]
|
||||
public static void GhostDirector_OnDestroy_Stub(object instance) { }
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GhostZone2Director), nameof(GhostZone2Director.OnDestroy))]
|
||||
public static bool OnDestroy(GhostZone2Director __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GhostDirector_OnDestroy_Stub(__instance);
|
||||
|
||||
__instance._lightsProjector.OnProjectorExtinguished -= __instance.OnLightsExtinguished;
|
||||
__instance._undergroundVolume.OnEntry -= __instance.OnEnterUnderground;
|
||||
__instance._undergroundVolume.OnExit -= __instance.OnExitUnderground;
|
||||
__instance._finalTotem.OnRinging -= __instance.OnAlarmRinging;
|
||||
for (int i = 0; i < __instance._cityGhosts.Length; i++)
|
||||
{
|
||||
__instance._cityGhosts[i].GetWorldObject<QSBGhostBrain>().OnIdentifyIntruder -= GhostManager.CustomOnCityGhostsIdentifiedIntruder;
|
||||
}
|
||||
|
||||
__instance._ghostTutorialArrival.OnEntry -= __instance.OnStartGhostTutorial;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GhostZone2Director), nameof(GhostZone2Director.Update))]
|
||||
public static bool Update(GhostZone2Director __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._lightsProjectorExtinguished)
|
||||
{
|
||||
if (__instance._ghostsAreAwake && !__instance._ghostsAlerted && Time.time >= __instance._ghostAlertTime)
|
||||
{
|
||||
__instance._ghostHowlAudioSource.PlayOneShot(global::AudioType.Ghost_SomeoneIsInHereHowl, 1f);
|
||||
__instance._ghostsAlerted = true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < QSBGhostZone2Director.ElevatorsStatus.Length; i++)
|
||||
{
|
||||
if (!QSBGhostZone2Director.ElevatorsStatus[i].activated && QSBGhostZone2Director.ElevatorsStatus[i].elevatorAction.reachedEndOfPath)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].ghostController.SetNodeMap(QSBGhostZone2Director.ElevatorsStatus[i].elevatorPair.nodeMap);
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].elevatorPair.elevator.topLight.FadeTo(1f, 0.2f);
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].elevatorPair.elevator.GoToDestination(0);
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].activated = true;
|
||||
}
|
||||
|
||||
if (!QSBGhostZone2Director.ElevatorsStatus[i].lightsDeactivated && QSBGhostZone2Director.ElevatorsStatus[i].activated && QSBGhostZone2Director.ElevatorsStatus[i].elevatorPair.elevator.isAtBottom)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].lightsDeactivated = true;
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].elevatorPair.elevator.topLight.FadeTo(0f, 0.2f);
|
||||
if (QSBGhostZone2Director.ElevatorsStatus[i].elevatorPair.cityDestination)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].ghostController.SetNodeMap(__instance._cityNodeMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].ghostController.SetNodeMap(__instance._undercityNodeMap);
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].ghostController.gameObject.GetComponent<Transform>().position = __instance._teleportNode.position;
|
||||
}
|
||||
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].elevatorAction.UseElevator();
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].timeSinceArrival = Time.time;
|
||||
}
|
||||
|
||||
if (QSBGhostZone2Director.ElevatorsStatus[i].lightsDeactivated && QSBGhostZone2Director.ElevatorsStatus[i].activated && !QSBGhostZone2Director.ElevatorsStatus[i].deactivated && Time.time >= QSBGhostZone2Director.ElevatorsStatus[i].timeSinceArrival + 2f)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].elevatorPair.elevator.GoToDestination(1);
|
||||
QSBGhostZone2Director.ElevatorsStatus[i].deactivated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(GhostZone2Director), nameof(GhostZone2Director.OnLightsExtinguished))]
|
||||
public static bool OnLightsExtinguished(GhostZone2Director __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"LIGHTS EXTINGUISHED");
|
||||
__instance._lightsProjectorExtinguished = true;
|
||||
__instance.WakeGhosts();
|
||||
|
||||
if (QSBGhostZone2Director.ElevatorsStatus == null)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus = new QSBGhostZone2Director.ElevatorStatus[__instance._elevators.Length];
|
||||
for (var j = 0; j < __instance._elevators.Length; j++)
|
||||
{
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].elevatorPair = __instance._elevators[j];
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].activated = false;
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].deactivated = false;
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].lightsDeactivated = false;
|
||||
}
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"ESCALATE THREAT AWARENESS");
|
||||
for (var i = 0; i < __instance._directedGhosts.Length; i++)
|
||||
{
|
||||
__instance._directedGhosts[i].EscalateThreatAwareness(GhostData.ThreatAwareness.SomeoneIsInHere);
|
||||
__instance._directedGhosts[i].GetWorldObject<QSBGhostBrain>().GetEffects().CancelStompyFootsteps();
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"SETUP ELEVATOR STATUS");
|
||||
for (var j = 0; j < QSBGhostZone2Director.ElevatorsStatus.Length; j++)
|
||||
{
|
||||
DebugLog.DebugWrite($"[{j}]");
|
||||
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;
|
||||
DebugLog.DebugWrite($"- CallToUseElevator on action");
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].elevatorAction.CallToUseElevator();
|
||||
DebugLog.DebugWrite($"- get ghost controller");
|
||||
QSBGhostZone2Director.ElevatorsStatus[j].ghostController = QSBGhostZone2Director.ElevatorsStatus[j].elevatorPair.ghost.GetComponent<GhostController>();
|
||||
}
|
||||
|
||||
__instance._ghostAlertTime = Time.time + 2f;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
176
QSB/EchoesOfTheEye/Ghosts/QSBGhostAction.cs
Normal file
176
QSB/EchoesOfTheEye/Ghosts/QSBGhostAction.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
public abstract class QSBGhostAction
|
||||
{
|
||||
protected QSBGhostBrain _brain;
|
||||
protected QSBGhostData _data => _brain._data;
|
||||
protected QSBGhostController _controller => _brain.AttachedObject._controller.GetWorldObject<QSBGhostController>();
|
||||
protected QSBGhostSensors _sensors => _brain.AttachedObject._sensors.GetWorldObject<QSBGhostSensors>();
|
||||
protected QSBGhostEffects _effects => _brain.AttachedObject._effects.GetWorldObject<QSBGhostEffects>();
|
||||
protected Transform _transform;
|
||||
protected bool _running;
|
||||
protected float _enterTime;
|
||||
|
||||
public static QSBGhostAction CreateAction(GhostAction.Name name)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return new QSBGhostActionStub { Name = name };
|
||||
}
|
||||
|
||||
QSBGhostAction ghostAction;
|
||||
switch (name)
|
||||
{
|
||||
case GhostAction.Name.Wait:
|
||||
ghostAction = new QSBWaitAction();
|
||||
break;
|
||||
case GhostAction.Name.Sleep:
|
||||
ghostAction = new QSBSleepAction();
|
||||
break;
|
||||
case GhostAction.Name.Sleepwalk:
|
||||
ghostAction = new QSBSleepwalkAction();
|
||||
break;
|
||||
case GhostAction.Name.PartyPath:
|
||||
ghostAction = new QSBPartyPathAction();
|
||||
break;
|
||||
case GhostAction.Name.PartyHouse:
|
||||
ghostAction = new QSBPartyHouseAction();
|
||||
break;
|
||||
case GhostAction.Name.ElevatorWalk:
|
||||
ghostAction = new QSBElevatorWalkAction();
|
||||
break;
|
||||
case GhostAction.Name.Sentry:
|
||||
ghostAction = new QSBSentryAction();
|
||||
break;
|
||||
case GhostAction.Name.Guard:
|
||||
ghostAction = new QSBGuardAction();
|
||||
break;
|
||||
case GhostAction.Name.IdentifyIntruder:
|
||||
ghostAction = new QSBIdentifyIntruderAction();
|
||||
break;
|
||||
case GhostAction.Name.Chase:
|
||||
ghostAction = new QSBChaseAction();
|
||||
break;
|
||||
case GhostAction.Name.Hunt:
|
||||
ghostAction = new QSBHuntAction();
|
||||
break;
|
||||
case GhostAction.Name.Stalk:
|
||||
ghostAction = new QSBStalkAction();
|
||||
break;
|
||||
case GhostAction.Name.Grab:
|
||||
ghostAction = new QSBGrabAction();
|
||||
break;
|
||||
case GhostAction.Name.SearchForIntruder:
|
||||
ghostAction = new QSBSearchAction();
|
||||
break;
|
||||
default:
|
||||
Debug.LogError("Failed to create action from name " + name);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ghostAction.GetName() != name)
|
||||
{
|
||||
Debug.LogError("New action name " + ghostAction.GetName() + "does not match supplied name " + name);
|
||||
Debug.Break();
|
||||
}
|
||||
|
||||
return ghostAction;
|
||||
}
|
||||
|
||||
public virtual void Initialize(QSBGhostBrain brain)
|
||||
{
|
||||
_brain = brain;
|
||||
this._transform = this._controller.AttachedObject.transform;
|
||||
}
|
||||
|
||||
public void EnterAction()
|
||||
{
|
||||
this._running = true;
|
||||
this._enterTime = Time.time;
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.OnEnterAction();
|
||||
}
|
||||
|
||||
public void ExitAction()
|
||||
{
|
||||
this._running = false;
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.OnExitAction();
|
||||
}
|
||||
|
||||
public abstract GhostAction.Name GetName();
|
||||
|
||||
public abstract float CalculateUtility();
|
||||
|
||||
public abstract bool Update_Action();
|
||||
|
||||
public virtual bool IsInterruptible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual float GetActionDelay()
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public virtual void FixedUpdate_Action()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnArriveAtPosition()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnTraversePathNode(GhostNode node)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnFaceNode(GhostNode node)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnFinishFaceNodeList()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void DrawGizmos(bool isGhostSelected)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnSetAsPending()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnEnterAction()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnExitAction()
|
||||
{
|
||||
}
|
||||
|
||||
protected float GetActionTimeElapsed()
|
||||
{
|
||||
if (!this._running)
|
||||
{
|
||||
return -1f;
|
||||
}
|
||||
return Time.time - this._enterTime;
|
||||
}
|
||||
}
|
125
QSB/EchoesOfTheEye/Ghosts/QSBGhostData.cs
Normal file
125
QSB/EchoesOfTheEye/Ghosts/QSBGhostData.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
public class QSBGhostData
|
||||
{
|
||||
public GhostData.ThreatAwareness threatAwareness;
|
||||
public GhostAction.Name currentAction = GhostAction.Name.None;
|
||||
public GhostAction.Name previousAction = GhostAction.Name.None;
|
||||
public bool isAlive = true;
|
||||
public bool hasWokenUp;
|
||||
public bool reduceGuardUtility;
|
||||
public bool fastStalkUnlocked;
|
||||
public float illuminatedByPlayerMeter;
|
||||
public bool reducedFrights_allowChase;
|
||||
public bool isIlluminated;
|
||||
public bool IsIlluminatedByAnyPlayer => players.Values.Any(x => x.sensor.isIlluminatedByPlayer);
|
||||
public Dictionary<PlayerInfo, GhostPlayer> players = new();
|
||||
public GhostPlayer localPlayer => players[QSBPlayerManager.LocalPlayer];
|
||||
public GhostPlayer interestedPlayer;
|
||||
|
||||
public void TabulaRasa()
|
||||
{
|
||||
threatAwareness = GhostData.ThreatAwareness.EverythingIsNormal;
|
||||
reduceGuardUtility = false;
|
||||
fastStalkUnlocked = false;
|
||||
illuminatedByPlayerMeter = 0f;
|
||||
|
||||
foreach (var player in players.Values)
|
||||
{
|
||||
player.isPlayerLocationKnown = false;
|
||||
player.wasPlayerLocationKnown = false;
|
||||
player.timeLastSawPlayer = 0f;
|
||||
player.timeSincePlayerLocationKnown = float.PositiveInfinity;
|
||||
player.playerMinLanternRange = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPlayerExitDreamWorld()
|
||||
{
|
||||
localPlayer.isPlayerLocationKnown = false;
|
||||
localPlayer.wasPlayerLocationKnown = false;
|
||||
reduceGuardUtility = false;
|
||||
fastStalkUnlocked = false;
|
||||
localPlayer.timeSincePlayerLocationKnown = float.PositiveInfinity;
|
||||
}
|
||||
|
||||
public void OnEnterAction(GhostAction.Name actionName)
|
||||
{
|
||||
if (actionName == GhostAction.Name.IdentifyIntruder || actionName - GhostAction.Name.Chase <= 2)
|
||||
{
|
||||
reduceGuardUtility = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void FixedUpdate_Data(GhostController controller, GhostSensors sensors)
|
||||
{
|
||||
foreach (var player in QSBPlayerManager.PlayerList)
|
||||
{
|
||||
if (!players.ContainsKey(player))
|
||||
{
|
||||
var newPlayer = new GhostPlayer
|
||||
{
|
||||
player = player
|
||||
};
|
||||
players.Add(player, newPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var pair in players)
|
||||
{
|
||||
var player = pair.Value;
|
||||
|
||||
if (!player.player.InDreamWorld)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player.wasPlayerLocationKnown = player.isPlayerLocationKnown;
|
||||
player.isPlayerLocationKnown = player.sensor.isPlayerVisible
|
||||
|| player.sensor.isPlayerHeldLanternVisible
|
||||
|| player.sensor.isIlluminatedByPlayer
|
||||
|| player.sensor.inContactWithPlayer;
|
||||
if (!reduceGuardUtility && player.sensor.isIlluminatedByPlayer)
|
||||
{
|
||||
reduceGuardUtility = true;
|
||||
}
|
||||
|
||||
var worldPosition = pair.Key.Body.transform.position - pair.Key.Body.transform.up;
|
||||
var worldVelocity = pair.Key.Velocity - controller.GetNodeMap().GetOWRigidbody().GetVelocity();
|
||||
player.playerLocation.Update(worldPosition, worldVelocity, controller);
|
||||
player.playerMinLanternRange = pair.Key.AssignedSimulationLantern.AttachedObject.GetLanternController().GetMinRange();
|
||||
if (player.isPlayerLocationKnown)
|
||||
{
|
||||
player.lastKnownPlayerLocation.CopyFromOther(player.playerLocation);
|
||||
player.lastKnownSensor.CopyFromOther(player.sensor);
|
||||
player.timeLastSawPlayer = Time.time;
|
||||
player.timeSincePlayerLocationKnown = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player.wasPlayerLocationKnown)
|
||||
{
|
||||
player.firstUnknownSensor.CopyFromOther(player.sensor);
|
||||
}
|
||||
|
||||
player.lastKnownPlayerLocation.Update(controller);
|
||||
player.timeSincePlayerLocationKnown += Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
if (threatAwareness >= GhostData.ThreatAwareness.IntruderConfirmed && IsIlluminatedByAnyPlayer && !PlayerData.GetReducedFrights())
|
||||
{
|
||||
illuminatedByPlayerMeter += Time.deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
illuminatedByPlayerMeter = Mathf.Max(0f, illuminatedByPlayerMeter - (Time.deltaTime * 0.5f));
|
||||
}
|
||||
}
|
31
QSB/EchoesOfTheEye/Ghosts/QSBGhostSensorData.cs
Normal file
31
QSB/EchoesOfTheEye/Ghosts/QSBGhostSensorData.cs
Normal file
@ -0,0 +1,31 @@
|
||||
namespace QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
public class QSBGhostSensorData
|
||||
{
|
||||
public bool isPlayerVisible;
|
||||
public bool isPlayerHeldLanternVisible;
|
||||
public bool isPlayerDroppedLanternVisible;
|
||||
public bool isPlayerHoldingLantern;
|
||||
public bool isIlluminatedByPlayer;
|
||||
public bool inContactWithPlayer;
|
||||
public bool isPlayerOccluded;
|
||||
public bool isPlayerIlluminated;
|
||||
public bool isPlayerIlluminatedByUs;
|
||||
public bool isPlayerInGuardVolume;
|
||||
|
||||
public bool knowsPlayerVelocity => isPlayerVisible || isPlayerHeldLanternVisible;
|
||||
|
||||
public void CopyFromOther(QSBGhostSensorData other)
|
||||
{
|
||||
isPlayerVisible = other.isPlayerVisible;
|
||||
isPlayerHeldLanternVisible = other.isPlayerHeldLanternVisible;
|
||||
isPlayerDroppedLanternVisible = other.isPlayerDroppedLanternVisible;
|
||||
isPlayerHoldingLantern = other.isPlayerHoldingLantern;
|
||||
isIlluminatedByPlayer = other.isIlluminatedByPlayer;
|
||||
inContactWithPlayer = other.inContactWithPlayer;
|
||||
isPlayerOccluded = other.isPlayerOccluded;
|
||||
isPlayerIlluminated = other.isPlayerIlluminated;
|
||||
isPlayerIlluminatedByUs = other.isPlayerIlluminatedByUs;
|
||||
isPlayerInGuardVolume = other.isPlayerInGuardVolume;
|
||||
}
|
||||
}
|
24
QSB/EchoesOfTheEye/Ghosts/QSBGhostZone2Director.cs
Normal file
24
QSB/EchoesOfTheEye/Ghosts/QSBGhostZone2Director.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.Actions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts;
|
||||
|
||||
public static class QSBGhostZone2Director
|
||||
{
|
||||
public static ElevatorStatus[] ElevatorsStatus;
|
||||
|
||||
public struct ElevatorStatus
|
||||
{
|
||||
public GhostZone2Director.ElevatorPair elevatorPair;
|
||||
public bool activated;
|
||||
public bool lightsDeactivated;
|
||||
public bool deactivated;
|
||||
public float timeSinceArrival;
|
||||
public QSBElevatorWalkAction elevatorAction;
|
||||
public GhostController ghostController;
|
||||
}
|
||||
}
|
5
QSB/EchoesOfTheEye/Ghosts/WorldObjects/IGhostObject.cs
Normal file
5
QSB/EchoesOfTheEye/Ghosts/WorldObjects/IGhostObject.cs
Normal file
@ -0,0 +1,5 @@
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
|
||||
public interface IGhostObject : IWorldObject { }
|
589
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostBrain.cs
Normal file
589
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostBrain.cs
Normal file
@ -0,0 +1,589 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
|
||||
public class QSBGhostBrain : WorldObject<GhostBrain>, IGhostObject
|
||||
{
|
||||
#region World Object Stuff
|
||||
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override async UniTask Init(CancellationToken ct)
|
||||
{
|
||||
Awake();
|
||||
Start();
|
||||
}
|
||||
|
||||
public override bool ShouldDisplayDebug()
|
||||
=> base.ShouldDisplayDebug()
|
||||
&& QSBCore.DebugSettings.DrawGhostAI;
|
||||
|
||||
public override string ReturnLabel()
|
||||
{
|
||||
var label = $"Name:{AttachedObject.ghostName}" +
|
||||
$"\r\nAwareness:{AttachedObject.GetThreatAwareness()}" +
|
||||
$"\r\nCurrent action:{AttachedObject.GetCurrentActionName()}" +
|
||||
$"\r\nIllumination meter:{_data.illuminatedByPlayerMeter}";
|
||||
|
||||
if (QSBCore.IsHost)
|
||||
{
|
||||
foreach (var action in _actionLibrary.OrderByDescending(x => x.CalculateUtility()))
|
||||
{
|
||||
label += $"\r\n{action.GetName()}:{action.CalculateUtility()}";
|
||||
}
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
public override void DisplayLines()
|
||||
{
|
||||
ControllerLines(AttachedObject._controller);
|
||||
DataLines(_data, AttachedObject._controller);
|
||||
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.DrawGizmos(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ControllerLines(GhostController controller)
|
||||
{
|
||||
Popcron.Gizmos.Sphere(controller.transform.position, 2f, Color.white);
|
||||
|
||||
if (controller._followNodePath)
|
||||
{
|
||||
for (var i = controller._nodePath.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Popcron.Gizmos.Sphere(controller.LocalToWorldPosition(controller._nodePath[i].localPosition), 0.25f, Color.cyan, 3);
|
||||
|
||||
var hasVisited = controller._pathIndex < i;
|
||||
var color = hasVisited ? Color.white : Color.cyan;
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
Popcron.Gizmos.Line(controller.LocalToWorldPosition(controller._nodePath[i].localPosition), controller.LocalToWorldPosition(controller._nodePath[i - 1].localPosition), color);
|
||||
}
|
||||
}
|
||||
|
||||
if (controller._hasFinalPathPosition)
|
||||
{
|
||||
Popcron.Gizmos.Sphere(controller.LocalToWorldPosition(controller._finalPathPosition), 0.3f, Color.red, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DataLines(QSBGhostData data, GhostController controller)
|
||||
{
|
||||
foreach (var player in data.players.Values)
|
||||
{
|
||||
if (player.timeSincePlayerLocationKnown != float.PositiveInfinity)
|
||||
{
|
||||
Popcron.Gizmos.Line(controller.transform.position, controller.LocalToWorldPosition(player.lastKnownPlayerLocation.localPosition), Color.magenta);
|
||||
Popcron.Gizmos.Sphere(controller.LocalToWorldPosition(player.lastKnownPlayerLocation.localPosition), 1f, Color.magenta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal QSBGhostData _data;
|
||||
private List<QSBGhostAction> _actionLibrary = new();
|
||||
private QSBGhostAction _currentAction;
|
||||
private QSBGhostAction _pendingAction;
|
||||
|
||||
public OWEvent<GhostBrain, QSBGhostData> OnIdentifyIntruder = new(4);
|
||||
|
||||
public GhostAction.Name GetCurrentActionName()
|
||||
{
|
||||
if (_currentAction == null)
|
||||
{
|
||||
return GhostAction.Name.None;
|
||||
}
|
||||
return _currentAction.GetName();
|
||||
}
|
||||
|
||||
public QSBGhostAction GetCurrentAction()
|
||||
{
|
||||
return _currentAction;
|
||||
}
|
||||
|
||||
public QSBGhostAction GetAction(GhostAction.Name actionName)
|
||||
{
|
||||
for (int i = 0; i < _actionLibrary.Count; i++)
|
||||
{
|
||||
if (_actionLibrary[i].GetName() == actionName)
|
||||
{
|
||||
return _actionLibrary[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public GhostData.ThreatAwareness GetThreatAwareness()
|
||||
{
|
||||
return _data.threatAwareness;
|
||||
}
|
||||
|
||||
public GhostEffects GetEffects()
|
||||
{
|
||||
return AttachedObject._effects;
|
||||
}
|
||||
|
||||
public bool CheckDreadAudioConditions()
|
||||
{
|
||||
return _currentAction != null
|
||||
&& _data.localPlayer.playerLocation.distance < 10f
|
||||
&& _currentAction.GetName() != GhostAction.Name.Sentry
|
||||
&& _currentAction.GetName() != GhostAction.Name.Grab;
|
||||
}
|
||||
|
||||
public bool CheckFearAudioConditions(bool fearAudioAlreadyPlaying)
|
||||
{
|
||||
if (_currentAction == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fearAudioAlreadyPlaying)
|
||||
{
|
||||
return _currentAction.GetName() is GhostAction.Name.Chase or GhostAction.Name.Grab;
|
||||
}
|
||||
|
||||
return _currentAction.GetName() == GhostAction.Name.Chase;
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
AttachedObject._controller = AttachedObject.GetComponent<GhostController>();
|
||||
AttachedObject._sensors = AttachedObject.GetComponent<GhostSensors>();
|
||||
_data = new();
|
||||
if (AttachedObject._data != null)
|
||||
{
|
||||
_data.threatAwareness = AttachedObject._data.threatAwareness;
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
AttachedObject.enabled = false;
|
||||
AttachedObject._controller.GetDreamLanternController().enabled = false;
|
||||
AttachedObject._controller.GetWorldObject<QSBGhostController>().Initialize(AttachedObject._nodeLayer, AttachedObject._effects.GetWorldObject<QSBGhostEffects>());
|
||||
AttachedObject._sensors.GetWorldObject<QSBGhostSensors>().Initialize(_data, AttachedObject._guardVolume);
|
||||
AttachedObject._effects.GetWorldObject<QSBGhostEffects>().Initialize(AttachedObject._controller.GetNodeRoot(), AttachedObject._controller, _data);
|
||||
AttachedObject._effects.OnCallForHelp += AttachedObject.OnCallForHelp;
|
||||
_data.reducedFrights_allowChase = AttachedObject._reducedFrights_allowChase;
|
||||
AttachedObject._controller.SetLanternConcealed(AttachedObject._startWithLanternConcealed, false);
|
||||
AttachedObject._intruderConfirmedBySelf = false;
|
||||
AttachedObject._intruderConfirmPending = false;
|
||||
AttachedObject._intruderConfirmTime = 0f;
|
||||
|
||||
DebugLog.DebugWrite($"{AttachedObject._name} setting up actions :");
|
||||
|
||||
for (var i = 0; i < AttachedObject._actions.Length; i++)
|
||||
{
|
||||
DebugLog.DebugWrite($"- {AttachedObject._actions[i]}");
|
||||
var ghostAction = QSBGhostAction.CreateAction(AttachedObject._actions[i]);
|
||||
ghostAction.Initialize(this);
|
||||
_actionLibrary.Add(ghostAction);
|
||||
}
|
||||
|
||||
ClearPendingAction();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
AttachedObject._sensors.RemoveEventListeners();
|
||||
AttachedObject._controller.OnArriveAtPosition -= AttachedObject.OnArriveAtPosition;
|
||||
AttachedObject._controller.OnTraversePathNode -= AttachedObject.OnTraversePathNode;
|
||||
AttachedObject._controller.OnFaceNode -= AttachedObject.OnFaceNode;
|
||||
AttachedObject._controller.OnFinishFaceNodeList -= AttachedObject.OnFinishFaceNodeList;
|
||||
AttachedObject._effects.OnCallForHelp -= AttachedObject.OnCallForHelp;
|
||||
GlobalMessenger.RemoveListener("EnterDreamWorld", new Callback(AttachedObject.OnEnterDreamWorld));
|
||||
GlobalMessenger.RemoveListener("ExitDreamWorld", new Callback(AttachedObject.OnExitDreamWorld));
|
||||
}
|
||||
|
||||
public void TabulaRasa()
|
||||
{
|
||||
AttachedObject._intruderConfirmedBySelf = false;
|
||||
AttachedObject._intruderConfirmPending = false;
|
||||
AttachedObject._intruderConfirmTime = 0f;
|
||||
AttachedObject._playResponseAudio = false;
|
||||
_data.TabulaRasa();
|
||||
}
|
||||
|
||||
public void Die()
|
||||
{
|
||||
if (!_data.isAlive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_data.isAlive = false;
|
||||
AttachedObject._controller.GetWorldObject<QSBGhostController>().StopMoving();
|
||||
AttachedObject._controller.GetWorldObject<QSBGhostController>().StopFacing();
|
||||
AttachedObject._controller.ExtinguishLantern();
|
||||
AttachedObject._controller.GetCollider().GetComponent<OWCollider>().SetActivation(false);
|
||||
AttachedObject._controller.GetGrabController().ReleasePlayer();
|
||||
_pendingAction = null;
|
||||
_currentAction = null;
|
||||
_data.currentAction = GhostAction.Name.None;
|
||||
AttachedObject._effects.PlayDeathAnimation();
|
||||
AttachedObject._effects.PlayDeathEffects();
|
||||
}
|
||||
|
||||
public void EscalateThreatAwareness(GhostData.ThreatAwareness newThreatAwareness)
|
||||
{
|
||||
DebugLog.DebugWrite($"{AttachedObject._name} Escalate threat awareness to {newThreatAwareness}");
|
||||
|
||||
if (_data.threatAwareness < newThreatAwareness)
|
||||
{
|
||||
_data.threatAwareness = newThreatAwareness;
|
||||
if (_data.isAlive && _data.threatAwareness == GhostData.ThreatAwareness.IntruderConfirmed)
|
||||
{
|
||||
if (AttachedObject._intruderConfirmedBySelf)
|
||||
{
|
||||
AttachedObject._effects.GetWorldObject<QSBGhostEffects>().PlayVoiceAudioFar(global::AudioType.Ghost_IntruderConfirmed, 1f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttachedObject._playResponseAudio)
|
||||
{
|
||||
AttachedObject._effects.GetWorldObject<QSBGhostEffects>().PlayVoiceAudioFar(global::AudioType.Ghost_IntruderConfirmedResponse, 1f);
|
||||
AttachedObject._playResponseAudio = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WakeUp()
|
||||
{
|
||||
DebugLog.DebugWrite($"Wake up!");
|
||||
_data.hasWokenUp = true;
|
||||
}
|
||||
|
||||
public bool HearGhostCall(Vector3 playerLocalPosition, float reactDelay, bool playResponseAudio = false)
|
||||
{
|
||||
if (_data.isAlive && !_data.hasWokenUp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_data.threatAwareness < GhostData.ThreatAwareness.IntruderConfirmed && !AttachedObject._intruderConfirmPending)
|
||||
{
|
||||
AttachedObject._intruderConfirmedBySelf = false;
|
||||
AttachedObject._intruderConfirmPending = true;
|
||||
AttachedObject._intruderConfirmTime = Time.time + reactDelay;
|
||||
AttachedObject._playResponseAudio = playResponseAudio;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HearCallForHelp(Vector3 playerLocalPosition, float reactDelay, GhostPlayer player)
|
||||
{
|
||||
if (_data.isAlive && !_data.hasWokenUp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"{AttachedObject._name} Hear call for help!");
|
||||
|
||||
if (_data.threatAwareness < GhostData.ThreatAwareness.IntruderConfirmed)
|
||||
{
|
||||
_data.threatAwareness = GhostData.ThreatAwareness.IntruderConfirmed;
|
||||
AttachedObject._intruderConfirmPending = false;
|
||||
}
|
||||
|
||||
AttachedObject._effects.PlayRespondToHelpCallAudio(reactDelay);
|
||||
_data.reduceGuardUtility = true;
|
||||
player.lastKnownPlayerLocation.UpdateLocalPosition(playerLocalPosition, AttachedObject._controller);
|
||||
player.wasPlayerLocationKnown = true;
|
||||
player.timeSincePlayerLocationKnown = 0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void HintPlayerLocation(PlayerInfo player)
|
||||
{
|
||||
var ghostPlayer = _data.players[player];
|
||||
HintPlayerLocation(ghostPlayer.playerLocation.localPosition, Time.time, ghostPlayer);
|
||||
}
|
||||
|
||||
public void HintPlayerLocation(Vector3 localPosition, float informationTime, GhostPlayer player)
|
||||
{
|
||||
if (!_data.hasWokenUp || player.isPlayerLocationKnown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (informationTime > player.timeLastSawPlayer)
|
||||
{
|
||||
player.lastKnownPlayerLocation.UpdateLocalPosition(localPosition, AttachedObject._controller);
|
||||
player.wasPlayerLocationKnown = true;
|
||||
player.timeSincePlayerLocationKnown = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
if (!AttachedObject.enabled)
|
||||
{
|
||||
DebugLog.DebugWrite($"attached object is not enabled!");
|
||||
return;
|
||||
}
|
||||
|
||||
AttachedObject._controller.FixedUpdate_Controller();
|
||||
AttachedObject._sensors.FixedUpdate_Sensors();
|
||||
_data.FixedUpdate_Data(AttachedObject._controller, AttachedObject._sensors);
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttachedObject.FixedUpdate_ThreatAwareness();
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.FixedUpdate_Action();
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!AttachedObject.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttachedObject._controller.Update_Controller();
|
||||
AttachedObject._sensors.Update_Sensors();
|
||||
AttachedObject._effects.Update_Effects();
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var flag = false;
|
||||
if (_currentAction != null)
|
||||
{
|
||||
flag = _currentAction.Update_Action();
|
||||
}
|
||||
|
||||
if (!flag && _currentAction != null)
|
||||
{
|
||||
_currentAction.ExitAction();
|
||||
_data.previousAction = _currentAction.GetName();
|
||||
_currentAction = null;
|
||||
_data.currentAction = GhostAction.Name.None;
|
||||
}
|
||||
|
||||
if (_data.isAlive && !Locator.GetDreamWorldController().IsExitingDream())
|
||||
{
|
||||
AttachedObject.EvaluateActions();
|
||||
}
|
||||
}
|
||||
|
||||
public void FixedUpdate_ThreatAwareness()
|
||||
{
|
||||
if (_data.threatAwareness == GhostData.ThreatAwareness.IntruderConfirmed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AttachedObject._intruderConfirmPending
|
||||
&& (_data.threatAwareness > GhostData.ThreatAwareness.EverythingIsNormal || _data.players.Values.Any(x => x.playerLocation.distance < 20f) || _data.players.Values.Any(x => x.sensor.isPlayerIlluminatedByUs))
|
||||
&& (_data.players.Values.Any(x => x.sensor.isPlayerVisible) || _data.players.Values.Any(x => x.sensor.inContactWithPlayer)))
|
||||
{
|
||||
DebugLog.DebugWrite($"INTRUDER CONFIRMED BY SELF");
|
||||
AttachedObject._intruderConfirmedBySelf = true;
|
||||
AttachedObject._intruderConfirmPending = true;
|
||||
var closestPlayer = _data.players.Values.MinBy(x => x.playerLocation.distance);
|
||||
var num = Mathf.Lerp(0.1f, 1.5f, Mathf.InverseLerp(5f, 25f, closestPlayer.playerLocation.distance));
|
||||
AttachedObject._intruderConfirmTime = Time.time + num;
|
||||
}
|
||||
|
||||
if (AttachedObject._intruderConfirmPending && Time.time > AttachedObject._intruderConfirmTime)
|
||||
{
|
||||
AttachedObject.EscalateThreatAwareness(GhostData.ThreatAwareness.IntruderConfirmed);
|
||||
OnIdentifyIntruder.Invoke(AttachedObject, _data);
|
||||
}
|
||||
}
|
||||
|
||||
public void EvaluateActions()
|
||||
{
|
||||
if (_currentAction != null && !_currentAction.IsInterruptible())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var num = float.NegativeInfinity;
|
||||
QSBGhostAction actionWithHighestUtility = null;
|
||||
for (var i = 0; i < _actionLibrary.Count; i++)
|
||||
{
|
||||
var num2 = _actionLibrary[i].CalculateUtility();
|
||||
if (num2 > num)
|
||||
{
|
||||
num = num2;
|
||||
actionWithHighestUtility = _actionLibrary[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (actionWithHighestUtility == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Couldn't find action with highest utility for {AttachedObject._name}?!", OWML.Common.MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var flag = false;
|
||||
if (_pendingAction == null || (actionWithHighestUtility.GetName() != _pendingAction.GetName() && num > AttachedObject._pendingActionUtility))
|
||||
{
|
||||
_pendingAction = actionWithHighestUtility;
|
||||
AttachedObject._pendingActionUtility = num;
|
||||
AttachedObject._pendingActionTimer = _pendingAction.GetActionDelay();
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (_pendingAction != null && _currentAction != null && _pendingAction.GetName() == _currentAction.GetName())
|
||||
{
|
||||
ClearPendingAction();
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
_pendingAction.OnSetAsPending();
|
||||
}
|
||||
|
||||
if (_pendingAction != null && AttachedObject._pendingActionTimer <= 0f)
|
||||
{
|
||||
ChangeAction(_pendingAction);
|
||||
}
|
||||
|
||||
if (_pendingAction != null)
|
||||
{
|
||||
AttachedObject._pendingActionTimer -= Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeAction(QSBGhostAction action, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new ChangeActionMessage(action?.GetName() ?? GhostAction.Name.None));
|
||||
}
|
||||
|
||||
DebugLog.DebugWrite($"{AttachedObject._name} Change action to {action?.GetName()}");
|
||||
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.ExitAction();
|
||||
_data.previousAction = _currentAction.GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
_data.previousAction = GhostAction.Name.None;
|
||||
}
|
||||
_currentAction = action;
|
||||
_data.currentAction = (action != null) ? action.GetName() : GhostAction.Name.None;
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.EnterAction();
|
||||
_data.OnEnterAction(_currentAction.GetName());
|
||||
}
|
||||
ClearPendingAction();
|
||||
}
|
||||
|
||||
public void ClearPendingAction()
|
||||
{
|
||||
_pendingAction = null;
|
||||
AttachedObject._pendingActionUtility = -100f;
|
||||
AttachedObject._pendingActionTimer = 0f;
|
||||
}
|
||||
|
||||
public void OnArriveAtPosition()
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.OnArriveAtPosition();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTraversePathNode(GhostNode node)
|
||||
{
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.OnTraversePathNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnFaceNode(GhostNode node)
|
||||
{
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.OnFaceNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnFinishFaceNodeList()
|
||||
{
|
||||
if (_currentAction != null)
|
||||
{
|
||||
_currentAction.OnFinishFaceNodeList();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCallForHelp()
|
||||
{
|
||||
DebugLog.DebugWrite($"{AttachedObject._name} - iterating through helper list for callforhelp");
|
||||
|
||||
if (AttachedObject._helperGhosts != null)
|
||||
{
|
||||
for (var i = 0; i < AttachedObject._helperGhosts.Length; i++)
|
||||
{
|
||||
AttachedObject._helperGhosts[i].GetWorldObject<QSBGhostBrain>().HearCallForHelp(_data.interestedPlayer.playerLocation.localPosition, 3f, _data.interestedPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEnterDreamWorld()
|
||||
{
|
||||
AttachedObject.enabled = true;
|
||||
AttachedObject._controller.GetDreamLanternController().enabled = true;
|
||||
}
|
||||
|
||||
public void OnExitDreamWorld()
|
||||
{
|
||||
AttachedObject.enabled = false;
|
||||
AttachedObject._controller.GetDreamLanternController().enabled = false;
|
||||
//ChangeAction(null);
|
||||
_data.OnPlayerExitDreamWorld();
|
||||
}
|
||||
}
|
270
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostController.cs
Normal file
270
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostController.cs
Normal file
@ -0,0 +1,270 @@
|
||||
using GhostEnums;
|
||||
using QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
|
||||
public class QSBGhostController : WorldObject<GhostController>, IGhostObject
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public QSBGhostEffects _effects;
|
||||
|
||||
public void Initialize(GhostNode.NodeLayer layer, QSBGhostEffects effects)
|
||||
{
|
||||
_effects = effects;
|
||||
AttachedObject._nodeRoot = AttachedObject.transform.parent = AttachedObject._nodeMap.transform;
|
||||
AttachedObject._nodeLayer = layer;
|
||||
AttachedObject._grabController.Initialize(effects.AttachedObject);
|
||||
AttachedObject._lantern.SetLit(true);
|
||||
AttachedObject.MoveLanternToCarrySocket(false, 0.1f);
|
||||
AttachedObject._playerCollider = Locator.GetPlayerBody().GetComponent<CapsuleCollider>();
|
||||
}
|
||||
|
||||
public void SetLanternConcealed(bool concealed, bool playAudio = true)
|
||||
{
|
||||
if (playAudio && AttachedObject._lantern.IsConcealed() != concealed)
|
||||
{
|
||||
_effects.PlayLanternAudio(concealed ? global::AudioType.Artifact_Conceal : global::AudioType.Artifact_Unconceal);
|
||||
}
|
||||
|
||||
AttachedObject._lantern.SetConcealed(concealed);
|
||||
if (concealed)
|
||||
{
|
||||
AttachedObject._lantern.SetFocus(0f);
|
||||
AttachedObject._updateLantern = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeLanternFocus(float focus, float focusRate = 2f)
|
||||
{
|
||||
if (focus > 0f)
|
||||
{
|
||||
AttachedObject._lantern.SetConcealed(false);
|
||||
}
|
||||
|
||||
if (focus > AttachedObject._targetLanternFocus)
|
||||
{
|
||||
_effects.PlayLanternAudio(global::AudioType.Artifact_Focus);
|
||||
}
|
||||
else if (focus < AttachedObject._targetLanternFocus)
|
||||
{
|
||||
_effects.PlayLanternAudio(global::AudioType.Artifact_Unfocus);
|
||||
}
|
||||
|
||||
AttachedObject._updateLantern = true;
|
||||
AttachedObject._targetLanternFocus = focus;
|
||||
AttachedObject._lanternFocusRate = focusRate;
|
||||
}
|
||||
|
||||
public void FacePlayer(PlayerInfo player, TurnSpeed turnSpeed, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new FacePlayerMessage(player.PlayerId, turnSpeed));
|
||||
}
|
||||
|
||||
AttachedObject._facingState = GhostController.FacingState.FaceTransform;
|
||||
AttachedObject._faceTransform = player.Camera.transform;
|
||||
AttachedObject._targetDegreesPerSecond = GhostConstants.GetTurnSpeed(turnSpeed);
|
||||
AttachedObject._angularAcceleration = GhostConstants.GetTurnAcceleration(turnSpeed);
|
||||
}
|
||||
|
||||
public void FaceLocalPosition(Vector3 localPosition, TurnSpeed turnSpeed)
|
||||
{
|
||||
FaceLocalPosition(localPosition, GhostConstants.GetTurnSpeed(turnSpeed), GhostConstants.GetTurnAcceleration(turnSpeed));
|
||||
}
|
||||
|
||||
public void FaceLocalPosition(Vector3 localPosition, float degreesPerSecond, float turnAcceleration = 360f, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new FaceLocalPositionMessage(localPosition, degreesPerSecond, turnAcceleration));
|
||||
}
|
||||
|
||||
AttachedObject.FaceLocalPosition(localPosition, degreesPerSecond, turnAcceleration);
|
||||
}
|
||||
|
||||
public void FaceNodeList(GhostNode[] nodeList, int numNodes, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern = false, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new FaceNodeListMessage(nodeList, numNodes, turnSpeed, nodeDelay, autoFocusLantern));
|
||||
}
|
||||
|
||||
AttachedObject.FaceNodeList(nodeList, numNodes, turnSpeed, nodeDelay, autoFocusLantern);
|
||||
}
|
||||
|
||||
public void FaceVelocity(bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new FaceVelocityMessage());
|
||||
}
|
||||
|
||||
AttachedObject.FaceVelocity();
|
||||
}
|
||||
|
||||
public void MoveToLocalPosition(Vector3 localPosition, MoveType moveType)
|
||||
{
|
||||
MoveToLocalPosition(localPosition, GhostConstants.GetMoveSpeed(moveType), GhostConstants.GetMoveAcceleration(moveType));
|
||||
}
|
||||
|
||||
public void MoveToLocalPosition(Vector3 localPosition, float speed, float acceleration = 10f, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new MoveToLocalPositionMessage(localPosition, speed, acceleration));
|
||||
}
|
||||
|
||||
AttachedObject.MoveToLocalPosition(localPosition, speed, acceleration);
|
||||
}
|
||||
|
||||
public void PathfindToLocalPosition(Vector3 localPosition, MoveType moveType)
|
||||
{
|
||||
PathfindToLocalPosition(localPosition, GhostConstants.GetMoveSpeed(moveType), GhostConstants.GetMoveAcceleration(moveType));
|
||||
}
|
||||
|
||||
public void PathfindToLocalPosition(Vector3 localPosition, float speed, float acceleration = 10f, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new PathfindLocalPositionMessage(localPosition, speed, acceleration));
|
||||
}
|
||||
|
||||
AttachedObject.PathfindToLocalPosition(localPosition, speed, acceleration);
|
||||
}
|
||||
|
||||
public void PathfindToNode(GhostNode node, MoveType moveType)
|
||||
{
|
||||
PathfindToNode(node, GhostConstants.GetMoveSpeed(moveType), GhostConstants.GetMoveAcceleration(moveType));
|
||||
}
|
||||
|
||||
public void PathfindToNode(GhostNode node, float speed, float acceleration = 10f, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new PathfindNodeMessage(node, speed, acceleration));
|
||||
}
|
||||
|
||||
AttachedObject.PathfindToNode(node, speed, acceleration);
|
||||
}
|
||||
|
||||
public void FaceNode(GhostNode node, TurnSpeed turnSpeed, float nodeDelay, bool autoFocusLantern = false, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new FaceNodeMessage(node, turnSpeed, nodeDelay, autoFocusLantern));
|
||||
}
|
||||
|
||||
AttachedObject.FaceNode(node, turnSpeed, nodeDelay, autoFocusLantern);
|
||||
}
|
||||
|
||||
public void FaceLocalDirection(Vector3 localDirection, TurnSpeed turnSpeed)
|
||||
{
|
||||
FaceLocalDirection(localDirection, GhostConstants.GetTurnSpeed(turnSpeed), GhostConstants.GetTurnAcceleration(turnSpeed));
|
||||
}
|
||||
|
||||
public void FaceLocalDirection(Vector3 localDirection, float degreesPerSecond, float turnAcceleration = 360f, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new FaceLocalDirectionMessage(localDirection, degreesPerSecond, turnAcceleration));
|
||||
}
|
||||
|
||||
AttachedObject.FaceLocalDirection(localDirection, degreesPerSecond, turnAcceleration);
|
||||
}
|
||||
|
||||
public void StopMoving()
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new StopMovingMessage(false));
|
||||
AttachedObject.StopMoving();
|
||||
}
|
||||
|
||||
public void StopMovingInstantly()
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new StopMovingMessage(true));
|
||||
AttachedObject.StopMovingInstantly();
|
||||
}
|
||||
|
||||
public void StopFacing(bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new StopFacingMessage());
|
||||
}
|
||||
|
||||
AttachedObject.StopFacing();
|
||||
}
|
||||
}
|
246
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostEffects.cs
Normal file
246
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostEffects.cs
Normal file
@ -0,0 +1,246 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
|
||||
public class QSBGhostEffects : WorldObject<GhostEffects>, IGhostObject
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
private QSBGhostData _data;
|
||||
|
||||
public void Initialize(Transform nodeRoot, GhostController controller, QSBGhostData data)
|
||||
{
|
||||
AttachedObject._animator = AttachedObject.GetComponent<Animator>();
|
||||
AttachedObject._controller = controller;
|
||||
_data = data;
|
||||
if (AttachedObject._feetAudioSourceFar != null)
|
||||
{
|
||||
AttachedObject._stompyFootsteps = true;
|
||||
}
|
||||
|
||||
AttachedObject.SetEyeGlow(AttachedObject._eyeGlow);
|
||||
}
|
||||
|
||||
public bool AllowFootstepAudio(bool usingTimer)
|
||||
{
|
||||
var flag = AttachedObject._stompyFootsteps || _data.currentAction == GhostAction.Name.Chase || _data.currentAction == GhostAction.Name.Grab;
|
||||
if (usingTimer != flag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var num = AttachedObject._stompyFootsteps ? AttachedObject._feetAudioSourceFar.maxDistance : AttachedObject._feetAudioSourceNear.maxDistance;
|
||||
return _data.localPlayer.playerLocation.distance < num + 5f;
|
||||
}
|
||||
|
||||
public void PlayLanternAudio(AudioType audioType)
|
||||
{
|
||||
if (_data.localPlayer.playerLocation.distance < AttachedObject._lanternAudioSource.GetAudioSource().maxDistance + 5f)
|
||||
{
|
||||
AttachedObject._lanternAudioSource.PlayOneShot(audioType, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update_Effects()
|
||||
{
|
||||
if (_data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AttachedObject._waitToRespondToHelpCall && Time.time >= AttachedObject._respondToHelpCallTime)
|
||||
{
|
||||
AttachedObject._waitToRespondToHelpCall = false;
|
||||
PlayVoiceAudioFar(AudioType.Ghost_CallForHelpResponse, 1f);
|
||||
}
|
||||
|
||||
if (AttachedObject._waitForGrabWindow && AttachedObject._animator.GetFloat(GhostEffects.AnimatorKeys.AnimCurve_GrabWindow) > 0.5f)
|
||||
{
|
||||
AttachedObject._waitForGrabWindow = false;
|
||||
AttachedObject.PlayGrabAudio(AudioType.Ghost_Grab_Swish);
|
||||
}
|
||||
|
||||
AttachedObject.Update_Footsteps();
|
||||
|
||||
var relativeVelocity = AttachedObject._controller.GetRelativeVelocity();
|
||||
var num = (AttachedObject._movementStyle == GhostEffects.MovementStyle.Chase) ? 8f : 2f;
|
||||
var targetValue = new Vector2(relativeVelocity.x / num, relativeVelocity.z / num);
|
||||
AttachedObject._smoothedMoveSpeed = AttachedObject._moveSpeedSpring.Update(AttachedObject._smoothedMoveSpeed, targetValue, Time.deltaTime);
|
||||
AttachedObject._animator.SetFloat(GhostEffects.AnimatorKeys.Float_MoveDirectionX, AttachedObject._smoothedMoveSpeed.x);
|
||||
AttachedObject._animator.SetFloat(GhostEffects.AnimatorKeys.Float_MoveDirectionY, AttachedObject._smoothedMoveSpeed.y);
|
||||
|
||||
AttachedObject._smoothedTurnSpeed = AttachedObject._turnSpeedSpring.Update(AttachedObject._smoothedTurnSpeed, AttachedObject._controller.GetAngularVelocity() / 90f, Time.deltaTime);
|
||||
AttachedObject._animator.SetFloat(GhostEffects.AnimatorKeys.Float_TurnSpeed, AttachedObject._smoothedTurnSpeed);
|
||||
|
||||
var target = _data.isIlluminated ? 1f : 0f;
|
||||
var num2 = _data.isIlluminated ? 8f : 0.8f;
|
||||
AttachedObject._eyeGlow = Mathf.MoveTowards(AttachedObject._eyeGlow, target, Time.deltaTime * num2);
|
||||
var num3 = (Locator.GetDreamWorldController().GetPlayerLantern().GetLanternController().GetLight().GetFlickerScale() - 1f + 0.07f) / 0.14f;
|
||||
num3 = Mathf.Lerp(0.7f, 1f, num3);
|
||||
AttachedObject.SetEyeGlow(AttachedObject._eyeGlow * num3);
|
||||
|
||||
if (AttachedObject._playingDeathSequence)
|
||||
{
|
||||
var @float = AttachedObject._animator.GetFloat(GhostEffects.AnimatorKeys.AnimCurve_DeathFade);
|
||||
for (var i = 0; i < AttachedObject._dissolveRenderers.Length; i++)
|
||||
{
|
||||
AttachedObject._dissolveRenderers[i].SetMaterialProperty(AttachedObject._propID_DissolveProgress, @float);
|
||||
}
|
||||
|
||||
for (var j = 0; j < AttachedObject._ditherRenderers.Length; j++)
|
||||
{
|
||||
AttachedObject._ditherRenderers[j].SetDitherFade(@float);
|
||||
}
|
||||
|
||||
if (AttachedObject._deathAnimComplete && (AttachedObject._deathParticleSystem == null || !AttachedObject._deathParticleSystem.isPlaying))
|
||||
{
|
||||
AttachedObject._playingDeathSequence = false;
|
||||
AttachedObject._controller.gameObject.SetActive(false);
|
||||
AttachedObject.OnGhostDeathComplete.Invoke();
|
||||
for (var k = 0; k < AttachedObject._dissolveRenderers.Length; k++)
|
||||
{
|
||||
AttachedObject._dissolveRenderers[k].SetMaterialProperty(AttachedObject._propID_DissolveProgress, 0f);
|
||||
}
|
||||
|
||||
for (var l = 0; l < AttachedObject._ditherRenderers.Length; l++)
|
||||
{
|
||||
AttachedObject._ditherRenderers[l].SetDitherFade(0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMovementStyle(GhostEffects.MovementStyle style, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new SetMovementStyleMessage(style));
|
||||
}
|
||||
|
||||
AttachedObject.SetMovementStyle(style);
|
||||
}
|
||||
|
||||
public void PlayVoiceAudioNear(AudioType audioType, float volumeScale, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new PlayVoiceAudioMessage(audioType, volumeScale, true));
|
||||
}
|
||||
|
||||
AttachedObject.PlayVoiceAudioNear(audioType, volumeScale);
|
||||
}
|
||||
|
||||
public void PlayVoiceAudioFar(AudioType audioType, float volumeScale, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new PlayVoiceAudioMessage(audioType, volumeScale, false));
|
||||
}
|
||||
|
||||
AttachedObject.PlayVoiceAudioFar(audioType, volumeScale);
|
||||
}
|
||||
|
||||
public void PlaySleepAnimation(bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new GhostAnimationTriggerMessage(GhostAnimationType.Sleep));
|
||||
}
|
||||
|
||||
AttachedObject.PlaySleepAnimation();
|
||||
}
|
||||
|
||||
public void PlayDefaultAnimation(bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new GhostAnimationTriggerMessage(GhostAnimationType.Default));
|
||||
}
|
||||
|
||||
AttachedObject.PlayDefaultAnimation();
|
||||
}
|
||||
|
||||
public void PlayGrabAnimation(bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new GhostAnimationTriggerMessage(GhostAnimationType.Grab));
|
||||
}
|
||||
|
||||
AttachedObject.PlayGrabAnimation();
|
||||
}
|
||||
|
||||
public void PlayBlowOutLanternAnimation(bool fast = false, bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new GhostAnimationTriggerMessage(fast ? GhostAnimationType.BlowOutLanternFast : GhostAnimationType.BlowOutLanternNormal));
|
||||
}
|
||||
|
||||
AttachedObject.PlayBlowOutLanternAnimation(fast);
|
||||
}
|
||||
|
||||
public void PlaySnapNeckAnimation(bool remote = false)
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SendMessage(new GhostAnimationTriggerMessage(GhostAnimationType.SnapNeck));
|
||||
}
|
||||
|
||||
AttachedObject.PlaySnapNeckAnimation();
|
||||
}
|
||||
}
|
16
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostNodeMap.cs
Normal file
16
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostNodeMap.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
|
||||
internal class QSBGhostNodeMap : WorldObject<GhostNodeMap>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
131
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostSensors.cs
Normal file
131
QSB/EchoesOfTheEye/Ghosts/WorldObjects/QSBGhostSensors.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
|
||||
public class QSBGhostSensors : WorldObject<GhostSensors>, IGhostObject
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool ShouldDisplayDebug() => false;
|
||||
|
||||
public QSBGhostData _data;
|
||||
|
||||
public void Initialize(QSBGhostData data, OWTriggerVolume guardVolume = null)
|
||||
{
|
||||
_data = data;
|
||||
AttachedObject._origEdgeCatcherSize = AttachedObject._contactEdgeCatcherShape.size;
|
||||
AttachedObject._guardVolume = guardVolume;
|
||||
}
|
||||
|
||||
public bool CanGrabPlayer(GhostPlayer player)
|
||||
=> !PlayerState.IsAttached()
|
||||
&& player.playerLocation.distanceXZ < 2f + AttachedObject._grabDistanceBuff
|
||||
&& player.playerLocation.degreesToPositionXZ < 20f + AttachedObject._grabAngleBuff
|
||||
&& AttachedObject._animator.GetFloat("GrabWindow") > 0.5f;
|
||||
|
||||
public void FixedUpdate_Sensors()
|
||||
{
|
||||
if (_data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pair in _data.players)
|
||||
{
|
||||
var player = pair.Value;
|
||||
|
||||
if (player.player.AssignedSimulationLantern == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var lanternController = player.player.AssignedSimulationLantern.AttachedObject.GetLanternController();
|
||||
var playerLightSensor = player.player.LightSensor;
|
||||
player.sensor.isPlayerHoldingLantern = lanternController.IsHeldByPlayer();
|
||||
_data.isIlluminated = AttachedObject._lightSensor.IsIlluminated();
|
||||
player.sensor.isIlluminatedByPlayer = (lanternController.IsHeldByPlayer() && AttachedObject._lightSensor.IsIlluminatedByLantern(lanternController));
|
||||
player.sensor.isPlayerIlluminatedByUs = playerLightSensor.IsIlluminatedByLantern(AttachedObject._lantern);
|
||||
player.sensor.isPlayerIlluminated = playerLightSensor.IsIlluminated();
|
||||
player.sensor.isPlayerVisible = false;
|
||||
player.sensor.isPlayerHeldLanternVisible = false;
|
||||
player.sensor.isPlayerDroppedLanternVisible = false;
|
||||
player.sensor.isPlayerOccluded = false;
|
||||
|
||||
if ((lanternController.IsHeldByPlayer() && !lanternController.IsConcealed()) || playerLightSensor.IsIlluminated())
|
||||
{
|
||||
var position = pair.Key.Camera.transform.position;
|
||||
if (AttachedObject.CheckPointInVisionCone(position))
|
||||
{
|
||||
if (AttachedObject.CheckLineOccluded(AttachedObject._sightOrigin.position, position))
|
||||
{
|
||||
player.sensor.isPlayerOccluded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.sensor.isPlayerVisible = playerLightSensor.IsIlluminated();
|
||||
player.sensor.isPlayerHeldLanternVisible = (lanternController.IsHeldByPlayer() && !lanternController.IsConcealed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lanternController.IsHeldByPlayer() && AttachedObject.CheckPointInVisionCone(lanternController.GetLightPosition()) && !AttachedObject.CheckLineOccluded(AttachedObject._sightOrigin.position, lanternController.GetLightPosition()))
|
||||
{
|
||||
player.sensor.isPlayerDroppedLanternVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var visiblePlayers = _data.players.Values.Where(x => x.sensor.isPlayerVisible || x.sensor.isPlayerHeldLanternVisible || x.sensor.inContactWithPlayer || x.sensor.isPlayerIlluminatedByUs);
|
||||
|
||||
if (visiblePlayers.Count() == 0) // no players visible
|
||||
{
|
||||
visiblePlayers = _data.players.Values.Where(x => x.sensor.isIlluminatedByPlayer);
|
||||
}
|
||||
|
||||
if (visiblePlayers.Count() == 0) // no players lighting us
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var closest = visiblePlayers.MinBy(x => x.playerLocation.distance);
|
||||
|
||||
if (_data.interestedPlayer != closest)
|
||||
{
|
||||
DebugLog.DebugWrite($"CHANGE INTERESTED PLAYER!");
|
||||
_data.interestedPlayer = closest;
|
||||
this.SendMessage(new ChangeInterestedPlayerMessage(closest.player.PlayerId));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEnterContactTrigger(GameObject hitObj)
|
||||
{
|
||||
if (hitObj.CompareTag("PlayerDetector"))
|
||||
{
|
||||
_data.localPlayer.sensor.inContactWithPlayer = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnExitContactTrigger(GameObject hitObj)
|
||||
{
|
||||
if (hitObj.CompareTag("PlayerDetector"))
|
||||
{
|
||||
_data.localPlayer.sensor.inContactWithPlayer = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,26 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.LightSensorSync;
|
||||
|
||||
internal class LightSensorManager : WorldObjectManager
|
||||
{
|
||||
/// <summary>
|
||||
/// light sensor apparently shows up in eye
|
||||
/// </summary>
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.Both;
|
||||
/// <summary>
|
||||
/// light sensor patches like to run even with no dlc
|
||||
/// </summary>
|
||||
public override bool DlcOnly => false;
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
public override bool DlcOnly => true;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) =>
|
||||
QSBWorldSync.Init<QSBLightSensor, SingleLightSensor>();
|
||||
public static bool ShouldIgnore(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 => !ShouldIgnore(x))
|
||||
.SortDeterministic();
|
||||
QSBWorldSync.Init<QSBLightSensor, SingleLightSensor>(list);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,11 @@ internal class LightSensorPatches : QSBPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LightSensorManager.ShouldIgnore(__instance))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var flag = __instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe);
|
||||
if (flag && !__instance.enabled)
|
||||
{
|
||||
@ -64,6 +69,11 @@ internal class LightSensorPatches : QSBPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LightSensorManager.ShouldIgnore(__instance))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbLightSensor = __instance.GetWorldObject<QSBLightSensor>();
|
||||
var illuminatedByLocal = qsbLightSensor.IlluminatedByLocal;
|
||||
qsbLightSensor.IlluminatedByLocal = false;
|
||||
|
@ -1,17 +1,3 @@
|
||||
using QSB.EchoesOfTheEye.DreamLantern.Messages;
|
||||
using QSB.Messaging;
|
||||
namespace QSB.ItemSync.WorldObjects.Items;
|
||||
|
||||
namespace QSB.ItemSync.WorldObjects.Items;
|
||||
|
||||
public class QSBDreamLanternItem : QSBItem<DreamLanternItem>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
base.SendInitialState(to);
|
||||
|
||||
if (AttachedObject._lanternController != null)
|
||||
{
|
||||
this.SendMessage(new DreamLanternLitMessage(AttachedObject._lanternController.IsLit()) { To = to });
|
||||
}
|
||||
}
|
||||
}
|
||||
public class QSBDreamLanternItem : QSBItem<DreamLanternItem> { }
|
||||
|
@ -53,4 +53,42 @@ public partial class PlayerInfo
|
||||
}
|
||||
}
|
||||
private GameObject _body;
|
||||
|
||||
public LightSensor LightSensor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsLocalPlayer)
|
||||
{
|
||||
return Locator.GetPlayerLightSensor();
|
||||
}
|
||||
|
||||
if (CameraBody == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Can't get LightSensor for {PlayerId}, because CameraBody is null.", MessageType.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return CameraBody.transform.Find("REMOTE_CameraDetector").GetComponent<LightSensor>();
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 Velocity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsLocalPlayer)
|
||||
{
|
||||
return Locator.GetPlayerBody().GetVelocity();
|
||||
}
|
||||
|
||||
if (Body == null)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - Can't get velocity for {PlayerId}, because Body is null.", MessageType.Error);
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
return Body.GetComponent<RemotePlayerVelocity>().Velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
QSB/Player/RemotePlayerVelocity.cs
Normal file
21
QSB/Player/RemotePlayerVelocity.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Player;
|
||||
|
||||
public class RemotePlayerVelocity : MonoBehaviour
|
||||
{
|
||||
private Vector3 _prevPosition;
|
||||
|
||||
public Vector3 Velocity { get; private set; }
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
Velocity = (transform.position - _prevPosition) / Time.fixedDeltaTime;
|
||||
_prevPosition = transform.position;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using QSB.ClientServerStateSync;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Player;
|
||||
using QSB.QuantumSync.WorldObjects;
|
||||
using QSB.ShipSync;
|
||||
@ -149,9 +150,18 @@ internal class DebugGUI : MonoBehaviour, IAddComponentOnStart
|
||||
WriteLine(2, $"Visible : {player.Visible}");
|
||||
WriteLine(2, $"Ready : {player.IsReady}");
|
||||
WriteLine(2, $"Suited Up : {player.SuitedUp}");
|
||||
WriteLine(2, $"InDreamWorld : {player.InDreamWorld}");
|
||||
|
||||
|
||||
if (player.IsReady && QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
WriteLine(2, $"Illuminated : {player.LightSensor.IsIlluminated()}");
|
||||
var singleLightSensor = player.LightSensor as SingleLightSensor;
|
||||
foreach (var item in singleLightSensor._lightSources)
|
||||
{
|
||||
WriteLine(2, $"- {item.GetLightSourceType()}");
|
||||
}
|
||||
|
||||
var networkTransform = player.TransformSync;
|
||||
var referenceSector = networkTransform.ReferenceSector;
|
||||
var referenceTransform = networkTransform.ReferenceTransform;
|
||||
@ -222,6 +232,37 @@ internal class DebugGUI : MonoBehaviour, IAddComponentOnStart
|
||||
|
||||
#endregion
|
||||
|
||||
if (QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
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()}");
|
||||
var interestedPlayer = ghost._data.interestedPlayer;
|
||||
WriteLine(4, $"InterestedPlayer:{(interestedPlayer == null ? "NULL" : interestedPlayer.player.PlayerId)}");
|
||||
|
||||
foreach (var player in ghost._data.players.Values)
|
||||
{
|
||||
WriteLine(4, $"{player.player.PlayerId}");
|
||||
WriteLine(4, $"- isPlayerVisible:{player.sensor.isPlayerVisible}");
|
||||
WriteLine(4, $"- isPlayerHeldLanternVisible:{player.sensor.isPlayerHeldLanternVisible}");
|
||||
WriteLine(4, $"- isIlluminatedByPlayer:{player.sensor.isIlluminatedByPlayer}");
|
||||
WriteLine(4, $"- isPlayerLocationKnown:{player.isPlayerLocationKnown}");
|
||||
WriteLine(4, $"- timeSincePlayerLocationKnown:{player.timeSincePlayerLocationKnown}");
|
||||
var lantern = player.player.AssignedSimulationLantern;
|
||||
WriteLine(4, $"- IsHeldByPlayer:{lantern.AttachedObject.GetLanternController().IsHeldByPlayer()}");
|
||||
WriteLine(4, $"- Concealed:{lantern.AttachedObject.GetLanternController().IsConcealed()}");
|
||||
var position = player.player.Camera.transform.position;
|
||||
WriteLine(4, $"- Camera in vision cone:{ghost.AttachedObject._sensors.CheckPointInVisionCone(position)}");
|
||||
WriteLine(4, $"- CheckLineOccluded:{ghost.AttachedObject._sensors.CheckLineOccluded(ghost.AttachedObject._sensors._sightOrigin.position, position)}");
|
||||
}
|
||||
|
||||
WriteLine(4, $"First check:{!ghost.AttachedObject._intruderConfirmPending}");
|
||||
WriteLine(4, $"Second check:{ghost._data.threatAwareness > GhostData.ThreatAwareness.EverythingIsNormal || ghost._data.players.Values.Any(x => x.playerLocation.distance < 20f) || ghost._data.players.Values.Any(x => x.sensor.isPlayerIlluminatedByUs)}");
|
||||
WriteLine(4, $"Third check:{ghost._data.players.Values.Any(x => x.sensor.isPlayerVisible) || ghost._data.players.Values.Any(x => x.sensor.inContactWithPlayer)}");
|
||||
}
|
||||
|
||||
/*
|
||||
#region Column4 - Quantum Object Possesion
|
||||
|
||||
foreach (var player in QSBPlayerManager.PlayerList)
|
||||
@ -260,20 +301,29 @@ internal class DebugGUI : MonoBehaviour, IAddComponentOnStart
|
||||
}
|
||||
|
||||
#endregion
|
||||
*/
|
||||
}
|
||||
|
||||
private static void DrawWorldObjectLabels()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLabels)
|
||||
if (QSBCore.DebugSettings.DrawLabels)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var obj in QSBWorldSync.GetWorldObjects())
|
||||
{
|
||||
if (obj.ShouldDisplayDebug())
|
||||
foreach (var obj in QSBWorldSync.GetWorldObjects())
|
||||
{
|
||||
DrawLabel(obj.AttachedObject.transform, obj.ReturnLabel());
|
||||
if (obj.ShouldDisplayDebug())
|
||||
{
|
||||
DrawLabel(obj.AttachedObject.transform, obj.ReturnLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (QSBCore.DebugSettings.DrawGhostAI)
|
||||
{
|
||||
foreach (var obj in QSBWorldSync.GetWorldObjects<IGhostObject>())
|
||||
{
|
||||
if (obj.ShouldDisplayDebug())
|
||||
{
|
||||
DrawLabel(obj.AttachedObject.transform, obj.ReturnLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -282,16 +332,24 @@ internal class DebugGUI : MonoBehaviour, IAddComponentOnStart
|
||||
|
||||
private static void DrawWorldObjectLines()
|
||||
{
|
||||
if (!QSBCore.DebugSettings.DrawLines)
|
||||
if (QSBCore.DebugSettings.DrawLines)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var obj in QSBWorldSync.GetWorldObjects())
|
||||
{
|
||||
if (obj.ShouldDisplayDebug())
|
||||
foreach (var obj in QSBWorldSync.GetWorldObjects())
|
||||
{
|
||||
obj.DisplayLines();
|
||||
if (obj.ShouldDisplayDebug())
|
||||
{
|
||||
obj.DisplayLines();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (QSBCore.DebugSettings.DrawGhostAI)
|
||||
{
|
||||
foreach (var obj in QSBWorldSync.GetWorldObjects<IGhostObject>())
|
||||
{
|
||||
if (obj.ShouldDisplayDebug())
|
||||
{
|
||||
obj.DisplayLines();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,4 +46,8 @@ public class DebugSettings
|
||||
[JsonProperty("greySkybox")]
|
||||
private bool _greySkybox;
|
||||
public bool GreySkybox => DebugMode && _greySkybox;
|
||||
|
||||
[JsonProperty("drawGhostAI")]
|
||||
private bool _drawGhostAI;
|
||||
public bool DrawGhostAI => DebugMode && _drawGhostAI;
|
||||
}
|
||||
|
@ -53,18 +53,11 @@ public static class QSBWorldSync
|
||||
|
||||
foreach (var manager in Managers)
|
||||
{
|
||||
if (manager.DlcOnly && !QSBCore.DLCInstalled)
|
||||
if (ShouldIgnoreManager(manager))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (manager.WorldObjectScene)
|
||||
{
|
||||
case WorldObjectScene.SolarSystem when QSBSceneManager.CurrentScene != OWScene.SolarSystem:
|
||||
case WorldObjectScene.Eye when QSBSceneManager.CurrentScene != OWScene.EyeOfTheUniverse:
|
||||
continue;
|
||||
}
|
||||
|
||||
var task = UniTask.Create(async () =>
|
||||
{
|
||||
await manager.Try("building world objects", async () =>
|
||||
@ -150,10 +143,32 @@ public static class QSBWorldSync
|
||||
|
||||
foreach (var manager in Managers)
|
||||
{
|
||||
if (ShouldIgnoreManager(manager))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
manager.Try("unbuilding world objects", manager.UnbuildWorldObjects);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldIgnoreManager(WorldObjectManager manager)
|
||||
{
|
||||
if (manager.DlcOnly && !QSBCore.DLCInstalled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (manager.WorldObjectScene)
|
||||
{
|
||||
case WorldObjectScene.SolarSystem when QSBSceneManager.CurrentScene != OWScene.SolarSystem:
|
||||
case WorldObjectScene.Eye when QSBSceneManager.CurrentScene != OWScene.EyeOfTheUniverse:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// =======================================================================================================
|
||||
|
||||
public static readonly List<CharacterDialogueTree> OldDialogueTrees = new();
|
||||
@ -176,7 +191,7 @@ public static class QSBWorldSync
|
||||
|
||||
private static void GameInit()
|
||||
{
|
||||
DebugLog.DebugWrite($"GameInit QSBWorldSync", MessageType.Info);
|
||||
DebugLog.DebugWrite("GameInit QSBWorldSync", MessageType.Info);
|
||||
|
||||
OldDialogueTrees.Clear();
|
||||
OldDialogueTrees.AddRange(GetUnityObjects<CharacterDialogueTree>().SortDeterministic());
|
||||
@ -195,7 +210,7 @@ public static class QSBWorldSync
|
||||
|
||||
private static void GameReset()
|
||||
{
|
||||
DebugLog.DebugWrite($"GameReset QSBWorldSync", MessageType.Info);
|
||||
DebugLog.DebugWrite("GameReset QSBWorldSync", MessageType.Info);
|
||||
|
||||
OldDialogueTrees.Clear();
|
||||
DialogueConditions.Clear();
|
||||
|
@ -10,5 +10,6 @@
|
||||
"drawLabels": false,
|
||||
"drawQuantumVisibilityObjects": false,
|
||||
"skipTitleScreen": false,
|
||||
"greySkybox": false
|
||||
"greySkybox": false,
|
||||
"drawGhostAI": false
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user