mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-01-29 00:32:43 +00:00
commit
c19536eaec
@ -16,7 +16,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Default Locations" Condition="!Exists('$(DevEnvLoc)')">
|
||||
<GameDir>C:\Program Files\Epic Games\OuterWilds</GameDir>
|
||||
<OwmlDir>$(AppData)\OuterWildsModManager\OWML</OwmlDir>
|
||||
<UnityAssetsDir>$(SolutionDir)\qsb-unityproject\Assets</UnityAssetsDir>
|
||||
</PropertyGroup>
|
||||
|
@ -39,6 +39,7 @@ public class AnimationSync : PlayerSyncObject
|
||||
/// <summary>
|
||||
/// This wipes the NetworkAnimator's fields, so it assumes the parameters have changed.
|
||||
/// Basically just forces it to set all its dirty flags.
|
||||
/// BUG: this doesnt work for other players because its only called by the host.
|
||||
/// </summary>
|
||||
private void SendInitialState(uint to) => NetworkAnimator.Invoke("Awake");
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
using UnityEngine;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ConversationSync;
|
||||
|
||||
[UsedInUnityProject]
|
||||
public class CameraFacingBillboard : MonoBehaviour
|
||||
{
|
||||
private OWCamera _activeCam;
|
||||
|
@ -3,7 +3,6 @@ using QSB.DeathSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
@ -14,96 +13,47 @@ public class DeathPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
/// <summary>
|
||||
/// don't take damage from impact in ship
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerResources), nameof(PlayerResources.OnImpact))]
|
||||
public static bool PlayerResources_OnImpact(PlayerResources __instance, ImpactData impact) =>
|
||||
// don't take damage from impact in ship
|
||||
!PlayerState.IsInsideShip();
|
||||
public static bool PlayerResources_OnImpact(PlayerResources __instance, ImpactData impact)
|
||||
{
|
||||
if (QSBCore.ShipDamage)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return !PlayerState.IsInsideShip();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// don't insta-die from impact in ship
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(HighSpeedImpactSensor), nameof(HighSpeedImpactSensor.FixedUpdate))]
|
||||
public static bool HighSpeedImpactSensor_FixedUpdate(HighSpeedImpactSensor __instance)
|
||||
[HarmonyPatch(typeof(HighSpeedImpactSensor), nameof(HighSpeedImpactSensor.HandlePlayerInsideShip))]
|
||||
public static bool HighSpeedImpactSensor_HandlePlayerInsideShip(HighSpeedImpactSensor __instance)
|
||||
{
|
||||
if (__instance._isPlayer && (PlayerState.IsAttached() || PlayerState.IsInsideShuttle() || PlayerState.UsingNomaiRemoteCamera()))
|
||||
if (QSBCore.ShipDamage)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._dieNextUpdate && !__instance._dead)
|
||||
var shipCenter = Locator.GetShipTransform().position + Locator.GetShipTransform().up * 2f;
|
||||
var distanceFromShip = Vector3.Distance(__instance._body.GetPosition(), shipCenter);
|
||||
if (distanceFromShip > 8f)
|
||||
{
|
||||
__instance._dead = true;
|
||||
__instance._dieNextUpdate = false;
|
||||
if (__instance.gameObject.CompareTag("Player"))
|
||||
{
|
||||
Locator.GetDeathManager().SetImpactDeathSpeed(__instance._impactSpeed);
|
||||
Locator.GetDeathManager().KillPlayer(DeathType.Impact);
|
||||
}
|
||||
else if (__instance.gameObject.CompareTag("Ship"))
|
||||
{
|
||||
__instance.GetComponent<ShipDamageController>().Explode();
|
||||
}
|
||||
__instance._body.SetPosition(shipCenter);
|
||||
}
|
||||
|
||||
if (__instance._isPlayer && PlayerState.IsInsideShip())
|
||||
if (!__instance._dead)
|
||||
{
|
||||
var shipCenter = Locator.GetShipTransform().position + Locator.GetShipTransform().up * 2f;
|
||||
var distanceFromShip = Vector3.Distance(__instance._body.GetPosition(), shipCenter);
|
||||
if (distanceFromShip > 8f)
|
||||
var a = __instance._body.GetVelocity() - Locator.GetShipBody().GetPointVelocity(__instance._body.GetPosition());
|
||||
if (a.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
__instance._body.SetPosition(shipCenter);
|
||||
}
|
||||
|
||||
if (!__instance._dead)
|
||||
{
|
||||
var a = __instance._body.GetVelocity() - Locator.GetShipBody().GetPointVelocity(__instance._body.GetPosition());
|
||||
if (a.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
__instance._impactSpeed = a.magnitude;
|
||||
__instance._body.AddVelocityChange(-a);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var passiveReferenceFrame = __instance._sectorDetector.GetPassiveReferenceFrame();
|
||||
if (!__instance._dead && passiveReferenceFrame != null)
|
||||
{
|
||||
var relativeVelocity = __instance._body.GetVelocity() - passiveReferenceFrame.GetOWRigidBody().GetPointVelocity(__instance._body.GetPosition());
|
||||
if (relativeVelocity.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
var hitCount = Physics.RaycastNonAlloc(__instance.transform.TransformPoint(__instance._localOffset), relativeVelocity, __instance._raycastHits, relativeVelocity.magnitude * Time.deltaTime + __instance._radius, OWLayerMask.physicalMask, QueryTriggerInteraction.Ignore);
|
||||
for (var i = 0; i < hitCount; i++)
|
||||
{
|
||||
if (__instance._raycastHits[i].rigidbody.mass > 10f && !__instance._raycastHits[i].rigidbody.Equals(__instance._body.GetRigidbody()))
|
||||
{
|
||||
var owRigidbody = __instance._raycastHits[i].rigidbody.GetComponent<OWRigidbody>();
|
||||
if (owRigidbody == null)
|
||||
{
|
||||
DebugLog.ToConsole("Rigidbody does not have attached OWRigidbody!!!", OWML.Common.MessageType.Error);
|
||||
Debug.Break();
|
||||
}
|
||||
else
|
||||
{
|
||||
relativeVelocity = __instance._body.GetVelocity() - owRigidbody.GetPointVelocity(__instance._body.GetPosition());
|
||||
var a2 = Vector3.Project(relativeVelocity, __instance._raycastHits[i].normal);
|
||||
if (a2.sqrMagnitude > __instance._sqrCheckSpeedThreshold)
|
||||
{
|
||||
__instance._body.AddVelocityChange(-a2);
|
||||
__instance._impactSpeed = a2.magnitude;
|
||||
if (!PlayerState.IsInsideTheEye())
|
||||
{
|
||||
__instance._dieNextUpdate = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
__instance._impactSpeed = a.magnitude;
|
||||
__instance._body.AddVelocityChange(-a);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync;
|
||||
|
||||
@ -11,18 +12,12 @@ public class AlarmTotemManager : WorldObjectManager
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
public override bool DlcOnly => true;
|
||||
|
||||
private QSBAlarmSequenceController _qsbAlarmSequenceController;
|
||||
public static AlarmBell[] AlarmBells;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
// QSBWorldSync.Init<QSBAlarmTotem, AlarmTotem>();
|
||||
QSBWorldSync.Init<QSBAlarmTotem, AlarmTotem>();
|
||||
QSBWorldSync.Init<QSBAlarmBell, AlarmBell>();
|
||||
|
||||
_qsbAlarmSequenceController = new GameObject(nameof(QSBAlarmSequenceController))
|
||||
.AddComponent<QSBAlarmSequenceController>();
|
||||
DontDestroyOnLoad(_qsbAlarmSequenceController.gameObject);
|
||||
AlarmBells = QSBWorldSync.GetUnityObjects<AlarmBell>().Where(x => x._lightController).SortDeterministic().ToArray();
|
||||
}
|
||||
|
||||
public override void UnbuildWorldObjects() =>
|
||||
Destroy(_qsbAlarmSequenceController.gameObject);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages;
|
||||
|
||||
public class SetVisibleMessage : QSBWorldObjectMessage<QSBAlarmTotem, bool>
|
||||
{
|
||||
public SetVisibleMessage(bool visible) : base(visible) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
if (WorldObject.AttachedObject._isPlayerVisible == Data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WorldObject.AttachedObject._isPlayerVisible = Data;
|
||||
if (Data)
|
||||
{
|
||||
Locator.GetAlarmSequenceController().IncreaseAlarmCounter();
|
||||
WorldObject.AttachedObject._secondsConcealed = 0f;
|
||||
WorldObject.AttachedObject._simTotemMaterials[0] = WorldObject.AttachedObject._simAlarmMaterial;
|
||||
WorldObject.AttachedObject._simTotemRenderer.sharedMaterials = WorldObject.AttachedObject._simTotemMaterials;
|
||||
WorldObject.AttachedObject._simVisionConeRenderer.SetColor(WorldObject.AttachedObject._simAlarmColor);
|
||||
GlobalMessenger.FireEvent("AlarmTotemTriggered");
|
||||
}
|
||||
else
|
||||
{
|
||||
Locator.GetAlarmSequenceController().DecreaseAlarmCounter();
|
||||
WorldObject.AttachedObject._secondsConcealed = 0f;
|
||||
WorldObject.AttachedObject._simTotemMaterials[0] = WorldObject.AttachedObject._origSimEyeMaterial;
|
||||
WorldObject.AttachedObject._simTotemRenderer.sharedMaterials = WorldObject.AttachedObject._simTotemMaterials;
|
||||
WorldObject.AttachedObject._simVisionConeRenderer.SetColor(WorldObject.AttachedObject._simVisionConeRenderer.GetOriginalColor());
|
||||
WorldObject.AttachedObject._pulseLightController.FadeTo(0f, 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages;
|
||||
|
||||
public class TotemEnabledMessage : QSBWorldObjectMessage<QSBAlarmTotem, bool>
|
||||
{
|
||||
public TotemEnabledMessage(bool enabled) : base(enabled) { }
|
||||
|
||||
public override void OnReceiveRemote() =>
|
||||
WorldObject.SetEnabled(Data);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages;
|
||||
|
||||
public class TotemVisibleForMessage : QSBWorldObjectMessage<QSBAlarmTotem, List<uint>>
|
||||
{
|
||||
public TotemVisibleForMessage(List<uint> visibleFor) : base(visibleFor) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
WorldObject.VisibleFor.Clear();
|
||||
WorldObject.VisibleFor.AddRange(Data);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync.Messages;
|
||||
|
||||
public class TotemVisibleMessage : QSBWorldObjectMessage<QSBAlarmTotem, bool>
|
||||
{
|
||||
public TotemVisibleMessage(bool visible) : base(visible) { }
|
||||
public override void OnReceiveLocal() => OnReceiveRemote();
|
||||
public override void OnReceiveRemote() => WorldObject.SetVisible(From, Data);
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
using HarmonyLib;
|
||||
using QSB.Patches;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(AlarmSequenceController))]
|
||||
public class AlarmSequenceControllerPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(AlarmSequenceController.IncreaseAlarmCounter))]
|
||||
private static bool IncreaseAlarmCounter(AlarmSequenceController __instance)
|
||||
{
|
||||
__instance._alarmCounter++;
|
||||
if (__instance._alarmCounter == 1)
|
||||
{
|
||||
__instance.PlayChimes();
|
||||
}
|
||||
__instance.enabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(AlarmSequenceController.DecreaseAlarmCounter))]
|
||||
private static bool DecreaseAlarmCounter(AlarmSequenceController __instance)
|
||||
{
|
||||
__instance._alarmCounter--;
|
||||
if (__instance._alarmCounter < 0)
|
||||
{
|
||||
__instance._alarmCounter = 0;
|
||||
Debug.LogError("Something went wrong, alarm counter should never drop below zero!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(AlarmSequenceController.StopChimes))]
|
||||
private static bool StopChimes(AlarmSequenceController __instance)
|
||||
{
|
||||
__instance._playing = false;
|
||||
__instance._stopRequested = false;
|
||||
__instance._animationStarted = false;
|
||||
foreach (var alarmBell in AlarmTotemManager.AlarmBells)
|
||||
{
|
||||
alarmBell.StopAnimation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(AlarmSequenceController.Update))]
|
||||
private static bool Update(AlarmSequenceController __instance)
|
||||
{
|
||||
__instance.UpdateWakeFraction();
|
||||
if (__instance._playing)
|
||||
{
|
||||
__instance.UpdateChimes();
|
||||
}
|
||||
__instance.UpdatePulse();
|
||||
if (!__instance._playing && __instance._alarmCounter == 0 && __instance._pulse <= 0.01f)
|
||||
{
|
||||
__instance._pulse = 0f;
|
||||
__instance._targetPulse = 0f;
|
||||
__instance.enabled = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(AlarmSequenceController.PlaySingleChime))]
|
||||
private static bool PlaySingleChime(AlarmSequenceController __instance)
|
||||
{
|
||||
foreach (var alarmBell in AlarmTotemManager.AlarmBells)
|
||||
{
|
||||
alarmBell.PlaySingleChime(__instance._chimeIndex);
|
||||
}
|
||||
if (!__instance._animationStarted && !__instance._dreamWorldController.IsInDream())
|
||||
{
|
||||
foreach (var alarmBell in AlarmTotemManager.AlarmBells)
|
||||
{
|
||||
alarmBell.PlayAnimation();
|
||||
}
|
||||
__instance._animationStarted = true;
|
||||
}
|
||||
if (__instance._dreamWorldController.IsInDream() && !__instance._dreamWorldController.IsExitingDream())
|
||||
{
|
||||
Locator.GetDreamWorldAudioController().PlaySingleAlarmChime(__instance._chimeIndex, __instance._volumeCurve.Evaluate(__instance._wakeFraction));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
using HarmonyLib;
|
||||
using QSB.AuthoritySync;
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.Messages;
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
@ -12,27 +15,58 @@ public class AlarmTotemPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
/*
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AlarmTotem), nameof(AlarmTotem.OnSectorOccupantAdded))]
|
||||
private static void OnSectorOccupantAdded(AlarmTotem __instance, SectorDetector sectorDetector)
|
||||
private static bool OnSectorOccupantAdded(AlarmTotem __instance, SectorDetector sectorDetector)
|
||||
{
|
||||
if (sectorDetector.GetOccupantType() == DynamicOccupant.Player && QSBWorldSync.AllObjectsReady)
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
__instance.GetWorldObject<QSBAlarmTotem>()
|
||||
.SendMessage(new TotemEnabledMessage(true));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sectorDetector.GetOccupantType() == DynamicOccupant.Player)
|
||||
{
|
||||
__instance.enabled = true;
|
||||
var qsbAlarmTotem = __instance.GetWorldObject<QSBAlarmTotem>();
|
||||
qsbAlarmTotem.RequestOwnership();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AlarmTotem), nameof(AlarmTotem.OnSectorOccupantRemoved))]
|
||||
private static void OnSectorOccupantRemoved(AlarmTotem __instance, SectorDetector sectorDetector)
|
||||
private static bool OnSectorOccupantRemoved(AlarmTotem __instance, SectorDetector sectorDetector)
|
||||
{
|
||||
if (sectorDetector.GetOccupantType() == DynamicOccupant.Player && QSBWorldSync.AllObjectsReady)
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
__instance.GetWorldObject<QSBAlarmTotem>()
|
||||
.SendMessage(new TotemEnabledMessage(false));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sectorDetector.GetOccupantType() == DynamicOccupant.Player)
|
||||
{
|
||||
__instance.enabled = false;
|
||||
var qsbAlarmTotem = __instance.GetWorldObject<QSBAlarmTotem>();
|
||||
qsbAlarmTotem.ReleaseOwnership();
|
||||
Delay.RunFramesLater(10, () =>
|
||||
{
|
||||
// no one else took ownership, so we can safely turn stuff off
|
||||
// ie turn off when no one else is there
|
||||
if (qsbAlarmTotem.Owner == 0)
|
||||
{
|
||||
__instance._pulseLightController.SetIntensity(0f);
|
||||
__instance._simTotemMaterials[0] = __instance._origSimEyeMaterial;
|
||||
__instance._simTotemRenderer.sharedMaterials = __instance._simTotemMaterials;
|
||||
__instance._simVisionConeRenderer.SetColor(__instance._simVisionConeRenderer.GetOriginalColor());
|
||||
if (__instance._isPlayerVisible)
|
||||
{
|
||||
__instance._isPlayerVisible = false;
|
||||
__instance._secondsConcealed = 0f;
|
||||
Locator.GetAlarmSequenceController().DecreaseAlarmCounter();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
@ -43,45 +77,87 @@ public class AlarmTotemPatches : QSBPatch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var qsbAlarmTotem = __instance.GetWorldObject<QSBAlarmTotem>();
|
||||
|
||||
var isLocallyVisible = qsbAlarmTotem.IsLocallyVisible;
|
||||
qsbAlarmTotem.IsLocallyVisible = __instance.CheckPlayerVisible();
|
||||
if (qsbAlarmTotem.IsLocallyVisible && !isLocallyVisible)
|
||||
if (qsbAlarmTotem.Owner != QSBPlayerManager.LocalPlayerId)
|
||||
{
|
||||
qsbAlarmTotem.SendMessage(new TotemVisibleMessage(true));
|
||||
}
|
||||
else if (isLocallyVisible && !qsbAlarmTotem.IsLocallyVisible)
|
||||
{
|
||||
qsbAlarmTotem.SendMessage(new TotemVisibleMessage(false));
|
||||
return false;
|
||||
}
|
||||
|
||||
var isPlayerVisible = __instance._isPlayerVisible;
|
||||
__instance._isPlayerVisible = qsbAlarmTotem.VisibleFor.Count > 0;
|
||||
if (__instance._isPlayerVisible && !isPlayerVisible)
|
||||
__instance._isPlayerVisible = __instance.CheckPlayerVisible();
|
||||
if (!isPlayerVisible && __instance._isPlayerVisible)
|
||||
{
|
||||
Locator.GetAlarmSequenceController().IncreaseAlarmCounter();
|
||||
__instance._secondsConcealed = 0f;
|
||||
__instance._simTotemMaterials[0] = __instance._simAlarmMaterial;
|
||||
__instance._simTotemRenderer.sharedMaterials = __instance._simTotemMaterials;
|
||||
__instance._simVisionConeRenderer.SetColor(__instance._simAlarmColor);
|
||||
if (__instance._isTutorialTotem)
|
||||
{
|
||||
GlobalMessenger.FireEvent("TutorialAlarmTotemTriggered");
|
||||
}
|
||||
GlobalMessenger.FireEvent("AlarmTotemTriggered");
|
||||
qsbAlarmTotem.SendMessage(new SetVisibleMessage(true));
|
||||
}
|
||||
else if (isPlayerVisible && !__instance._isPlayerVisible)
|
||||
{
|
||||
Locator.GetAlarmSequenceController().DecreaseAlarmCounter();
|
||||
__instance._secondsConcealed = 0f;
|
||||
__instance._simTotemMaterials[0] = __instance._origSimEyeMaterial;
|
||||
__instance._simTotemRenderer.sharedMaterials = __instance._simTotemMaterials;
|
||||
__instance._simVisionConeRenderer.SetColor(__instance._simVisionConeRenderer.GetOriginalColor());
|
||||
__instance._pulseLightController.FadeTo(0f, 0.5f);
|
||||
qsbAlarmTotem.SendMessage(new SetVisibleMessage(false));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AlarmTotem), nameof(AlarmTotem.CheckPlayerVisible))]
|
||||
private static bool CheckPlayerVisible(AlarmTotem __instance, out bool __result)
|
||||
{
|
||||
if (!__instance._isFaceOpen)
|
||||
{
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
foreach (var player in QSBPlayerManager.PlayerList)
|
||||
{
|
||||
var position = player.Camera.transform.position;
|
||||
if (__instance.CheckPointInVisionCone(position) && !__instance.CheckLineOccluded(__instance._sightOrigin.position, position))
|
||||
{
|
||||
if (player.LightSensor.IsIlluminated())
|
||||
{
|
||||
__result = true;
|
||||
return false;
|
||||
}
|
||||
if (player.AssignedSimulationLantern == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var lanternController = player.AssignedSimulationLantern.AttachedObject.GetLanternController();
|
||||
if (lanternController.IsHeldByPlayer())
|
||||
{
|
||||
if (lanternController.IsConcealed())
|
||||
{
|
||||
if (!__instance._hasConcealedFromAlarm)
|
||||
{
|
||||
__instance._secondsConcealed += Time.deltaTime;
|
||||
if (__instance._secondsConcealed > 1f)
|
||||
{
|
||||
__instance._hasConcealedFromAlarm = true;
|
||||
GlobalMessenger.FireEvent("ConcealFromAlarmTotem");
|
||||
}
|
||||
}
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
__result = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(AlarmBell), nameof(AlarmBell.OnEntry))]
|
||||
|
@ -1,245 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync;
|
||||
|
||||
/// <summary>
|
||||
/// copied and modified from base game
|
||||
/// </summary>
|
||||
public class QSBAlarmSequenceController : MonoBehaviour
|
||||
{
|
||||
private const int CHIME_COUNT = 3;
|
||||
|
||||
[SerializeField]
|
||||
private float _wakeDuration = 5f;
|
||||
|
||||
[SerializeField]
|
||||
private float _recoveryDuration = 2f;
|
||||
|
||||
[Header("Audio")]
|
||||
[SerializeField]
|
||||
private float _audioLingerTime = 3f;
|
||||
|
||||
[SerializeField]
|
||||
private AnimationCurve _volumeCurve;
|
||||
|
||||
[Header("Pulse")]
|
||||
[SerializeField]
|
||||
private float _pulseAttackLength = 0.2f;
|
||||
|
||||
[SerializeField]
|
||||
private float _pulseHoldLength = 0.1f;
|
||||
|
||||
[SerializeField]
|
||||
private float _pulseCooldownLength = 1f;
|
||||
|
||||
private DreamWorldController _dreamWorldController;
|
||||
|
||||
private AlarmBell _activeBell;
|
||||
|
||||
private int _alarmCounter;
|
||||
|
||||
private float _wakeFraction;
|
||||
|
||||
private float _targetPulse;
|
||||
|
||||
private float _pulse;
|
||||
|
||||
private bool _playing;
|
||||
|
||||
private bool _animationStarted;
|
||||
|
||||
private bool _stopRequested;
|
||||
|
||||
private float _stopTime;
|
||||
|
||||
private int _chimeIndex;
|
||||
|
||||
private float _lastChimeTime;
|
||||
|
||||
private float _chimeInterval;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Locator.RegisterAlarmSequenceController(this);
|
||||
GlobalMessenger.AddListener("ExitDreamWorld", OnExitDreamWorld);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_dreamWorldController = Locator.GetDreamWorldController();
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
GlobalMessenger.RemoveListener("ExitDreamWorld", OnExitDreamWorld);
|
||||
}
|
||||
|
||||
public void RegisterDreamEyeMaskController(DreamEyeMaskController dreamEyeMaskController) { }
|
||||
|
||||
public float GetPulseIntensity() => _pulse;
|
||||
|
||||
public bool IsAlarmWakingPlayer() => _alarmCounter > 0 && !PlayerState.IsResurrected();
|
||||
|
||||
public void IncreaseAlarmCounter()
|
||||
{
|
||||
if (!_dreamWorldController.IsInDream() || _dreamWorldController.IsExitingDream())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_alarmCounter++;
|
||||
if (_alarmCounter == 1)
|
||||
{
|
||||
PlayChimes();
|
||||
}
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public void DecreaseAlarmCounter()
|
||||
{
|
||||
if (!_dreamWorldController.IsInDream() || _dreamWorldController.IsExitingDream())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_alarmCounter--;
|
||||
if (_alarmCounter < 0)
|
||||
{
|
||||
_alarmCounter = 0;
|
||||
Debug.LogError("Something went wrong, alarm counter should never drop below zero!");
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayChimes()
|
||||
{
|
||||
if (Locator.GetDreamWorldController().GetDreamCampfire() != null)
|
||||
{
|
||||
_activeBell = Locator.GetDreamWorldController().GetDreamCampfire().GetAlarmBell();
|
||||
}
|
||||
|
||||
_playing = true;
|
||||
_chimeInterval = 1f;
|
||||
_lastChimeTime = 0f;
|
||||
_stopRequested = false;
|
||||
}
|
||||
|
||||
private void StopChimes()
|
||||
{
|
||||
_playing = false;
|
||||
_stopRequested = false;
|
||||
_animationStarted = false;
|
||||
if (_activeBell != null)
|
||||
{
|
||||
_activeBell.StopAnimation();
|
||||
_activeBell = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_dreamWorldController.IsInDream() && !_dreamWorldController.IsExitingDream())
|
||||
{
|
||||
UpdateWakeFraction();
|
||||
}
|
||||
|
||||
if (_playing)
|
||||
{
|
||||
UpdateChimes();
|
||||
}
|
||||
|
||||
UpdatePulse();
|
||||
if (!_playing && _alarmCounter == 0 && _pulse <= 0.01f)
|
||||
{
|
||||
_pulse = 0f;
|
||||
_targetPulse = 0f;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateWakeFraction()
|
||||
{
|
||||
if (_alarmCounter > 0)
|
||||
{
|
||||
_wakeFraction = Mathf.MoveTowards(_wakeFraction, 1f, Time.deltaTime / _wakeDuration);
|
||||
if (_wakeFraction >= 1f && !PlayerState.IsResurrected())
|
||||
{
|
||||
_dreamWorldController.ExitDreamWorld(DreamWakeType.Alarm);
|
||||
}
|
||||
}
|
||||
else if (_wakeFraction > 0f)
|
||||
{
|
||||
_wakeFraction = Mathf.MoveTowards(_wakeFraction, 0f, Time.deltaTime / _recoveryDuration);
|
||||
if (_wakeFraction <= 0f)
|
||||
{
|
||||
StopChimes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateChimes()
|
||||
{
|
||||
if (Time.time > _lastChimeTime + _chimeInterval)
|
||||
{
|
||||
if (!PlayerState.IsResurrected())
|
||||
{
|
||||
PlaySingleChime();
|
||||
}
|
||||
|
||||
_targetPulse = 1f;
|
||||
_lastChimeTime = Time.time;
|
||||
_chimeInterval = Mathf.Max(_chimeInterval - 0.08f, 0.4f);
|
||||
_chimeIndex++;
|
||||
_chimeIndex = _chimeIndex >= CHIME_COUNT ? 0 : _chimeIndex;
|
||||
}
|
||||
|
||||
if (_stopRequested && Time.time > _stopTime)
|
||||
{
|
||||
StopChimes();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePulse()
|
||||
{
|
||||
if (Time.time > _lastChimeTime + _pulseAttackLength + _pulseHoldLength)
|
||||
{
|
||||
var num = _pulseCooldownLength * _chimeInterval;
|
||||
_targetPulse = Mathf.MoveTowards(_targetPulse, 0f, 1f / num * Time.deltaTime);
|
||||
_pulse = _targetPulse;
|
||||
return;
|
||||
}
|
||||
|
||||
_pulse = Mathf.MoveTowards(_pulse, _targetPulse, 1f / _pulseAttackLength * Time.deltaTime);
|
||||
}
|
||||
|
||||
private void PlaySingleChime()
|
||||
{
|
||||
if (_activeBell != null)
|
||||
{
|
||||
_activeBell.PlaySingleChime(_chimeIndex);
|
||||
if (!_animationStarted && !_dreamWorldController.IsInDream())
|
||||
{
|
||||
_activeBell.PlayAnimation();
|
||||
_animationStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_dreamWorldController.IsInDream() && !_dreamWorldController.IsExitingDream())
|
||||
{
|
||||
Locator.GetDreamWorldAudioController().PlaySingleAlarmChime(_chimeIndex, _volumeCurve.Evaluate(_wakeFraction));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExitDreamWorld()
|
||||
{
|
||||
if (_playing)
|
||||
{
|
||||
_stopRequested = true;
|
||||
_stopTime = Time.time + _audioLingerTime;
|
||||
}
|
||||
|
||||
_alarmCounter = 0;
|
||||
_wakeFraction = 0f;
|
||||
}
|
||||
}
|
@ -1,83 +1,17 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.AuthoritySync;
|
||||
using QSB.EchoesOfTheEye.AlarmTotemSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: make this not NRE (by not doing enable sync) and then readd it back in
|
||||
/// </summary>
|
||||
public class QSBAlarmTotem : WorldObject<AlarmTotem>
|
||||
public class QSBAlarmTotem : AuthWorldObject<AlarmTotem>
|
||||
{
|
||||
public readonly List<uint> VisibleFor = new();
|
||||
public bool IsLocallyVisible;
|
||||
public override bool CanOwn => AttachedObject.enabled;
|
||||
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
this.SendMessage(new TotemEnabledMessage(AttachedObject.enabled) { To = to });
|
||||
this.SendMessage(new TotemVisibleForMessage(VisibleFor) { To = to });
|
||||
}
|
||||
base.SendInitialState(to);
|
||||
|
||||
public override async UniTask Init(CancellationToken ct) =>
|
||||
QSBPlayerManager.OnRemovePlayer += OnPlayerLeave;
|
||||
|
||||
public override void OnRemoval() =>
|
||||
QSBPlayerManager.OnRemovePlayer -= OnPlayerLeave;
|
||||
|
||||
private void OnPlayerLeave(PlayerInfo player) =>
|
||||
VisibleFor.QuickRemove(player.PlayerId);
|
||||
|
||||
public void SetVisible(uint playerId, bool visible)
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
VisibleFor.SafeAdd(playerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisibleFor.QuickRemove(playerId);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEnabled(bool enabled)
|
||||
{
|
||||
if (AttachedObject.enabled == enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled &&
|
||||
AttachedObject._sector &&
|
||||
AttachedObject._sector.ContainsOccupant(DynamicOccupant.Player))
|
||||
{
|
||||
// local player is in sector, do not disable
|
||||
return;
|
||||
}
|
||||
|
||||
AttachedObject.enabled = enabled;
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
AttachedObject._simTotemMaterials[0] = AttachedObject._origSimEyeMaterial;
|
||||
AttachedObject._simTotemRenderer.sharedMaterials = AttachedObject._simTotemMaterials;
|
||||
AttachedObject._simVisionConeRenderer.SetColor(AttachedObject._simVisionConeRenderer.GetOriginalColor());
|
||||
AttachedObject._pulseLightController.SetIntensity(0f);
|
||||
/*
|
||||
if (AttachedObject._isPlayerVisible)
|
||||
{
|
||||
AttachedObject._isPlayerVisible = false;
|
||||
Locator.GetAlarmSequenceController().DecreaseAlarmCounter();
|
||||
}
|
||||
*/
|
||||
if (IsLocallyVisible)
|
||||
{
|
||||
IsLocallyVisible = false;
|
||||
this.SendMessage(new TotemVisibleMessage(false));
|
||||
}
|
||||
}
|
||||
this.SendMessage(new SetVisibleMessage(AttachedObject._isPlayerVisible) { To = to });
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ public class DreamLanternManager : WorldObjectManager
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
public override bool DlcOnly => true;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) =>
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct)
|
||||
{
|
||||
QSBWorldSync.Init<QSBDreamLanternController, DreamLanternController>();
|
||||
QSBWorldSync.Init<QSBDreamLanternItem, DreamLanternItem>();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ItemSync.WorldObjects.Items;
|
||||
namespace QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
|
||||
public class QSBDreamLanternItem : QSBItem<DreamLanternItem>
|
||||
{
|
114
QSB/EchoesOfTheEye/DreamWorld/Messages/DreamWorldFakePlayer.cs
Normal file
114
QSB/EchoesOfTheEye/DreamWorld/Messages/DreamWorldFakePlayer.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using HarmonyLib;
|
||||
using QSB.Animation.Player;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.DreamWorld.Messages;
|
||||
|
||||
public class DreamWorldFakePlayer : MonoBehaviour
|
||||
{
|
||||
private static readonly List<DreamWorldFakePlayer> _instances = new();
|
||||
|
||||
public static void Create(PlayerInfo player)
|
||||
{
|
||||
var go = new GameObject($"player {player} DreamWorldFakePlayer");
|
||||
go.SetActive(false);
|
||||
go.AddComponent<DreamWorldFakePlayer>()._player = player;
|
||||
go.SetActive(true);
|
||||
}
|
||||
|
||||
public static void Destroy(PlayerInfo player)
|
||||
{
|
||||
foreach (var dreamWorldFakePlayer in _instances)
|
||||
{
|
||||
if (dreamWorldFakePlayer._player == player)
|
||||
{
|
||||
Destroy(dreamWorldFakePlayer.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PlayerInfo _player;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_instances.SafeAdd(this);
|
||||
QSBPlayerManager.OnRemovePlayer += OnRemovePlayer;
|
||||
|
||||
transform.parent = _player.TransformSync.ReferenceTransform;
|
||||
transform.localPosition = _player.TransformSync.transform.position;
|
||||
transform.localRotation = _player.TransformSync.transform.rotation;
|
||||
|
||||
#region fake player
|
||||
|
||||
var fakePlayer = _player.Body.transform.Find("REMOTE_Traveller_HEA_Player_v2").gameObject.InstantiateInactive();
|
||||
fakePlayer.transform.SetParent(transform, false);
|
||||
|
||||
Destroy(fakePlayer.GetComponent<Animator>());
|
||||
Destroy(fakePlayer.GetComponent<PlayerHeadRotationSync>());
|
||||
|
||||
var REMOTE_ItemCarryTool = fakePlayer.transform.Find(
|
||||
// TODO : kill me for my sins
|
||||
"Traveller_Rig_v01:Traveller_Trajectory_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_ROOT_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_Spine_01_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_Spine_02_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_Spine_Top_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_RT_Arm_Clavicle_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_RT_Arm_Shoulder_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_RT_Arm_Elbow_Jnt/" +
|
||||
"Traveller_Rig_v01:Traveller_RT_Arm_Wrist_Jnt/" +
|
||||
"REMOTE_ItemCarryTool"
|
||||
).gameObject;
|
||||
Destroy(REMOTE_ItemCarryTool);
|
||||
|
||||
fakePlayer.SetActive(true);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_instances.QuickRemove(this);
|
||||
QSBPlayerManager.OnRemovePlayer -= OnRemovePlayer;
|
||||
}
|
||||
|
||||
private void OnRemovePlayer(PlayerInfo player)
|
||||
{
|
||||
if (player != _player)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public class DreamWorldFakePlayerPatch : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
/// <summary>
|
||||
/// do this early to create the fake player BEFORE teleporting
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(DreamWorldController), nameof(DreamWorldController.EnterDreamWorld))]
|
||||
[HarmonyPrefix]
|
||||
private static void EnterDreamWorld()
|
||||
{
|
||||
if (Locator.GetToolModeSwapper().GetItemCarryTool().GetHeldItemType() == ItemType.DreamLantern)
|
||||
{
|
||||
new DreamWorldFakePlayerMessage().Send();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DreamWorldFakePlayerMessage : QSBMessage
|
||||
{
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
DreamWorldFakePlayer.Create(QSBPlayerManager.GetPlayer(From));
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Player.TransformSync;
|
||||
|
@ -50,5 +50,8 @@ internal class ExitDreamWorldMessage : QSBMessage
|
||||
ghost.GetEffects().OnSectorOccupantsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
Locator.GetAlarmSequenceController().OnExitDreamWorld();
|
||||
DreamWorldFakePlayer.Destroy(player);
|
||||
}
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ namespace QSB.EchoesOfTheEye.RaftSync.Messages;
|
||||
|
||||
public class RaftDockOnPressInteractMessage : QSBWorldObjectMessage<QSBRaftDock>
|
||||
{
|
||||
public override void OnReceiveRemote() => WorldObject.OnPressInteract();
|
||||
public override void OnReceiveRemote() => WorldObject.AttachedObject.OnPressInteract();
|
||||
}
|
||||
|
@ -6,6 +6,4 @@ namespace QSB.EchoesOfTheEye.RaftSync.WorldObjects;
|
||||
public class QSBRaftDock : WorldObject<RaftDock>, IQSBDropTarget
|
||||
{
|
||||
IItemDropTarget IQSBDropTarget.AttachedObject => AttachedObject;
|
||||
|
||||
public void OnPressInteract() => AttachedObject.OnPressInteract();
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ public class QSBSarcophagus : WorldObject<SarcophagusController>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
if (AttachedObject._isOpen)
|
||||
if (AttachedObject._isOpen || AttachedObject._isSlightlyOpen)
|
||||
{
|
||||
this.SendMessage(new OpenMessage());
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
using QSB.EchoesOfTheEye.VisionTorch.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.VisionTorch.Messages;
|
||||
|
||||
public class VisionTorchProjectMessage : QSBWorldObjectMessage<QSBVisionTorchItem, bool>
|
||||
{
|
||||
public VisionTorchProjectMessage(bool projecting) : base(projecting) { }
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
WorldObject.AttachedObject._isProjecting = Data;
|
||||
WorldObject.AttachedObject._mindProjectorTrigger.SetProjectorActive(Data);
|
||||
// prevent the torch from actually doing things remotely
|
||||
WorldObject.AttachedObject.mindProjectorTrigger._triggerVolume.SetTriggerActivation(false);
|
||||
}
|
||||
}
|
45
QSB/EchoesOfTheEye/VisionTorch/Patches/VisionTorchPatches.cs
Normal file
45
QSB/EchoesOfTheEye/VisionTorch/Patches/VisionTorchPatches.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using HarmonyLib;
|
||||
using QSB.EchoesOfTheEye.VisionTorch.Messages;
|
||||
using QSB.EchoesOfTheEye.VisionTorch.WorldObjects;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.WorldSync;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.VisionTorch.Patches;
|
||||
|
||||
public class VisionTorchPatches : QSBPatch
|
||||
{
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(VisionTorchItem), nameof(VisionTorchItem.Update))]
|
||||
private static bool Update(VisionTorchItem __instance)
|
||||
{
|
||||
if (!QSBWorldSync.AllObjectsReady)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PlayerState.IsViewingProjector() && __instance._mindSlideProjector.mindSlideCollection.slideCollectionContainer.slideIndex == 1)
|
||||
{
|
||||
OWInput.ChangeInputMode(InputMode.None);
|
||||
__instance._mindSlideProjector.OnProjectionComplete += __instance.OnProjectionComplete;
|
||||
__instance.enabled = false;
|
||||
return false;
|
||||
}
|
||||
__instance._wasProjecting = __instance._isProjecting;
|
||||
__instance._isProjecting = OWInput.IsPressed(InputLibrary.toolActionPrimary, InputMode.Character);
|
||||
if (__instance._isProjecting && !__instance._wasProjecting)
|
||||
{
|
||||
__instance._mindProjectorTrigger.SetProjectorActive(true);
|
||||
__instance.GetWorldObject<QSBVisionTorchItem>().SendMessage(new VisionTorchProjectMessage(true));
|
||||
}
|
||||
else if (!__instance._isProjecting && __instance._wasProjecting)
|
||||
{
|
||||
__instance._mindProjectorTrigger.SetProjectorActive(false);
|
||||
__instance.GetWorldObject<QSBVisionTorchItem>().SendMessage(new VisionTorchProjectMessage(false));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
15
QSB/EchoesOfTheEye/VisionTorch/VisionTorchManager.cs
Normal file
15
QSB/EchoesOfTheEye/VisionTorch/VisionTorchManager.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using QSB.EchoesOfTheEye.VisionTorch.WorldObjects;
|
||||
using QSB.WorldSync;
|
||||
using System.Threading;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.VisionTorch;
|
||||
|
||||
public class VisionTorchManager : WorldObjectManager
|
||||
{
|
||||
public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem;
|
||||
public override bool DlcOnly => true;
|
||||
|
||||
public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) =>
|
||||
QSBWorldSync.Init<QSBVisionTorchItem, VisionTorchItem>();
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using QSB.EchoesOfTheEye.VisionTorch.Messages;
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.EchoesOfTheEye.VisionTorch.WorldObjects;
|
||||
|
||||
public class QSBVisionTorchItem : QSBItem<VisionTorchItem>
|
||||
{
|
||||
public override void SendInitialState(uint to)
|
||||
{
|
||||
this.SendMessage(new VisionTorchProjectMessage(AttachedObject._isProjecting) { To = to });
|
||||
}
|
||||
}
|
@ -4,9 +4,6 @@ using UnityEngine;
|
||||
|
||||
namespace QSB;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: TEST THIS. see if things horribly break. this could be huge.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(OWExtensions))]
|
||||
public class GetAttachedOWRigidbodyPatch : QSBPatch
|
||||
{
|
||||
|
@ -9,7 +9,23 @@ internal class InputPatches : QSBPatch
|
||||
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(OWInput), nameof(OWInput.Update))]
|
||||
public static bool OWInput_Update()
|
||||
=> QSBInputManager.Instance.InputsEnabled;
|
||||
[HarmonyPatch(typeof(AbstractCommands), nameof(AbstractCommands.Update))]
|
||||
public static bool AbstractCommands_Update(AbstractCommands __instance)
|
||||
{
|
||||
if (QSBInputManager.Instance.InputsEnabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.Consumed = false;
|
||||
__instance.WasActiveLastFrame = __instance.IsActiveThisFrame;
|
||||
__instance.IsActiveThisFrame = false;
|
||||
|
||||
if (__instance.WasActiveLastFrame)
|
||||
{
|
||||
__instance.InputStartedTime = float.MaxValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.ItemSync.WorldObjects.Sockets;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
@ -21,14 +20,13 @@ internal class ItemManager : WorldObjectManager
|
||||
DebugLog.DebugWrite("Building OWItems...", MessageType.Info);
|
||||
|
||||
// Items
|
||||
QSBWorldSync.Init<QSBDreamLanternItem, DreamLanternItem>();
|
||||
QSBWorldSync.Init<QSBNomaiConversationStone, NomaiConversationStone>();
|
||||
QSBWorldSync.Init<QSBScrollItem, ScrollItem>();
|
||||
QSBWorldSync.Init<QSBSharedStone, SharedStone>();
|
||||
QSBWorldSync.Init<QSBSimpleLanternItem, SimpleLanternItem>();
|
||||
QSBWorldSync.Init<QSBSlideReelItem, SlideReelItem>();
|
||||
QSBWorldSync.Init<QSBVisionTorchItem, VisionTorchItem>();
|
||||
QSBWorldSync.Init<QSBWarpCoreItem, WarpCoreItem>();
|
||||
// dream lantern and vision torch are set up in their own managers
|
||||
|
||||
// Sockets
|
||||
QSBWorldSync.Init<QSBItemSocket, OWItemSocket>();
|
||||
|
@ -149,6 +149,12 @@ internal class ItemRemotePatches : QSBPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._isProjecting)
|
||||
{
|
||||
__instance._mindProjectorTrigger.SetProjectorActive(false);
|
||||
__instance._isProjecting = false;
|
||||
}
|
||||
|
||||
// if (Locator.GetDreamWorldController().IsInDream())
|
||||
// {
|
||||
base_DropItem(__instance, position, normal, parent, sector, customDropTarget);
|
||||
@ -213,6 +219,12 @@ internal class ItemRemotePatches : QSBPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance._isProjecting)
|
||||
{
|
||||
__instance._mindProjectorTrigger.SetProjectorActive(false);
|
||||
__instance._isProjecting = false;
|
||||
}
|
||||
|
||||
base_SocketItem(__instance, socketTransform, sector);
|
||||
if (__instance._visionBeam != null)
|
||||
{
|
||||
|
@ -1,6 +0,0 @@
|
||||
namespace QSB.ItemSync.WorldObjects.Items;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: SYNC THIS SHIT LMAOOOOOO
|
||||
/// </summary>
|
||||
internal class QSBVisionTorchItem : QSBItem<VisionTorchItem> { }
|
@ -2,7 +2,6 @@
|
||||
using QSB.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -82,28 +81,4 @@ public static class QSBLocalization
|
||||
Current = newTranslation;
|
||||
LanguageChanged?.Invoke();
|
||||
}
|
||||
|
||||
public static CultureInfo CultureInfo
|
||||
=> Current.Language switch
|
||||
{
|
||||
/*
|
||||
* Language tags from BCP-47 standard, implemented by windows
|
||||
* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c
|
||||
* I have no fucking idea if this will work on linux. ¯\_(ツ)_/¯
|
||||
*/
|
||||
|
||||
TextTranslation.Language.ENGLISH => new CultureInfo("en"),
|
||||
TextTranslation.Language.SPANISH_LA => new CultureInfo("es-419"),
|
||||
TextTranslation.Language.GERMAN => new CultureInfo("de"),
|
||||
TextTranslation.Language.FRENCH => new CultureInfo("fr"),
|
||||
TextTranslation.Language.ITALIAN => new CultureInfo("it"),
|
||||
TextTranslation.Language.POLISH => new CultureInfo("pl"),
|
||||
TextTranslation.Language.PORTUGUESE_BR => new CultureInfo("pt-BR"),
|
||||
TextTranslation.Language.JAPANESE => new CultureInfo("ja"),
|
||||
TextTranslation.Language.RUSSIAN => new CultureInfo("ru"),
|
||||
TextTranslation.Language.CHINESE_SIMPLE => new CultureInfo("zh-Hans"),
|
||||
TextTranslation.Language.KOREAN => new CultureInfo("ko"),
|
||||
TextTranslation.Language.TURKISH => new CultureInfo("tr"),
|
||||
_ => new CultureInfo("en") // what
|
||||
};
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
|
||||
private const int _titleButtonIndex = 2;
|
||||
private float _connectPopupOpenTime;
|
||||
|
||||
private const string UpdateChangelog = "QSB Version 0.22.0\r\nFixed lots of bugs, and added lots of SFX and VFX stuff.";
|
||||
private const string UpdateChangelog = "QSB Version 0.23.0\r\nA lot of small improvements and bug fixes.";
|
||||
|
||||
private Action<bool> PopupClose;
|
||||
|
||||
|
@ -74,6 +74,9 @@ internal class UseFlightConsoleMessage : QSBMessage<bool>
|
||||
// Client messes up its position when they start flying it
|
||||
// We can just recall it immediately so its in the right place.
|
||||
var console = QSBWorldSync.GetUnityObject<RemoteFlightConsole>();
|
||||
console.RespawnModelShip(false);
|
||||
if (console._modelShipBody) // for when model ship is destroyed
|
||||
{
|
||||
console.RespawnModelShip(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
using Mirror;
|
||||
using QSB.Player;
|
||||
using QSB.Utility;
|
||||
using QSB.Player;
|
||||
using QSB.Player.TransformSync;
|
||||
using QSB.Utility.VariableSync;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ModelShip;
|
||||
@ -26,7 +23,7 @@ public class ModelShipThrusterVariableSyncer : MonoBehaviour
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer.FlyingModelShip)
|
||||
if (PlayerTransformSync.LocalInstance && QSBPlayerManager.LocalPlayer.FlyingModelShip)
|
||||
{
|
||||
GetFromShip();
|
||||
return;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using QSB.Utility;
|
||||
using QSB.ServerSettings;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Player;
|
||||
@ -34,7 +35,16 @@ public class PlayerHUDMarker : HUDDistanceMarker
|
||||
return false;
|
||||
}
|
||||
|
||||
return _player.IsReady && !_player.IsDead && (!_player.InDreamWorld || QSBPlayerManager.LocalPlayer.InDreamWorld) && _player.Visible;
|
||||
if (!ServerSettingsManager.ShowPlayerNames)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _player.IsReady &&
|
||||
!_player.IsDead &&
|
||||
_player.Visible &&
|
||||
_player.InDreamWorld == QSBPlayerManager.LocalPlayer.InDreamWorld &&
|
||||
_player.IsInMoon == QSBPlayerManager.LocalPlayer.IsInMoon;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
@ -22,6 +22,7 @@ public partial class PlayerInfo
|
||||
public uint PlayerId { get; }
|
||||
public string Name { get; set; }
|
||||
public PlayerHUDMarker HudMarker { get; set; }
|
||||
public PlayerMapMarker MapMarker { get; set; }
|
||||
public PlayerTransformSync TransformSync { get; }
|
||||
public ClientState State { get; set; }
|
||||
public EyeState EyeState { get; set; }
|
||||
|
@ -1,4 +1,6 @@
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.EchoesOfTheEye.DreamLantern;
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
|
||||
namespace QSB.Player;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using QSB.Utility;
|
||||
using QSB.ServerSettings;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.Player;
|
||||
@ -31,6 +32,7 @@ public class PlayerMapMarker : MonoBehaviour
|
||||
public void Init(PlayerInfo player)
|
||||
{
|
||||
_player = player;
|
||||
_player.MapMarker = this;
|
||||
_hasBeenSetUpForInit = true;
|
||||
}
|
||||
|
||||
@ -57,10 +59,20 @@ public class PlayerMapMarker : MonoBehaviour
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ServerSettingsManager.ShowPlayerNames)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var playerScreenPos = Locator.GetActiveCamera().WorldToScreenPoint(transform.position);
|
||||
var isInfrontOfCamera = playerScreenPos.z > 0f;
|
||||
|
||||
return _player.IsReady && !_player.IsDead && (!_player.InDreamWorld || QSBPlayerManager.LocalPlayer.InDreamWorld) && _player.Visible && isInfrontOfCamera;
|
||||
return isInfrontOfCamera &&
|
||||
_player.IsReady &&
|
||||
!_player.IsDead &&
|
||||
_player.Visible &&
|
||||
_player.InDreamWorld == QSBPlayerManager.LocalPlayer.InDreamWorld &&
|
||||
_player.IsInMoon == QSBPlayerManager.LocalPlayer.IsInMoon;
|
||||
}
|
||||
|
||||
public void LateUpdate()
|
||||
@ -77,4 +89,9 @@ public class PlayerMapMarker : MonoBehaviour
|
||||
_canvasMarker.SetVisibility(shouldBeVisible);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ public class PlayerTransformSync : SectoredTransformSync
|
||||
QSBPatch.Remote = false;
|
||||
base.OnStopClient();
|
||||
Player.HudMarker?.Remove();
|
||||
Player.MapMarker?.Remove();
|
||||
QSBPlayerManager.PlayerList.Remove(Player);
|
||||
DebugLog.DebugWrite($"Remove Player : {Player}", MessageType.Info);
|
||||
}
|
||||
|
@ -27,7 +27,20 @@ public static class ShaderReplacer
|
||||
continue;
|
||||
}
|
||||
|
||||
material.shader = replacementShader;
|
||||
// preserve override tag and render queue (for Standard shader)
|
||||
// keywords and properties are already preserved
|
||||
if (material.renderQueue != material.shader.renderQueue)
|
||||
{
|
||||
var renderType = material.GetTag("RenderType", false);
|
||||
var renderQueue = material.renderQueue;
|
||||
material.shader = replacementShader;
|
||||
material.SetOverrideTag("RenderType", renderType);
|
||||
material.renderQueue = renderQueue;
|
||||
}
|
||||
else
|
||||
{
|
||||
material.shader = replacementShader;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,17 +7,6 @@
|
||||
<NoWarn>CS1998;CS0649</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="clean before building" BeforeTargets="PreBuildEvent">
|
||||
<ItemGroup>
|
||||
<_Files Remove="@(_Files)" />
|
||||
<_Files Include="$(OutputPath)\*.dll" />
|
||||
<_Files Include="$(OutputPath)\*.exe" />
|
||||
<_Files Include="$(OutputPath)\*.pdb" />
|
||||
|
||||
<_Files Include="$(OutputPath)\AssetBundles\*" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(_Files)" />
|
||||
</Target>
|
||||
<Target Name="clean after building" AfterTargets="PostBuildEvent">
|
||||
<ItemGroup>
|
||||
<_Files Remove="@(_Files)" />
|
||||
@ -31,33 +20,13 @@
|
||||
</Target>
|
||||
|
||||
<PropertyGroup>
|
||||
<GameDllsDir Condition="Exists('$(GameDir)')">$(GameDir)\OuterWilds_Data\Managed</GameDllsDir>
|
||||
<UnityDllsDir Condition="Exists('$(UnityAssetsDir)')">$(UnityAssetsDir)\Dlls</UnityDllsDir>
|
||||
</PropertyGroup>
|
||||
<Target Name="copy dlls to unity" AfterTargets="PostBuildEvent" Condition="Exists('$(UnityDllsDir)') and Exists('$(GameDllsDir)')">
|
||||
<ItemGroup>
|
||||
<_Files Remove="@(_Files)" />
|
||||
<_Files Include="$(UnityDllsDir)\*.dll" />
|
||||
<_Files Include="$(UnityDllsDir)\*.exe" />
|
||||
<_Files Include="$(UnityDllsDir)\*.pdb" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(_Files)" />
|
||||
|
||||
<Target Name="copy dlls to unity" AfterTargets="PostBuildEvent" Condition="Exists('$(UnityDllsDir)')">
|
||||
<ItemGroup>
|
||||
<_Files Remove="@(_Files)" />
|
||||
<_Files Include="$(OutputPath)\*.dll" />
|
||||
<_Files Include="$(OutputPath)\*.exe" />
|
||||
<_Files Include="$(OutputPath)\*.pdb" />
|
||||
|
||||
<_Files Include="$(GameDllsDir)\EOS-SDK.dll" />
|
||||
<_Files Include="$(GameDllsDir)\Autofac.dll" />
|
||||
<_Files Include="$(GameDllsDir)\Newtonsoft.Json.dll" />
|
||||
<_Files Include="$(GameDllsDir)\0Harmony.dll" />
|
||||
<_Files Include="$(GameDllsDir)\MonoMod*.dll" />
|
||||
<_Files Include="$(GameDllsDir)\Mono.Cecil.dll" />
|
||||
<_Files Include="$(GameDllsDir)\OWML*.dll" />
|
||||
<_Files Include="$(GameDllsDir)\NAudio-Unity.dll" />
|
||||
<_Files Include="$(GameDllsDir)\com.rlabrecque.steamworks.net.dll" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(_Files)" DestinationFolder="$(UnityDllsDir)" />
|
||||
</Target>
|
||||
@ -90,7 +59,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OuterWildsGameLibs" Version="1.1.13.393" IncludeAssets="compile" />
|
||||
<PackageReference Include="OWML" Version="2.7.0" IncludeAssets="compile" />
|
||||
<PackageReference Include="OWML" Version="2.8.0" IncludeAssets="compile" />
|
||||
<Reference Include="..\Mirror\*.dll" />
|
||||
<Reference Include="..\UniTask\*.dll" />
|
||||
<ProjectReference Include="..\EpicOnlineTransport\EpicOnlineTransport.csproj" />
|
||||
|
@ -4,9 +4,11 @@ using OWML.Common;
|
||||
using OWML.ModHelper;
|
||||
using QSB.Localization;
|
||||
using QSB.Menus;
|
||||
using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.QuantumSync;
|
||||
using QSB.SaveSync;
|
||||
using QSB.ServerSettings;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
@ -56,6 +58,8 @@ public class QSBCore : ModBehaviour
|
||||
Application.version.Split('.').Take(3).Join(delimiter: ".");
|
||||
public static bool DLCInstalled => EntitlementsManager.IsDlcOwned() == EntitlementsManager.AsyncOwnershipStatus.Owned;
|
||||
public static bool IncompatibleModsAllowed { get; private set; }
|
||||
public static bool ShowPlayerNames { get; private set; }
|
||||
public static bool ShipDamage { get; private set; }
|
||||
public static GameVendor GameVendor { get; private set; } = GameVendor.None;
|
||||
public static bool IsStandalone => GameVendor is GameVendor.Epic or GameVendor.Steam;
|
||||
public static IProfileManager ProfileManager => IsStandalone
|
||||
@ -242,6 +246,14 @@ public class QSBCore : ModBehaviour
|
||||
{
|
||||
DefaultServerIP = config.GetSettingsValue<string>("defaultServerIP");
|
||||
IncompatibleModsAllowed = config.GetSettingsValue<bool>("incompatibleModsAllowed");
|
||||
ShowPlayerNames = config.GetSettingsValue<bool>("showPlayerNames");
|
||||
ShipDamage = config.GetSettingsValue<bool>("shipDamage");
|
||||
|
||||
if (IsHost)
|
||||
{
|
||||
ServerSettingsManager.ServerShowPlayerNames = ShowPlayerNames;
|
||||
new ServerSettingsMessage().Send();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
|
@ -315,7 +315,11 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
Destroy(GetComponent<RespawnOnDeath>());
|
||||
Destroy(GetComponent<ServerStateManager>());
|
||||
Destroy(GetComponent<ClientStateManager>());
|
||||
QSBPlayerManager.PlayerList.ForEach(player => player.HudMarker?.Remove());
|
||||
QSBPlayerManager.PlayerList.ForEach(player =>
|
||||
{
|
||||
player.HudMarker?.Remove();
|
||||
player.MapMarker?.Remove();
|
||||
});
|
||||
|
||||
QSBWorldSync.RemoveWorldObjects();
|
||||
|
||||
@ -397,7 +401,11 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
|
||||
DebugLog.DebugWrite("OnStopServer", MessageType.Info);
|
||||
Destroy(GetComponent<RespawnOnDeath>());
|
||||
DebugLog.ToConsole("Server stopped!", MessageType.Info);
|
||||
QSBPlayerManager.PlayerList.ForEach(player => player.HudMarker?.Remove());
|
||||
QSBPlayerManager.PlayerList.ForEach(player =>
|
||||
{
|
||||
player.HudMarker?.Remove();
|
||||
player.MapMarker?.Remove();
|
||||
});
|
||||
|
||||
base.OnStopServer();
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ public class RespawnHUDMarker : HUDDistanceMarker
|
||||
{
|
||||
_markerRadius = 0.2f;
|
||||
_markerTarget = transform;
|
||||
_markerLabel = QSBLocalization.Current.RespawnPlayer.ToUpper(QSBLocalization.CultureInfo);
|
||||
_markerLabel = QSBLocalization.Current.RespawnPlayer.ToUpper();
|
||||
_isReady = true;
|
||||
|
||||
base.InitCanvasMarker();
|
||||
|
@ -1,6 +1,7 @@
|
||||
using QSB.ConversationSync.Messages;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.ServerSettings;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.SaveSync.Messages;
|
||||
@ -21,6 +22,7 @@ internal class RequestGameStateMessage : QSBMessage
|
||||
}
|
||||
|
||||
new GameStateMessage(From).Send();
|
||||
new ServerSettingsMessage().Send();
|
||||
|
||||
var gameSave = PlayerData._currentGameSave;
|
||||
|
||||
|
10
QSB/ServerSettings/ServerSettingsManager.cs
Normal file
10
QSB/ServerSettings/ServerSettingsManager.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ServerSettings;
|
||||
|
||||
internal class ServerSettingsManager : MonoBehaviour, IAddComponentOnStart
|
||||
{
|
||||
public static bool ServerShowPlayerNames;
|
||||
public static bool ShowPlayerNames => (ServerShowPlayerNames || QSBCore.IsHost) && QSBCore.ShowPlayerNames;
|
||||
}
|
27
QSB/ServerSettings/ServerSettingsMessage.cs
Normal file
27
QSB/ServerSettings/ServerSettingsMessage.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Mirror;
|
||||
using QSB.Messaging;
|
||||
|
||||
namespace QSB.ServerSettings;
|
||||
|
||||
internal class ServerSettingsMessage : QSBMessage
|
||||
{
|
||||
private bool _showPlayerNames;
|
||||
|
||||
public ServerSettingsMessage()
|
||||
=> _showPlayerNames = QSBCore.ShowPlayerNames;
|
||||
|
||||
public override void Serialize(NetworkWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
writer.Write(_showPlayerNames);
|
||||
}
|
||||
|
||||
public override void Deserialize(NetworkReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
_showPlayerNames = reader.ReadBool();
|
||||
}
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
=> ServerSettingsManager.ServerShowPlayerNames = _showPlayerNames;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using Mirror;
|
||||
using QSB.Player;
|
||||
using QSB.Player.TransformSync;
|
||||
using QSB.Utility.VariableSync;
|
||||
using QSB.WorldSync;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.ShipSync;
|
||||
@ -21,7 +21,7 @@ public class ShipThrusterVariableSyncer : NetworkBehaviour
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (QSBPlayerManager.LocalPlayer.FlyingShip)
|
||||
if (PlayerTransformSync.LocalInstance && QSBPlayerManager.LocalPlayer.FlyingShip)
|
||||
{
|
||||
GetFromShip();
|
||||
return;
|
||||
|
@ -4,6 +4,7 @@ using QSB.Messaging;
|
||||
using QSB.Patches;
|
||||
using QSB.TimeSync.Messages;
|
||||
using QSB.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QSB.TimeSync.Patches;
|
||||
|
||||
@ -25,8 +26,8 @@ internal class TimePatches : QSBPatch
|
||||
public static void PlayerCameraEffectController_WakeUp(PlayerCameraEffectController __instance)
|
||||
{
|
||||
// prevent funny thing when you pause while waking up
|
||||
QSBInputManager.Instance.SetInputsEnabled(false);
|
||||
Delay.RunWhen(() => !__instance._isOpeningEyes, () => QSBInputManager.Instance.SetInputsEnabled(true));
|
||||
Locator.GetPauseCommandListener().AddPauseCommandLock();
|
||||
Delay.RunWhen(() => !__instance._isOpeningEyes, () => Locator.GetPauseCommandListener().RemovePauseCommandLock());
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
|
@ -8,6 +8,7 @@ using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
using QSB.Player.Messages;
|
||||
using QSB.TimeSync.Messages;
|
||||
using QSB.TimeSync.Patches;
|
||||
using QSB.Utility;
|
||||
using QSB.WorldSync;
|
||||
using System;
|
||||
@ -46,7 +47,6 @@ public class WakeUpSync : NetworkBehaviour
|
||||
{
|
||||
OWTime.SetTimeScale(1f);
|
||||
OWTime.SetMaxDeltaTime(0.06666667f);
|
||||
OWTime.SetFixedTimestep(0.01666667f);
|
||||
Locator.GetActiveCamera().enabled = true;
|
||||
CurrentState = State.NotLoaded;
|
||||
CurrentReason = null;
|
||||
@ -214,7 +214,6 @@ public class WakeUpSync : NetworkBehaviour
|
||||
CurrentState = State.FastForwarding;
|
||||
CurrentReason = reason;
|
||||
OWTime.SetMaxDeltaTime(0.033333335f);
|
||||
OWTime.SetFixedTimestep(0.033333335f);
|
||||
TimeSyncUI.TargetTime = _serverTime;
|
||||
TimeSyncUI.Start(TimeSyncType.FastForwarding, reason);
|
||||
}
|
||||
@ -245,7 +244,6 @@ public class WakeUpSync : NetworkBehaviour
|
||||
{
|
||||
OWTime.SetTimeScale(1f);
|
||||
OWTime.SetMaxDeltaTime(0.06666667f);
|
||||
OWTime.SetFixedTimestep(0.01666667f);
|
||||
Locator.GetActiveCamera().enabled = true;
|
||||
CurrentState = State.Loaded;
|
||||
CurrentReason = null;
|
||||
|
126
QSB/Translations/zh_CN.json
Normal file
126
QSB/Translations/zh_CN.json
Normal file
@ -0,0 +1,126 @@
|
||||
{
|
||||
"Language": "CHINESE_SIMPLE",
|
||||
"MainMenuHost": "开启多人游戏",
|
||||
"MainMenuConnect": "连接至多人游戏",
|
||||
"PauseMenuDisconnect": "断开连接",
|
||||
"PauseMenuStopHosting": "关闭服务器",
|
||||
"PublicIPAddress": "公共IP地址\n\n(您的多人存档数据将被覆盖)",
|
||||
"ProductUserID": "用户ID\n\n(您的多人存档数据将被覆盖)",
|
||||
"Connect": "连接",
|
||||
"Cancel": "取消",
|
||||
"HostExistingOrNewOrCopy": "您想使用一个现有的多人探险存档,或是开启一个新的存档,还是把现有的单人探险存档复制到多人探险存档?",
|
||||
"HostNewOrCopy": "您想开启一个新的存档,还是把现有的单人探险存档复制到多人探险存档?",
|
||||
"HostExistingOrNew": "您想使用一个现有的多人探险存档,还是开启一个新的存档?",
|
||||
"ExistingSave": "现有存档",
|
||||
"NewSave": "新建存档",
|
||||
"CopySave": "复制存档",
|
||||
"DisconnectAreYouSure": "您确定要断开连接吗?\n将会退出至主菜单。",
|
||||
"Yes": "是",
|
||||
"No": "否",
|
||||
"StopHostingAreYouSure": "您确定要停止服务器吗?\n将会与所有人断开连接并且使他们退出至主菜单。",
|
||||
"CopyProductUserIDToClipboard": "开启服务器\n其他玩家需要使用您的用户ID连接至服务器,用户ID是:\n{0}\n您想要复制到剪切板吗?",
|
||||
"Connecting": "连接中……",
|
||||
"OK": "好的",
|
||||
"ServerRefusedConnection": "服务器拒绝了您的连接\n{0}",
|
||||
"ClientDisconnectWithError": "客户端发生错误导致连接断开\n{0}",
|
||||
"QSBVersionMismatch": "QSB版本号不匹配。(客户端:{0},服务端:{1})",
|
||||
"OWVersionMismatch": "《星际拓荒》版本号不匹配。(客户端:{0},服务端:{1})",
|
||||
"DLCMismatch": "DLC安装情况不匹配。(客户端:{0},服务端:{1})",
|
||||
"GameProgressLimit": "游戏中时间太久了。",
|
||||
"AddonMismatch": "插件不匹配(客户端:{0}插件,服务端:{1}插件)",
|
||||
"IncompatibleMod": "使用了不兼容/不允许的模组,检测到的第一个模组是{0}",
|
||||
"PlayerJoinedTheGame": "{0}加入了游戏!",
|
||||
"PlayerWasKicked": "{0}被踢出了游戏。",
|
||||
"KickedFromServer": "被踢出了游戏,理由是:{0}",
|
||||
"RespawnPlayer": "复活玩家",
|
||||
"TimeSyncTooFarBehind": "{0}\n快进以匹配服务器时间……",
|
||||
"TimeSyncWaitingForStartOfServer": "等待服务器开始……",
|
||||
"TimeSyncTooFarAhead": "{0}\n暂停等待以匹配服务器时间……",
|
||||
"TimeSyncWaitForAllToReady": "等待开始轮回……",
|
||||
"TimeSyncWaitForAllToDie": "等待轮回结束……",
|
||||
"GalaxyMapEveryoneNotPresent": "现在还不是时候。需要所有人见证这个时刻。",
|
||||
"YouAreDead": "你死了。",
|
||||
"WaitingForRespawn": "等待某人将你复活……",
|
||||
"WaitingForAllToDie": "等待{0}死亡……",
|
||||
"AttachToShip": "固定在飞船上",
|
||||
"DetachFromShip": "取消固定",
|
||||
"DeathMessages": {
|
||||
"Default": [
|
||||
"{0}死了",
|
||||
"{0}被杀了"
|
||||
],
|
||||
"Impact": [
|
||||
"{0}忘记使用推进器了",
|
||||
"{0}拥抱了地面",
|
||||
"{0}狠狠的撞上了地面",
|
||||
"{0}爆炸了",
|
||||
"{0}因为撞击而死",
|
||||
"{0}撞击地面过猛"
|
||||
],
|
||||
"Asphyxiation": [
|
||||
"{0}忘记了需要呼吸",
|
||||
"{0}窒息了",
|
||||
"{0}窒息而死",
|
||||
"{0}忘记了怎么呼吸",
|
||||
"{0}忘记检查氧气了",
|
||||
"{0}把空气用完了",
|
||||
"{0}把氧气用完了",
|
||||
"{0}不再需要呼吸了"
|
||||
],
|
||||
"Energy": [
|
||||
"{0}被烹饪了"
|
||||
],
|
||||
"Supernova": [
|
||||
"{0}没有时间了",
|
||||
"{0}被烧掉了",
|
||||
"{0}被超新星烤熟了",
|
||||
"{0}升华了",
|
||||
"{0}遗忘了时间"
|
||||
],
|
||||
"Digestion": [
|
||||
"{0}被吃掉了",
|
||||
"{0}发现了一条鱼",
|
||||
"{0}遇到了邪恶生物",
|
||||
"{0}惹错了鱼",
|
||||
"{0}被消化了",
|
||||
"{0}因为消化系统而死"
|
||||
],
|
||||
"Crushed": [
|
||||
"{0}被压死了",
|
||||
"{0}被压扁了",
|
||||
"{0}埋葬了自己",
|
||||
"{0}没能及时逃离",
|
||||
"{0}想在沙子中游泳",
|
||||
"{0}小看了沙子的力量",
|
||||
"{0}卡在沙子下面了"
|
||||
],
|
||||
"Lava": [
|
||||
"{0}在岩浆中死去",
|
||||
"{0}融化了",
|
||||
"{0}尝试在岩浆中游泳",
|
||||
"{0}掉进了岩浆",
|
||||
"{0}因为岩浆而死",
|
||||
"{0}在岩浆中游泳",
|
||||
"{0}被岩浆烧毁"
|
||||
],
|
||||
"BlackHole": [
|
||||
"{0}尝试追寻自己的记忆"
|
||||
],
|
||||
"DreamExplosion": [
|
||||
"{0}爆炸了",
|
||||
"{0}是第一个吃螃蟹的人",
|
||||
"{0}被炸死了",
|
||||
"{0}被炸了",
|
||||
"{0}因为爆炸而死",
|
||||
"{0}使用了错误的文物"
|
||||
],
|
||||
"CrushedByElevator": [
|
||||
"{0}被粉碎了",
|
||||
"{0}被挤扁了",
|
||||
"{0}被电梯撞击",
|
||||
"{0}站在了电梯下",
|
||||
"{0}变成了一个平板人",
|
||||
"{0}被电梯挤扁了"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using OWML.Common;
|
||||
using QSB.EchoesOfTheEye.DreamLantern;
|
||||
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
|
||||
using QSB.ItemSync.WorldObjects.Items;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player;
|
||||
|
@ -5,6 +5,8 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
namespace QSB.Utility;
|
||||
|
||||
public static class DebugLog
|
||||
|
@ -31,11 +31,11 @@ public static class DeterministicManager
|
||||
}
|
||||
};
|
||||
|
||||
public static void OnWorldObjectsReady()
|
||||
public static void OnWorldObjectsAdded()
|
||||
{
|
||||
if (QSBCore.DebugSettings.DumpWorldObjects)
|
||||
{
|
||||
using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, "world objects.csv")))
|
||||
using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, $"[{DebugLog.ProcessInstanceId}] world objects.csv")))
|
||||
{
|
||||
file.WriteLine("world object,deterministic path");
|
||||
foreach (var worldObject in QSBWorldSync.GetWorldObjects())
|
||||
@ -51,7 +51,7 @@ public static class DeterministicManager
|
||||
}
|
||||
}
|
||||
|
||||
using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, "cache.csv")))
|
||||
using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, $"[{DebugLog.ProcessInstanceId}] cache.csv")))
|
||||
{
|
||||
file.WriteLine("name,instance id,sibling index,parent,parent instance id");
|
||||
foreach (var (transform, (siblingIndex, parent)) in _cache)
|
||||
@ -267,7 +267,7 @@ public static class DeterministicManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// only call this before world objects ready
|
||||
/// only call this before world objects added
|
||||
/// </summary>
|
||||
public static string DeterministicPath(this Component component)
|
||||
{
|
||||
@ -298,7 +298,7 @@ public static class DeterministicManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// only call this before world objects ready
|
||||
/// only call this before world objects added
|
||||
/// </summary>
|
||||
public static IEnumerable<T> SortDeterministic<T>(this IEnumerable<T> components) where T : Component
|
||||
=> components.OrderBy(DeterministicPath);
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
@ -214,5 +215,23 @@ public static class Extensions
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/24031467
|
||||
public static string GetMD5Hash(this IEnumerable<string> list)
|
||||
{
|
||||
using var md5 = System.Security.Cryptography.MD5.Create();
|
||||
|
||||
var longString = string.Concat(list);
|
||||
var bytes = Encoding.ASCII.GetBytes(longString);
|
||||
var hashBytes = md5.ComputeHash(bytes);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < hashBytes.Length; i++)
|
||||
{
|
||||
sb.Append(hashBytes[i].ToString("X2"));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -4,6 +4,7 @@ using UnityEngine.UI;
|
||||
|
||||
namespace QSB.Utility;
|
||||
|
||||
[UsedInUnityProject]
|
||||
public class ZOverride : MonoBehaviour
|
||||
{
|
||||
private const string shaderTestMode = "unity_GUIZTestMode";
|
||||
|
@ -20,6 +20,7 @@ namespace QSB.WorldSync;
|
||||
public static class QSBWorldSync
|
||||
{
|
||||
public static WorldObjectManager[] Managers;
|
||||
public static string WorldObjectsHash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set when all WorldObjectManagers have called Init() on all their objects (AKA all the objects are created)
|
||||
@ -82,8 +83,14 @@ public static class QSBWorldSync
|
||||
AllObjectsAdded = true;
|
||||
DebugLog.DebugWrite("World Objects added.", MessageType.Success);
|
||||
|
||||
DeterministicManager.OnWorldObjectsAdded();
|
||||
|
||||
WorldObjectsHash = WorldObjects.Select(x => x.GetType().Name).GetMD5Hash();
|
||||
DebugLog.DebugWrite($"WorldObject hash is {WorldObjectsHash}");
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
new WorldObjectsHashMessage().Send();
|
||||
new RequestLinksMessage().Send();
|
||||
}
|
||||
|
||||
@ -96,8 +103,6 @@ public static class QSBWorldSync
|
||||
AllObjectsReady = true;
|
||||
DebugLog.DebugWrite("World Objects ready.", MessageType.Success);
|
||||
|
||||
DeterministicManager.OnWorldObjectsReady();
|
||||
|
||||
if (!QSBCore.IsHost)
|
||||
{
|
||||
new RequestInitialStatesMessage().Send();
|
||||
@ -247,7 +252,7 @@ public static class QSBWorldSync
|
||||
|
||||
if (WorldObjects[objectId] is not TWorldObject worldObject)
|
||||
{
|
||||
DebugLog.ToConsole($"Error - {typeof(TWorldObject).Name} id {objectId} is actually {WorldObjects[objectId].GetType().Name}.", MessageType.Error);
|
||||
DebugLog.ToConsole($"Error - WorldObject id {objectId} is {WorldObjects[objectId].GetType().Name}, expected {typeof(TWorldObject).Name}.", MessageType.Error);
|
||||
return default;
|
||||
}
|
||||
|
||||
|
30
QSB/WorldSync/WorldObjectsHashMessage.cs
Normal file
30
QSB/WorldSync/WorldObjectsHashMessage.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using OWML.Common;
|
||||
using QSB.Messaging;
|
||||
using QSB.Player.Messages;
|
||||
using QSB.Utility;
|
||||
|
||||
namespace QSB.WorldSync;
|
||||
|
||||
/// <summary>
|
||||
/// sends QSBWorldSync.WorldObjectsHash to the server for sanity checking
|
||||
/// </summary>
|
||||
internal class WorldObjectsHashMessage : QSBMessage<string>
|
||||
{
|
||||
public WorldObjectsHashMessage() : base(QSBWorldSync.WorldObjectsHash) => To = 0;
|
||||
|
||||
public override void OnReceiveRemote()
|
||||
{
|
||||
var serverHash = QSBWorldSync.WorldObjectsHash;
|
||||
|
||||
if (serverHash != Data)
|
||||
{
|
||||
// oh fuck oh no oh god
|
||||
DebugLog.ToConsole($"Kicking {From} because their WorldObjects hash is wrong. (server:{serverHash}, client:{Data})", MessageType.Error);
|
||||
new PlayerKickMessage(From, $"WorldObject hash error. (Server:{serverHash}, Client:{Data})").Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog.DebugWrite($"WorldObject hash from {From} verified!", MessageType.Success);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,29 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"settings": {
|
||||
"defaultServerIP": "localhost",
|
||||
"incompatibleModsAllowed": false
|
||||
"defaultServerIP": {
|
||||
"title": "Last Entered IP/ID",
|
||||
"type": "text",
|
||||
"value": "localhost",
|
||||
"tooltip": "Used if you leave the connect prompt blank."
|
||||
},
|
||||
"incompatibleModsAllowed": {
|
||||
"title": "Incompatible Mods Allowed",
|
||||
"type": "toggle",
|
||||
"value": false,
|
||||
"tooltip": "Kicks players if they have certain mods."
|
||||
},
|
||||
"showPlayerNames": {
|
||||
"title": "Show Player Names",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip": "Shows player names in the HUD and the map view."
|
||||
},
|
||||
"shipDamage": {
|
||||
"title": "Ship Damage",
|
||||
"type": "toggle",
|
||||
"value": true,
|
||||
"tooltip": "Take impact damage when inside the ship."
|
||||
}
|
||||
}
|
||||
}
|
@ -7,8 +7,8 @@
|
||||
"body": "- Disable *all* other mods. (Can heavily affect performance)\n- Make sure you are not running any other network-intensive applications."
|
||||
},
|
||||
"uniqueName": "Raicuparta.QuantumSpaceBuddies",
|
||||
"version": "0.22.0",
|
||||
"owmlVersion": "2.7.0",
|
||||
"version": "0.23.0",
|
||||
"owmlVersion": "2.8.0",
|
||||
"dependencies": [ "_nebula.MenuFramework", "JohnCorby.VanillaFix" ],
|
||||
"pathsToPreserve": [ "debugsettings.json", "storage.json" ]
|
||||
}
|
||||
|
@ -99,7 +99,6 @@ See [TRANSLATING.md](TRANSLATING.md)
|
||||
- Clone QSB's source
|
||||
- Open the file `DevEnv.targets` in your favorite text editor
|
||||
- (optional if copying built dlls manually) Edit the entry `<OwmlDir>` to point to your OWML directory (it is installed inside the Mod Manager directory)
|
||||
- (optional if no unity project) Edit the entry `<GameDir>` to point to the directory where Outer Wilds is installed
|
||||
- (optional if no unity project) Edit the entry `<UnityAssetsDir>` to point to the Assets folder of the QSB unity project
|
||||
- Open the project solution file `QSB.sln` in Visual Studio 2022
|
||||
|
||||
@ -171,7 +170,7 @@ The template for this file is this :
|
||||
|
||||
### Authors
|
||||
|
||||
- [\_nebula](https://github.com/misternebula) - Developer of v0.3 onwards
|
||||
- [\_nebula](https://github.com/misternebula) - Developer of v0.3.0 onwards
|
||||
- [JohnCorby](https://github.com/JohnCorby) - Co-developer of v0.13.0 onwards.
|
||||
- [AmazingAlek](https://github.com/amazingalek) - Developer of v0.1.0 - v0.7.1.
|
||||
- [Raicuparta](https://github.com/Raicuparta) - Developer of v0.1.0 - v0.2.0.
|
||||
@ -180,9 +179,10 @@ The template for this file is this :
|
||||
|
||||
- [Chris Yeninas](https://github.com/PhantomGamers) - Help with project files and GitHub workflows.
|
||||
- [Tlya](https://github.com/Tllya) - Russian translation.
|
||||
- [Xen](https://github.com/xen-42) - French translation.
|
||||
- [Xen](https://github.com/xen-42) - French translation, and help with particle effects and sounds.
|
||||
- [ShoosGun](https://github.com/ShoosGun) - Portuguese translation.
|
||||
- [DertolleDude](https://github.com/DertolleDude) - German translation.
|
||||
- [SakuradaYuki](https://github.com/SakuradaYuki) - Chinese translation.
|
||||
|
||||
### Special Thanks
|
||||
- Thanks to Logan Ver Hoef for help with the game code, and for helping make the damn game in the first place.
|
||||
|
@ -10,13 +10,13 @@ QSB can only be translated to the languages Outer Wilds supports - so if you don
|
||||
- Russian
|
||||
- Portuguese (Brazil)
|
||||
- German
|
||||
- Chinese (Simplified)
|
||||
|
||||
### Un-translated languages :
|
||||
- Spanish (Latin American)
|
||||
- Italian
|
||||
- Polish
|
||||
- Japanese
|
||||
- Chinese (Simplified)
|
||||
- Korean
|
||||
- Turkish
|
||||
|
||||
@ -38,4 +38,4 @@ Once you are happy with it, go to [this page](https://github.com/misternebula/qu
|
||||
|
||||
Scroll down to the "Propose new file" box. In the title box (default "Create new file") write something along the lines of "Add translation for (language)".
|
||||
|
||||
Press the big green "Propose new file", and we'll review it soon!
|
||||
Press the big green "Propose new file", and we'll review it soon!
|
||||
|
Loading…
x
Reference in New Issue
Block a user