Merge branch 'dev' into picture-frame-doors

This commit is contained in:
Mister_Nebula 2022-03-11 09:07:11 +00:00
commit c8ce4bdfb4
15 changed files with 181 additions and 433 deletions

View File

@ -1,5 +1,4 @@
using Mirror; using QSB.ItemSync.WorldObjects.Items;
using QSB.ItemSync.WorldObjects.Items;
using QSB.Messaging; using QSB.Messaging;
using QSB.Player; using QSB.Player;
using QSB.SectorSync.WorldObjects; using QSB.SectorSync.WorldObjects;
@ -8,39 +7,21 @@ using UnityEngine;
namespace QSB.ItemSync.Messages; namespace QSB.ItemSync.Messages;
internal class DropItemMessage : QSBWorldObjectMessage<IQSBItem> internal class DropItemMessage : QSBWorldObjectMessage<IQSBItem,
(Vector3 LocalPos, Vector3 LocalNorm, int SectorId)>
{ {
private Vector3 Position; public DropItemMessage(Vector3 position, Vector3 normal, Sector sector) : base((
private Vector3 Normal; sector.transform.InverseTransformPoint(position),
private int SectorId; sector.transform.InverseTransformDirection(normal),
sector.GetWorldObject<QSBSector>().ObjectId
public DropItemMessage(Vector3 position, Vector3 normal, Sector sector) )) { }
{
Position = position;
Normal = normal;
SectorId = sector.GetWorldObject<QSBSector>().ObjectId;
}
public override void Serialize(NetworkWriter writer)
{
base.Serialize(writer);
writer.Write(Position);
writer.Write(Normal);
writer.Write(SectorId);
}
public override void Deserialize(NetworkReader reader)
{
base.Deserialize(reader);
Position = reader.ReadVector3();
Normal = reader.ReadVector3();
SectorId = reader.Read<int>();
}
public override void OnReceiveRemote() public override void OnReceiveRemote()
{ {
var sector = SectorId.GetWorldObject<QSBSector>().AttachedObject; var sector = Data.SectorId.GetWorldObject<QSBSector>().AttachedObject;
WorldObject.DropItem(sector.transform.TransformPoint(Position), Normal, sector); var position = sector.transform.TransformPoint(Data.LocalPos);
var normal = sector.transform.TransformDirection(Data.LocalNorm);
WorldObject.DropItem(position, normal, sector);
var player = QSBPlayerManager.GetPlayer(From); var player = QSBPlayerManager.GetPlayer(From);
player.HeldItem = WorldObject; player.HeldItem = WorldObject;

View File

@ -90,7 +90,7 @@ internal class ItemPatches : QSBPatch
var parent = (customDropTarget == null) var parent = (customDropTarget == null)
? targetRigidbody.transform ? targetRigidbody.transform
: customDropTarget.GetItemDropTargetTransform(hit.collider.gameObject); : customDropTarget.GetItemDropTargetTransform(hit.collider.gameObject);
var IQSBItem = __instance._heldItem.GetWorldObject<IQSBItem>(); var qsbItem = __instance._heldItem.GetWorldObject<IQSBItem>();
__instance._heldItem.DropItem(hit.point, hit.normal, parent, sector, customDropTarget); __instance._heldItem.DropItem(hit.point, hit.normal, parent, sector, customDropTarget);
__instance._heldItem = null; __instance._heldItem = null;
QSBPlayerManager.LocalPlayer.HeldItem = null; QSBPlayerManager.LocalPlayer.HeldItem = null;
@ -98,12 +98,13 @@ internal class ItemPatches : QSBPatch
var parentSector = parent.GetComponentInChildren<Sector>(); var parentSector = parent.GetComponentInChildren<Sector>();
if (parentSector != null) if (parentSector != null)
{ {
var localPos = parentSector.transform.InverseTransformPoint(hit.point); qsbItem.SendMessage(new DropItemMessage(hit.point, hit.normal, parentSector));
IQSBItem.SendMessage(new DropItemMessage(localPos, hit.normal, parentSector)); }
return false; else
{
DebugLog.ToConsole($"Error - No sector found for rigidbody {targetRigidbody.name}!.", MessageType.Error);
} }
DebugLog.ToConsole($"Error - No sector found for rigidbody {targetRigidbody.name}!.", MessageType.Error);
return false; return false;
} }
} }

View File

@ -1,11 +0,0 @@
using QSB.Messaging;
using QSB.MeteorSync.WorldObjects;
namespace QSB.MeteorSync.Messages;
public class FragmentDamageMessage : QSBWorldObjectMessage<QSBFragment, float>
{
public FragmentDamageMessage(float damage) : base(damage) { }
public override void OnReceiveRemote() => WorldObject.AddDamage(Data);
}

View File

@ -1,147 +1,27 @@
using Mirror; using OWML.Common;
using OWML.Common;
using QSB.Messaging; using QSB.Messaging;
using QSB.MeteorSync.WorldObjects; using QSB.MeteorSync.WorldObjects;
using QSB.Utility; using QSB.Utility;
using UnityEngine;
namespace QSB.MeteorSync.Messages; namespace QSB.MeteorSync.Messages;
/// called when we request a resync on client join /// <summary>
/// pain /// original integrity, leash length
public class FragmentInitialStateMessage : QSBWorldObjectMessage<QSBFragment> /// </summary>
public class FragmentInitialStateMessage : QSBWorldObjectMessage<QSBFragment, (float OrigIntegrity, float LeashLength)>
{ {
private float Integrity; public FragmentInitialStateMessage(float origIntegrity, float leashLength) : base((origIntegrity, leashLength)) { }
private float OrigIntegrity;
private float LeashLength;
private bool IsDetached;
private bool IsThruWhiteHole;
private Vector3 RelPos;
private Quaternion RelRot;
private Vector3 RelVel;
private Vector3 RelAngVel;
public FragmentInitialStateMessage(QSBFragment qsbFragment)
{
Integrity = qsbFragment.AttachedObject._integrity;
OrigIntegrity = qsbFragment.AttachedObject._origIntegrity;
LeashLength = qsbFragment.LeashLength;
IsDetached = qsbFragment.IsDetached;
if (IsDetached)
{
IsThruWhiteHole = qsbFragment.IsThruWhiteHole;
var body = qsbFragment.Body;
var refBody = qsbFragment.RefBody;
var pos = body.GetPosition();
RelPos = refBody.transform.ToRelPos(pos);
RelRot = refBody.transform.ToRelRot(body.GetRotation());
RelVel = refBody.ToRelVel(body.GetVelocity(), pos);
RelAngVel = refBody.ToRelAngVel(body.GetAngularVelocity());
}
}
public override void Serialize(NetworkWriter writer)
{
base.Serialize(writer);
writer.Write(Integrity);
writer.Write(OrigIntegrity);
writer.Write(LeashLength);
writer.Write(IsDetached);
if (IsDetached)
{
writer.Write(IsThruWhiteHole);
writer.Write(RelPos);
writer.Write(RelRot);
writer.Write(RelVel);
writer.Write(RelAngVel);
}
}
public override void Deserialize(NetworkReader reader)
{
base.Deserialize(reader);
Integrity = reader.Read<float>();
OrigIntegrity = reader.Read<float>();
LeashLength = reader.Read<float>();
IsDetached = reader.Read<bool>();
if (IsDetached)
{
IsThruWhiteHole = reader.Read<bool>();
RelPos = reader.ReadVector3();
RelRot = reader.ReadQuaternion();
RelVel = reader.ReadVector3();
RelAngVel = reader.ReadVector3();
}
}
public override void OnReceiveRemote() public override void OnReceiveRemote()
{ {
WorldObject.AttachedObject._origIntegrity = OrigIntegrity; WorldObject.AttachedObject._origIntegrity = Data.OrigIntegrity;
WorldObject.LeashLength = LeashLength; if (WorldObject.LeashLength == null)
if (!OWMath.ApproxEquals(WorldObject.AttachedObject._integrity, Integrity))
{ {
WorldObject.AttachedObject._integrity = Integrity; WorldObject.LeashLength = Data.LeashLength;
WorldObject.AttachedObject.CallOnTakeDamage();
} }
else
if (IsDetached && !WorldObject.IsDetached)
{ {
// the detach is delayed, so wait until that happens DebugLog.ToConsole($"leash length for {WorldObject} already set", MessageType.Warning);
Delay.RunWhen(() => WorldObject.IsDetached, () =>
{
var body = WorldObject.Body;
if (IsThruWhiteHole && !WorldObject.IsThruWhiteHole)
{
var whiteHoleVolume = MeteorManager.WhiteHoleVolume;
var attachedFluidDetector = body.GetAttachedFluidDetector();
var attachedForceDetector = body.GetAttachedForceDetector();
if (attachedFluidDetector is ConstantFluidDetector constantFluidDetector)
{
constantFluidDetector.SetDetectableFluid(whiteHoleVolume._fluidVolume);
}
if (attachedForceDetector is ConstantForceDetector constantForceDetector)
{
constantForceDetector.ClearAllFields();
}
WorldObject.DetachableFragment.ChangeFragmentSector(whiteHoleVolume._whiteHoleSector,
whiteHoleVolume._whiteHoleProxyShadowSuperGroup);
WorldObject.DetachableFragment.EndWarpScaling();
body.gameObject.AddComponent<DebrisLeash>().Init(whiteHoleVolume._whiteHoleBody, WorldObject.LeashLength);
whiteHoleVolume._ejectedBodyList.Add(body);
}
else if (!IsThruWhiteHole && WorldObject.IsThruWhiteHole)
{
// should only happen if client is way too far ahead and they try to connect. we fail here.
DebugLog.ToConsole($"{WorldObject} is thru white hole, but msg is not. fuck", MessageType.Error);
return;
}
if (WorldObject.IsThruWhiteHole)
{
var debrisLeash = body.GetComponent<DebrisLeash>();
debrisLeash._deccelerating = false;
debrisLeash.enabled = true;
}
var refBody = WorldObject.RefBody;
var pos = refBody.transform.FromRelPos(RelPos);
body.SetPosition(pos);
body.SetRotation(refBody.transform.FromRelRot(RelRot));
body.SetVelocity(refBody.FromRelVel(RelVel, pos));
body.SetAngularVelocity(refBody.FromRelAngVel(RelAngVel));
});
}
else if (!IsDetached && WorldObject.IsDetached)
{
// should only happen if client is way too far ahead and they try to connect. we fail here.
DebugLog.ToConsole($"{WorldObject} is detached, but msg is not. fuck", MessageType.Error);
} }
} }
} }

View File

@ -0,0 +1,10 @@
using QSB.Messaging;
using QSB.MeteorSync.WorldObjects;
namespace QSB.MeteorSync.Messages;
public class FragmentIntegrityMessage : QSBWorldObjectMessage<QSBFragment, float>
{
public FragmentIntegrityMessage(float integrity) : base(integrity) { }
public override void OnReceiveRemote() => WorldObject.SetIntegrity(Data);
}

View File

@ -1,33 +1,18 @@
using Mirror; using QSB.Messaging;
using QSB.Messaging;
using QSB.MeteorSync.WorldObjects; using QSB.MeteorSync.WorldObjects;
using QSB.WorldSync;
namespace QSB.MeteorSync.Messages; namespace QSB.MeteorSync.Messages;
public class MeteorLaunchMessage : QSBWorldObjectMessage<QSBMeteorLauncher> public class MeteorLaunchMessage : QSBWorldObjectMessage<QSBMeteorLauncher, (int MeteorId, float LaunchSpeed)>
{ {
private int MeteorId; public MeteorLaunchMessage(MeteorController meteor, float launchSpeed) : base((
private float LaunchSpeed; meteor.GetWorldObject<QSBMeteor>().ObjectId,
launchSpeed
)) { }
public MeteorLaunchMessage(QSBMeteorLauncher qsbMeteorLauncher) public override void OnReceiveRemote() => WorldObject.LaunchMeteor(
{ Data.MeteorId.GetWorldObject<QSBMeteor>().AttachedObject,
MeteorId = qsbMeteorLauncher.MeteorId; Data.LaunchSpeed
LaunchSpeed = qsbMeteorLauncher.LaunchSpeed; );
}
public override void Serialize(NetworkWriter writer)
{
base.Serialize(writer);
writer.Write(MeteorId);
writer.Write(LaunchSpeed);
}
public override void Deserialize(NetworkReader reader)
{
base.Deserialize(reader);
MeteorId = reader.Read<int>();
LaunchSpeed = reader.Read<float>();
}
public override void OnReceiveRemote() => WorldObject.LaunchMeteor(MeteorId, LaunchSpeed);
} }

View File

@ -3,6 +3,9 @@ using QSB.MeteorSync.WorldObjects;
namespace QSB.MeteorSync.Messages; namespace QSB.MeteorSync.Messages;
/// <summary>
/// for syncing impact with a remote player/probe
/// </summary>
public class MeteorSpecialImpactMessage : QSBWorldObjectMessage<QSBMeteor> public class MeteorSpecialImpactMessage : QSBWorldObjectMessage<QSBMeteor>
{ {
public override void OnReceiveRemote() => WorldObject.SpecialImpact(); public override void OnReceiveRemote() => WorldObject.SpecialImpact();

View File

@ -1,5 +1,6 @@
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using QSB.MeteorSync.WorldObjects; using QSB.MeteorSync.WorldObjects;
using QSB.Utility;
using QSB.WorldSync; using QSB.WorldSync;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -18,8 +19,14 @@ public class MeteorManager : WorldObjectManager
await UniTask.WaitUntil(() => LateInitializerManager.isDoneInitializing, cancellationToken: ct); await UniTask.WaitUntil(() => LateInitializerManager.isDoneInitializing, cancellationToken: ct);
WhiteHoleVolume = QSBWorldSync.GetUnityObjects<WhiteHoleVolume>().First(); WhiteHoleVolume = QSBWorldSync.GetUnityObjects<WhiteHoleVolume>().First();
QSBWorldSync.Init<QSBMeteorLauncher, MeteorLauncher>();
QSBWorldSync.Init<QSBMeteor, MeteorController>();
QSBWorldSync.Init<QSBFragment, FragmentIntegrity>(); QSBWorldSync.Init<QSBFragment, FragmentIntegrity>();
var meteorLaunchers = QSBWorldSync.GetUnityObjects<MeteorLauncher>().SortDeterministic().ToList();
QSBWorldSync.Init<QSBMeteorLauncher, MeteorLauncher>(meteorLaunchers);
// order by pool instead of using SortDeterministic
var meteors = meteorLaunchers.SelectMany(x =>
x._meteorPool.EmptyIfNull().Concat(x._dynamicMeteorPool.EmptyIfNull()));
QSBWorldSync.Init<QSBMeteor, MeteorController>(meteors);
} }
} }

View File

@ -19,13 +19,8 @@ public class MeteorServerPatches : QSBPatch
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(MeteorLauncher), nameof(MeteorLauncher.FixedUpdate))] [HarmonyPatch(typeof(MeteorLauncher), nameof(MeteorLauncher.FixedUpdate))]
public static bool FixedUpdate(MeteorLauncher __instance) public static bool MeteorLauncher_FixedUpdate(MeteorLauncher __instance)
{ {
if (!QSBWorldSync.AllObjectsReady)
{
return true;
}
if (__instance._launchedMeteors != null) if (__instance._launchedMeteors != null)
{ {
for (var i = __instance._launchedMeteors.Count - 1; i >= 0; i--) for (var i = __instance._launchedMeteors.Count - 1; i >= 0; i--)
@ -63,13 +58,13 @@ public class MeteorServerPatches : QSBPatch
if (!__instance._areParticlesPlaying) if (!__instance._areParticlesPlaying)
{ {
__instance._areParticlesPlaying = true; __instance._areParticlesPlaying = true;
foreach (var particleSystem in __instance._launchParticles) foreach (var launchParticle in __instance._launchParticles)
{ {
particleSystem.Play(); launchParticle.Play();
} }
var qsbMeteorLauncher = __instance.GetWorldObject<QSBMeteorLauncher>(); __instance.GetWorldObject<QSBMeteorLauncher>()
qsbMeteorLauncher.SendMessage(new MeteorPreLaunchMessage()); .SendMessage(new MeteorPreLaunchMessage());
} }
if (Time.time > __instance._lastLaunchTime + __instance._launchDelay + 2.3f) if (Time.time > __instance._lastLaunchTime + __instance._launchDelay + 2.3f)
@ -78,9 +73,9 @@ public class MeteorServerPatches : QSBPatch
__instance._lastLaunchTime = Time.time; __instance._lastLaunchTime = Time.time;
__instance._launchDelay = Random.Range(__instance._minInterval, __instance._maxInterval); __instance._launchDelay = Random.Range(__instance._minInterval, __instance._maxInterval);
__instance._areParticlesPlaying = false; __instance._areParticlesPlaying = false;
foreach (var particleSystem in __instance._launchParticles) foreach (var launchParticle in __instance._launchParticles)
{ {
particleSystem.Stop(); launchParticle.Stop();
} }
} }
} }
@ -90,7 +85,7 @@ public class MeteorServerPatches : QSBPatch
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(MeteorLauncher), nameof(MeteorLauncher.LaunchMeteor))] [HarmonyPatch(typeof(MeteorLauncher), nameof(MeteorLauncher.LaunchMeteor))]
public static bool LaunchMeteor(MeteorLauncher __instance) public static bool MeteorLauncher_LaunchMeteor(MeteorLauncher __instance)
{ {
var flag = __instance._dynamicMeteorPool != null && (__instance._meteorPool == null || Random.value < __instance._dynamicProbability); var flag = __instance._dynamicMeteorPool != null && (__instance._meteorPool == null || Random.value < __instance._dynamicProbability);
MeteorController meteorController = null; MeteorController meteorController = null;
@ -125,13 +120,9 @@ public class MeteorServerPatches : QSBPatch
if (meteorController != null) if (meteorController != null)
{ {
var qsbMeteorLauncher = __instance.GetWorldObject<QSBMeteorLauncher>(); var launchSpeed = Random.Range(__instance._minLaunchSpeed, __instance._maxLaunchSpeed);
var qsbMeteor = meteorController.GetWorldObject<QSBMeteor>();
qsbMeteorLauncher.MeteorId = qsbMeteor.ObjectId; var linearVelocity = __instance._parentBody.GetPointVelocity(__instance.transform.position) + __instance.transform.TransformDirection(__instance._launchDirection) * launchSpeed;
qsbMeteorLauncher.LaunchSpeed = Random.Range(__instance._minLaunchSpeed, __instance._maxLaunchSpeed);
var linearVelocity = __instance._parentBody.GetPointVelocity(__instance.transform.position) + (__instance.transform.TransformDirection(__instance._launchDirection) * qsbMeteorLauncher.LaunchSpeed);
var angularVelocity = __instance.transform.forward * 2f; var angularVelocity = __instance.transform.forward * 2f;
meteorController.Launch(null, __instance.transform.position, __instance.transform.rotation, linearVelocity, angularVelocity); meteorController.Launch(null, __instance.transform.position, __instance.transform.rotation, linearVelocity, angularVelocity);
if (__instance._audioSector.ContainsOccupant(DynamicOccupant.Player)) if (__instance._audioSector.ContainsOccupant(DynamicOccupant.Player))
@ -140,32 +131,18 @@ public class MeteorServerPatches : QSBPatch
__instance._launchSource.PlayOneShot(AudioType.BH_MeteorLaunch); __instance._launchSource.PlayOneShot(AudioType.BH_MeteorLaunch);
} }
qsbMeteorLauncher.SendMessage(new MeteorLaunchMessage(qsbMeteorLauncher)); __instance.GetWorldObject<QSBMeteorLauncher>()
.SendMessage(new MeteorLaunchMessage(meteorController, launchSpeed));
} }
return false; return false;
} }
[HarmonyPostfix]
[HarmonyPatch(typeof(MeteorController), nameof(MeteorController.Impact))]
public static void Impact(MeteorController __instance,
GameObject hitObject, Vector3 impactPoint, Vector3 impactVel)
{
var qsbMeteor = __instance.GetWorldObject<QSBMeteor>();
if (QSBMeteor.IsSpecialImpact(hitObject))
{
qsbMeteor.SendMessage(new MeteorSpecialImpactMessage());
}
}
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(FragmentIntegrity), nameof(FragmentIntegrity.AddDamage))] [HarmonyPatch(typeof(FragmentIntegrity), nameof(FragmentIntegrity.AddDamage))]
public static void AddDamage(FragmentIntegrity __instance, public static void FragmentIntegrity_AddDamage(FragmentIntegrity __instance) =>
float damage) __instance.GetWorldObject<QSBFragment>()
{ .SendMessage(new FragmentIntegrityMessage(__instance._integrity));
var qsbFragment = __instance.GetWorldObject<QSBFragment>();
qsbFragment.SendMessage(new FragmentDamageMessage(damage));
}
} }
/// <summary> /// <summary>
@ -177,97 +154,13 @@ public class MeteorClientPatches : QSBPatch
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(MeteorLauncher), nameof(MeteorLauncher.FixedUpdate))] [HarmonyPatch(typeof(MeteorLauncher), nameof(MeteorLauncher.FixedUpdate))]
public static bool FixedUpdate(MeteorLauncher __instance) public static bool MeteorLauncher_FixedUpdate()
=> false; => false;
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(MeteorLauncher), nameof(MeteorLauncher.LaunchMeteor))] [HarmonyPatch(typeof(FragmentIntegrity), nameof(FragmentIntegrity.AddDamage))]
public static bool LaunchMeteor(MeteorLauncher __instance) public static bool FragmentIntegrity_AddDamage()
{ => false;
var qsbMeteorLauncher = __instance.GetWorldObject<QSBMeteorLauncher>();
MeteorController meteorController = null;
QSBMeteor qsbMeteor;
bool MeteorMatches(MeteorController x)
{
qsbMeteor = x.GetWorldObject<QSBMeteor>();
return qsbMeteor.ObjectId == qsbMeteorLauncher.MeteorId;
}
if (__instance._meteorPool != null)
{
meteorController = __instance._meteorPool.Find(MeteorMatches);
if (meteorController != null)
{
meteorController.Initialize(__instance.transform, __instance._detectableField, __instance._detectableFluid);
}
}
else if (__instance._dynamicMeteorPool != null)
{
meteorController = __instance._dynamicMeteorPool.Find(MeteorMatches);
if (meteorController != null)
{
meteorController.Initialize(__instance.transform, null, null);
}
}
if (meteorController != null)
{
var linearVelocity = __instance._parentBody.GetPointVelocity(__instance.transform.position) + (__instance.transform.TransformDirection(__instance._launchDirection) * qsbMeteorLauncher.LaunchSpeed);
var angularVelocity = __instance.transform.forward * 2f;
meteorController.Launch(null, __instance.transform.position, __instance.transform.rotation, linearVelocity, angularVelocity);
if (__instance._audioSector.ContainsOccupant(DynamicOccupant.Player))
{
__instance._launchSource.pitch = Random.Range(0.4f, 0.6f);
__instance._launchSource.PlayOneShot(AudioType.BH_MeteorLaunch);
}
}
else
{
DebugLog.ToConsole($"{qsbMeteorLauncher} - could not find meteor {qsbMeteorLauncher.MeteorId} in pool", MessageType.Warning);
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(MeteorController), nameof(MeteorController.Impact))]
public static bool Impact(MeteorController __instance,
GameObject hitObject, Vector3 impactPoint, Vector3 impactVel)
{
__instance._intactRenderer.enabled = false;
__instance._impactLight.enabled = true;
__instance._impactLight.intensity = __instance._impactLightCurve.Evaluate(0f);
var rotation = Quaternion.LookRotation(impactVel);
foreach (var particleSystem in __instance._impactParticles)
{
particleSystem.transform.rotation = rotation;
particleSystem.Play();
}
__instance._impactSource.PlayOneShot(AudioType.BH_MeteorImpact);
foreach (var owCollider in __instance._owColliders)
{
owCollider.SetActivation(false);
}
__instance._owRigidbody.MakeKinematic();
__instance.transform.SetParent(hitObject.GetAttachedOWRigidbody().transform);
FragmentSurfaceProxy.UntrackMeteor(__instance);
FragmentCollisionProxy.UntrackMeteor(__instance);
__instance._ignoringCollisions = false;
__instance._hasImpacted = true;
__instance._impactTime = Time.time;
var qsbMeteor = __instance.GetWorldObject<QSBMeteor>();
if (QSBMeteor.IsSpecialImpact(hitObject))
{
qsbMeteor.SendMessage(new MeteorSpecialImpactMessage());
}
return false;
}
} }
/// <summary> /// <summary>
@ -277,86 +170,46 @@ public class MeteorPatches : QSBPatch
{ {
public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect;
[HarmonyPostfix]
[HarmonyPatch(typeof(MeteorController), nameof(MeteorController.Impact))]
public static void MeteorController_Impact(MeteorController __instance,
GameObject hitObject, Vector3 impactPoint, Vector3 impactVel)
{
if (QSBMeteor.IsSpecialImpact(hitObject))
{
__instance.GetWorldObject<QSBMeteor>()
.SendMessage(new MeteorSpecialImpactMessage());
}
}
[HarmonyPrefix] [HarmonyPrefix]
[HarmonyPatch(typeof(DetachableFragment), nameof(DetachableFragment.Detach))] [HarmonyPatch(typeof(DetachableFragment), nameof(DetachableFragment.Detach))]
public static void Detach_Prefix(DetachableFragment __instance, out FragmentIntegrity __state) => public static void DetachableFragment_Detach_Prefix(DetachableFragment __instance, out FragmentIntegrity __state) =>
// this gets set to null in Detach, so store it here and and then restore it in postfix // this gets set to null in Detach, so store it here and and then restore it in postfix
__state = __instance._fragmentIntegrity; __state = __instance._fragmentIntegrity;
[HarmonyPostfix] [HarmonyPostfix]
[HarmonyPatch(typeof(DetachableFragment), nameof(DetachableFragment.Detach))] [HarmonyPatch(typeof(DetachableFragment), nameof(DetachableFragment.Detach))]
public static void Detach_Postfix(DetachableFragment __instance, FragmentIntegrity __state) => public static void DetachableFragment_Detach_Postfix(DetachableFragment __instance, FragmentIntegrity __state) =>
__instance._fragmentIntegrity = __state; __instance._fragmentIntegrity = __state;
[HarmonyPrefix] [HarmonyPostfix]
[HarmonyPatch(typeof(DebrisLeash), nameof(DebrisLeash.MoveByDistance))] [HarmonyPatch(typeof(DebrisLeash), nameof(DebrisLeash.Init))]
public static bool MoveByDistance(DebrisLeash __instance, public static void DebrisLeash_Init(DebrisLeash __instance)
float distance)
{ {
if (__instance._detachableFragment == null || __instance._detachableFragment._fragmentIntegrity == null) if (__instance._detachableFragment == null || __instance._detachableFragment._fragmentIntegrity == null)
{ {
return true; return;
} }
var qsbFragment = __instance._detachableFragment._fragmentIntegrity.GetWorldObject<QSBFragment>(); var qsbFragment = __instance._detachableFragment._fragmentIntegrity.GetWorldObject<QSBFragment>();
if (qsbFragment.LeashLength != null)
if (__instance.enabled)
{ {
var vector = __instance._attachedBody.GetPosition() - __instance._anchorBody.GetPosition(); __instance._leashLength = (float)qsbFragment.LeashLength;
var d = Mathf.Min(distance, qsbFragment.LeashLength - vector.magnitude);
__instance._attachedBody.SetPosition(__instance._anchorBody.GetPosition() + (vector.normalized * d));
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(DebrisLeash), nameof(DebrisLeash.FixedUpdate))]
public static bool FixedUpdate(DebrisLeash __instance)
{
if (__instance._detachableFragment == null || __instance._detachableFragment._fragmentIntegrity == null)
{
return true;
}
if (!QSBWorldSync.AllObjectsReady)
{
return true;
}
var qsbFragment = __instance._detachableFragment._fragmentIntegrity.GetWorldObject<QSBFragment>();
if (!__instance._deccelerating)
{
var num = Vector3.Distance(__instance._attachedBody.GetPosition(), __instance._anchorBody.GetPosition());
var num2 = Mathf.Pow(__instance._attachedBody.GetVelocity().magnitude, 2f) / (2f * __instance._deccel);
var vector = __instance._attachedBody.GetVelocity() - __instance._anchorBody.GetVelocity();
if (num >= qsbFragment.LeashLength - num2 && vector.magnitude > 0.1f)
{
__instance._deccelerating = true;
return false;
}
} }
else else
{ {
var vector2 = __instance._attachedBody.GetVelocity() - __instance._anchorBody.GetVelocity(); DebugLog.ToConsole($"DebrisLeash.Init called for {qsbFragment} before LeashLength was set", MessageType.Warning);
var velocityChange = -vector2.normalized * Mathf.Min(__instance._deccel * Time.deltaTime, vector2.magnitude); }
if (velocityChange.magnitude < 0.01f)
{
__instance._attachedBody.SetVelocity(__instance._anchorBody.GetVelocity());
__instance._deccelerating = false;
if (__instance._detachableFragment != null)
{
__instance._detachableFragment.ComeToRest(__instance._anchorBody);
}
__instance.enabled = false;
return false;
}
__instance._attachedBody.AddVelocityChange(velocityChange);
}
return false;
} }
} }

View File

@ -11,27 +11,34 @@ public class QSBFragment : WorldObject<FragmentIntegrity>
{ {
public override async UniTask Init(CancellationToken ct) public override async UniTask Init(CancellationToken ct)
{ {
DetachableFragment = AttachedObject.GetComponent<DetachableFragment>();
if (QSBCore.IsHost) if (QSBCore.IsHost)
{ {
LeashLength = Random.Range(MeteorManager.WhiteHoleVolume._debrisDistMin, MeteorManager.WhiteHoleVolume._debrisDistMax); LeashLength = Random.Range(MeteorManager.WhiteHoleVolume._debrisDistMin, MeteorManager.WhiteHoleVolume._debrisDistMax);
// SetIntegrity(0);
} }
} }
public override void SendInitialState(uint to) => public override void SendInitialState(uint to)
this.SendMessage(new FragmentInitialStateMessage(this) { To = to }); {
this.SendMessage(new FragmentInitialStateMessage(AttachedObject._origIntegrity, (float)LeashLength) { To = to });
public DetachableFragment DetachableFragment; this.SendMessage(new FragmentIntegrityMessage(AttachedObject._integrity));
public bool IsDetached => DetachableFragment != null && DetachableFragment._isDetached; }
public bool IsThruWhiteHole => IsDetached && DetachableFragment._sector != null &&
DetachableFragment._sector._parentSector == MeteorManager.WhiteHoleVolume._whiteHoleSector; public void SetIntegrity(float integrity)
public OWRigidbody RefBody => IsThruWhiteHole ? MeteorManager.WhiteHoleVolume._whiteHoleBody : Locator._brittleHollow._owRigidbody; {
public OWRigidbody Body => IsDetached ? AttachedObject.transform.parent.parent.GetAttachedOWRigidbody() : null; if (OWMath.ApproxEquals(AttachedObject._integrity, integrity))
{
/// what the leash length will be when we eventually detach and fall thru white hole return;
public float LeashLength; }
public void AddDamage(float damage) AttachedObject._integrity = integrity;
=> AttachedObject.AddDamage(damage); AttachedObject.CallOnTakeDamage();
}
/// <summary>
/// what the leash length will be when we eventually detach and fall thru white hole.
/// <para/>
/// generated by the server and sent to clients in the initial state message.
/// </summary>
public float? LeashLength;
} }

View File

@ -1,26 +1,38 @@
using QSB.WorldSync; using Cysharp.Threading.Tasks;
using QSB.WorldSync;
using System.Threading;
using UnityEngine; using UnityEngine;
namespace QSB.MeteorSync.WorldObjects; namespace QSB.MeteorSync.WorldObjects;
public class QSBMeteor : WorldObject<MeteorController> public class QSBMeteor : WorldObject<MeteorController>
{ {
private QSBMeteorLauncher _qsbMeteorLauncher;
public override async UniTask Init(CancellationToken ct)
{
var meteorLauncher = AttachedObject._suspendRoot.GetComponent<MeteorLauncher>();
await UniTask.WaitUntil(() => QSBWorldSync.AllObjectsAdded, cancellationToken: ct);
_qsbMeteorLauncher = meteorLauncher.GetWorldObject<QSBMeteorLauncher>();
}
public override void SendInitialState(uint to) public override void SendInitialState(uint to)
{ {
// todo SendInitialState // todo SendInitialState
} }
public static bool IsSpecialImpact(GameObject go) => public static bool IsSpecialImpact(GameObject go) =>
go == Locator.GetPlayerCollider().gameObject || (Locator.GetProbe() != null && go == Locator.GetProbe()._anchor._collider.gameObject); go == Locator.GetPlayerCollider().gameObject ||
Locator.GetProbe() != null && go == Locator.GetProbe()._anchor._collider.gameObject;
public void SpecialImpact() public void SpecialImpact()
{ {
AttachedObject._intactRenderer.enabled = false; AttachedObject._intactRenderer.enabled = false;
AttachedObject._impactLight.enabled = true; AttachedObject._impactLight.enabled = true;
AttachedObject._impactLight.intensity = AttachedObject._impactLightCurve.Evaluate(0f); AttachedObject._impactLight.intensity = AttachedObject._impactLightCurve.Evaluate(0f);
foreach (var particleSystem in AttachedObject._impactParticles) foreach (var impactParticle in AttachedObject._impactParticles)
{ {
particleSystem.Play(); impactParticle.Play();
} }
AttachedObject._impactSource.PlayOneShot(AudioType.BH_MeteorImpact); AttachedObject._impactSource.PlayOneShot(AudioType.BH_MeteorImpact);

View File

@ -1,34 +1,52 @@
using QSB.WorldSync; using Cysharp.Threading.Tasks;
using QSB.Utility;
using QSB.WorldSync;
using System.Linq;
using System.Threading;
using UnityEngine;
namespace QSB.MeteorSync.WorldObjects; namespace QSB.MeteorSync.WorldObjects;
public class QSBMeteorLauncher : WorldObject<MeteorLauncher> public class QSBMeteorLauncher : WorldObject<MeteorLauncher>
{ {
private QSBMeteor[] _qsbMeteors;
public override async UniTask Init(CancellationToken ct)
{
var meteors = AttachedObject._meteorPool.EmptyIfNull().Concat(AttachedObject._dynamicMeteorPool.EmptyIfNull());
await UniTask.WaitUntil(() => QSBWorldSync.AllObjectsAdded, cancellationToken: ct);
_qsbMeteors = meteors.Select(x => x.GetWorldObject<QSBMeteor>()).ToArray();
}
public override void SendInitialState(uint to) public override void SendInitialState(uint to)
{ {
// todo SendInitialState // todo SendInitialState
} }
public int MeteorId;
public float LaunchSpeed;
public void PreLaunchMeteor() public void PreLaunchMeteor()
{ {
foreach (var particleSystem in AttachedObject._launchParticles) foreach (var launchParticle in AttachedObject._launchParticles)
{ {
particleSystem.Play(); launchParticle.Play();
} }
} }
public void LaunchMeteor(int meteorId, float launchSpeed) public void LaunchMeteor(MeteorController meteor, float launchSpeed)
{ {
MeteorId = meteorId; meteor.Initialize(AttachedObject.transform, AttachedObject._detectableField, AttachedObject._detectableFluid);
LaunchSpeed = launchSpeed;
AttachedObject.LaunchMeteor(); var linearVelocity = AttachedObject._parentBody.GetPointVelocity(AttachedObject.transform.position) + AttachedObject.transform.TransformDirection(AttachedObject._launchDirection) * launchSpeed;
foreach (var particleSystem in AttachedObject._launchParticles) var angularVelocity = AttachedObject.transform.forward * 2f;
meteor.Launch(null, AttachedObject.transform.position, AttachedObject.transform.rotation, linearVelocity, angularVelocity);
if (AttachedObject._audioSector.ContainsOccupant(DynamicOccupant.Player))
{ {
particleSystem.Stop(); AttachedObject._launchSource.pitch = Random.Range(0.4f, 0.6f);
AttachedObject._launchSource.PlayOneShot(AudioType.BH_MeteorLaunch);
}
foreach (var launchParticle in AttachedObject._launchParticles)
{
launchParticle.Stop();
} }
} }
} }

View File

@ -5,8 +5,10 @@ using QSB.OrbSync.Messages;
using QSB.OrbSync.TransformSync; using QSB.OrbSync.TransformSync;
using QSB.Utility; using QSB.Utility;
using QSB.WorldSync; using QSB.WorldSync;
using System;
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object;
namespace QSB.OrbSync.WorldObjects; namespace QSB.OrbSync.WorldObjects;
@ -37,7 +39,7 @@ public class QSBOrb : WorldObject<NomaiInterfaceOrb>
public override void SendInitialState(uint to) public override void SendInitialState(uint to)
{ {
this.SendMessage(new OrbDragMessage(AttachedObject._isBeingDragged) { To = to }); this.SendMessage(new OrbDragMessage(AttachedObject._isBeingDragged) { To = to });
var slotIndex = AttachedObject._slots.IndexOf(AttachedObject._occupiedSlot); var slotIndex = Array.IndexOf(AttachedObject._slots, AttachedObject._occupiedSlot);
this.SendMessage(new OrbSlotMessage(slotIndex, false) { To = to }); this.SendMessage(new OrbSlotMessage(slotIndex, false) { To = to });
} }

View File

@ -74,7 +74,7 @@ public class PlayerJoinMessage : QSBMessage
var player = QSBPlayerManager.GetPlayer(From); var player = QSBPlayerManager.GetPlayer(From);
player.Name = PlayerName; player.Name = PlayerName;
DebugLog.ToAll($"{player} joined!", MessageType.Info); DebugLog.ToAll($"{player.Name} joined!", MessageType.Info);
DebugLog.DebugWrite($"{player} joined. qsbVersion:{QSBVersion}, gameVersion:{GameVersion}, dlcInstalled:{DlcInstalled}", MessageType.Info); DebugLog.DebugWrite($"{player} joined. qsbVersion:{QSBVersion}, gameVersion:{GameVersion}, dlcInstalled:{DlcInstalled}", MessageType.Info);
} }

View File

@ -156,7 +156,7 @@ public static class Extensions
return y; return y;
} }
public static int IndexOf<T>(this T[] array, T value) => Array.IndexOf(array, value); public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source) => source ?? Enumerable.Empty<T>();
public static bool IsInRange<T>(this IList<T> list, int index) => index >= 0 && index < list.Count; public static bool IsInRange<T>(this IList<T> list, int index) => index >= 0 && index < list.Count;