## Improvements
- The sphere colliders added to custom items and item sockets to make
them interactible no longer have collision by default. Mod authors can
restore the previous behavior by setting `colliderIsTrigger` to false.
Resolves #1054.
- Nomai scrolls created using `translatorTexts` no longer have
collision. This matches how scrolls behave in the base game.

## Bug fixes
- Fixed details not having collision if they were immediately
deactivated after being created (for instance, via
`activationCondition`)
- Fixed light sensors disabling themselves on activation if their detail
was immediately deactivated after being created
- Fixed quantum groups with number of objects equal to number of sockets
not having correct scales and including a duplicate of the final object.
This commit is contained in:
xen-42 2025-03-01 18:53:21 -05:00 committed by GitHub
commit df7b5207ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 90 additions and 26 deletions

View File

@ -25,5 +25,8 @@ public static class GroupsBuilder
go.GetAddComponent<SectorCullGroup>()._sector = sector;
go.GetAddComponent<SectorCollisionGroup>()._sector = sector;
go.GetAddComponent<SectorLightsCullGroup>()._sector = sector;
// SectorCollisionGroup is unique among the sector groups because it only attaches its event listener on Start() instead of Awake(), so if the detail gets immediately deactivated then it never gets attached. To avoid this, we'll attach the event listener manually (even if it means getting attached twice).
sector.OnSectorOccupantsUpdated += go.GetComponent<SectorCollisionGroup>().OnSectorOccupantsUpdated;
}
}

View File

@ -356,7 +356,10 @@ namespace NewHorizons.Builder.Props
singleLightSensor._sector.OnSectorOccupantsUpdated -= singleLightSensor.OnSectorOccupantsUpdated;
}
singleLightSensor._sector = sector;
singleLightSensor._sector.OnSectorOccupantsUpdated += singleLightSensor.OnSectorOccupantsUpdated;
if (singleLightSensor._sector != null)
{
singleLightSensor._sector.OnSectorOccupantsUpdated += singleLightSensor.OnSectorOccupantsUpdated;
}
}
}

View File

@ -1,6 +1,7 @@
using NewHorizons.Components.Props;
using NewHorizons.External.Modules.Props.Item;
using NewHorizons.Handlers;
using NewHorizons.Utility.OuterWilds;
using NewHorizons.Utility.OWML;
using OWML.Common;
using OWML.Utils;
@ -27,6 +28,8 @@ namespace NewHorizons.Builder.Props
public static NHItem MakeItem(GameObject go, GameObject planetGO, Sector sector, ItemInfo info, IModBehaviour mod)
{
go.layer = Layer.Interactible;
var itemName = info.name;
if (string.IsNullOrEmpty(itemName))
{
@ -93,10 +96,13 @@ namespace NewHorizons.Builder.Props
if (info.colliderRadius > 0f)
{
go.AddComponent<SphereCollider>().radius = info.colliderRadius;
var col = go.AddComponent<SphereCollider>();
col.radius = info.colliderRadius;
col.isTrigger = info.colliderIsTrigger;
go.GetAddComponent<OWCollider>();
}
// Wait until next frame when all objects are built before trying to socket the item if it has an initial socket
Delay.FireOnNextUpdate(() =>
{
if (item != null && !string.IsNullOrEmpty(info.pathToInitialSocket))
@ -133,6 +139,8 @@ namespace NewHorizons.Builder.Props
public static NHItemSocket MakeSocket(GameObject go, GameObject planetGO, Sector sector, ItemSocketInfo info)
{
go.layer = Layer.Interactible;
var itemType = EnumUtils.TryParse(info.itemType, true, out ItemType result) ? result : ItemType.Invalid;
if (itemType == ItemType.Invalid && !string.IsNullOrEmpty(info.itemType))
{
@ -151,15 +159,18 @@ namespace NewHorizons.Builder.Props
if (socket._socketTransform == null)
{
var socketGO = GeneralPropBuilder.MakeNew("Socket", planetGO, sector, info, defaultParent: go.transform);
if (info.colliderRadius > 0f)
{
go.AddComponent<SphereCollider>().radius = info.colliderRadius;
go.GetAddComponent<OWCollider>();
}
socketGO.SetActive(true);
socket._socketTransform = socketGO.transform;
}
if (info.colliderRadius > 0f)
{
var col = go.AddComponent<SphereCollider>();
col.radius = info.colliderRadius;
col.isTrigger = info.colliderIsTrigger;
go.GetAddComponent<OWCollider>();
}
socket.ItemType = itemType;
socket.UseGiveTakePrompts = info.useGiveTakePrompts;
socket.InsertCondition = info.insertCondition;
@ -169,6 +180,7 @@ namespace NewHorizons.Builder.Props
socket.ClearRemovalConditionOnInsert = info.clearRemovalConditionOnInsert;
socket.RemovalFact = info.removalFact;
// Wait until initial item socketing is done before considering the socket empty
Delay.FireInNUpdates(() =>
{
if (socket != null && !socket._socketedItem)

View File

@ -312,7 +312,7 @@ namespace NewHorizons.Builder.Props
scrollItem._nomaiWallText = nomaiWallText;
scrollItem.SetSector(sector);
customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Geo").GetComponent<MeshRenderer>().enabled = true;
customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Collider").gameObject.SetActive(true);
customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Collider").gameObject.SetActive(false);
nomaiWallText.gameObject.GetComponent<Collider>().enabled = false;
customScroll.GetComponent<CapsuleCollider>().enabled = true;
}

View File

@ -69,13 +69,17 @@ namespace NewHorizons.Builder.Props
{
(GameObject go, QuantumDetailInfo detail)[] propsInGroup = quantumGroup.details.Select(x => (DetailBuilder.GetGameObjectFromDetailInfo(x), x)).ToArray();
GameObject specialProp = null;
QuantumDetailInfo specialInfo = null;
if (propsInGroup.Length == quantumGroup.sockets.Length)
{
// Special case!
specialProp = propsInGroup.Last().go;
propsInGroup.Last().go.SetActive(false);
// Will be manually positioned on the sockets anyway
specialInfo = propsInGroup.Last().detail;
specialInfo.parentPath = string.Empty;
specialInfo.isRelativeToParent = false;
var propsInGroupList = propsInGroup.ToList();
propsInGroupList.RemoveAt(propsInGroup.Length - 1);
propsInGroup = propsInGroupList.ToArray();
@ -117,13 +121,13 @@ namespace NewHorizons.Builder.Props
prop.go.SetActive(true);
}
if (specialProp != null)
if (specialInfo != null)
{
// Can't have 4 objects in 4 slots
// Instead we have a duplicate of the final object for each slot, which appears when that slot is "empty"
for (int i = 0; i < sockets.Length; i++)
{
var emptySocketObject = DetailBuilder.Make(planetGO, sector, mod, specialProp, new DetailInfo());
{
var emptySocketObject = DetailBuilder.Make(planetGO, sector, mod, new DetailInfo(specialInfo));
var socket = sockets[i];
socket._emptySocketObject = emptySocketObject;
emptySocketObject.SetActive(socket._quantumObject == null);

View File

@ -206,7 +206,7 @@ namespace NewHorizons.Builder.Props.TranslatorText
scrollItem._nomaiWallText = nomaiWallText;
scrollItem.SetSector(sector);
customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Geo").GetComponent<MeshRenderer>().enabled = true;
customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Collider").gameObject.SetActive(true);
customScroll.transform.Find("Props_NOM_Scroll/Props_NOM_Scroll_Collider").gameObject.SetActive(false);
nomaiWallText.gameObject.GetComponent<Collider>().enabled = false;
customScroll.GetComponent<CapsuleCollider>().enabled = true;
scrollItem._nomaiWallText.HideImmediate();

View File

@ -32,6 +32,10 @@ namespace NewHorizons.External.Modules.Props.Item
/// </summary>
[DefaultValue(0.5f)] public float colliderRadius = 0.5f;
/// <summary>
/// Whether the added sphere collider will be a trigger (interactible but does not collide). Defaults to true.
/// </summary>
[DefaultValue(true)] public bool colliderIsTrigger = true;
/// <summary>
/// Whether the item can be dropped. Defaults to true.
/// </summary>
[DefaultValue(true)] public bool droppable = true;

View File

@ -19,6 +19,15 @@ namespace NewHorizons.External.Modules.Props.Item
/// </summary>
[DefaultValue(2f)] public float interactRange = 2f;
/// <summary>
/// Default collider radius when interacting with the socket
/// </summary>
[DefaultValue(0f)]
public float colliderRadius = 0f;
/// <summary>
/// Whether the added sphere collider will be a trigger (interactible but does not collide). Defaults to true.
/// </summary>
[DefaultValue(true)] public bool colliderIsTrigger = true;
/// <summary>
/// Whether to use "Give Item" / "Take Item" prompts instead of "Insert Item" / "Remove Item".
/// </summary>
public bool useGiveTakePrompts;
@ -46,10 +55,5 @@ namespace NewHorizons.External.Modules.Props.Item
/// A ship log fact to reveal when removing an item from this socket, or when the socket is empty.
/// </summary>
public string removalFact;
/// <summary>
/// Default collider radius when interacting with the socket
/// </summary>
[DefaultValue(0f)]
public float colliderRadius = 0f;
}
}

View File

@ -0,0 +1,24 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NewHorizons.Patches.EchoesOfTheEyePatches
{
[HarmonyPatch(typeof(SingleLightSensor))]
public static class SingleLightSensorPatches
{
[HarmonyPostfix]
[HarmonyPatch(nameof(SingleLightSensor.Start))]
public static void Start(SingleLightSensor __instance)
{
// SingleLightSensor assumes that the sector will be empty when it starts and disables itself, but this may not be true if it starts disabled and is activated later, or spawned via the API
if (__instance._sector && __instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe))
{
__instance.OnSectorOccupantsUpdated();
}
}
}
}

View File

@ -1121,6 +1121,11 @@
"format": "float",
"default": 0.5
},
"colliderIsTrigger": {
"type": "boolean",
"description": "Whether the added sphere collider will be a trigger (interactible but does not collide). Defaults to true.",
"default": true
},
"droppable": {
"type": "boolean",
"description": "Whether the item can be dropped. Defaults to true.",
@ -1230,6 +1235,17 @@
"format": "float",
"default": 2.0
},
"colliderRadius": {
"type": "number",
"description": "Default collider radius when interacting with the socket",
"format": "float",
"default": 0.0
},
"colliderIsTrigger": {
"type": "boolean",
"description": "Whether the added sphere collider will be a trigger (interactible but does not collide). Defaults to true.",
"default": true
},
"useGiveTakePrompts": {
"type": "boolean",
"description": "Whether to use \"Give Item\" / \"Take Item\" prompts instead of \"Insert Item\" / \"Remove Item\"."
@ -1259,12 +1275,6 @@
"removalFact": {
"type": "string",
"description": "A ship log fact to reveal when removing an item from this socket, or when the socket is empty."
},
"colliderRadius": {
"type": "number",
"description": "Default collider radius when interacting with the socket",
"format": "float",
"default": 0.0
}
}
},

View File

@ -4,7 +4,7 @@
"author": "xen, Bwc9876, JohnCorby, MegaPiggy, and friends",
"name": "New Horizons",
"uniqueName": "xen.NewHorizons",
"version": "1.27.0",
"version": "1.27.1",
"owmlVersion": "2.12.1",
"dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ],
"conflicts": [ "PacificEngine.OW_CommonResources" ],