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,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);
}
}
@ -141,15 +91,10 @@ 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;
}
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>();
console.RespawnModelShip(false);
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,11 +29,18 @@ public static class ShaderReplacer
// preserve override tag and render queue (for Standard shader)
// keywords and properties are already preserved
var renderType = material.GetTag("RenderType", false);
var renderQueue = material.renderQueue;
material.shader = replacementShader;
material.SetOverrideTag("RenderType", renderType);
material.renderQueue = renderQueue;
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
@ -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!