## Minor features
- Added `repairVolumes` which act like the repairable nodes on the
"satellite" in the Zero-G cave. Resolves #1084
- NH will now recommend users installed the Chinese Font Fix mod if they
do not have it and the language is set to Simplified Chinese.
- Added `ClearSystem` to the API for Nomai Sky to use
This commit is contained in:
xen-42 2025-05-24 20:57:41 -04:00 committed by GitHub
commit 40cdfc94d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 405 additions and 6 deletions

View File

@ -6,6 +6,7 @@
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_3": "之后只需要系好安全带并启动自动驾驶即可进行跃迁!"
},
"UIDictionary": {
"INSTALL_OUTER_WILDS_CHINESE_FONT_FIX": "在享受故事之前建议安装Outer Wilds Chinese Fix这个Mod来解决缺字问题。",
"INTERSTELLAR_MODE": "恒星际模式",
"FREQ_STATUE": "挪麦雕像",
"FREQ_WARP_CORE": "反引力推进",

View File

@ -6,6 +6,7 @@
"NEW_HORIZONS_WARP_DRIVE_DIALOGUE_3": "Then just buckle up and engage the autopilot to warp there!"
},
"UIDictionary": {
"INSTALL_OUTER_WILDS_CHINESE_FONT_FIX": "What why is this message being shown when the language is not set to Chinese go report this bug!",
"INTERSTELLAR_MODE": "Interstellar Mode",
"FREQ_STATUE": "Nomai Statue",
"FREQ_WARP_CORE": "Anti-Graviton Flux",

View File

@ -459,11 +459,6 @@ namespace NewHorizons.Builder.ShipLog
private static MapModeObject ConstructPrimaryNode(List<NewHorizonsBody> bodies)
{
float DistanceFromPrimary(NewHorizonsBody body)
{
return Mathf.Max(body.Config.Orbit.semiMajorAxis, body.Config.Orbit.staticPosition?.Length() ?? 0f);
}
foreach (NewHorizonsBody body in bodies.Where(b => b.Config.Base.centerOfSolarSystem))
{
bodies.Sort((b, o) => b.Config.Orbit.semiMajorAxis.CompareTo(o.Config.Orbit.semiMajorAxis));

View File

@ -0,0 +1,52 @@
using NewHorizons.Builder.Props;
using NewHorizons.Components.Volumes;
using NewHorizons.External.Modules.Props;
using NewHorizons.External.Modules.Volumes.VolumeInfos;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace NewHorizons.Builder.Volumes
{
public static class RepairVolumeBuilder
{
public static NHRepairReceiver Make(GameObject planetGO, Sector sector, RepairVolumeInfo info)
{
// Repair receivers aren't technically volumes (no OWTriggerVolume) so we don't use the VolumeBuilder
var go = GeneralPropBuilder.MakeNew("RepairVolume", planetGO, sector, info);
if (info.shape != null)
{
ShapeBuilder.AddCollider(go, info.shape);
}
else
{
var shapeInfo = new ShapeInfo()
{
type = ShapeType.Sphere,
useShape = false,
hasCollision = true,
radius = info.radius,
};
ShapeBuilder.AddCollider(go, shapeInfo);
}
var repairReceiver = go.AddComponent<NHRepairReceiver>();
repairReceiver.displayName = info.name ?? info.rename ?? go.name;
repairReceiver.repairFraction = info.repairFraction;
repairReceiver.repairTime = info.repairTime;
repairReceiver.repairDistance = info.repairDistance;
repairReceiver.damagedCondition = info.damagedCondition;
repairReceiver.repairedCondition = info.repairedCondition;
repairReceiver.revealFact = info.revealFact;
go.SetActive(true);
return repairReceiver;
}
}
}

View File

@ -222,6 +222,13 @@ namespace NewHorizons.Builder.Volumes
VolumeBuilder.MakeAndEnable<ReferenceFrameBlockerVolume>(go, sector, referenceFrameBlockerVolume);
}
}
if (config.Volumes.repairVolumes != null)
{
foreach (var repairVolume in config.Volumes.repairVolumes)
{
RepairVolumeBuilder.Make(go, sector, repairVolume);
}
}
if (config.Volumes.speedTrapVolumes != null)
{
foreach (var speedTrapVolume in config.Volumes.speedTrapVolumes)

View File

@ -0,0 +1,103 @@
using NewHorizons.Handlers;
using OWML.Utils;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
namespace NewHorizons.Components.Volumes
{
// RepairReceiver isn't set up for proper subclassing but a subclass is necessary for the first-person manipulator to detect it.
public class NHRepairReceiver : RepairReceiver
{
public static Type RepairReceiverType = EnumUtils.Create<Type>("NewHorizons");
public class RepairEvent : UnityEvent<NHRepairReceiver> { }
public RepairEvent OnRepaired = new();
public RepairEvent OnDamaged = new();
public string displayName;
public float repairTime;
public string damagedCondition;
public string repairedCondition;
public string revealFact;
float _repairFraction = 0f;
UITextType _uiTextType = UITextType.None;
public float repairFraction
{
get => _repairFraction;
set
{
var prevValue = _repairFraction;
_repairFraction = Mathf.Clamp01(value);
if (prevValue < 1f && _repairFraction >= 1f)
{
Repair();
}
else if (prevValue >= 1f && _repairFraction < 1f)
{
Damage();
}
}
}
public new virtual bool IsRepairable() => IsDamaged();
public new virtual bool IsDamaged() => _repairFraction < 1f;
public new virtual float GetRepairFraction() => _repairFraction;
protected new void Awake()
{
base.Awake();
_type = RepairReceiverType;
if (IsDamaged()) Damage();
else Repair();
}
public new virtual void RepairTick()
{
if (!IsRepairable()) return;
repairFraction += Time.deltaTime / repairTime;
}
public new virtual UITextType GetRepairableName()
{
if (_uiTextType != UITextType.None) return _uiTextType;
var value = TranslationHandler.GetTranslation(displayName, TranslationHandler.TextType.UI);
_uiTextType = (UITextType)TranslationHandler.AddUI(value, false);
return _uiTextType;
}
void Damage()
{
if (!string.IsNullOrEmpty(damagedCondition))
{
DialogueConditionManager.SharedInstance.SetConditionState(damagedCondition, true);
}
if (!string.IsNullOrEmpty(repairedCondition))
{
DialogueConditionManager.SharedInstance.SetConditionState(repairedCondition, false);
}
OnDamaged.Invoke(this);
}
void Repair()
{
if (!string.IsNullOrEmpty(damagedCondition))
{
DialogueConditionManager.SharedInstance.SetConditionState(damagedCondition, false);
}
if (!string.IsNullOrEmpty(repairedCondition))
{
DialogueConditionManager.SharedInstance.SetConditionState(repairedCondition, true);
}
if (!string.IsNullOrEmpty(revealFact))
{
Locator.GetShipLogManager().RevealFact(revealFact);
}
OnRepaired.Invoke(this);
}
}
}

View File

@ -0,0 +1,49 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NewHorizons.External.Modules.Volumes.VolumeInfos
{
[JsonObject]
public class RepairVolumeInfo : VolumeInfo
{
/// <summary>
/// The name displayed in the UI when the player is repairing this object. If not set, the name of the object will be used.
/// </summary>
public string name;
/// <summary>
/// How much of the object is initially repaired. 0 = not repaired, 1 = fully repaired.
/// </summary>
[DefaultValue(0f)] public float repairFraction = 0f;
/// <summary>
/// The time it takes to repair the object. Defaults to 3 seconds.
/// </summary>
[DefaultValue(3f)] public float repairTime = 3f;
/// <summary>
/// The distance from the object that the player can be to repair it. Defaults to 3 meters.
/// </summary>
[DefaultValue(3f)] public float repairDistance = 3f;
/// <summary>
/// A dialogue condition that will be set while the object is damaged. It will be unset when the object is repaired.
/// </summary>
public string damagedCondition;
/// <summary>
/// A dialogue condition that will be set when the object is repaired. It will be unset if the object is damaged again.
/// </summary>
public string repairedCondition;
/// <summary>
/// A ship log fact that will be revealed when the object is repaired.
/// </summary>
public string revealFact;
}
}

View File

@ -85,6 +85,11 @@ namespace NewHorizons.External.Modules.Volumes
/// </summary>
public VolumeInfo[] referenceFrameBlockerVolumes;
/// <summary>
/// Add repair volumes to this planet.
/// </summary>
public RepairVolumeInfo[] repairVolumes;
/// <summary>
/// Add triggers that reveal parts of the ship log on this planet.
/// </summary>

View File

@ -191,6 +191,7 @@ namespace NewHorizons.External
if (_activeProfile != null && !_activeProfile.PopupsRead.Contains(id))
{
_activeProfile.PopupsRead.Add(id);
Save();
}
}

View File

@ -255,5 +255,12 @@ namespace NewHorizons
/// <param name="persistentConditionRequired">Persistent condition required for this title screen to appear.</param>
/// <param name="factRequired">Ship log fact required for this title screen to appear.</param>
void RegisterTitleScreenBuilder(IModBehaviour mod, Action<GameObject> builder, bool disableNHPlanets = true, bool shareTitleScreen = false, string persistentConditionRequired = null, string factRequired = null);
/// <summary>
/// Clears all loaded configs for the given system.
/// This exists solely for Nomai Sky to use :bleh:
/// </summary>
/// <param name="name"></param>
void ClearSystem(string name);
}
}

View File

@ -439,6 +439,11 @@ namespace NewHorizons
TitleSceneHandler.Init();
}
if (isTitleScreen)
{
MenuHandler.TitleScreen();
}
// EOTU fixes
if (isEyeOfTheUniverse)
{

View File

@ -367,5 +367,17 @@ namespace NewHorizons
public void RegisterTitleScreenBuilder(IModBehaviour mod, Action<GameObject> builder, bool disableNHPlanets = true, bool shareTitleScreen = false, string persistentConditionRequired = null, string factRequired = null)
=> TitleSceneHandler.RegisterBuilder(mod, builder, disableNHPlanets, shareTitleScreen, persistentConditionRequired, factRequired);
public void ClearSystem(string name)
{
if (Main.SystemDict.ContainsKey(name))
{
Main.SystemDict.Remove(name);
}
if (Main.BodyDict.ContainsKey(name))
{
Main.BodyDict.Remove(name);
}
}
}
}

View File

@ -3,9 +3,11 @@ using NewHorizons.Handlers;
using NewHorizons.Utility;
using NewHorizons.Utility.OWML;
using OWML.Common;
using OWML.ModHelper;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using static TextTranslation;
namespace NewHorizons.OtherMods.MenuFramework
{
@ -61,5 +63,18 @@ namespace NewHorizons.OtherMods.MenuFramework
public static void RegisterFailedConfig(string filename) => _failedFiles.Add(filename);
public static void RegisterOneTimePopup(IModBehaviour mod, string message, bool repeat) => _registeredPopups.Add((mod, message, repeat));
public static void TitleScreen()
{
// Custom popup for recommending the Chinese Outer Wilds Font Fix mod if they are playing in chinese
// Only shows once per profile
if (TextTranslation.Get().m_language == Language.CHINESE_SIMPLE
&& !Main.Instance.ModHelper.Interaction.ModExists("nice2cu1.OuterWildFixFont")
&& !NewHorizonsData.HasReadOneTimePopup("INSTALL_OUTER_WILDS_CHINESE_FONT_FIX"))
{
Main.Instance.ModHelper.MenuHelper.PopupMenuManager.RegisterStartupPopup(TranslationHandler.GetTranslation("INSTALL_OUTER_WILDS_CHINESE_FONT_FIX", TranslationHandler.TextType.UI));
NewHorizonsData.ReadOneTimePopup("INSTALL_OUTER_WILDS_CHINESE_FONT_FIX");
}
}
}
}

View File

@ -0,0 +1,62 @@
using HarmonyLib;
using NewHorizons.Components.Volumes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NewHorizons.Patches.VolumePatches
{
[HarmonyPatch(typeof(RepairReceiver))]
public static class RepairReceiverPatches
{
// We can't actually override these methods so we patch the base class methods to invoke the subclass methods dynamically
[HarmonyPostfix, HarmonyPatch(nameof(RepairReceiver.IsRepairable))]
public static void IsRepairable(RepairReceiver __instance, ref bool __result)
{
if (__instance is NHRepairReceiver r)
{
__result = r.IsRepairable();
}
}
[HarmonyPostfix, HarmonyPatch(nameof(RepairReceiver.RepairTick))]
public static void RepairTick(RepairReceiver __instance)
{
if (__instance is NHRepairReceiver r)
{
r.RepairTick();
}
}
[HarmonyPostfix, HarmonyPatch(nameof(RepairReceiver.IsDamaged))]
public static void IsDamaged(RepairReceiver __instance, ref bool __result)
{
if (__instance is NHRepairReceiver r)
{
__result = r.IsDamaged();
}
}
[HarmonyPostfix, HarmonyPatch(nameof(RepairReceiver.GetRepairableName))]
public static void GetRepairableName(RepairReceiver __instance, ref UITextType __result)
{
if (__instance is NHRepairReceiver r)
{
__result = r.GetRepairableName();
}
}
[HarmonyPostfix, HarmonyPatch(nameof(RepairReceiver.GetRepairFraction))]
public static void GetRepairFraction(RepairReceiver __instance, ref float __result)
{
if (__instance is NHRepairReceiver r)
{
__result = r.GetRepairFraction();
}
}
}
}

View File

@ -5450,6 +5450,13 @@
"$ref": "#/definitions/VolumeInfo"
}
},
"repairVolumes": {
"type": "array",
"description": "Add repair volumes to this planet.",
"items": {
"$ref": "#/definitions/RepairVolumeInfo"
}
},
"revealVolumes": {
"type": "array",
"description": "Add triggers that reveal parts of the ship log on this planet.",
@ -6722,6 +6729,83 @@
}
}
},
"RepairVolumeInfo": {
"type": "object",
"additionalProperties": false,
"properties": {
"radius": {
"type": "number",
"description": "The radius of this volume, if a shape is not specified.",
"format": "float",
"default": 1.0
},
"shape": {
"description": "The shape of this volume. Defaults to a sphere with a radius of `radius` if not specified.",
"$ref": "#/definitions/ShapeInfo"
},
"rotation": {
"description": "Rotation of the object",
"$ref": "#/definitions/MVector3"
},
"alignRadial": {
"type": [
"boolean",
"null"
],
"description": "Do we try to automatically align this object to stand upright relative to the body's center? Stacks with rotation.\nDefaults to true for geysers, tornados, and volcanoes, and false for everything else."
},
"position": {
"description": "Position of the object",
"$ref": "#/definitions/MVector3"
},
"isRelativeToParent": {
"type": "boolean",
"description": "Whether the positional and rotational coordinates are relative to parent instead of the root planet object."
},
"parentPath": {
"type": "string",
"description": "The relative path from the planet to the parent of this object. Optional (will default to the root sector)."
},
"rename": {
"type": "string",
"description": "An optional rename of this object"
},
"name": {
"type": "string",
"description": "The name displayed in the UI when the player is repairing this object. If not set, the name of the object will be used."
},
"repairFraction": {
"type": "number",
"description": "How much of the object is initially repaired. 0 = not repaired, 1 = fully repaired.",
"format": "float",
"default": 0.0
},
"repairTime": {
"type": "number",
"description": "The time it takes to repair the object. Defaults to 3 seconds.",
"format": "float",
"default": 3.0
},
"repairDistance": {
"type": "number",
"description": "The distance from the object that the player can be to repair it. Defaults to 3 meters.",
"format": "float",
"default": 3.0
},
"damagedCondition": {
"type": "string",
"description": "A dialogue condition that will be set while the object is damaged. It will be unset when the object is repaired."
},
"repairedCondition": {
"type": "string",
"description": "A dialogue condition that will be set when the object is repaired. It will be unset if the object is damaged again."
},
"revealFact": {
"type": "string",
"description": "A ship log fact that will be revealed when the object is repaired."
}
}
},
"RevealVolumeInfo": {
"type": "object",
"additionalProperties": false,

View File

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