Merge pull request #718 from qsb-dev/dev

1.5.0
- Fix some quantum objects in NH mods. They should move around now, at least.
- Fix most items in NH mods. Custom item logic will not work, but you can do normal item things (pick up, drop, insert into slot).
- Fix chat/HUD working in custom systems. It still does not work in the eye yet.
This commit is contained in:
Will Corby 2025-03-01 21:52:54 -08:00 committed by GitHub
commit 8edacfea52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 132 additions and 37 deletions

2
.editorconfig Normal file
View File

@ -0,0 +1,2 @@
[*.cs]
indent_style = tab

View File

@ -6,6 +6,7 @@ using NewHorizons.Components.Orbital;
using OWML.Common;
using QSB;
using QSB.HUD;
using QSB.HUD.Messages;
using QSB.Messaging;
using QSB.Utility;
using UnityEngine;
@ -33,6 +34,8 @@ namespace QSBNH
// Allow time for MultiplayerHUDManager.OnWakeUp to run
Delay.RunNextFrame(() =>
{
var triggers = new List<PlanetTrigger>();
var currentPlanets = NewHorizons.Main.BodyDict[NewHorizons.Main.Instance.CurrentStarSystem];
foreach (var planet in currentPlanets)
{
@ -62,9 +65,21 @@ namespace QSBNH
if (!nhAstro.isVanilla)
{
MultiplayerHUDManager.CreateTrigger(nhAstro.GetRootSector().gameObject, astroObjName);
triggers.Add(MultiplayerHUDManager.CreateTrigger(nhAstro.GetRootSector().gameObject, astroObjName));
}
}
foreach (var trigger in triggers)
{
if (!trigger._sector.ContainsOccupant(DynamicOccupant.Player))
{
continue;
}
MultiplayerHUDManager.HUDIconStack.Push(trigger.PlanetID);
new PlanetMessage(trigger.PlanetID).Send();
break;
}
});
}

View File

@ -375,24 +375,46 @@ public class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
CreatePlanetToSprite();
CreateTrigger("TowerTwin_Body/Sector_TowerTwin", AstroObject.Name.TowerTwin);
CreateTrigger("CaveTwin_Body/Sector_CaveTwin", AstroObject.Name.CaveTwin);
CreateTrigger("TimberHearth_Body/Sector_TH", AstroObject.Name.TimberHearth);
CreateTrigger("Moon_Body/Sector_THM", AstroObject.Name.TimberMoon);
CreateTrigger("BrittleHollow_Body/Sector_BH", AstroObject.Name.BrittleHollow);
CreateTrigger("VolcanicMoon_Body/Sector_VM", AstroObject.Name.VolcanicMoon);
CreateTrigger("GiantsDeep_Body/Sector_GD", AstroObject.Name.GiantsDeep);
CreateTrigger("DarkBramble_Body/Sector_DB", AstroObject.Name.DarkBramble);
CreateTrigger("Comet_Body/Sector_CO", AstroObject.Name.Comet);
CreateTrigger("WhiteHole_Body/Sector_WhiteHole", AstroObject.Name.WhiteHole);
CreateTrigger("RingWorld_Body/Sector_RingWorld", AstroObject.Name.RingWorld); // TODO : this doesnt work????
CreateTrigger("QuantumMoon_Body/Sector_QuantumMoon", AstroObject.Name.QuantumMoon);
var planetTriggers = new List<PlanetTrigger>
{
CreateTrigger("TowerTwin_Body/Sector_TowerTwin", AstroObject.Name.TowerTwin),
CreateTrigger("CaveTwin_Body/Sector_CaveTwin", AstroObject.Name.CaveTwin),
CreateTrigger("TimberHearth_Body/Sector_TH", AstroObject.Name.TimberHearth),
CreateTrigger("Moon_Body/Sector_THM", AstroObject.Name.TimberMoon),
CreateTrigger("BrittleHollow_Body/Sector_BH", AstroObject.Name.BrittleHollow),
CreateTrigger("VolcanicMoon_Body/Sector_VM", AstroObject.Name.VolcanicMoon),
CreateTrigger("GiantsDeep_Body/Sector_GD", AstroObject.Name.GiantsDeep),
CreateTrigger("DarkBramble_Body/Sector_DB", AstroObject.Name.DarkBramble),
CreateTrigger("Comet_Body/Sector_CO", AstroObject.Name.Comet),
CreateTrigger("WhiteHole_Body/Sector_WhiteHole", AstroObject.Name.WhiteHole),
CreateTrigger("RingWorld_Body/Sector_RingWorld", AstroObject.Name.RingWorld),
CreateTrigger("QuantumMoon_Body/Sector_QuantumMoon", AstroObject.Name.QuantumMoon)
};
HUDIconStack.Clear();
HUDIconStack.Push("__SPACE__");
HUDIconStack.Push("TimberHearth");
new PlanetMessage("TimberHearth").Send();
foreach (var trigger in planetTriggers)
{
if (trigger == null)
{
continue;
}
if (trigger._sector == null)
{
continue;
}
if (!trigger._sector.ContainsOccupant(DynamicOccupant.Player))
{
continue;
}
HUDIconStack.Push(trigger.PlanetID);
new PlanetMessage(trigger.PlanetID).Send();
break;
}
_textChat = multiplayerGroup.transform.Find("TextChat");
var inputFieldGO = _textChat.Find("InputField");
@ -561,6 +583,7 @@ public class MultiplayerHUDManager : MonoBehaviour, IAddComponentOnStart
triggerGO.transform.SetParent(parent.transform, false);
triggerGO.SetActive(false);
var trigger = triggerGO.AddComponent<PlanetTrigger>();
trigger.Reset(); // Force _sector to be found immediately
trigger.PlanetID = name;
triggerGO.SetActive(true);
return trigger;

View File

@ -1,4 +1,5 @@
using Cysharp.Threading.Tasks;
using HarmonyLib;
using OWML.Common;
using QSB.ItemSync.WorldObjects;
using QSB.ItemSync.WorldObjects.Items;
@ -6,6 +7,8 @@ using QSB.ItemSync.WorldObjects.Sockets;
using QSB.Utility;
using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;
@ -21,21 +24,66 @@ public class ItemManager : WorldObjectManager
DebugLog.DebugWrite("Building OWItems...", MessageType.Info);
// Items
QSBWorldSync.Init<QSBNomaiConversationStone, NomaiConversationStone>();
QSBWorldSync.Init<QSBScrollItem, ScrollItem>();
QSBWorldSync.Init<QSBSharedStone, SharedStone>();
QSBWorldSync.Init<QSBSimpleLanternItem, SimpleLanternItem>();
QSBWorldSync.Init<QSBSlideReelItem, SlideReelItem>();
QSBWorldSync.Init<QSBWarpCoreItem, WarpCoreItem>();
// dream lantern and vision torch are set up in their own managers
// the rest can just use the generic thing below because they dont have special things using them
// Use the basic QSBItem class for any items that do not require custom code through a derived class (mod compatibility)
// QSB addons can still define their own QSBItem derived classes and they will just get skipped here
var handledItemTypes = new HashSet<Type>(GetHandledItemTypes()); // set cuz we do Contains below
DebugLog.DebugWrite($"Handled OWItem types (the rest will get generic QSBItem support) are: {handledItemTypes.Join()}");
var otherItemlistToInitFrom = QSBWorldSync.GetUnityObjects<OWItem>()
.Where(x => !handledItemTypes.Contains(x.GetType()))
.SortDeterministic();
// could make a subclass for this but i dont care, and would have to filter out from reflection thing below
QSBWorldSync.Init<QSBItem<OWItem>, OWItem>(otherItemlistToInitFrom);
// Sockets
QSBWorldSync.Init<QSBItemSocket, OWItemSocket>();
// other drop targets that don't already have world objects
// breaks if mod adds custom subclass of IItemDropTarget and then makes WorldObject of that but can just fix that when that happens
var listToInitFrom = QSBWorldSync.GetUnityObjects<MonoBehaviour>()
.Where(x => x is IItemDropTarget and not (RaftDock or RaftController or PrisonCellElevator))
.SortDeterministic();
QSBWorldSync.Init<QSBOtherDropTarget, MonoBehaviour>(listToInitFrom);
}
/// <summary>
/// Gets all types that extend QSBItem and returns the list of OWItem types that are already handled by dedicated classes
/// </summary>
private static IEnumerable<Type> GetHandledItemTypes()
{
var assemblies = QSBCore.Addons.Values
.Select(x => x.GetType().Assembly)
.Append(typeof(QSBCore).Assembly);
if (QSBCore.QSBNHAssembly != null)
{
assemblies = assemblies.Append(QSBCore.QSBNHAssembly);
}
// If the class inherits from QSBItem<T>, this will return what T is else null
static Type GetTypeFromQSBItem(Type type)
{
if (type.IsInterface || type.IsAbstract || type.BaseType == null)
{
return null;
}
if (type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(QSBItem<>))
{
return type.BaseType.GetGenericArguments()[0];
}
else
{
return GetTypeFromQSBItem(type.BaseType);
}
}
return assemblies.SelectMany(x => x.GetTypes())
.Select(GetTypeFromQSBItem)
.Where(x => x != null)
.OrderBy(x => x.FullName);
}
}

View File

@ -12,6 +12,8 @@ using UnityEngine;
namespace QSB.ItemSync.WorldObjects.Items;
// not abstract so modded items can use this QSBItem<OWItem>
// could use QSBOtherItem subclass but i dont feel like it
public class QSBItem<T> : WorldObject<T>, IQSBItem
where T : OWItem
{

View File

@ -1,3 +0,0 @@
namespace QSB.ItemSync.WorldObjects.Items;
public class QSBNomaiConversationStone : QSBItem<NomaiConversationStone> { }

View File

@ -1,3 +0,0 @@
namespace QSB.ItemSync.WorldObjects.Items;
public class QSBScrollItem : QSBItem<ScrollItem> { }

View File

@ -1,3 +0,0 @@
namespace QSB.ItemSync.WorldObjects.Items;
public class QSBSharedStone : QSBItem<SharedStone> { }

View File

@ -1,3 +0,0 @@
namespace QSB.ItemSync.WorldObjects.Items;
public class QSBSimpleLanternItem : QSBItem<SimpleLanternItem> { }

View File

@ -3,4 +3,4 @@
public class QSBWarpCoreItem : QSBItem<WarpCoreItem>
{
public bool IsVesselCoreType() => AttachedObject.IsVesselCoreType();
}
}

View File

@ -71,6 +71,15 @@ public abstract class QSBQuantumObject<T> : WorldObject<T>, IQSBQuantumObject
if (attachedShapes.All(x => x.enabled && x.gameObject.activeInHierarchy && x.active))
{
IsEnabled = true;
// If the shapes are already enabled then OnShapeActivated won't get triggered until it turns off and on again
// Then nobody ends up controlling the object until that happens
// This doesn't affect base game because there are no quantum objects at spawn, but can affect NH mods
// In this case where the host has spawned in and the object is enabled with no ControllingPlayer, the host should take control
if (QSBCore.IsHost)
{
// first player is the host
ControllingPlayer = QSBPlayerManager.PlayerList[0].PlayerId;
}
}
else
{
@ -215,7 +224,7 @@ public abstract class QSBQuantumObject<T> : WorldObject<T>, IQSBQuantumObject
{
_visibleToProbes.Add(player);
}
AttachedObject._visibleInProbeSnapshot = true;
return;
}
@ -261,7 +270,7 @@ public abstract class QSBQuantumObject<T> : WorldObject<T>, IQSBQuantumObject
{
_visibleToProbes.Remove(player);
}
AttachedObject._visibleInProbeSnapshot = _visibleToProbes.Any();
}

View File

@ -1,6 +1,8 @@
using Cysharp.Threading.Tasks;
using OWML.Common;
using QSB.Messaging;
using QSB.Player;
using QSB.QuantumSync.Messages;
using QSB.Utility;
using QSB.WorldSync;
using System;
@ -35,7 +37,13 @@ public class QSBSocketedQuantumObject : QSBQuantumObject<SocketedQuantumObject>
{
base.SendInitialState(to);
// todo SendInitialState
if (this.ControllingPlayer != 0 && AttachedObject._occupiedSocket != null)
{
this.SendMessage(new SocketStateChangeMessage(
AttachedObject._occupiedSocket.GetWorldObject<QSBQuantumSocket>().ObjectId,
AttachedObject.transform.localRotation)
{ To = to });
}
}
public void MoveToSocket(uint playerId, int socketId, Quaternion localRotation)

View File

@ -4,7 +4,7 @@
"author": "Nebula, John, Alek, & Rai",
"name": "Quantum Space Buddies",
"uniqueName": "Raicuparta.QuantumSpaceBuddies",
"version": "1.4.2",
"version": "1.5.0",
"owmlVersion": "2.14.0",
"dependencies": [ "JohnCorby.VanillaFix" ],
"pathsToPreserve": [ "debugsettings.json" ],

View File

@ -114,7 +114,7 @@ See the [QSB Developer Wiki](https://github.com/qsb-dev/quantum-space-buddies/wi
### Contributers
- [xen](https://github.com/xen-42) - Help with syncing particle/sound effects, fixing lantern item bugs, and syncing addon data.
- [xen](https://github.com/xen-42) - Help with syncing particle/sound effects, fixing lantern item bugs, syncing addon data, and NH compatibility/bug fixes.
- [Moonstone](https://github.com/MoonstoneStudios) - Improvements to elevators and lifts.
- [Chris Yeninas](https://github.com/PhantomGamers) - Help with project files and GitHub workflows.
- [Locochoco](https://github.com/loco-choco) - Code improvements.