Skip to content

Commit

Permalink
1.23.0 (#956)
Browse files Browse the repository at this point in the history
Thank you Hawkbar for implementing custom Dreamworlds in this update!

## Major features
- Added `Dream.inDreamWorld` to planet configs to treat a planet as if
it were part of the dream world dimension. The planet's contents will be
deactivated unless you enter via a dream arrival point or another dream
world dimension. Closes #916
- Added `dreamCampfires` and `dreamArrivalPoints` as new linked prop
types, which allow you to create new entry points into Echoes of the
Eye's dream world or create similar experiences using custom planets.

## Improvements
- General support for dream world simulations even if the vanilla dream
world is disabled in custom solar systems.
- Preserve held items when warping (closes #192). Note that if you leave
an item in another system and warp out, that item will be gone forever
from all systems until you start a new loop. Some items (i.e., slide
reels) do not work between systems.

## Bug fixes
- Time loop will be disabled in other systems if you removed the AWC and
warped while holding it or using the Vessel. Closes #952
- Fixed configs getting loaded more than once if parent body is updated
again #921
  • Loading branch information
xen-42 authored Oct 6, 2024
2 parents 8da9a0e + 3855d26 commit bb978af
Show file tree
Hide file tree
Showing 25 changed files with 1,060 additions and 19 deletions.
107 changes: 107 additions & 0 deletions NewHorizons/Builder/Body/DreamDimensionBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using NewHorizons.Components.EOTE;
using NewHorizons.Components.Props;
using NewHorizons.External;
using NewHorizons.External.Configs;
using NewHorizons.Utility;
using NewHorizons.Utility.OuterWilds;
using NewHorizons.Utility.OWML;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace NewHorizons.Builder.Body
{
public static class DreamDimensionBuilder
{
private static Material gridMaterial;
private static Material waterMaterial;

private readonly static string[] EXCLUDED_OBJECT_NAMES =
{
"Prefab_IP_SIM_",
"Props_IP_SIM_",
"Effects_IP_SIM_",
};

private readonly static string[] EXCLUDED_SHADER_NAMES =
{
"Fog",
"Simulation Bubble",
"Foliage",
"Flame",
};

public static void Make(GameObject planetGO, Sector sector, NewHorizonsBody body)
{
var bodyWantsSimMeshes = body.Config.Dream?.generateSimulationMeshes ?? false;
var propsWantSimMeshes = body.Config.Props?.dreamArrivalPoints?.Any(p => p.generateSimulationMeshes) ?? false;
if (bodyWantsSimMeshes || propsWantSimMeshes)
{
MakeDreamSimulationMeshes(sector ? sector.gameObject : planetGO);
}

if (body.Config?.Dream?.inDreamWorld ?? false)
{
var dreamDimension = planetGO.AddComponent<DreamDimension>();
Delay.FireInNUpdates(() =>
{
dreamDimension.Initialize();
}, 4);
}

}

public static void MakeDreamSimulationMeshes(GameObject go)
{
if (gridMaterial == null) gridMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Terrain_IP_DreamGrid_mat");
if (waterMaterial == null) waterMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Terrain_IP_DreamGrid_mat");

foreach (var mr in go.GetComponentsInChildren<MeshRenderer>(true))
{
if (mr.GetType() != typeof(MeshRenderer)) continue;
var mf = mr.GetComponent<MeshFilter>();
if (mf == null) continue;
if (!CheckMeshCreationHeuristic(mr.gameObject, mr.sharedMaterials)) continue;
var simMesh = new GameObject("SimulationMesh").AddComponent<DreamSimulationMesh>();
simMesh.Init(mr.transform, GetMeshMaterial(go, mr.sharedMaterials));
}
}

private static Material GetMeshMaterial(GameObject go, Material[] materials)
{
if (materials.Any(m => m.name.Contains("Ocean_Stencil_mat"))) return waterMaterial;
return gridMaterial;
}

private static bool CheckMeshCreationHeuristic(GameObject go, Material[] materials)
{
if (go.layer == Layer.DreamSimulation) return false;
var mr = go.GetComponent<MeshRenderer>();
if (EXCLUDED_SHADER_NAMES.Any(name => materials.Any(mat => mat.shader.name.Contains(name)))) return false;
if (go.transform.parent)
{
foreach (Transform c in go.transform.parent)
{
if (c && c.gameObject.layer == Layer.DreamSimulation) return false;
}
if (go.transform.parent.parent)
{
foreach (Transform c in go.transform.parent.parent)
{
if (c && c.gameObject.layer == Layer.DreamSimulation) return false;
}
}
}
var t = go.transform;
while (t != null)
{
if (EXCLUDED_OBJECT_NAMES.Any(t.name.Contains)) return false;
t = t.parent;
}
return true;
}
}
}
1 change: 1 addition & 0 deletions NewHorizons/Builder/General/AmbientLightBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using NewHorizons.Utility;
using NewHorizons.External.Modules;
using NewHorizons.Utility.Files;
using NewHorizons.Components;

namespace NewHorizons.Builder.General
{
Expand Down
22 changes: 14 additions & 8 deletions NewHorizons/Builder/Orbital/OrbitlineBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@ public static class OrbitlineBuilder
private static Material _dottedLineMaterial;
private static Material _lineMaterial;

public static OrbitLine Make(GameObject planetGO, NHAstroObject astroObject, bool isMoon, PlanetConfig config)
public static GameObject Make(GameObject planetGO, bool isMoon, PlanetConfig config)
{
var orbitGO = new GameObject("Orbit");
orbitGO.transform.parent = planetGO.transform;
orbitGO.transform.localPosition = Vector3.zero;

Delay.FireOnNextUpdate(() => PostMake(orbitGO, planetGO, isMoon, config));
return orbitGO;
}

private static void PostMake(GameObject orbitGO, GameObject planetGO, bool isMoon, PlanetConfig config)
{
if (_dottedLineMaterial == null) _dottedLineMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Effects_SPA_OrbitLine_Dotted_mat");
if (_lineMaterial == null) _lineMaterial = SearchUtilities.FindResourceOfTypeAndName<Material>("Effects_SPA_OrbitLine_mat");

if (_dottedLineMaterial == null || _lineMaterial == null) return null;
// Might've been otherwise destroyed when updating
if (orbitGO == null) return;

var orbitGO = new GameObject("Orbit");
orbitGO.transform.parent = planetGO.transform;
orbitGO.transform.localPosition = Vector3.zero;
var astroObject = planetGO.GetComponent<NHAstroObject>();

var lineRenderer = orbitGO.AddComponent<LineRenderer>();

Expand Down Expand Up @@ -47,7 +56,6 @@ public static OrbitLine Make(GameObject planetGO, NHAstroObject astroObject, boo
else
{
orbitLine = orbitGO.AddComponent<NHOrbitLine>();

(orbitLine as NHOrbitLine).SetFromParameters(astroObject);
}

Expand Down Expand Up @@ -94,8 +102,6 @@ public static OrbitLine Make(GameObject planetGO, NHAstroObject astroObject, boo
orbitGO.SetActive(false);
};
}

return orbitLine;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using NewHorizons.Components.Props;
using NewHorizons.External.Modules.Props;
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
using NewHorizons.Handlers;
using NewHorizons.Utility;
using NewHorizons.Utility.OWML;
using OWML.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace NewHorizons.Builder.Props.EchoesOfTheEye
{
public static class DreamArrivalPointBuilder
{
private static GameObject _prefab;

internal static void InitPrefab()
{
if (_prefab == null)
{
_prefab = SearchUtilities.Find("DreamWorld_Body/Sector_DreamWorld/Sector_DreamZone_1/DreamFireHouse_1/Interactibles_DreamFireHouse_1/Prefab_IP_DreamArrivalPoint_Zone1").InstantiateInactive().Rename("Prefab_DreamArrivalPoint").DontDestroyOnLoad();
if (_prefab == null)
{
NHLogger.LogWarning($"Tried to make a dream arrival point but couldn't. Do you have the DLC installed?");
return;
}
else
{
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
var dreamArrivalPoint = _prefab.GetComponent<DreamArrivalPoint>();
dreamArrivalPoint._location = DreamArrivalPoint.Location.Undefined;
dreamArrivalPoint._sector = null;
dreamArrivalPoint._entrywayVolumes = new OWTriggerVolume[0];
dreamArrivalPoint._raftSpawn = null;
dreamArrivalPoint._connectedDreamCampfire = null;
dreamArrivalPoint._campfire._sector = null;
}
}
}

public static GameObject Make(GameObject planetGO, Sector sector, DreamArrivalPointInfo info, IModBehaviour mod)
{
InitPrefab();

if (_prefab == null || sector == null) return null;

var arrivalPointObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));

StreamingHandler.SetUpStreaming(arrivalPointObj, sector);

DreamArrivalPoint arrivalPoint = arrivalPointObj.GetComponent<DreamArrivalPoint>();
arrivalPoint._sector = arrivalPoint.GetComponentInParent<Sector>();
arrivalPoint._location = DreamHandler.GetDreamArrivalLocation(info.id);
Locator.RegisterDreamArrivalPoint(arrivalPoint, arrivalPoint._location);

return arrivalPointObj;
}
}
}
74 changes: 74 additions & 0 deletions NewHorizons/Builder/Props/EchoesOfTheEye/DreamCampfireBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using NewHorizons.External.Modules.Props;
using NewHorizons.External.Modules.Props.EchoesOfTheEye;
using NewHorizons.Handlers;
using NewHorizons.Utility;
using NewHorizons.Utility.OWML;
using OWML.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace NewHorizons.Builder.Props.EchoesOfTheEye
{
public static class DreamCampfireBuilder
{
private static GameObject _prefab;

internal static void InitPrefab()
{
if (_prefab == null)
{
_prefab = SearchUtilities.Find("RingWorld_Body/Sector_RingInterior/Sector_Zone4/Sector_PrisonDocks/Sector_PrisonInterior/Interactibles_PrisonInterior/Prefab_IP_DreamCampfire").InstantiateInactive().Rename("Prefab_DreamCampfire").DontDestroyOnLoad();
if (_prefab == null)
{
NHLogger.LogWarning($"Tried to make a dream campfire but couldn't. Do you have the DLC installed?");
return;
}
else
{
_prefab.AddComponent<DestroyOnDLC>()._destroyOnDLCNotOwned = true;
var campfire = _prefab.GetComponentInChildren<DreamCampfire>();
campfire._dreamArrivalLocation = DreamArrivalPoint.Location.Undefined;
campfire._sector = null;
campfire._entrywayVolumes = new OWTriggerVolume[0];
}
}
}

public static GameObject Make(GameObject planetGO, Sector sector, DreamCampfireInfo info, IModBehaviour mod)
{
InitPrefab();

if (_prefab == null || sector == null) return null;

var campfireObj = DetailBuilder.Make(planetGO, sector, mod, _prefab, new DetailInfo(info));

var campfire = campfireObj.GetComponentInChildren<DreamCampfire>();
campfire._dreamArrivalLocation = DreamHandler.GetDreamArrivalLocation(info.id);

// The streaming groups on DreamCampfires get set on Start() so we wait until after to change it again
Delay.FireInNUpdates(() => {
var streaming = campfireObj.GetComponentInChildren<DreamCampfireStreaming>();
if (streaming != null)
{
var targetArrivalPoint = Locator.GetDreamArrivalPoint(campfire._dreamArrivalLocation);
if (targetArrivalPoint != null)
{
var streamingGroup = targetArrivalPoint.transform.root.GetComponentInChildren<StreamingGroup>();
if (streamingGroup)
{
streaming._streamingGroup = streamingGroup;
}
}
}
}, 2);

Locator.RegisterDreamCampfire(campfire, campfire._dreamArrivalLocation);

return campfireObj;
}
}
}
3 changes: 3 additions & 0 deletions NewHorizons/Builder/Props/PropBuildManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using NewHorizons.Builder.Body;
using NewHorizons.Builder.Props.Audio;
using NewHorizons.Builder.Props.EchoesOfTheEye;
using NewHorizons.Builder.Props.TranslatorText;
using NewHorizons.Builder.ShipLog;
using NewHorizons.External;
Expand Down Expand Up @@ -101,6 +102,8 @@ public static void Make(GameObject go, Sector sector, OWRigidbody planetBody, Ne
// If a prop has set its parentPath and the parent cannot be found, add it to the next pass and try again later
nextPass = new List<Action>();

if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamCampfires, (campfire) => DreamCampfireBuilder.Make(go, sector, campfire, mod), (campfire) => campfire.id);
if (Main.HasDLC) MakeGeneralProps(go, config.Props.dreamArrivalPoints, (point) => DreamArrivalPointBuilder.Make(go, sector, point, mod), (point) => point.id);
MakeGeneralProps(go, config.Props.gravityCannons, (cannon) => GravityCannonBuilder.Make(go, sector, cannon, mod), (cannon) => cannon.shuttleID);
MakeGeneralProps(go, config.Props.shuttles, (shuttle) => ShuttleBuilder.Make(go, sector, mod, shuttle), (shuttle) => shuttle.id);
MakeGeneralProps(go, config.Props.details, (detail) => DetailBuilder.Make(go, sector, mod, detail), (detail) => detail.path);
Expand Down
66 changes: 66 additions & 0 deletions NewHorizons/Components/EOTE/DreamDimension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace NewHorizons.Components.EOTE
{
public class DreamDimension : MonoBehaviour
{
private bool initialized;
private bool active;
private List<GameObject> toggledObjects = new();

public void Initialize()
{
if (initialized) return;

foreach (Transform child in transform)
{
if (child.gameObject.name == "FieldDetector") continue;
toggledObjects.Add(child.gameObject);
}

initialized = true;
UpdateState();
}

public void SetActive(bool active)
{
if (this.active != active)
{
this.active = active;
UpdateState();
}
}

void Awake()
{
GlobalMessenger.AddListener("EnterDreamWorld", OnEnterDreamWorld);
GlobalMessenger.AddListener("ExitDreamWorld", OnExitDreamWorld);
}

void OnDestroy()
{
GlobalMessenger.RemoveListener("EnterDreamWorld", OnEnterDreamWorld);
GlobalMessenger.RemoveListener("ExitDreamWorld", OnExitDreamWorld);
}

void UpdateState()
{
foreach (var obj in toggledObjects) obj.SetActive(active);
}

void OnEnterDreamWorld()
{
SetActive(true);
}

void OnExitDreamWorld()
{
SetActive(false);
}
}
}
Loading

0 comments on commit bb978af

Please sign in to comment.