Merge branch 'dev' into nh-stuff

This commit is contained in:
_nebula 2022-12-05 19:24:33 +00:00
commit 7113eec0e9
37 changed files with 649 additions and 503 deletions

View File

@ -4,6 +4,6 @@
</PropertyGroup>
<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" />
</ItemGroup>
</Project>

View File

@ -4,7 +4,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OuterWildsGameLibs" Version="1.1.13.393" />
<PackageReference Include="OWML" Version="2.7.0" />
<PackageReference Include="OWML" Version="2.8.0" />
<Reference Include="../Mirror/*.dll" />
</ItemGroup>
</Project>

View File

@ -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");

View File

@ -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,41 +13,33 @@ 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)
{
__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();
}
}
if (__instance._isPlayer && PlayerState.IsInsideShip())
{
var shipCenter = Locator.GetShipTransform().position + Locator.GetShipTransform().up * 2f;
var distanceFromShip = Vector3.Distance(__instance._body.GetPosition(), shipCenter);
if (distanceFromShip > 8f)
@ -69,47 +60,6 @@ public class DeathPatches : QSBPatch
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;
}
}
}
}
}
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DeathManager), nameof(DeathManager.KillPlayer))]
private static bool DeathManager_KillPlayer(DeathManager __instance, DeathType deathType)
@ -141,16 +91,11 @@ public class DeathPatches : QSBPatch
Achievements.Earn(Achievements.Type.EARLY_ADOPTER);
}
if (PlayerState.InDreamWorld())
if (PlayerState.InDreamWorld() && deathType != DeathType.Dream && deathType != DeathType.DreamExplosion && deathType != DeathType.Supernova && deathType != DeathType.TimeLoop && deathType != DeathType.Meditation)
{
// exit dream world either way to prevent goof with respawn
// TODO: test
Locator.GetDreamWorldController().ExitDreamWorld(deathType);
if (deathType != DeathType.Dream && deathType != DeathType.DreamExplosion && deathType != DeathType.Supernova && deathType != DeathType.TimeLoop && deathType != DeathType.Meditation)
{
return;
}
}
if (!@this._isDying)
{

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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))]

View File

@ -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;
}
}

View File

@ -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 });
}
}

View 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));
}
}

View File

@ -1,7 +1,5 @@
using QSB.EchoesOfTheEye.DreamLantern;
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.EchoesOfTheEye.DreamLantern.WorldObjects;
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
using QSB.ItemSync.WorldObjects.Items;
using QSB.Messaging;
using QSB.Player;
using QSB.Player.TransformSync;

View File

@ -50,5 +50,8 @@ internal class ExitDreamWorldMessage : QSBMessage
ghost.GetEffects().OnSectorOccupantsUpdated();
}
}
Locator.GetAlarmSequenceController().OnExitDreamWorld();
DreamWorldFakePlayer.Destroy(player);
}
}

View File

@ -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
{

View File

@ -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;

View File

@ -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>();
if (console._modelShipBody) // for when model ship is destroyed
{
console.RespawnModelShip(false);
}
}
}

View File

@ -1,4 +1,5 @@
using QSB.Utility;
using QSB.ServerSettings;
using QSB.Utility;
using UnityEngine;
namespace QSB.Player;
@ -34,6 +35,11 @@ public class PlayerHUDMarker : HUDDistanceMarker
return false;
}
if (!ServerSettingsManager.ShowPlayerNames)
{
return false;
}
return _player.IsReady &&
!_player.IsDead &&
_player.Visible &&

View File

@ -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; }

View File

@ -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,6 +59,11 @@ public class PlayerMapMarker : MonoBehaviour
return false;
}
if (!ServerSettingsManager.ShowPlayerNames)
{
return false;
}
var playerScreenPos = Locator.GetActiveCamera().WorldToScreenPoint(transform.position);
var isInfrontOfCamera = playerScreenPos.z > 0f;
@ -82,4 +89,9 @@ public class PlayerMapMarker : MonoBehaviour
_canvasMarker.SetVisibility(shouldBeVisible);
}
}
public void Remove()
{
// TODO
}
}

View File

@ -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);
}

View File

@ -29,12 +29,19 @@ public static class ShaderReplacer
// 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;
}
}
}
}
}

View File

@ -59,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" />

View File

@ -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
@ -247,6 +251,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()

View File

@ -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();
}

View File

@ -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;

View 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;
}

View 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;
}

View File

@ -4,6 +4,7 @@ using QSB.Messaging;
using QSB.Patches;
using QSB.TimeSync.Messages;
using QSB.Utility;
using UnityEngine;
namespace QSB.TimeSync.Patches;

View File

@ -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
View 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}被电梯挤扁了"
]
}
}

View File

@ -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."
}
}
}

View File

@ -8,7 +8,7 @@
},
"uniqueName": "Raicuparta.QuantumSpaceBuddies",
"version": "0.23.0",
"owmlVersion": "2.7.0",
"owmlVersion": "2.8.0",
"dependencies": [ "_nebula.MenuFramework", "JohnCorby.VanillaFix" ],
"pathsToPreserve": [ "debugsettings.json", "storage.json" ]
}

View File

@ -170,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.
@ -179,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.

View File

@ -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