Merge pull request #23 from Bwc9876/bwc9876/custom-ship-log

Add Custom Ship Logs
This commit is contained in:
Nick 2022-02-06 22:58:18 -05:00 committed by GitHub
commit 28b29c78af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1429 additions and 11 deletions

3
.gitignore vendored
View File

@ -6,4 +6,5 @@ obj
zip
*.zip
*/Build/*
*/Build/*
.idea/

View File

@ -1,18 +1,651 @@
using NewHorizons.Components;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using NewHorizons.External;
using NewHorizons.Utility;
using OWML.Common;
using UnityEngine;
using UnityEngine.UI;
using Logger = NewHorizons.Utility.Logger;
namespace NewHorizons.Builder.General
{
public static class ShipLogBuilder
{
public static readonly string PAN_ROOT_PATH = "Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/MapMode/ScaleRoot/PanRoot";
public static ShipLogStarChartMode ShipLogStarChartMode;
private static Dictionary<string, NewHorizonsBody> astroIdToBody = new Dictionary<string, NewHorizonsBody>();
private static NewHorizonsBody GetConfigFromEntry(ShipLogEntry entry)
{
return astroIdToBody[entry._astroObjectID];
}
#region Map Mode
public class MapModeBuilder
{
private class MapModeObject
{
public int x;
public int y;
public int branch_width;
public int branch_height;
public int level;
public NewHorizonsBody mainBody;
public ShipLogAstroObject astroObject;
public List<MapModeObject> children;
public MapModeObject parent;
public MapModeObject lastSibling;
public void increment_width()
{
branch_width++;
parent?.increment_width();
}
public void increment_height()
{
branch_height++;
parent?.increment_height();
}
}
public static string GetAstroBodyShipLogName(string id)
{
if (astroIdToBody.ContainsKey(id))
{
return astroIdToBody[id].Config.Name;
}
else
{
return id;
}
}
public static ShipLogAstroObject[][] ConstructMapMode(string systemName, GameObject transformParent, int layer)
{
MapModeObject rootObject = ConstructPrimaryNode(systemName);
if (rootObject.mainBody != null)
{
MakeAllNodes(ref rootObject, transformParent, layer);
}
int maxAmount = Main.BodyDict[Main.Instance.CurrentStarSystem].Count;
ShipLogAstroObject[][] navMatrix = new ShipLogAstroObject[maxAmount][];
for (int i = 0; i < maxAmount; i++)
{
navMatrix[i] = new ShipLogAstroObject[maxAmount];
}
CreateNavigationMatrix(rootObject, ref navMatrix);
navMatrix = navMatrix.Where(a => a.Count(c => c != null) > 0).Prepend(new ShipLogAstroObject[1]).ToArray();
for (var index = 0; index < navMatrix.Length; index++)
{
navMatrix[index] = navMatrix[index].Where(a => a != null).ToArray();
}
return navMatrix;
}
private static void CreateNavigationMatrix(MapModeObject root, ref ShipLogAstroObject[][] navMatrix)
{
if (root.astroObject != null)
{
navMatrix[root.y][root.x] = root.astroObject;
}
foreach (MapModeObject child in root.children)
{
CreateNavigationMatrix(child, ref navMatrix);
}
}
private static void MakeAllNodes(ref MapModeObject parentNode, GameObject parent, int layer)
{
MakeNode(ref parentNode, parent, layer);
for (var i = 0; i < parentNode.children.Count; i++)
{
MapModeObject child = parentNode.children[i];
MakeAllNodes(ref child, parent, layer);
parentNode.children[i] = child;
}
}
private static GameObject CreateImage(GameObject nodeGO, IModAssets assets, string imagePath, string name, int layer)
{
GameObject newImageGO = new GameObject(name);
newImageGO.layer = layer;
newImageGO.transform.SetParent(nodeGO.transform);
RectTransform transform = newImageGO.AddComponent<RectTransform>();
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
Image newImage = newImageGO.AddComponent<Image>();
if (imagePath == "DEFAULT")
{
newImage.sprite = Locator.GetShipLogManager()._shipLogLibrary.defaultEntrySprite;
}
else
{
Texture2D newTexture = assets.GetTexture(imagePath);
Rect rect = new Rect(0, 0, newTexture.width, newTexture.height);
Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2);
newImage.sprite = Sprite.Create(newTexture, rect, pivot);
}
return newImageGO;
}
public static T KeyByValue<T, W>(Dictionary<T, W> dict, W val)
{
T key = default;
foreach (KeyValuePair<T, W> pair in dict)
{
if (EqualityComparer<W>.Default.Equals(pair.Value, val))
{
key = pair.Key;
break;
}
}
return key;
}
private static string GetAstroObjectId(NewHorizonsBody body)
{
if (astroIdToBody.ContainsValue(body))
{
return KeyByValue(astroIdToBody, body);
}
else
{
return body.Config.Name;
}
}
private static void CreateShipLogAstroObject(GameObject nodeGO, ref MapModeObject node, GameObject referenceUnviewedSprite, int layer)
{
const float unviewedIconOffset = 15;
ShipLogAstroObject astroObject = nodeGO.AddComponent<ShipLogAstroObject>();
astroObject._id = GetAstroObjectId(node.mainBody);
string imagePath = node.mainBody.Config.ShipLog?.mapMode?.revealedSprite ?? "DEFAULT";
string outlinePath = node.mainBody.Config.ShipLog?.mapMode?.outlineSprite ?? imagePath;
astroObject._imageObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, imagePath, node.mainBody.Config.Name + " Revealed", layer);
astroObject._outlineObj = CreateImage(nodeGO, node.mainBody.Mod.Assets, outlinePath, node.mainBody.Config.Name + " Outline", layer);
astroObject._unviewedObj = Object.Instantiate(referenceUnviewedSprite, nodeGO.transform, false);
if (node.mainBody.Config.FocalPoint != null)
{
astroObject._imageObj.GetComponent<Image>().enabled = false;
astroObject._outlineObj.GetComponent<Image>().enabled = false;
astroObject._unviewedObj.GetComponent<Image>().enabled = false;
astroObject.transform.localScale = node.lastSibling.astroObject.transform.localScale;
}
astroObject._invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false;
Rect imageRect = astroObject._imageObj.GetComponent<RectTransform>().rect;
astroObject._unviewedObj.transform.localPosition = new Vector3(imageRect.width / 2 + unviewedIconOffset, imageRect.height / 2 + unviewedIconOffset, 0);
node.astroObject = astroObject;
}
private static void ConnectNodeToLastSibling(MapModeObject node)
{
Vector2 fromPosition = node.astroObject.transform.localPosition;
Vector2 toPosition = node.lastSibling.astroObject.transform.localPosition;
GameObject newLink = new GameObject("Line_ShipLog");
newLink.layer = node.astroObject.gameObject.layer;
newLink.SetActive(false);
RectTransform transform = newLink.AddComponent<RectTransform>();
transform.SetParent(node.astroObject.transform.parent);
Vector2 center = toPosition + (fromPosition - toPosition) / 2;
transform.localPosition = new Vector3(center.x, center.y, -1);
transform.localRotation = Quaternion.identity;
transform.localScale = node.level % 2 == 0 ? new Vector3(node.astroObject.transform.localScale.x / 5f, Mathf.Abs(fromPosition.y - toPosition.y) / 100f, 1) : new Vector3(Mathf.Abs(fromPosition.x - toPosition.x) / 100f, node.astroObject.transform.localScale.y / 5f , 1);
Image linkImage = newLink.AddComponent<Image>();
linkImage.color = new Color(0.28f, 0.28f, 0.5f, 0.28f);
ShipLogModule.ShipLogDetailInfo linkDetailInfo = new ShipLogModule.ShipLogDetailInfo()
{
invisibleWhenHidden = node.mainBody.Config.ShipLog?.mapMode?.invisibleWhenHidden ?? false
};
ShipLogDetail linkDetail = newLink.AddComponent<ShipLogDetail>();
linkDetail.Init(linkDetailInfo, linkImage, linkImage);
transform.SetParent(node.astroObject.transform);
transform.SetAsFirstSibling();
newLink.SetActive(true);
}
private static void MakeDetail(ShipLogModule.ShipLogDetailInfo info, Transform parent, IModAssets assets)
{
GameObject detailGameObject = new GameObject("Detail");
detailGameObject.transform.SetParent(parent);
detailGameObject.SetActive(false);
RectTransform detailTransform = detailGameObject.AddComponent<RectTransform>();
detailTransform.localPosition = (Vector2)(info.position ?? new MVector2(0, 0));
detailTransform.localRotation = Quaternion.Euler(0f, 0f, info.rotation);
detailTransform.localScale = (Vector2)(info.scale ?? new MVector2(0, 0));
string revealedPath = info.revealedSprite ?? "DEFAULT";
string outlinePath = info.outlineSprite ?? revealedPath;
Image revealedImage = CreateImage(detailGameObject, assets, revealedPath, "Detail Revealed", parent.gameObject.layer).GetComponent<Image>();
Image outlineImage = CreateImage(detailGameObject, assets, outlinePath, "Detail Outline", parent.gameObject.layer).GetComponent<Image>();
ShipLogDetail detail = detailGameObject.AddComponent<ShipLogDetail>();
detail.Init(info, revealedImage, outlineImage);
detailGameObject.SetActive(true);
}
private static void MakeDetails(MapModeObject node)
{
if (node.mainBody.Config.ShipLog?.mapMode?.details?.Length > 0)
{
GameObject detailsParent = new GameObject("Details");
detailsParent.transform.SetParent(node.astroObject.transform);
detailsParent.SetActive(false);
RectTransform detailsTransform = detailsParent.AddComponent<RectTransform>();
detailsTransform.localPosition = Vector3.zero;
detailsTransform.localRotation = Quaternion.identity;
detailsTransform.localScale = Vector3.one;
foreach (ShipLogModule.ShipLogDetailInfo detailInfo in node.mainBody.Config.ShipLog.mapMode.details)
{
MakeDetail(detailInfo, detailsTransform, node.mainBody.Mod.Assets);
}
detailsParent.SetActive(true);
}
}
private static void MakeNode(ref MapModeObject node, GameObject parent, int layer)
{
const float padding = 250f;
GameObject newNodeGO = new GameObject(node.mainBody.Config.Name + "_ShipLog");
newNodeGO.layer = layer;
newNodeGO.transform.SetParent(parent.transform);
RectTransform transform = newNodeGO.AddComponent<RectTransform>();
float scale = node.mainBody.Config.ShipLog?.mapMode?.scale?? 1f;
Vector2 position = Vector2.zero;
if (node.lastSibling != null)
{
ShipLogAstroObject lastAstroObject = node.lastSibling.astroObject;
Vector3 lastPosition = lastAstroObject.transform.localPosition;
position = lastPosition;
float extraDistance = (node.mainBody.Config.ShipLog?.mapMode?.offset ?? 0f) * 100;
if (node.level % 2 == 0)
{
position.y += padding * (node.y - node.lastSibling.y) + extraDistance;
}
else
{
position.x += padding * (node.x - node.lastSibling.x) + extraDistance;
}
}
transform.localPosition = new Vector3(position.x, position.y, 0);
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one * scale;
CreateShipLogAstroObject(newNodeGO, ref node, GameObject.Find(PAN_ROOT_PATH + "/TimberHearth/UnviewedIcon"), layer);
if (node.lastSibling != null) ConnectNodeToLastSibling(node);
MakeDetails(node);
transform.SetAsFirstSibling();
}
private static MapModeObject ConstructPrimaryNode(string systemName)
{
foreach (NewHorizonsBody body in Main.BodyDict[systemName].Where(b => b.Config.Base.CenterOfSolarSystem))
{
List<NewHorizonsBody> searchList = Main.BodyDict[systemName].Where(b => (b.Config.ShipLog?.mapMode?.remove ?? false) == false).ToList();
searchList.Sort((b, o) => b.Config.Orbit.SemiMajorAxis.CompareTo(o.Config.Orbit.SemiMajorAxis));
MapModeObject newNode = new MapModeObject
{
mainBody = body,
level = 0,
x = 0,
y = 0
};
newNode.children = ConstructChildrenNodes(systemName, newNode, searchList);
return newNode;
}
Logger.LogError("Couldn't find center of system!");
return new MapModeObject();
}
private static List<MapModeObject> ConstructChildrenNodes(string systemName, MapModeObject parent, List<NewHorizonsBody> searchList)
{
List<MapModeObject> children = new List<MapModeObject>();
int newX = parent.x;
int newY = parent.y;
int newLevel = parent.level + 1;
MapModeObject lastSibling = parent;
foreach (NewHorizonsBody body in searchList.Where(b => b.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name))
{
if (body.Config.Orbit.PrimaryBody == parent.mainBody.Config.Name)
{
bool even = newLevel % 2 == 0;
newX = even ? newX : newX + 1;
newY = even ? newY + 1 : newY;
MapModeObject newNode = new MapModeObject()
{
mainBody = body,
level = newLevel,
x = newX,
y = newY,
parent = parent,
lastSibling = lastSibling
};
newNode.children = ConstructChildrenNodes(systemName, newNode, searchList);
if (even)
{
newY += newNode.branch_height;
parent.increment_height();
newY += 1;
}
else
{
newX += newNode.branch_width;
parent.increment_width();
newX += 1;
}
lastSibling = newNode;
children.Add(newNode);
}
}
return children;
}
}
#endregion
#region Rumor Mode
public static class RumorModeBuilder
{
private static Dictionary<CuriosityName, Color> curiosityColors = new Dictionary<CuriosityName, Color>();
private static Dictionary<CuriosityName, Color> curiosityHighlightColors = new Dictionary<CuriosityName, Color>();
private static Dictionary<string, CuriosityName> rawNameToCuriosityName = new Dictionary<string, CuriosityName>();
private static Dictionary<string, string> entryIdToRawName = new Dictionary<string, string>();
public static void AddCuriosityColors(ShipLogModule.CuriosityColorInfo[] newColors)
{
foreach (ShipLogModule.CuriosityColorInfo newColor in newColors)
{
if (rawNameToCuriosityName.ContainsKey(newColor.id) == false)
{
CuriosityName newName = (CuriosityName) 8 + rawNameToCuriosityName.Count;
rawNameToCuriosityName.Add(newColor.id, newName);
curiosityColors.Add(newName, newColor.color.ToColor());
curiosityHighlightColors.Add(newName, newColor.highlightColor.ToColor());
}
}
}
public static Color GetCuriosityColor(CuriosityName curiosityName, bool highlighted, Color defaultColor, Color defaultHighlight)
{
if (curiosityColors.ContainsKey(curiosityName) && curiosityHighlightColors.ContainsKey(curiosityName))
{
return (highlighted ? curiosityHighlightColors : curiosityColors)[curiosityName];
}
else
{
return highlighted? defaultHighlight : defaultColor;
}
}
public static void AddBodyToShipLog(ShipLogManager manager, NewHorizonsBody body)
{
string systemName = body.Config.StarSystem;
XElement astroBodyFile = XElement.Load(Main.Instance.ModHelper.Manifest.ModFolderPath + body.Config.ShipLog.xmlFile);
XElement astroBodyId = astroBodyFile.Element("ID");
if (astroBodyId == null)
{
Logger.LogError("Failed to load ship logs for " + systemName + "!");
}
else
{
astroBodyId.SetValue(systemName + "/" + astroBodyId.Value);
foreach (XElement entryElement in astroBodyFile.DescendantsAndSelf("Entry"))
{
XElement curiosityName = entryElement.Element("Curiosity");
XElement id = entryElement.Element("ID");
if (curiosityName != null && id != null && entryIdToRawName.ContainsKey(id.Value) == false)
{
entryIdToRawName.Add(id.Value, curiosityName.Value);
}
AddTranslation(entryElement);
}
TextAsset newAsset = new TextAsset(astroBodyFile.ToString());
List<TextAsset> newBodies = new List<TextAsset>(manager._shipLogXmlAssets) {newAsset};
manager._shipLogXmlAssets = newBodies.ToArray();
if (astroIdToBody.ContainsKey(astroBodyId.Value) == false)
{
astroIdToBody.Add(astroBodyId.Value, body);
}
}
}
public static void GenerateEntryData(ShipLogManager manager)
{
const int step = 400;
int colAccumulator = 0;
int rowAccumulator = 0;
foreach(ShipLogEntry entry in manager._entryList)
{
if (manager._entryDataDict.ContainsKey(entry._id) == false)
{
NewHorizonsBody body = GetConfigFromEntry(entry);
Vector2? manualEntryPosition = GetManualEntryPosition(entry._id, body.Config.ShipLog);
Vector2 entryPosition;
if (manualEntryPosition == null)
{
entryPosition = new Vector2(colAccumulator, rowAccumulator);
}
else
{
entryPosition = (Vector2) manualEntryPosition;
}
EntryData newData = new EntryData
{
id = entry._id,
cardPosition = entryPosition,
sprite = body.Config.ShipLog.spriteFolder == null? null : GetEntrySprite(entry._id, body)
};
entry.SetSprite(newData.sprite == null? manager._shipLogLibrary.defaultEntrySprite : newData.sprite);
manager._entryDataDict.Add(entry._id, newData);
int index = manager._entryList.IndexOf(entry);
if (index < manager._entryList.Count - 2 && manager._entryList[index + 1]._astroObjectID != entry._astroObjectID)
{
rowAccumulator += step;
colAccumulator = 0;
}
else
{
colAccumulator += step;
}
}
}
}
private static void AddTranslation(XElement entry)
{
Dictionary<string, string> table = TextTranslation.Get().m_table.theShipLogTable;
XElement nameElement = entry.Element("Name");
if (nameElement != null)
{
string name = nameElement.Value;
table[name] = name;
foreach (XElement rumorFact in entry.Elements("RumorFact"))
{
XElement rumorName = rumorFact.Element("RumorName");
if (rumorName != null)
{
table[rumorName.Value] = rumorName.Value;
}
XElement rumorText = rumorFact.Element("Text");
if (rumorText != null)
{
table[name + rumorText.Value] = rumorText.Value;
}
}
foreach (XElement exploreFact in entry.Elements("ExploreFact"))
{
XElement exploreText = exploreFact.Element("Text");
if (exploreText != null)
{
table[name + exploreText.Value] = exploreText.Value;
}
}
}
}
public static void UpdateEntryCuriosity(ref ShipLogEntry entry)
{
if (entryIdToRawName.ContainsKey(entry._id))
{
entry._curiosity = rawNameToCuriosityName[entryIdToRawName[entry._id]];
}
}
private static Sprite GetEntrySprite(string entryId, NewHorizonsBody body)
{
IModAssets assets = body.Mod.Assets;
string path = body.Config.ShipLog.spriteFolder + "/" + entryId + ".png";
if (File.Exists(Main.Instance.ModHelper.Manifest.ModFolderPath + path))
{
Texture2D newTexture = assets.GetTexture(path);
Rect rect = new Rect(0, 0, newTexture.width, newTexture.height);
Vector2 pivot = new Vector2(newTexture.width / 2, newTexture.height / 2);
return Sprite.Create(newTexture, rect, pivot);
}
else
{
return null;
}
}
private static Vector2? GetManualEntryPosition(string entryId, ShipLogModule config)
{
if (config.positions == null) return null;
foreach (ShipLogModule.EntryPositionInfo position in config.positions)
{
if (position.id == entryId)
{
return position.position;
}
}
return null;
}
}
#endregion
#region Fact Reveals
public static class RevealBuilder
{
public static void Make(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod)
{
GameObject newRevealGO = MakeGameObject(go, sector, info, mod);
switch (info.revealOn.ToLower())
{
case "enter":
MakeTrigger(newRevealGO, sector, info, mod);
break;
case "observe":
MakeObservable(newRevealGO, sector, info, mod);
break;
case "snapshot":
MakeSnapshot(newRevealGO, sector, info, mod);
break;
default:
Logger.LogError("Invalid revealOn: " + info.revealOn);
break;
}
newRevealGO.SetActive(true);
}
private static SphereShape MakeShape(GameObject go, PropModule.RevealInfo info, Shape.CollisionMode collisionMode)
{
SphereShape newShape = go.AddComponent<SphereShape>();
newShape.radius = info.radius;
newShape.SetCollisionMode(collisionMode);
return newShape;
}
private static GameObject MakeGameObject(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod)
{
GameObject revealTriggerVolume = new GameObject("Reveal Volume (" + info.revealOn + ")");
revealTriggerVolume.SetActive(false);
revealTriggerVolume.transform.parent = sector?.transform ?? go.transform;
revealTriggerVolume.transform.localPosition = info.position;
return revealTriggerVolume;
}
private static void MakeTrigger(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod)
{
SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Volume);
OWTriggerVolume newVolume = go.AddComponent<OWTriggerVolume>();
newVolume._shape = newShape;
ShipLogFactListTriggerVolume volume = go.AddComponent<ShipLogFactListTriggerVolume>();
volume._factIDs = info.reveals;
}
private static void MakeObservable(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod)
{
go.layer = LayerMask.NameToLayer("Interactible");
SphereCollider newSphere = go.AddComponent<SphereCollider>();
newSphere.radius = info.radius;
OWCollider newCollider = go.AddComponent<OWCollider>();
ShipLogFactObserveTrigger newObserveTrigger = go.AddComponent<ShipLogFactObserveTrigger>();
newObserveTrigger._factIDs = info.reveals;
newObserveTrigger._maxViewDistance = info.maxDistance == -1f ? 2f : info.maxDistance;
newObserveTrigger._maxViewAngle = info.maxAngle;
newObserveTrigger._owCollider = newCollider;
newObserveTrigger._disableColliderOnRevealFact = true;
}
private static void MakeSnapshot(GameObject go, Sector sector, PropModule.RevealInfo info, IModHelper mod)
{
SphereShape newShape = MakeShape(go, info, Shape.CollisionMode.Manual);
ShapeVisibilityTracker newTracker = go.AddComponent<ShapeVisibilityTracker>();
newTracker._shapes = new Shape[] {newShape};
ShipLogFactSnapshotTrigger newSnapshotTrigger = go.AddComponent<ShipLogFactSnapshotTrigger>();
newSnapshotTrigger._maxDistance = info.maxDistance == -1f ? 200f : info.maxDistance;
newSnapshotTrigger._factIDs = info.reveals;
}
}
#endregion
#region Entry Locations
public static class EntryLocationBuilder
{
private static List<ShipLogEntryLocation> locationsToInitialize = new List<ShipLogEntryLocation>();
public static void Make(GameObject go, Sector sector, PropModule.EntryLocationInfo info, IModHelper mod)
{
GameObject entryLocationGameObject = new GameObject("Entry Location (" + info.id + ")");
entryLocationGameObject.SetActive(false);
entryLocationGameObject.transform.parent = sector?.transform ?? go.transform;
entryLocationGameObject.transform.localPosition = info.position;
ShipLogEntryLocation newLocation = entryLocationGameObject.AddComponent<ShipLogEntryLocation>();
newLocation._entryID = info.id;
newLocation._isWithinCloakField = info.cloaked;
locationsToInitialize.Add(newLocation);
entryLocationGameObject.SetActive(true);
}
public static void InitializeLocations()
{
locationsToInitialize.ForEach(l => l.InitEntry());
locationsToInitialize.Clear();
}
}
#endregion
public static void Init()
{
var shipLogRoot = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas");

View File

@ -8,6 +8,7 @@ using UnityEngine;
using Random = UnityEngine.Random;
using Logger = NewHorizons.Utility.Logger;
using System.Reflection;
using NewHorizons.Builder.General;
using NewHorizons.Utility;
using OWML.Common;
@ -53,6 +54,20 @@ namespace NewHorizons.Builder.Props
DialogueBuilder.Make(go, sector, dialogueInfo, mod);
}
}
if (config.Props.Reveal != null)
{
foreach (var revealInfo in config.Props.Reveal)
{
ShipLogBuilder.RevealBuilder.Make(go, sector, revealInfo, mod);
}
}
if (config.Props.EntryLocation != null)
{
foreach (var entryLocationInfo in config.Props.EntryLocation)
{
ShipLogBuilder.EntryLocationBuilder.Make(go, sector, entryLocationInfo, mod);
}
}
}
public static GameObject LoadPrefab(string assetBundle, string path, string uniqueModName, IModAssets assets)

View File

@ -145,6 +145,7 @@ namespace NewHorizons.Builder.Props
}
audioSignal._name = name;
audioSignal._sourceRadius = info.SourceRadius;
audioSignal._revealFactID = info.Reveals;
audioSignal._onlyAudibleToScope = info.OnlyAudibleToScope;
audioSignal._identificationDistance = info.IdentificationRadius;
audioSignal._canBePickedUpByScope = true;

View File

@ -0,0 +1,47 @@
using NewHorizons.External;
using OWML.Common;
using UnityEngine;
using UnityEngine.UI;
using Logger = NewHorizons.Utility.Logger;
namespace NewHorizons.Components
{
public class ShipLogDetail : MonoBehaviour
{
private Image revealedImage;
private Image outlineImage;
private ShipLogModule.ShipLogDetailInfo detailInfo;
public void Init(ShipLogModule.ShipLogDetailInfo info, Image revealed, Image outline)
{
detailInfo = info;
revealedImage = revealed;
outlineImage = outline;
revealedImage.enabled = false;
outlineImage.enabled = false;
}
public void UpdateState(ShipLogEntry.State parentState)
{
switch (parentState)
{
case ShipLogEntry.State.Explored:
outlineImage.enabled = false;
revealedImage.enabled = true;
break;
case ShipLogEntry.State.Rumored:
revealedImage.enabled = false;
outlineImage.enabled = true;
break;
case ShipLogEntry.State.Hidden:
revealedImage.enabled = false;
outlineImage.enabled = !detailInfo.invisibleWhenHidden;
break;
case ShipLogEntry.State.None:
revealedImage.enabled = false;
outlineImage.enabled = false;
break;
}
}
}
}

View File

@ -20,6 +20,7 @@ namespace NewHorizons.External
StarModule Star { get; }
FocalPointModule FocalPoint { get; }
PropModule Props { get; }
ShipLogModule ShipLog { get; }
SpawnModule Spawn { get; }
SignalModule Signal { get; }
SingularityModule Singularity { get; }

View File

@ -23,6 +23,7 @@ namespace NewHorizons.External
public StarModule Star { get; set; }
public FocalPointModule FocalPoint { get; set; }
public PropModule Props { get; set; }
public ShipLogModule ShipLog { get; set; }
public SpawnModule Spawn { get; set; }
public SignalModule Signal { get; set; }
public SingularityModule Singularity { get; set; }

View File

@ -15,6 +15,8 @@ namespace NewHorizons.External
public GeyserInfo[] Geysers;
public TornadoInfo[] Tornados;
public DialogueInfo[] Dialogue;
public RevealInfo[] Reveal;
public EntryLocationInfo[] EntryLocation;
public class ScatterInfo
{
@ -68,5 +70,22 @@ namespace NewHorizons.External
public MVector3 remoteTriggerPosition;
public string blockAfterPersistentCondition;
}
public class RevealInfo
{
public string revealOn;
public string[] reveals;
public MVector3 position;
public float radius = 1f;
public float maxDistance = -1f; // Snapshot & Observe Only
public float maxAngle = 180f; // Observe Only
}
public class EntryLocationInfo
{
public string id;
public bool cloaked;
public MVector3 position;
}
}
}

48
NewHorizons/External/ShipLogModule.cs vendored Normal file
View File

@ -0,0 +1,48 @@
using NewHorizons.Utility;
namespace NewHorizons.External
{
public class ShipLogModule : Module
{
public string xmlFile;
public string spriteFolder;
public string[] initialReveal;
public MapModeInfo mapMode;
public CuriosityColorInfo[] curiosities;
public EntryPositionInfo[] positions;
public class MapModeInfo
{
public string revealedSprite;
public string outlineSprite;
public float scale = 1f;
public bool invisibleWhenHidden;
public float offset = 0f;
public bool remove = false;
public ShipLogDetailInfo[] details;
}
public class CuriosityColorInfo
{
public string id;
public MColor color;
public MColor highlightColor;
}
public class EntryPositionInfo
{
public string id;
public MVector2 position;
}
public class ShipLogDetailInfo
{
public string revealedSprite;
public string outlineSprite;
public float rotation = 0f;
public bool invisibleWhenHidden;
public MVector2 position;
public MVector2 scale;
}
}
}

View File

@ -18,6 +18,7 @@ namespace NewHorizons.External
public string Name;
public string AudioClip = null;
public string AudioFilePath = null;
public string Reveals = "";
public float SourceRadius = 1f;
public float DetectionRadius = 0f;
public float IdentificationRadius = 10f;

View File

@ -12,12 +12,13 @@ using NewHorizons.Utility;
using OWML.Common;
using OWML.ModHelper;
using OWML.Utils;
using PacificEngine.OW_CommonResources.Game.Player;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Epic.OnlineServices;
using PacificEngine.OW_CommonResources.Game.Player;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

View File

@ -2,7 +2,7 @@
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ProjectFiles</ProjectView>
<OutputPath>$(AppData)\OuterWildsModManager\OWML\Mods\xen.NewHorizons</OutputPath>
<OuterWildsModsDirectory>$(AppData)\OuterWildsModManager\OWML\Mods</OuterWildsModsDirectory>
<OutputPath>$(AppData)\OuterWildsModManager\OWML\Mods\xen.NewHorizons</OutputPath>
<OuterWildsModsDirectory>$(AppData)\OuterWildsModManager\OWML\Mods</OuterWildsModsDirectory>
</PropertyGroup>
</Project>
</Project>

View File

@ -5,11 +5,17 @@ using NewHorizons.External;
using OWML.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Harmony;
using NewHorizons.Utility;
using OWML.Utils;
using UnityEngine;
using Logger = NewHorizons.Utility.Logger;
using Object = UnityEngine.Object;
namespace NewHorizons.Tools
{
@ -41,17 +47,41 @@ namespace NewHorizons.Tools
var playerDataLearnFrequency = typeof(PlayerData).GetMethod("LearnFrequency");
Main.Instance.ModHelper.HarmonyHelper.AddPrefix(playerDataLearnFrequency, typeof(Patches), nameof(Patches.OnPlayerDataLearnFrequency));
var playerDataKnowsMultipleFrequencies = typeof(PlayerData).GetMethod("KnowsMultipleFrequencies");
Main.Instance.ModHelper.HarmonyHelper.AddPrefix(playerDataKnowsMultipleFrequencies, typeof(Patches), nameof(Patches.OnPlayerDataKnowsMultipleFrequencies));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix(playerDataKnowsMultipleFrequencies, typeof(Patches), nameof(Patches.OnPlayerDataKnowsMultipleFrequencies));
var playerDataResetGame = typeof(PlayerData).GetMethod("ResetGame");
Main.Instance.ModHelper.HarmonyHelper.AddPostfix(playerDataResetGame, typeof(Patches), nameof(Patches.OnPlayerDataResetGame));
var playerDataGetNewlyRevealedFactIDs = typeof(PlayerData).GetMethod("GetNewlyRevealedFactIDs");
Main.Instance.ModHelper.HarmonyHelper.AddPostfix(playerDataGetNewlyRevealedFactIDs, typeof(Patches), nameof(Patches.OnPlayerDataGetNewlyRevealedFactIDsComplete));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<BlackHoleVolume>("Start", typeof(Patches), nameof(Patches.OnBlackHoleVolumeStart));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<WhiteHoleVolume>("Awake", typeof(Patches), nameof(Patches.OnWhiteHoleVolumeAwake));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ProbeLauncher>("UpdateOrbitalLaunchValues", typeof(Patches), nameof(Patches.OnProbeLauncherUpdateOrbitalLaunchValues));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<SurveyorProbe>("IsLaunched", typeof(Patches), nameof(Patches.OnSurveyorProbeIsLaunched));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipLogManager>("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwake));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipLogManager>("Start", typeof(Patches), nameof(Patches.OnShipLogManagerStart));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipLogManager>("IsFactRevealed", typeof(Patches), nameof(Patches.OnShipLogManagerIsFactRevealed));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipLogManager>("CheckForCompletionAchievement", typeof(Patches), nameof(Patches.OnShipLogManagerCheckForCompletionAchievement));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<UIStyleManager>("GetCuriosityColor", typeof(Patches), nameof(Patches.OnUIStyleManagerGetCuriosityColor));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipLogSandFunnel>("Awake", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipLogSandFunnel>("UpdateState", typeof(Patches), nameof(Patches.DisableShipLogSandFunnel));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipLogAstroObject>("GetName", typeof(Patches), nameof(Patches.OnShipLogAstroObjectGetName));
Main.Instance.ModHelper.HarmonyHelper.AddPrefix<ShipCockpitController>("Update", typeof(Patches), nameof(Patches.OnShipCockpitControllerUpdate));
// Postfixes
Main.Instance.ModHelper.HarmonyHelper.AddPostfix<MapController>("Awake", typeof(Patches), nameof(Patches.OnMapControllerAwake));
Main.Instance.ModHelper.HarmonyHelper.AddPostfix<ShipLogManager>("Awake", typeof(Patches), nameof(Patches.OnShipLogManagerAwakeComplete));
Main.Instance.ModHelper.HarmonyHelper.AddPostfix<ShipLogAstroObject>("UpdateState", typeof(Patches), nameof(Patches.OnShipLogAstroObjectUpdateState));
Main.Instance.ModHelper.HarmonyHelper.AddPostfix<ShipLogMapMode>("EnterMode", typeof(Patches), nameof(Patches.OnShipLogMapModeEnterMode));
Main.Instance.ModHelper.HarmonyHelper.AddPostfix<ShipLogMapMode>("Initialize", typeof(Patches), nameof(Patches.OnShipLogMapModeInitialize));
}
public static bool GetHUDDisplayName(ReferenceFrame __instance, ref string __result)
@ -303,7 +333,7 @@ namespace NewHorizons.Tools
}
return true;
}
public static void OnPlayerDataResetGame()
{
NewHorizonsData.Reset();
@ -347,5 +377,204 @@ namespace NewHorizons.Tools
}
return false;
}
public static bool OnShipCockpitControllerUpdate(ShipCockpitController __instance)
{
if(__instance._playerAtFlightConsole && OWInput.IsNewlyPressed(InputLibrary.autopilot, InputMode.ShipCockpit))
{
var targetSystem = ShipLogBuilder.ShipLogStarChartMode.GetTargetStarSystem();
if (targetSystem != null)
{
Main.Instance.ChangeCurrentStarSystem(targetSystem, true);
return false;
}
}
return true;
}
#region ShipLog
public static void OnShipLogManagerAwake(ShipLogManager __instance)
{
Logger.Log("Beginning Ship Log Generation For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log);
if (Main.Instance.CurrentStarSystem != "SolarSystem")
{
__instance._shipLogXmlAssets = new TextAsset[] {};
foreach (ShipLogEntryLocation logEntryLocation in GameObject.FindObjectsOfType<ShipLogEntryLocation>())
{
logEntryLocation._initialized = true;
}
}
foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem])
{
if (body.Config.ShipLog?.curiosities != null)
{
ShipLogBuilder.RumorModeBuilder.AddCuriosityColors(body.Config.ShipLog.curiosities);
}
}
foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem])
{
if (body.Config.ShipLog?.xmlFile != null)
{
ShipLogBuilder.RumorModeBuilder.AddBodyToShipLog(__instance, body);
}
}
}
public static void OnShipLogManagerAwakeComplete(ShipLogManager __instance)
{
ShipLogBuilder.RumorModeBuilder.GenerateEntryData(__instance);
for (var i = 0; i < __instance._entryList.Count; i++)
{
ShipLogEntry logEntry = __instance._entryList[i];
ShipLogBuilder.RumorModeBuilder.UpdateEntryCuriosity(ref logEntry);
}
Logger.Log("Ship Log Generation Complete For: " + Main.Instance.CurrentStarSystem, Logger.LogType.Log);
}
public static bool OnShipLogManagerIsFactRevealed(ShipLogManager __instance, ref bool __result, string __0)
{
if (Main.Instance.CurrentStarSystem == "SolarSystem")
{
return true;
}
else
{
if (__instance._factDict.ContainsKey(__0) == false)
{
__result = false;
return false;
}
else
{
return true;
}
}
}
public static bool OnShipLogManagerCheckForCompletionAchievement()
{
return Main.Instance.CurrentStarSystem == "SolarSystem";
}
public static bool OnShipLogManagerStart(ShipLogManager __instance)
{
if (Main.Instance.CurrentStarSystem == "SolarSystem")
{
return true;
}
else
{
foreach (NewHorizonsBody body in Main.BodyDict[Main.Instance.CurrentStarSystem])
{
foreach (string fact in body.Config.ShipLog?.initialReveal ?? Array.Empty<string>())
{
__instance.RevealFact(fact, false, false);
}
}
ShipLogBuilder.EntryLocationBuilder.InitializeLocations();
return false;
}
}
public static bool OnUIStyleManagerGetCuriosityColor(UIStyleManager __instance, CuriosityName __0, bool __1, ref Color __result)
{
if (Main.Instance.CurrentStarSystem == "SolarSystem")
{
return true;
}
else
{
__result = ShipLogBuilder.RumorModeBuilder.GetCuriosityColor(__0, __1, __instance._neutralColor, __instance._neutralHighlight);
return false;
}
}
private static void DeleteDetail(string name)
{
Object.Destroy(GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH + "/" + name));
}
public static void OnShipLogMapModeInitialize(ShipLogMapMode __instance)
{
if (Main.Instance.CurrentStarSystem != "SolarSystem")
{
GameObject panRoot = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH);
GameObject sunObject = GameObject.Find(ShipLogBuilder.PAN_ROOT_PATH + "/Sun");
ShipLogAstroObject[][] navMatrix = ShipLogBuilder.MapModeBuilder.ConstructMapMode(Main.Instance.CurrentStarSystem, panRoot, sunObject.layer);
if (navMatrix.Length <= 1)
{
Logger.LogWarning("No planets suitable for map mode found! Defaulting to vanilla menu (expect weirdness!).");
}
else
{
__instance._astroObjects = navMatrix;
__instance._startingAstroObjectID = navMatrix[1][0].GetID();
List<GameObject> delete = SearchUtilities.GetAllChildren(panRoot).Where(g => g.name.Contains("_ShipLog") == false).ToList();
foreach (GameObject gameObject in delete)
{
DeleteDetail(gameObject.name);
}
// Just Lie About Having A Sand Funnel
__instance._sandFunnel = __instance.gameObject.AddComponent<ShipLogSandFunnel>();
}
}
Logger.Log("Map Mode Construction Complete", Logger.LogType.Log);
}
public static bool OnShipLogAstroObjectGetName(ShipLogAstroObject __instance, ref string __result)
{
if (Main.Instance.CurrentStarSystem == "SolarSystem")
{
return true;
}
else
{
__result = ShipLogBuilder.MapModeBuilder.GetAstroBodyShipLogName(__instance.GetID());
return false;
}
}
public static void OnShipLogAstroObjectUpdateState(ShipLogAstroObject __instance)
{
Transform detailsParent = __instance.transform.Find("Details");
if (detailsParent != null)
{
foreach (GameObject child in SearchUtilities.GetAllChildren(detailsParent.gameObject))
{
Component detail;
if (child.TryGetComponent(typeof(ShipLogDetail), out detail))
{
(detail as ShipLogDetail)?.UpdateState(__instance._state);
}
}
}
Transform lineObject = __instance.transform.Find("Line_ShipLog");
if (lineObject != null)
{
ShipLogDetail lineDetail = lineObject.gameObject.GetComponent<ShipLogDetail>();
lineDetail.UpdateState(__instance._state);
}
}
public static bool DisableShipLogSandFunnel()
{
return Main.Instance.CurrentStarSystem == "SolarSystem";
}
public static void OnPlayerDataGetNewlyRevealedFactIDsComplete(ref List<string> __result)
{
ShipLogManager manager = Locator.GetShipLogManager();
__result = __result.Where(e => manager.GetFact(e) != null).ToList();
}
# endregion
public static void OnShipLogMapModeEnterMode(ShipLogMapMode __instance)
{
var newPrompt = "Interstellar Mode";
__instance._detectiveModePrompt.SetText(newPrompt);
var text = GameObject.Find("Ship_Body/Module_Cabin/Systems_Cabin/ShipLogPivot/ShipLog/ShipLogPivot/ShipLogCanvas/ScreenPromptListScaleRoot/ScreenPromptList_UpperRight/ScreenPrompt/Text").GetComponent<UnityEngine.UI.Text>();
text.text = newPrompt;
}
}
}

View File

@ -0,0 +1,26 @@
using UnityEngine;
namespace NewHorizons.Utility
{
public class MVector2
{
public MVector2(float x, float y)
{
X = x;
Y = y;
}
public float X { get; }
public float Y { get; }
public static implicit operator MVector2(Vector2 vec)
{
return new MVector2(vec.x, vec.y);
}
public static implicit operator Vector2(MVector2 vec)
{
return new Vector2(vec.X, vec.Y);
}
}
}

View File

@ -154,5 +154,15 @@ namespace NewHorizons.Utility
return go;
}
public static List<GameObject> GetAllChildren(GameObject parent)
{
List<GameObject> children = new List<GameObject>();
foreach (Transform child in parent.transform)
{
children.Add(child.gameObject);
}
return children;
}
}
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="DialogueTree">
<xs:complexType>
<xs:sequence>
<xs:element name="NameField" type="xs:string"/>
<xs:element name="DialogueNode" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="EntryCondition" type="xs:string" minOccurs="0"/>
<xs:element name="Dialogue">
<xs:complexType>
<xs:sequence>
<xs:element name="Page" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="RevealFacts" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="FactID" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="DialogueOptionsList" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="DialogueOption" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="RequiredPersistentCondition" type="xs:string" minOccurs="0"/>
<xs:element name="CancelledPersistentCondition" type="xs:string" minOccurs="0"/>
<xs:element name="Text" type="xs:string"/>
<xs:element name="DialogueTarget" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -669,7 +669,7 @@
"generateColliders": {
"type": "bool",
"default": false,
"description": "For each mesh filter found here should we make a mesh collider?"
"description": "For each mesh filter found here should we make a mesh collider?"
}
}
}
@ -788,6 +788,94 @@
}
}
}
},
"reveal": {
"type": "array",
"description": "A set of volumes that reveal ship log fact",
"items": {
"type": "object",
"properties": {
"revealOn": {
"type": "string",
"description": "'enter', 'observe', or 'snapshot' what needs to be done to the volume to unlock the facts"
},
"reveals": {
"type": "array",
"description": "A list of facts to reveal",
"items": {
"type": "string"
}
},
"position": {
"type": "object",
"description": "The position to place the volume at",
"properties": {
"x": {
"type": "number",
"default": 0
},
"y": {
"type": "number",
"default": 0
},
"z": {
"type": "number",
"default": 0
}
}
},
"radius": {
"type": "number",
"description": "The radius of the volume",
"default": 1.0
},
"maxDistance": {
"type": "number",
"description": "The max distance the user can be away from the volume to reveal the fact (snapshot and observe only)"
},
"maxAngle": {
"type": "number",
"description": "The max view angle the player can see the volume with to unlock the fact",
"default": 180.0
}
}
}
},
"entryLocation": {
"type": "array",
"description": "A set of locations for ship log entries",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The ID of the entry this location is for"
},
"cloaked": {
"type": "bool",
"description": "Whether this entry location is in a cloaking field",
"default": false
},
"position": {
"type": "object",
"description": "The position of this entry location",
"properties": {
"x": {
"type": "number",
"default": 0
},
"y": {
"type": "number",
"default": 0
},
"z": {
"type": "number",
"default": 0
}
}
}
}
}
}
}
},
@ -883,6 +971,10 @@
"type": "string",
"description": "Relative filepath to the .wav file to use as the audio. Mutually exclusive with audioClip"
},
"reveals": {
"type": "string",
"description": "A ship log fact to reveal when the signal is identified"
},
"sourceRadius": {
"type": "number",
"default": 1,
@ -1151,6 +1243,213 @@
}
}
}
},
"ShipLog": {
"type": "object",
"properties": {
"xmlFile": {
"type": "string",
"description": "The xml file to load ship log entries from"
},
"spriteFolder": {
"type": "string",
"description": "A path to the folder where entry sprites are stored"
},
"initialReveal": {
"type": "array",
"description": "A list of fact IDs to reveal when the game starts",
"items": {
"type": "string"
}
},
"positions": {
"type": "array",
"description": "A set of positions to use instead of automatic layout in rumor mode",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The name of the entry to apply the position"
},
"position": {
"type": "object",
"properties": {
"x": {
"type": "number",
"default": 0
},
"y": {
"type": "number",
"default": 0
}
}
}
}
}
},
"MapMode": {
"type": "object",
"properties": {
"revealedSprite": {
"type": "string",
"description": "The path to the sprite to show when the planet is revealed in map mode"
},
"outlineSprite": {
"type": "string",
"description": "The path to the sprite to show when the planet is unexplored in map mode"
},
"scale": {
"type": "number",
"description": "Scale to apply to the planet in map mode",
"default": 1
},
"invisibleWhenHidden": {
"type": "bool",
"description": "Hide the planet completely if unexplored instead of showing an outline",
"default": false
},
"offset": {
"type": "number",
"description": "Extra distance to apply to this object in map mode",
"default": 0
},
"remove": {
"type": "boolean",
"description": "Completely remove this planet (and it's children) from map mode",
"default": false
},
"details": {
"type": "array",
"description": "Place non-selectable object in map mode (like sand funnels)",
"items": {
"type": "object",
"properties": {
"revealedSprite": {
"type": "string",
"description": "The sprite to show when the parent AstroBody is revealed"
},
"outlineSprite": {
"type": "string",
"description": "The sprite to show when the parent AstroBody is rumored/unexplored"
},
"rotation": {
"type": "number",
"description": "The angle in degrees to rotate the detail",
"default": 0
},
"invisibleWhenHidden": {
"type": "boolean",
"description": "Whether to completely hide this detail when the parent AstroBody is unexplored",
"default": false
},
"position": {
"type": "object",
"description": "The position (relative to the parent) to place the detail",
"properties": {
"x": {
"type": "number",
"default": 0
},
"y": {
"type": "number",
"default": 0
}
}
},
"scale": {
"type": "object",
"description": "The amount to scale the x and y axis of the detail by",
"properties": {
"x": {
"type": "number",
"default": 0
},
"y": {
"type": "number",
"default": 0
}
}
}
}
}
}
}
},
"Curiosities": {
"type": "array",
"description": "A set of colors to apply to curiosities",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The ID of the curiosity to apply the color to"
},
"color": {
"type": "object",
"description": "The color to apply to entries with this curiosity",
"properties": {
"R": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
},
"G": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
},
"B": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
},
"A": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
}
}
},
"highlightColor": {
"type": "object",
"description": "The color to apply to highlighted entries with this curiosity",
"properties": {
"R": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
},
"G": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
},
"B": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
},
"A": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 255
}
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="AstroObjectEntry">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:string"/>
<xs:element name="Entry" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:string"/>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Curiosity" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="IsCuriosity" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="RumorFact" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:string"/>
<xs:element name="SourceID" type="xs:string" minOccurs="0"/>
<xs:element name="RumorName" type="xs:string"/>
<xs:element name="Text" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ExploreFact" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="ID" type="xs:string"/>
<xs:element name="Text" type="xs:string"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>