mirror of
https://github.com/Wincent01/InfectedRose.git
synced 2025-12-21 12:09:45 -06:00
Lots of changes, too late to describe it all.
This commit is contained in:
@@ -9,6 +9,12 @@ namespace InfectedRose.Core
|
||||
public static string ReadNiString(this BitReader @this, bool wide = false, bool small = false)
|
||||
{
|
||||
var len = small ? @this.Read<byte>() : @this.Read<uint>();
|
||||
|
||||
if (len == uint.MaxValue)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var str = new char[len];
|
||||
|
||||
for (var i = 0; i < len; i++)
|
||||
|
||||
@@ -62,6 +62,27 @@ namespace InfectedRose.Database
|
||||
}
|
||||
}
|
||||
|
||||
public int GetFieldIndex(string name)
|
||||
{
|
||||
var index = -1;
|
||||
|
||||
for (var i = 0; i < Table.TableInfo.Count; i++)
|
||||
{
|
||||
var column = Table.TableInfo[i];
|
||||
|
||||
if (column.Name == name) index = i;
|
||||
}
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
throw new KeyNotFoundException(
|
||||
$"The given field key of '{name}' does not exist in the {Table.Name} table"
|
||||
);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public IEnumerator<Field> GetEnumerator()
|
||||
{
|
||||
int index = default;
|
||||
|
||||
@@ -286,6 +286,24 @@ namespace InfectedRose.Database
|
||||
return Create(max + 1);
|
||||
}
|
||||
|
||||
public Row CreateMin(int minKey)
|
||||
{
|
||||
if (TableInfo[0].Type != DataType.Integer)
|
||||
throw new NotSupportedException("AccessDatabase can only generate primary keys for Int32 types.");
|
||||
|
||||
var max = Count > 0 ? this.Max(c => c.Key) : 0;
|
||||
|
||||
for (var i = minKey; i < max; i++)
|
||||
{
|
||||
if (!ContainsKey(i) && !ClaimedKeys.Contains(i))
|
||||
{
|
||||
return Create(i);
|
||||
}
|
||||
}
|
||||
|
||||
return Create(max + 1);
|
||||
}
|
||||
|
||||
public Row CreateWithFilter(ICollection<int> exclude)
|
||||
{
|
||||
if (TableInfo[0].Type != DataType.Integer)
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of items that were generated by the mod and put into the main resource directory
|
||||
///
|
||||
/// These items are deleted upon startup.
|
||||
/// </summary>
|
||||
public class Artifacts : Dictionary<string, string>
|
||||
{
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
/// <summary>
|
||||
/// A list of items that were generated by the mod and put into the main resource directory
|
||||
///
|
||||
/// These items are deleted upon startup.
|
||||
/// </summary>
|
||||
public class Artifacts : List<string>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
151
InfectedRose.Interface/JsonExtensions.cs
Normal file
151
InfectedRose.Interface/JsonExtensions.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public static class JsonExtensions
|
||||
{
|
||||
public static T? ToObject<T>(this JsonElement element, JsonSerializerOptions? options = null)
|
||||
{
|
||||
var bufferWriter = new ArrayBufferWriter<byte>();
|
||||
using (var writer = new Utf8JsonWriter(bufferWriter))
|
||||
element.WriteTo(writer);
|
||||
return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options);
|
||||
}
|
||||
|
||||
public static T? ToObject<T>(this JsonDocument document, JsonSerializerOptions? options = null)
|
||||
{
|
||||
if (document == null)
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
return document.RootElement.ToObject<T>(options);
|
||||
}
|
||||
|
||||
public static T? ToObject<T>(this JsonValue element, JsonSerializerOptions? options = null)
|
||||
{
|
||||
var bufferWriter = new ArrayBufferWriter<byte>();
|
||||
using (var writer = new Utf8JsonWriter(bufferWriter))
|
||||
element.WriteTo(writer);
|
||||
return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options);
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ToDictionary(this JsonValue element)
|
||||
{
|
||||
var dictionary = new Dictionary<string, object>();
|
||||
|
||||
var str = element.ToString();
|
||||
|
||||
var json = JsonDocument.Parse(str);
|
||||
|
||||
var root = json.RootElement;
|
||||
|
||||
foreach (var property in root.EnumerateObject())
|
||||
{
|
||||
var value = property.Value;
|
||||
|
||||
if (value.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var array = new List<object>();
|
||||
|
||||
foreach (var item in value.EnumerateArray())
|
||||
{
|
||||
if (item.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
array.Add(item.ToDictionary());
|
||||
}
|
||||
else
|
||||
{
|
||||
array.Add(item.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
dictionary.Add(property.Name, array);
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
dictionary.Add(property.Name, value.ToDictionary());
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
dictionary.Add(property.Name, value.ToString());
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
dictionary.Add(property.Name, value.GetDouble());
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.True)
|
||||
{
|
||||
dictionary.Add(property.Name, true);
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.False)
|
||||
{
|
||||
dictionary.Add(property.Name, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
dictionary.Add(property.Name, value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ToDictionary(this JsonElement element)
|
||||
{
|
||||
var dictionary = new Dictionary<string, object>();
|
||||
|
||||
foreach (var property in element.EnumerateObject())
|
||||
{
|
||||
var value = property.Value;
|
||||
|
||||
if (value.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var array = new List<object>();
|
||||
|
||||
foreach (var item in value.EnumerateArray())
|
||||
{
|
||||
if (item.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
array.Add(item.ToDictionary());
|
||||
}
|
||||
else
|
||||
{
|
||||
array.Add(item.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
dictionary.Add(property.Name, array);
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
dictionary.Add(property.Name, value.ToDictionary());
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
dictionary.Add(property.Name, value.ToString());
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
dictionary.Add(property.Name, value.GetDouble());
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.True)
|
||||
{
|
||||
dictionary.Add(property.Name, true);
|
||||
}
|
||||
else if (value.ValueKind == JsonValueKind.False)
|
||||
{
|
||||
dictionary.Add(property.Name, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
dictionary.Add(property.Name, value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
[XmlRoot("locales")]
|
||||
public class Locales
|
||||
{
|
||||
[XmlRoot("locales")]
|
||||
public class Locales
|
||||
{
|
||||
[XmlAttribute("count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
[XmlElement("locale")]
|
||||
public string[] Locale { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
[XmlRoot("translation")]
|
||||
public class Translation
|
||||
{
|
||||
[XmlRoot("translation")]
|
||||
public class Translation
|
||||
{
|
||||
[XmlAttribute("locale")]
|
||||
public string Locale { get; set; }
|
||||
|
||||
[XmlText]
|
||||
public string Text { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
[XmlRoot("phrase")]
|
||||
public class Phrase
|
||||
{
|
||||
[XmlRoot("phrase")]
|
||||
public class Phrase
|
||||
{
|
||||
[XmlAttribute("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[XmlElement("translation")]
|
||||
public List<Translation> Translations { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
[XmlRoot("phrases")]
|
||||
public class Phrases
|
||||
{
|
||||
[XmlRoot("phrases")]
|
||||
public class Phrases
|
||||
{
|
||||
[XmlAttribute("count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
[XmlElement("phrase")]
|
||||
public List<Phrase> Phrase { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
[XmlRoot("localization")]
|
||||
public class Localization
|
||||
{
|
||||
[XmlRoot("localization")]
|
||||
public class Localization
|
||||
{
|
||||
[XmlAttribute("version")]
|
||||
public float Version { get; set; } = 1.200000f;
|
||||
|
||||
@@ -54,5 +54,4 @@ namespace InfectedRose.Interface
|
||||
|
||||
[XmlElement("phrases")]
|
||||
public Phrases Phrases { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public class Lookup : Dictionary<string, int>
|
||||
{
|
||||
public class Lookup : Dictionary<string, int>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public class Manifest
|
||||
{
|
||||
public class Manifest
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = "mod-name";
|
||||
|
||||
[JsonPropertyName("files")]
|
||||
public string[] Files { get; set; } = {"mod.json"};
|
||||
}
|
||||
|
||||
[JsonPropertyName("resources")]
|
||||
public string Resources { get; set; } = "resources";
|
||||
}
|
||||
@@ -6,10 +6,10 @@ using System.Text.Json.Serialization;
|
||||
using InfectedRose.Interface.Templates;
|
||||
using InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public class Mod
|
||||
{
|
||||
public class Mod
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; } = "";
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace InfectedRose.Interface
|
||||
public string Type { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("action")]
|
||||
public string Action { get; set; } = "add";
|
||||
public string Action { get; set; }
|
||||
|
||||
[JsonPropertyName("show-defaults")]
|
||||
public bool? ShowDefaults { get; set; }
|
||||
@@ -53,10 +53,7 @@ namespace InfectedRose.Interface
|
||||
public Zone? Zone { get; set; }
|
||||
|
||||
[JsonPropertyName("locale")]
|
||||
public Dictionary<string, string>? Locale { get; set; } = new Dictionary<string, string>
|
||||
{
|
||||
{"en_US", ""}
|
||||
};
|
||||
public Dictionary<string, string>? Locale { get; set; }
|
||||
|
||||
[JsonPropertyName("values")]
|
||||
public Dictionary<string, object?> Values { get; set; } = new Dictionary<string, object?>();
|
||||
@@ -111,5 +108,4 @@ namespace InfectedRose.Interface
|
||||
{
|
||||
return (int) Enum.Parse(typeof(ComponentId), Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,17 @@ using InfectedRose.Database;
|
||||
using InfectedRose.Database.Fdb;
|
||||
using InfectedRose.Interface.Templates;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public class IdCallback
|
||||
{
|
||||
public class IdCallback
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public Action<int> Callback { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModContext
|
||||
{
|
||||
public static class ModContext
|
||||
{
|
||||
public static Mods Configuration { get; set; }
|
||||
|
||||
public static string Root { get; set; }
|
||||
@@ -52,30 +52,6 @@ namespace InfectedRose.Interface
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void AwaitId(JsonValue json, Action<int> callback)
|
||||
{
|
||||
if (json.TryGetValue<int>(out var value))
|
||||
{
|
||||
callback(value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json.TryGetValue<string>(out var id)) throw new Exception($"Invalid id: {json}");
|
||||
|
||||
if (id.StartsWith("lego-universe:"))
|
||||
{
|
||||
// Parse ID from legacy format
|
||||
callback(int.Parse(id[14..]));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!id.Contains(':')) throw new Exception($"Invalid id: {json}, does not contain any ':' to define scope");
|
||||
|
||||
IdCallbacks.Add(new IdCallback { Id = id, Callback = callback });
|
||||
}
|
||||
|
||||
public static bool AwaitIdIfRequired(JsonValue json, Action<int> callback)
|
||||
{
|
||||
if (!IsId(json)) return false;
|
||||
@@ -86,8 +62,13 @@ namespace InfectedRose.Interface
|
||||
|
||||
}
|
||||
|
||||
public static void AwaitId(string id, Action<int> callback, bool requireMod = false, bool nonDefault = false)
|
||||
public static void AwaitId(string? id, Action<int> callback, bool requireMod = false, bool nonDefault = false)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (int.TryParse(id, out var integer))
|
||||
{
|
||||
callback(integer);
|
||||
@@ -113,6 +94,43 @@ namespace InfectedRose.Interface
|
||||
IdCallbacks.Add(new IdCallback { Id = id, Callback = callback });
|
||||
}
|
||||
|
||||
public static void AwaitId(JsonValue json, Action<int> callback, bool requireMod = false, bool nonDefault = false)
|
||||
{
|
||||
if (json.TryGetValue<int>(out var value))
|
||||
{
|
||||
callback(value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.TryGetValue<string>(out var id))
|
||||
{
|
||||
AwaitId(id, callback, requireMod, nonDefault);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception($"Undefined reference to id: {json}");
|
||||
}
|
||||
|
||||
public static void AwaitMultiple(string[] ids, Action callback)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
AwaitId(id, _ =>
|
||||
{
|
||||
count++;
|
||||
|
||||
if (count == ids.Length)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static int AssertId(JsonValue json)
|
||||
{
|
||||
if (json.TryGetValue<int>(out var value))
|
||||
@@ -162,54 +180,68 @@ namespace InfectedRose.Interface
|
||||
|
||||
public static void RegisterId(string id, int value)
|
||||
{
|
||||
Lookup[id] = value;
|
||||
|
||||
foreach (var idCallback in IdCallbacks.Where(callback => callback.Id == id).ToArray())
|
||||
{
|
||||
idCallback.Callback(value);
|
||||
|
||||
IdCallbacks.Remove(idCallback);
|
||||
}
|
||||
|
||||
Lookup[id] = value;
|
||||
}
|
||||
|
||||
public static void RegisterArtifact(string source, string destination)
|
||||
public static void RegisterArtifact(string file)
|
||||
{
|
||||
Artifacts[source] = destination;
|
||||
Artifacts.Add(file);
|
||||
}
|
||||
|
||||
public static void CreateArtifact(string source, string destination)
|
||||
public static void CreateArtifactFrom(string sourceFile, string destination)
|
||||
{
|
||||
if (!File.Exists(source))
|
||||
var dr = new Uri(Path.Combine(Root, "../", destination));
|
||||
|
||||
var src = Path.GetRelativePath(Root, Directory.GetCurrentDirectory());
|
||||
|
||||
var resources = Configuration.ResourceFolder;
|
||||
|
||||
var relative = sourceFile.Split("mods/").Last();
|
||||
|
||||
// Create the path to the resource folder
|
||||
var actual = new Uri(Path.Combine(Root, resources, src, relative));
|
||||
|
||||
var relativePath = dr.MakeRelativeUri(actual).ToString();
|
||||
|
||||
if (File.Exists(dr.LocalPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Could not create artifact to non-existent file: {source}");
|
||||
var artifactsRoot = Path.Combine(Root, "artifacts");
|
||||
|
||||
if (!Directory.Exists(artifactsRoot))
|
||||
{
|
||||
Directory.CreateDirectory(artifactsRoot);
|
||||
}
|
||||
|
||||
File.CreateSymbolicLink(source, destination);
|
||||
// Copy the directory structure to the artifacts folder
|
||||
var artifacts = Path.Combine(artifactsRoot, Path.GetDirectoryName(destination) ?? "./");
|
||||
|
||||
RegisterArtifact(source, destination);
|
||||
if (!Directory.Exists(artifacts))
|
||||
{
|
||||
Directory.CreateDirectory(artifacts);
|
||||
}
|
||||
|
||||
public static void CreateArtifactFrom(string sourceFile, string destinationDirectory)
|
||||
var dest = Path.Combine(artifacts, Path.GetFileName(dr.LocalPath));
|
||||
|
||||
if (!File.Exists(dest))
|
||||
{
|
||||
var dr = Path.Combine(Root, "../", destinationDirectory);
|
||||
|
||||
var src = Path.GetRelativePath(dr, Directory.GetCurrentDirectory());
|
||||
|
||||
var destination = Path.Combine(dr, Path.GetFileName(sourceFile));
|
||||
|
||||
src = Path.Combine(src, sourceFile);
|
||||
|
||||
if (File.Exists(destination))
|
||||
{
|
||||
File.Delete(destination);
|
||||
File.Copy(dr.LocalPath, dest, true);
|
||||
}
|
||||
|
||||
File.CreateSymbolicLink(destination, src);
|
||||
File.Delete(dr.LocalPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
RegisterArtifact(dr.LocalPath);
|
||||
}
|
||||
|
||||
src = Path.GetRelativePath(Root, src);
|
||||
destination = Path.GetRelativePath(Root, destination);
|
||||
|
||||
RegisterArtifact(src, destination);
|
||||
File.CreateSymbolicLink(dr.LocalPath, relativePath);
|
||||
}
|
||||
|
||||
public static Mod GetMod(string id)
|
||||
@@ -351,7 +383,7 @@ namespace InfectedRose.Interface
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string ParseValue(string value, bool ignoreSpecialRoot = false, string root = "../../res/")
|
||||
public static string ParseValue(string value, bool ignoreSpecialRoot = false, string root = "../../res/", string columnName = "")
|
||||
{
|
||||
if (value.StartsWith("INCLUDE:"))
|
||||
{
|
||||
@@ -360,101 +392,85 @@ namespace InfectedRose.Interface
|
||||
return File.ReadAllText(value);
|
||||
}
|
||||
|
||||
if (!value.StartsWith("ASSET:"))
|
||||
if (value.StartsWith("ICON:"))
|
||||
{
|
||||
value = value.Substring(5);
|
||||
|
||||
if (!value.Contains(':'))
|
||||
{
|
||||
throw new Exception($"Invalid icon: {value}");
|
||||
}
|
||||
|
||||
var folder = value.Split(':');
|
||||
|
||||
var path = Path.Combine(Configuration.ResourceFolder, folder[0], "resources", "icon", folder[1]);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Configuration.ResourceFolder))
|
||||
{
|
||||
throw new Exception("Resource folder not set");
|
||||
}
|
||||
|
||||
var resources = Configuration.ResourceFolder;
|
||||
|
||||
if (!value.Contains("mods/"))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value = value.Substring(6);
|
||||
if (columnName == "LXFMLFolder")
|
||||
{
|
||||
var r = value.Split("mods/").Last();
|
||||
var a = new Uri(Path.Combine(Root, resources, r));
|
||||
var s = new Uri(Path.Combine(Root, "../", "res/BrickModels/"));
|
||||
|
||||
return s.MakeRelativeUri(a).ToString();
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(value);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(extension) || string.IsNullOrWhiteSpace(Path.GetDirectoryName(value)))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
var createSymlink = false;
|
||||
|
||||
if (value.StartsWith("PHYSICS:"))
|
||||
if (extension == ".hkx")
|
||||
{
|
||||
root = "../../res/physics/";
|
||||
|
||||
value = value.Substring(8);
|
||||
root = "res/physics/";
|
||||
}
|
||||
else if (value.StartsWith("MAP:"))
|
||||
else if (extension == ".luz")
|
||||
{
|
||||
root = "../../res/maps/";
|
||||
|
||||
value = value.Substring(4);
|
||||
root = "res/maps/";
|
||||
}
|
||||
else if (value.StartsWith("ICON:"))
|
||||
else if (extension == ".dds")
|
||||
{
|
||||
root = "../../res/mesh/bricks/";
|
||||
|
||||
value = value.Substring(5);
|
||||
root = "res/mesh/bricks/";
|
||||
}
|
||||
else if (value.StartsWith("LXFML:"))
|
||||
else if (extension == ".lxfml")
|
||||
{
|
||||
createSymlink = true;
|
||||
|
||||
root = "res/BrickModels/";
|
||||
|
||||
value = value.Substring(6);
|
||||
}
|
||||
|
||||
if (ignoreSpecialRoot)
|
||||
else if (extension is ".kfm" or ".nif")
|
||||
{
|
||||
root = "../../res/";
|
||||
root = "res/";
|
||||
}
|
||||
|
||||
string final;
|
||||
// Take all parts after the mods folder
|
||||
var relative = value.Split("mods/").Last();
|
||||
|
||||
// Copy the file to the selected resource folder is specified
|
||||
if (!string.IsNullOrWhiteSpace(Configuration.ResourceFolder))
|
||||
{
|
||||
final = Path.Combine(Root, Configuration.ResourceFolder, Path.GetRelativePath(Root, Directory.GetCurrentDirectory()), value);
|
||||
// Create the path to the resource folder
|
||||
var actual = new Uri(Path.Combine(Root, resources, relative));
|
||||
|
||||
var directory = Path.GetDirectoryName(final);
|
||||
var source = new Uri(Path.Combine(Root, "../", root));
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory!);
|
||||
}
|
||||
// Get the relative path from the source to the resource folder
|
||||
var relativePath = source.MakeRelativeUri(actual).ToString();
|
||||
|
||||
if (!File.Exists(final))
|
||||
{
|
||||
File.Copy(value, final);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final = value;
|
||||
}
|
||||
|
||||
if (createSymlink)
|
||||
{
|
||||
CreateArtifactFrom(value, root);
|
||||
|
||||
/*
|
||||
var dr = Path.Combine(Root, "../", root);
|
||||
|
||||
var src = Path.GetRelativePath(dr, Directory.GetCurrentDirectory());
|
||||
|
||||
var destination = Path.Combine(dr, Path.GetFileName(value));
|
||||
|
||||
src = Path.Combine(src, value);
|
||||
|
||||
Console.WriteLine(destination);
|
||||
|
||||
if (File.Exists(destination))
|
||||
{
|
||||
File.Delete(destination);
|
||||
}
|
||||
|
||||
File.CreateSymbolicLink(destination, src);
|
||||
*/
|
||||
}
|
||||
|
||||
root = Root + root;
|
||||
|
||||
// Get the relative path from root to asset
|
||||
var finalRelative = Path.GetRelativePath(root, final);
|
||||
|
||||
return finalRelative;
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
public static int AddIcon(string file)
|
||||
@@ -475,6 +491,26 @@ namespace InfectedRose.Interface
|
||||
return icon.Key;
|
||||
}
|
||||
|
||||
public static int AddIcon(string file, out string path)
|
||||
{
|
||||
if (file.StartsWith("lego-universe:"))
|
||||
{
|
||||
throw new Exception("Cannot add icon with lego-universe ID");
|
||||
}
|
||||
|
||||
file = ParseValue(file);
|
||||
|
||||
var table = Database["Icons"]!;
|
||||
|
||||
var icon = table.Create()!;
|
||||
|
||||
path = "..\\..\\textures/../" + file;
|
||||
|
||||
icon["IconPath"].Value = path;
|
||||
|
||||
return icon.Key;
|
||||
}
|
||||
|
||||
public static void ApplyValues(Dictionary<string, object> values, Row row, Table table, bool defaultNull = false)
|
||||
{
|
||||
for (var index = 1; index < table.TableInfo.Count; index++)
|
||||
@@ -584,6 +620,10 @@ namespace InfectedRose.Interface
|
||||
{
|
||||
var str = objValue.ToString();
|
||||
|
||||
var parsed = ParseValue(str);
|
||||
|
||||
if (parsed != str) break;
|
||||
|
||||
if (str.Contains(':'))
|
||||
{
|
||||
AwaitId(str, id =>
|
||||
@@ -603,7 +643,58 @@ namespace InfectedRose.Interface
|
||||
break;
|
||||
case DataType.Varchar:
|
||||
case DataType.Text:
|
||||
field.Value = ParseValue(objValue.ToString()!);
|
||||
{
|
||||
field.Value = "";
|
||||
|
||||
var str = objValue.ToString()!;
|
||||
|
||||
if (str.Contains(",") && str.Contains(":"))
|
||||
{
|
||||
var parts = str.Split(",");
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var count = 1;
|
||||
|
||||
var parts2 = part.Split(":");
|
||||
|
||||
if (parts2.Length >= 3)
|
||||
{
|
||||
var last = parts2.Last();
|
||||
|
||||
if (int.TryParse(last, out var c))
|
||||
{
|
||||
count = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Invalid count {last} in {str}");
|
||||
}
|
||||
}
|
||||
|
||||
// Value is all parts except the last
|
||||
var value = string.Join(":", parts2.Take(parts2.Length - 1));
|
||||
|
||||
AwaitId(value, id =>
|
||||
{
|
||||
if (field.Value == null || field.Value == "")
|
||||
{
|
||||
field.Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
field.Value = $"{field.Value},";
|
||||
}
|
||||
|
||||
field.Value = $"{field.Value}{id}:{count}";
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
field.Value = ParseValue(objValue.ToString()!, columnName: info.Name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataType.Boolean:
|
||||
field.Value = mod.GetValue<bool>(key);
|
||||
@@ -614,5 +705,4 @@ namespace InfectedRose.Interface
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public abstract class ModType
|
||||
{
|
||||
public abstract class ModType
|
||||
{
|
||||
public abstract void Apply(Mod mod);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ModTypeAttribute : Attribute
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ModTypeAttribute : Attribute
|
||||
{
|
||||
public string Type { get; set; }
|
||||
|
||||
public ModTypeAttribute(string type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public class ModPriority
|
||||
{
|
||||
public class ModPriority
|
||||
{
|
||||
[JsonPropertyName("directory")]
|
||||
public string Directory { get; set; }
|
||||
|
||||
[JsonPropertyName("priority")]
|
||||
public int Priority { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class Mods
|
||||
{
|
||||
public class Mods
|
||||
{
|
||||
[JsonPropertyName("version")]
|
||||
public string Version { get; set; } = "";
|
||||
|
||||
@@ -27,9 +27,8 @@ namespace InfectedRose.Interface
|
||||
public string? Copy { get; set; }
|
||||
|
||||
[JsonPropertyName("resource-folder")]
|
||||
public string ResourceFolder { get; set; }
|
||||
public string ResourceFolder { get; set; } = "../res/maps/__mods__";
|
||||
|
||||
[JsonPropertyName("priorities")]
|
||||
public List<ModPriority> Priorities { get; set; } = new List<ModPriority>();
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using InfectedRose.Database;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public class PrimaryKeyRegistry
|
||||
{
|
||||
public class PrimaryKeyRegistry
|
||||
{
|
||||
public Dictionary<string, string[]> PrimaryKeys { get; } = new Dictionary<string, string[]>
|
||||
{
|
||||
{"AICombatRoles", new[] {"id"}},
|
||||
@@ -176,5 +176,4 @@ namespace InfectedRose.Interface
|
||||
PrimaryKeys.Add(table.Name, primaryKey.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -8,31 +7,30 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Serialization;
|
||||
using CommandLine;
|
||||
using InfectedRose.Database;
|
||||
using InfectedRose.Database.Fdb;
|
||||
using InfectedRose.Database.Generic;
|
||||
using InfectedRose.Interface.Templates;
|
||||
using InfectedRose.Interface.Templates.World;
|
||||
using InfectedRose.Luz;
|
||||
using InfectedRose.Lvl;
|
||||
using InfectedRose.Terrain;
|
||||
using InfectedRose.Utilities;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using RakDotNet.IO;
|
||||
using SqlExt = InfectedRose.Database.Sql.ColumnExtensions;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public const string Version = "0.1";
|
||||
|
||||
public static Options CommandLineOptions { get; set; }
|
||||
|
||||
public static Dictionary<string, ModType> ModTypes { get; set; } = new Dictionary<string, ModType>();
|
||||
|
||||
public static List<string> Zones { get; set; } = new List<string>();
|
||||
|
||||
public class Options
|
||||
{
|
||||
[Option('i', "input", Required = false, HelpText = "Path to mods.json.")]
|
||||
@@ -98,10 +96,10 @@ namespace InfectedRose.Interface
|
||||
|
||||
var table = ModContext.Database[tableName];
|
||||
|
||||
if (table != null)
|
||||
{
|
||||
Row row;
|
||||
|
||||
if (table != null)
|
||||
{
|
||||
if (mod.Action == "add")
|
||||
{
|
||||
row = table.Create();
|
||||
@@ -136,6 +134,10 @@ namespace InfectedRose.Interface
|
||||
throw new InvalidOperationException($"Could not find mod to edit of id '{mod.Id}' in table '{tableName}'.");
|
||||
}
|
||||
}
|
||||
else if (mod.Action == "delete")
|
||||
{
|
||||
goto delete;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown action '{mod.Action}' for mod of id '{mod.Id}'.");
|
||||
@@ -145,6 +147,46 @@ namespace InfectedRose.Interface
|
||||
|
||||
ModContext.ApplyValues(mod, row, table);
|
||||
}
|
||||
|
||||
delete:
|
||||
|
||||
if (mod.Action == "delete")
|
||||
{
|
||||
if (!Enum.TryParse<ComponentId>(mod.Type, out var componentId))
|
||||
{
|
||||
throw new Exception($"Could not parse component ID {mod.Type}!");
|
||||
}
|
||||
|
||||
if (!mod.Id.StartsWith("lego-universe:"))
|
||||
{
|
||||
throw new InvalidOperationException($"Can not delete mod of id '{mod.Id}'; only mods starting with a prefix of 'lego-universe:' can be deleted.");
|
||||
}
|
||||
|
||||
var id = int.Parse(mod.Id.Split(':')[1]);
|
||||
|
||||
var componentRegistry = ModContext.Database["ComponentsRegistry"];
|
||||
|
||||
var componentRow = componentRegistry.SeekMultiple(id).FirstOrDefault(
|
||||
c => (int)c["component_type"].Value == (int)componentId
|
||||
);
|
||||
|
||||
if (componentRow == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find component with ID {id} of type {componentId}!");
|
||||
}
|
||||
|
||||
if (table != null)
|
||||
{
|
||||
if (!table.Seek((int)componentRow["component_id"].Value, out row))
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find mod to delete of id '{mod.Id}' in table '{tableName}'.");
|
||||
}
|
||||
|
||||
table.Remove(row);
|
||||
}
|
||||
|
||||
componentRegistry.Remove(componentRow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For entries without tables, like the MissionNPCComponent
|
||||
@@ -174,9 +216,9 @@ namespace InfectedRose.Interface
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public static void ApplyModFile(Manifest manifest, string file)
|
||||
public static void ApplyModFile(Manifest manifest, string file, bool applyZone = false)
|
||||
{
|
||||
Mod[] mods = ReadJson<Mod[]>(file);
|
||||
var mods = ReadJson<Mod[]>(file);
|
||||
|
||||
var directory = Directory.GetCurrentDirectory();
|
||||
|
||||
@@ -186,10 +228,19 @@ namespace InfectedRose.Interface
|
||||
{
|
||||
ModContext.Mods[mod.Id] = mod;
|
||||
|
||||
if (mod.Type == "zone" && !applyZone)
|
||||
{
|
||||
Zones.Add(file);
|
||||
|
||||
Directory.SetCurrentDirectory(directory);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyMod(mod);
|
||||
|
||||
if (!mod.ShowDefaults.HasValue || mod.ShowDefaults.Value == false)
|
||||
{
|
||||
if (mod.ShowDefaults.HasValue && mod.ShowDefaults.Value) continue;
|
||||
|
||||
foreach (var (key, value) in mod.Defaults)
|
||||
{
|
||||
if (mod.HasValue(key) && (mod.Values[key] == null || (value != null && mod.Values[key].ToString() == value.ToString())))
|
||||
@@ -198,7 +249,6 @@ namespace InfectedRose.Interface
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Directory.SetCurrentDirectory(directory);
|
||||
|
||||
@@ -219,10 +269,39 @@ namespace InfectedRose.Interface
|
||||
|
||||
var directory = Path.GetDirectoryName(file)!;
|
||||
|
||||
var resources = ModContext.Configuration.ResourceFolder;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(resources))
|
||||
{
|
||||
if (!Directory.Exists(resources))
|
||||
{
|
||||
Directory.CreateDirectory(resources);
|
||||
}
|
||||
|
||||
// Get the directory name of the mod (last part of the path)
|
||||
var modName = manifest.Name;
|
||||
|
||||
var link = Path.Combine(resources, modName);
|
||||
|
||||
if (Directory.Exists(link))
|
||||
{
|
||||
Directory.Delete(link);
|
||||
}
|
||||
|
||||
Directory.CreateSymbolicLink(link, Path.GetRelativePath(resources, directory));
|
||||
}
|
||||
|
||||
foreach (var modFile in manifest.Files)
|
||||
{
|
||||
ApplyModFile(manifest, Path.Combine(directory, modFile));
|
||||
}
|
||||
|
||||
foreach (var zone in Zones)
|
||||
{
|
||||
ApplyModFile(manifest, zone, true);
|
||||
}
|
||||
|
||||
Zones.Clear();
|
||||
}
|
||||
|
||||
public static void SaveDatabase()
|
||||
@@ -325,7 +404,20 @@ namespace InfectedRose.Interface
|
||||
table.Recalculate();
|
||||
}
|
||||
|
||||
accessDatabase.Save(Path.Combine(CommandLineOptions.Input, "../../res/cdclient.fdb"));
|
||||
// Set the permissions of the database to allow writing
|
||||
var clientDatabase = Path.Combine(CommandLineOptions.Input, "../../res/cdclient.fdb");
|
||||
|
||||
if (File.Exists(clientDatabase))
|
||||
{
|
||||
var attributes = File.GetAttributes(clientDatabase);
|
||||
|
||||
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
||||
{
|
||||
File.SetAttributes(clientDatabase, attributes & ~FileAttributes.ReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
accessDatabase.Save(clientDatabase);
|
||||
|
||||
connection.Close();
|
||||
|
||||
@@ -860,7 +952,7 @@ namespace InfectedRose.Interface
|
||||
// Load artifacts
|
||||
ModContext.Artifacts = ReadOrCreateJson<Artifacts>("artifacts.json");
|
||||
|
||||
foreach (var (key, value) in ModContext.Artifacts)
|
||||
foreach (var value in ModContext.Artifacts)
|
||||
{
|
||||
if (File.Exists(value))
|
||||
{
|
||||
@@ -868,6 +960,29 @@ namespace InfectedRose.Interface
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the "ModContext.Root/artifacts" folder to "ModContext.Root/../", overwriting any existing files
|
||||
var artifactsSourcePath = Path.Combine(ModContext.Root, "./artifacts");
|
||||
var artifactsDestinationPath = Path.Combine(ModContext.Root, "../");
|
||||
|
||||
// Get all files and copy them with replacement, keeping folder structure
|
||||
foreach (var sourcePath in Directory.GetFiles(artifactsSourcePath, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
var destinationPath = sourcePath.Replace(artifactsSourcePath, artifactsDestinationPath);
|
||||
var destinationDirectory = Path.GetDirectoryName(destinationPath);
|
||||
|
||||
if (!Directory.Exists(destinationDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(destinationDirectory!);
|
||||
}
|
||||
|
||||
if (File.Exists(destinationPath))
|
||||
{
|
||||
File.Delete(destinationPath);
|
||||
}
|
||||
|
||||
File.Copy(sourcePath, destinationPath);
|
||||
}
|
||||
|
||||
ModContext.Artifacts.Clear();
|
||||
|
||||
var localeSourcePath = Path.Combine(Path.GetDirectoryName(CommandLineOptions.Input)!, "./locale.xml");
|
||||
@@ -942,6 +1057,38 @@ namespace InfectedRose.Interface
|
||||
|
||||
Console.SetCursorPosition(origin.Left, origin.Top);
|
||||
|
||||
var componentsRegistry = ModContext.Database["ComponentsRegistry"];
|
||||
var renderComponentTable = ModContext.Database["RenderComponent"];
|
||||
|
||||
/*
|
||||
foreach (var itemComponent in ModContext.Database["ItemComponent"])
|
||||
{
|
||||
if (!"special_r".Equals(itemComponent[1].Value)) continue;
|
||||
|
||||
var componentRow = componentsRegistry.FirstOrDefault(
|
||||
c => itemComponent.Key.Equals(c[2].Value) && ((int) ComponentId.ItemComponent).Equals(c[1].Value)
|
||||
);
|
||||
|
||||
if (componentRow == null) continue;
|
||||
|
||||
var lot = componentRow.Key;
|
||||
|
||||
var renderComponentEntry = componentsRegistry.SeekMultiple(lot).FirstOrDefault(c => 2.Equals(c[1].Value));
|
||||
|
||||
if (renderComponentEntry == null) continue;
|
||||
|
||||
if (!renderComponentTable.Seek((int) renderComponentEntry[2].Value, out var renderComponent)) continue;
|
||||
|
||||
var field = renderComponent[5];
|
||||
|
||||
if (field.Value is null or 0) continue;
|
||||
|
||||
Console.WriteLine($"Found {field.Value} for {lot}");
|
||||
}
|
||||
|
||||
return;
|
||||
*/
|
||||
|
||||
if (CommandLineOptions.Export == "object")
|
||||
{
|
||||
Console.Write($"Generating copy of {CommandLineOptions.ExportId} as ");
|
||||
@@ -992,6 +1139,8 @@ namespace InfectedRose.Interface
|
||||
ApplyManifest(Path.Combine(priority, "./manifest.json"));
|
||||
}
|
||||
|
||||
WorldInstance.SaveWorldInstances();
|
||||
|
||||
// Check for unresolved references and print errors
|
||||
if (ModContext.IdCallbacks.Count > 0)
|
||||
{
|
||||
@@ -1069,5 +1218,4 @@ namespace InfectedRose.Interface
|
||||
|
||||
Console.WriteLine("Complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using InfectedRose.Database;
|
||||
|
||||
namespace InfectedRose.Interface
|
||||
namespace InfectedRose.Interface;
|
||||
|
||||
public static class TableExtensions
|
||||
{
|
||||
public static class TableExtensions
|
||||
{
|
||||
public static Row FromLookup(this Table @this, string id)
|
||||
{
|
||||
return !ModContext.Lookup.TryGetValue(id, out var value) ? @this.Create() : @this.Create(value);
|
||||
@@ -39,5 +39,4 @@ namespace InfectedRose.Interface
|
||||
|
||||
throw new KeyNotFoundException($"Could not determine the ID of {mod.Id}, no old ids match lookup.json.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
public class Behavior
|
||||
{
|
||||
public class Behavior
|
||||
{
|
||||
[JsonPropertyName("template-id")]
|
||||
[JsonPropertyName("template")]
|
||||
public string Template { get; set; }
|
||||
|
||||
[JsonPropertyName("effect")]
|
||||
@@ -17,7 +17,7 @@ namespace InfectedRose.Interface.Templates
|
||||
public string? EffectHandle { get; set; }
|
||||
|
||||
[JsonPropertyName("parameters")]
|
||||
public Dictionary<string, object> Parameters { get; set; }
|
||||
public Dictionary<string, object>? Parameters { get; set; }
|
||||
|
||||
public int Apply(string id)
|
||||
{
|
||||
@@ -53,6 +53,8 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
behaviorTemplate["effectHandle"].Value = EffectHandle;
|
||||
|
||||
if (Parameters == null) return behaviorTemplate.Key;
|
||||
|
||||
foreach (var (key, value) in Parameters)
|
||||
{
|
||||
var parameter = behaviorParameterTable.Create(behaviorTemplate.Key);
|
||||
@@ -67,6 +69,21 @@ namespace InfectedRose.Interface.Templates
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if true or false
|
||||
if (value.ToString()!.Equals("true", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
valueField.Value = 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value.ToString()!.Equals("false", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
valueField.Value = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ModContext.AwaitId(value.ToString()!, i =>
|
||||
{
|
||||
valueField.Value = (float) i;
|
||||
@@ -75,5 +92,4 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
return behaviorTemplate.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
public enum ComponentId
|
||||
{
|
||||
public enum ComponentId
|
||||
{
|
||||
ControllablePhysicsComponent = 1,
|
||||
RenderComponent = 2,
|
||||
SimplePhysicsComponent = 3,
|
||||
@@ -105,5 +105,4 @@ namespace InfectedRose.Interface.Templates
|
||||
CombatMediatorComponent = 101,
|
||||
Component107 = 107,
|
||||
Possesable = 108
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
public class Effect
|
||||
{
|
||||
public class Effect
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
@@ -56,5 +56,4 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
return behaviorEffect.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("enemy")]
|
||||
public class EnemyMod : ModType
|
||||
{
|
||||
[ModType("enemy")]
|
||||
public class EnemyMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
if (mod.Action != "add")
|
||||
@@ -100,5 +100,4 @@ namespace InfectedRose.Interface.Templates
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.MovementAIComponent);
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.BaseCombatAIComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("environmental")]
|
||||
public class EnvironmentalMod : ModType
|
||||
{
|
||||
[ModType("environmental")]
|
||||
public class EnvironmentalMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
if (mod.Action != "add")
|
||||
@@ -20,5 +20,4 @@ namespace InfectedRose.Interface.Templates
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.SimplePhysicsComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
InfectedRose.Interface/Templates/FileMod.cs
Normal file
13
InfectedRose.Interface/Templates/FileMod.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("file")]
|
||||
public class FileMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
var source = mod.GetValue<string>("source");
|
||||
var destination = mod.GetValue<string>("destination");
|
||||
|
||||
ModContext.CreateArtifactFrom(source, destination);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("flag")]
|
||||
public class FlagMod : ModType
|
||||
{
|
||||
[ModType("flag")]
|
||||
public class FlagMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
if (mod.Action != "add")
|
||||
@@ -22,5 +22,4 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
ModContext.RegisterId(mod.Id, row.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
using System;
|
||||
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("item")]
|
||||
public class ItemMod : ModType
|
||||
{
|
||||
[ModType("item")]
|
||||
public class ItemMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
if (mod.Action != "add")
|
||||
{
|
||||
return;
|
||||
throw new Exception("ItemMod can only be used with the add action");
|
||||
}
|
||||
|
||||
mod.Default("nametag", false);
|
||||
@@ -21,12 +23,25 @@ namespace InfectedRose.Interface.Templates
|
||||
mod.Default("shader_id", 23);
|
||||
mod.Default("audioEquipMetaEventSet", "Weapon_Hammer_Generic");
|
||||
|
||||
var obj = ObjectMod.CreateObject(mod);
|
||||
if (mod.HasValue("icon"))
|
||||
{
|
||||
var iconId = ModContext.AddIcon(mod.GetValue<string>("icon"), out var path);
|
||||
|
||||
mod.Values["icon_asset"] = path;
|
||||
mod.Values["IconID"] = iconId;
|
||||
}
|
||||
|
||||
var obj = ObjectMod.CreateObject(mod, false);
|
||||
|
||||
obj["type"].Value = "Loot";
|
||||
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.ItemComponent);
|
||||
|
||||
if (mod.HasValue("icon"))
|
||||
{
|
||||
mod.Values.Remove("icon_asset");
|
||||
mod.Values.Remove("IconID");
|
||||
}
|
||||
}
|
||||
}
|
||||
13
InfectedRose.Interface/Templates/LocaleMod.cs
Normal file
13
InfectedRose.Interface/Templates/LocaleMod.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("locale")]
|
||||
public class LocaleMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
foreach (var value in mod.Values)
|
||||
{
|
||||
ModContext.AddToLocale(value.Key, value.Key, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
InfectedRose.Interface/Templates/LootMatrixMod.cs
Normal file
82
InfectedRose.Interface/Templates/LootMatrixMod.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using InfectedRose.Database;
|
||||
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("loot-matrix")]
|
||||
public class LootMatrixMod : ModType
|
||||
{
|
||||
class LootMatrix
|
||||
{
|
||||
[JsonPropertyName("rarity-table")]
|
||||
public int? RarityTableIndex { get; set; }
|
||||
|
||||
[JsonPropertyName("percent")]
|
||||
public float? Percent { get; set; }
|
||||
|
||||
[JsonPropertyName("min-quantity")]
|
||||
public int? MinQuantity { get; set; }
|
||||
|
||||
[JsonPropertyName("max-quantity")]
|
||||
public int? MaxQuantity { get; set; }
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public int? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("flag-id")]
|
||||
public string? FlagId { get; set; }
|
||||
}
|
||||
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
var lootMatrix = ModContext.Database["LootMatrix"]!;
|
||||
var lootMatrixIndex = ModContext.Database["LootMatrixIndex"]!;
|
||||
|
||||
int modId;
|
||||
|
||||
if (mod.ExplicitId.HasValue)
|
||||
{
|
||||
modId = mod.ExplicitId.Value;
|
||||
}
|
||||
else if (mod.Id.StartsWith("lego-universe:"))
|
||||
{
|
||||
modId = int.Parse(mod.Id[14..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var indexRow = lootMatrixIndex.Create();
|
||||
|
||||
modId = indexRow.Key;
|
||||
}
|
||||
|
||||
ModContext.RegisterId(mod.Id, modId);
|
||||
|
||||
foreach (var (key, _) in mod.Values)
|
||||
{
|
||||
ModContext.AwaitId(key, lootTableIndex =>
|
||||
{
|
||||
var row = lootMatrix.Create(modId);
|
||||
|
||||
row["LootTableIndex"].Value = modId;
|
||||
|
||||
var value = mod.GetValue<LootMatrix>(key);
|
||||
|
||||
row["LootTableIndex"].Value = lootTableIndex;
|
||||
row["RarityTableIndex"].Value = value.RarityTableIndex ?? 4;
|
||||
row["percent"].Value = value.Percent ?? 1;
|
||||
row["minToDrop"].Value = value.MinQuantity ?? 1;
|
||||
row["maxToDrop"].Value = value.MaxQuantity ?? 1;
|
||||
row["id"].Value = value.Id ?? lootMatrix.Max(l => (int)l[6].Value);
|
||||
if (value.FlagId != null)
|
||||
{
|
||||
ModContext.AwaitId(value.FlagId, flagId =>
|
||||
{
|
||||
row["flagID"].Value = flagId;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
72
InfectedRose.Interface/Templates/LootTableMod.cs
Normal file
72
InfectedRose.Interface/Templates/LootTableMod.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using InfectedRose.Database;
|
||||
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("loot-table")]
|
||||
public class LootTableMod : ModType
|
||||
{
|
||||
class LootTable
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public int? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("mission-drop")]
|
||||
public bool? MissionDrop { get; set; }
|
||||
|
||||
[JsonPropertyName("sort-priority")]
|
||||
public int? SortPriority { get; set; }
|
||||
}
|
||||
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
var lootTable = ModContext.Database["LootTable"]!;
|
||||
var lootTableIndex = ModContext.Database["LootTableIndex"]!;
|
||||
|
||||
int modId;
|
||||
|
||||
if (mod.ExplicitId.HasValue)
|
||||
{
|
||||
modId = mod.ExplicitId.Value;
|
||||
}
|
||||
else if (mod.Id.StartsWith("lego-universe:"))
|
||||
{
|
||||
modId = int.Parse(mod.Id[14..]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var indexRow = lootTableIndex.Create();
|
||||
|
||||
modId = indexRow.Key;
|
||||
}
|
||||
|
||||
ModContext.RegisterId(mod.Id, modId);
|
||||
|
||||
foreach (var (key, _) in mod.Values)
|
||||
{
|
||||
ModContext.AwaitId(key, itemId =>
|
||||
{
|
||||
var row = lootTable.Create(itemId);
|
||||
|
||||
row["LootTableIndex"].Value = modId;
|
||||
|
||||
var value = mod.GetValue<LootTable?>(key);
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
row["id"].Value = lootTable.Max(l => (int)l[2].Value);
|
||||
row["MissionDrop"].Value = false;
|
||||
row["sortPriority"].Value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
row["id"].Value = value.Id ?? lootTable.Max(l => (int)l[2].Value);
|
||||
row["MissionDrop"].Value = value.MissionDrop ?? false;
|
||||
row["sortPriority"].Value = value.SortPriority ?? 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("mission")]
|
||||
public class MissionMod : ModType
|
||||
{
|
||||
[ModType("mission")]
|
||||
public class MissionMod : ModType
|
||||
{
|
||||
// Proud of this one :D
|
||||
public static void ParsePrerequisites(StringBuilder builder, StringBuilder missionId, string str, int i, Action<string> done)
|
||||
{
|
||||
@@ -170,7 +170,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item1"), id =>
|
||||
{
|
||||
mission["reward_item1"].Value = id;
|
||||
mission["reward_item1"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item2"), id =>
|
||||
{
|
||||
mission["reward_item2"].Value = id;
|
||||
mission["reward_item2"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item3"), id =>
|
||||
{
|
||||
mission["reward_item3"].Value = id;
|
||||
mission["reward_item3"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item4"), id =>
|
||||
{
|
||||
mission["reward_item4"].Value = id;
|
||||
mission["reward_item4"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item1_repeatable"), id =>
|
||||
{
|
||||
mission["reward_item1_repeatable"].Value = id;
|
||||
mission["reward_item1_repeatable"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item2_repeatable"), id =>
|
||||
{
|
||||
mission["reward_item2_repeatable"].Value = id;
|
||||
mission["reward_item2_repeatable"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item3_repeatable"), id =>
|
||||
{
|
||||
mission["reward_item3_repeatable"].Value = id;
|
||||
mission["reward_item3_repeatable"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_item4_repeatable"), id =>
|
||||
{
|
||||
mission["reward_item4_repeatable"].Value = id;
|
||||
mission["reward_item4_repeatable"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_emote"), id =>
|
||||
{
|
||||
mission["reward_emote"].Value = id;
|
||||
mission["reward_emote"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_emote2"), id =>
|
||||
{
|
||||
mission["reward_emote2"].Value = id;
|
||||
mission["reward_emote2"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_emote3"), id =>
|
||||
{
|
||||
mission["reward_emote3"].Value = id;
|
||||
mission["reward_emote3"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("reward_emote4"), id =>
|
||||
{
|
||||
mission["reward_emote4"].Value = id;
|
||||
mission["reward_emote4"].Value = id == 0 ? -1 : id;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -401,5 +401,4 @@ namespace InfectedRose.Interface.Templates
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,10 @@ using System.Collections.Generic;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
public class MissionModTask
|
||||
{
|
||||
public class MissionModTask
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
@@ -35,5 +35,4 @@ namespace InfectedRose.Interface.Templates
|
||||
{
|
||||
{"en_US", ""}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
public class MissionOffer
|
||||
{
|
||||
public class MissionOffer
|
||||
{
|
||||
[JsonPropertyName("mission")]
|
||||
public JsonValue Mission { get; set; }
|
||||
|
||||
@@ -13,5 +13,4 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
[JsonPropertyName("offer")]
|
||||
public bool Offer { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
public enum MissionTaskType
|
||||
{
|
||||
public enum MissionTaskType
|
||||
{
|
||||
Smash = 0,
|
||||
Script = 1,
|
||||
Activity = 2,
|
||||
@@ -20,5 +20,4 @@ namespace InfectedRose.Interface.Templates
|
||||
Racing = 23,
|
||||
PlayerFlag = 24,
|
||||
VisitProperty = 30
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("npc")]
|
||||
public class NpcMod : ModType
|
||||
{
|
||||
[ModType("npc")]
|
||||
public class NpcMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
if (mod.Action != "add")
|
||||
@@ -52,5 +52,4 @@ namespace InfectedRose.Interface.Templates
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
|
||||
ObjectMod.AddComponent(mod, obj, ComponentId.MinifigComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using InfectedRose.Database;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("object")]
|
||||
public class ObjectMod : ModType
|
||||
{
|
||||
[ModType("object")]
|
||||
public class ObjectMod : ModType
|
||||
{
|
||||
public static Row CreateObject(Mod mod)
|
||||
public static Row CreateObject(Mod mod, bool checkIcon = true)
|
||||
{
|
||||
var table = ModContext.Database["Objects"];
|
||||
|
||||
@@ -19,6 +20,19 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
ModContext.RegisterId(mod.Id, row.Key);
|
||||
|
||||
if (!mod.HasValue("interactionDistance") || mod.GetValue<float>("interactionDistance") == 0.0f)
|
||||
{
|
||||
row["interactionDistance"].Value = 10.0f;
|
||||
}
|
||||
|
||||
if (checkIcon && mod.HasValue("icon"))
|
||||
{
|
||||
var iconId = ModContext.AddIcon(mod.GetValue<string>("icon"), out var path);
|
||||
|
||||
mod.Values["icon_asset"] = path;
|
||||
mod.Values["IconID"] = iconId;
|
||||
}
|
||||
|
||||
if (mod.Components != null)
|
||||
{
|
||||
foreach (var component in mod.Components)
|
||||
@@ -27,6 +41,12 @@ namespace InfectedRose.Interface.Templates
|
||||
}
|
||||
}
|
||||
|
||||
if (checkIcon && mod.HasValue("icon"))
|
||||
{
|
||||
mod.Values.Remove("icon_asset");
|
||||
mod.Values.Remove("IconID");
|
||||
}
|
||||
|
||||
if (mod.Locale != null)
|
||||
{
|
||||
foreach (var (locale, text) in mod.Locale)
|
||||
@@ -35,6 +55,16 @@ namespace InfectedRose.Interface.Templates
|
||||
}
|
||||
}
|
||||
|
||||
if (mod.HasValue("description-locale"))
|
||||
{
|
||||
var description = mod.GetValue<Dictionary<string, string>>("description-locale");
|
||||
|
||||
foreach (var (locale, text) in description)
|
||||
{
|
||||
ModContext.AddToLocale($"Objects_{row.Key}_description", text, locale);
|
||||
}
|
||||
}
|
||||
|
||||
if (mod.Skills != null && mod.Skills.Length > 0)
|
||||
{
|
||||
AddComponent(mod, row, ComponentId.SkillComponent);
|
||||
@@ -264,6 +294,18 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
Row component;
|
||||
|
||||
foreach (var r in ModContext.Database["ComponentsRegistry"].SeekMultiple(obj.Key))
|
||||
{
|
||||
if ((int)r["component_type"].Value != (int)componentId) continue;
|
||||
|
||||
if (table != null && table.Seek((int) r["component_id"].Value, out var row))
|
||||
{
|
||||
return row;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
var row = ModContext.Database["ComponentsRegistry"].Create(obj.Key);
|
||||
@@ -308,5 +350,4 @@ namespace InfectedRose.Interface.Templates
|
||||
EditObject(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
public class ObjectSkillEntry
|
||||
{
|
||||
public class ObjectSkillEntry
|
||||
{
|
||||
[JsonPropertyName("skill-id")]
|
||||
public JsonValue SkillId { get; set; }
|
||||
|
||||
@@ -13,5 +13,4 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
[JsonPropertyName("combat-ai-weight")]
|
||||
public int CombatAiWeight { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("quickbuild")]
|
||||
public class QuickBuildMod : ModType
|
||||
{
|
||||
[ModType("quickbuild")]
|
||||
public class QuickBuildMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
if (mod.Action != "add")
|
||||
@@ -75,5 +75,4 @@ namespace InfectedRose.Interface.Templates
|
||||
ModContext.ParseValue(mod.GetValue<string>("lxfml"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
InfectedRose.Interface/Templates/RecipeMod.cs
Normal file
63
InfectedRose.Interface/Templates/RecipeMod.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using InfectedRose.Database.Generic;
|
||||
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("recipe")]
|
||||
public class RecipeMod : ModType
|
||||
{
|
||||
class RecipeList : Dictionary<string, int>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
foreach (var (key, _) in mod.Values)
|
||||
{
|
||||
ModContext.AwaitId(key, objectId =>
|
||||
{
|
||||
var itemTable = ModContext.GetComponentTable(ComponentId.ItemComponent)!;
|
||||
|
||||
var componentRegistry = ModContext.Database["ComponentsRegistry"]!;
|
||||
|
||||
var itemComponentEntry = componentRegistry.SeekMultiple(objectId).FirstOrDefault(
|
||||
c => c.Value<int>("component_type") == (int) ComponentId.ItemComponent
|
||||
);
|
||||
|
||||
if (itemComponentEntry == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Item component entry for {key} not found.");
|
||||
}
|
||||
|
||||
if (!itemTable.Seek(itemComponentEntry.Value<int>("component_id"), out var itemComponent))
|
||||
{
|
||||
throw new KeyNotFoundException($"Item component for {key} not found.");
|
||||
}
|
||||
|
||||
var field = itemComponent["currencyCosts"];
|
||||
|
||||
field.Value = "";
|
||||
|
||||
foreach (var (itemId, quantity) in mod.GetValue<RecipeList>(key))
|
||||
{
|
||||
ModContext.AwaitId(itemId, id =>
|
||||
{
|
||||
var fieldValue = (string) field.Value;
|
||||
|
||||
if (fieldValue != "")
|
||||
{
|
||||
fieldValue += ",";
|
||||
}
|
||||
|
||||
fieldValue = $"{fieldValue}{id}:{quantity}";
|
||||
|
||||
field.Value = fieldValue;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("skill")]
|
||||
public class SkillMod : ModType
|
||||
{
|
||||
[ModType("skill")]
|
||||
public class SkillMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
if (mod.Action != "add")
|
||||
@@ -14,6 +14,11 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
var skillBehavior = skillBehaviorTable.FromLookup(mod);
|
||||
|
||||
if (mod.Locale != null)
|
||||
{
|
||||
ModContext.AddToLocale($"SkillBehavior_{skillBehavior.Key}_name", mod.Locale);
|
||||
}
|
||||
|
||||
ModContext.RegisterId(mod.Id, skillBehavior.Key);
|
||||
|
||||
foreach (var (key, behavior) in mod.Behaviors)
|
||||
@@ -21,8 +26,20 @@ namespace InfectedRose.Interface.Templates
|
||||
behavior.Apply(key);
|
||||
}
|
||||
|
||||
if (mod.HasValue("icon"))
|
||||
{
|
||||
var iconId = ModContext.AddIcon(mod.GetValue<string>("icon"), out var path);
|
||||
|
||||
mod.Values["skillIcon"] = iconId;
|
||||
}
|
||||
|
||||
ModContext.ApplyValues(mod, skillBehavior, skillBehaviorTable);
|
||||
|
||||
if (mod.HasValue("icon"))
|
||||
{
|
||||
mod.Values.Remove("skillIcon");
|
||||
}
|
||||
|
||||
if (mod.HasValue("root-behavior"))
|
||||
{
|
||||
ModContext.AwaitId(mod.GetValue<string>("root-behavior"), i =>
|
||||
@@ -31,5 +48,4 @@ namespace InfectedRose.Interface.Templates
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("sql")]
|
||||
public class SqlMod : ModType
|
||||
{
|
||||
[ModType("sql")]
|
||||
public class SqlMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
switch (mod.Action)
|
||||
@@ -15,5 +15,4 @@ namespace InfectedRose.Interface.Templates
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public struct Color3
|
||||
{
|
||||
public struct Color3
|
||||
{
|
||||
[JsonPropertyName("r")]
|
||||
public float R { get; set; }
|
||||
|
||||
@@ -13,5 +13,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
|
||||
[JsonPropertyName("b")]
|
||||
public float B { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
{
|
||||
public class DataDictionary : Dictionary<string, DataValue>
|
||||
{
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class DataDictionary : Dictionary<string, DataValue>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,103 @@
|
||||
using System;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class DataValue
|
||||
{
|
||||
public class DataValue
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public object Value { get; set; }
|
||||
public JsonValue Value { get; set; }
|
||||
|
||||
public static int ParseTypeString(string type)
|
||||
[JsonPropertyName("type")]
|
||||
public string? TypeInternal { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public FieldType Type
|
||||
{
|
||||
get
|
||||
{
|
||||
if (TypeInternal == null)
|
||||
{
|
||||
var str = Value.ToString();
|
||||
|
||||
if (int.TryParse(str, out var i))
|
||||
{
|
||||
return FieldType.Integer;
|
||||
}
|
||||
|
||||
if (float.TryParse(str, out var f))
|
||||
{
|
||||
return FieldType.Float;
|
||||
}
|
||||
|
||||
// Count instances of \u001f
|
||||
var instances = str.Split("\u001f").Length;
|
||||
|
||||
if (instances == 3)
|
||||
{
|
||||
return FieldType.Position;
|
||||
}
|
||||
|
||||
if (instances == 4)
|
||||
{
|
||||
return FieldType.Rotation;
|
||||
}
|
||||
|
||||
return FieldType.String;
|
||||
}
|
||||
|
||||
if (Enum.TryParse<FieldType>(TypeInternal, out var fieldType)) return fieldType;
|
||||
|
||||
fieldType = ParseFieldType(TypeInternal);
|
||||
|
||||
TypeInternal = fieldType.ToString();
|
||||
|
||||
return fieldType;
|
||||
}
|
||||
set => TypeInternal = value.ToString();
|
||||
}
|
||||
|
||||
public static string ParseFieldType(FieldType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
"int" => 1,
|
||||
"float" => 3,
|
||||
"double" => 4,
|
||||
"uint" => 5,
|
||||
"bool" => 7,
|
||||
"long" => 8,
|
||||
"blob" => 13,
|
||||
"string" => 0,
|
||||
"id" => 100,
|
||||
_ => 0
|
||||
FieldType.Id => "id",
|
||||
FieldType.Object => "id",
|
||||
FieldType.WorldObject => "id",
|
||||
FieldType.Integer => "int",
|
||||
FieldType.UnsignedInteger => "uint",
|
||||
FieldType.String => "string",
|
||||
FieldType.Buffer => "blob",
|
||||
FieldType.Skill => "id",
|
||||
FieldType.Flag => "id",
|
||||
FieldType.Mission => "id",
|
||||
FieldType.Float => "float",
|
||||
FieldType.Double => "double",
|
||||
FieldType.Boolean => "bool",
|
||||
FieldType.Model => "id",
|
||||
FieldType.Physics => "id",
|
||||
FieldType.Icon => "id",
|
||||
FieldType.None => "string",
|
||||
FieldType.Position => "string",
|
||||
FieldType.Rotation => "string",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
}
|
||||
|
||||
public static FieldType ParseFieldType(string type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
"id" => FieldType.Object,
|
||||
"int" => FieldType.Integer,
|
||||
"uint" => FieldType.UnsignedInteger,
|
||||
"string" => FieldType.String,
|
||||
"blob" => FieldType.Buffer,
|
||||
"float" => FieldType.Float,
|
||||
"double" => FieldType.Double,
|
||||
"bool" => FieldType.Boolean,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, $"Unknown type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,38 +118,71 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
};
|
||||
}
|
||||
|
||||
public static int ParseType(string type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
"int" => 1,
|
||||
"float" => 3,
|
||||
"double" => 4,
|
||||
"uint" => 5,
|
||||
"bool" => 7,
|
||||
"long" => 8,
|
||||
"blob" => 13,
|
||||
"string" => 0,
|
||||
"id" => 0,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public int TypeId
|
||||
{
|
||||
get => ParseTypeString(Type);
|
||||
set => Type = ParseType(value);
|
||||
set => ParseFieldType(ParseType(value));
|
||||
get => ParseType(ParseFieldType(Type));
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public object? ObjectValue
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case FieldType.None:
|
||||
return null;
|
||||
case FieldType.Id:
|
||||
case FieldType.Object:
|
||||
case FieldType.WorldObject:
|
||||
case FieldType.String:
|
||||
case FieldType.Buffer:
|
||||
case FieldType.Rotation:
|
||||
case FieldType.Position:
|
||||
case FieldType.Skill:
|
||||
case FieldType.Flag:
|
||||
case FieldType.Mission:
|
||||
case FieldType.Model:
|
||||
case FieldType.Physics:
|
||||
case FieldType.Icon:
|
||||
return Value.ToString();
|
||||
case FieldType.Integer:
|
||||
return Value.GetValue<int>();
|
||||
case FieldType.UnsignedInteger:
|
||||
return Value.GetValue<uint>();
|
||||
case FieldType.Float:
|
||||
return Value.GetValue<float>();
|
||||
case FieldType.Double:
|
||||
return Value.GetValue<double>();
|
||||
case FieldType.Boolean:
|
||||
return Value.GetValue<bool>();
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var type = TypeId;
|
||||
var value = Value;
|
||||
|
||||
if (value is false) value = 0;
|
||||
else if (value is true) value = 1;
|
||||
else if (type == 0 || type == 13) value = value.ToString();
|
||||
|
||||
if (type == 100)
|
||||
{
|
||||
value = ModContext.AssertId((string)value!);
|
||||
type = 1;
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
value = value.ToString();
|
||||
|
||||
if (value == "True") value = "1";
|
||||
else if (value == "False") value = "0";
|
||||
}
|
||||
|
||||
return $"{type}:{value}";
|
||||
}
|
||||
return $"{TypeId}:{Value}";
|
||||
}
|
||||
}
|
||||
24
InfectedRose.Interface/Templates/ValueTypes/FieldType.cs
Normal file
24
InfectedRose.Interface/Templates/ValueTypes/FieldType.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public enum FieldType
|
||||
{
|
||||
None,
|
||||
Id,
|
||||
Object,
|
||||
WorldObject,
|
||||
Integer,
|
||||
UnsignedInteger,
|
||||
String,
|
||||
Buffer,
|
||||
Position,
|
||||
Rotation,
|
||||
Skill,
|
||||
Flag,
|
||||
Mission,
|
||||
Float,
|
||||
Double,
|
||||
Boolean,
|
||||
Model,
|
||||
Physics,
|
||||
Icon,
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class LevelTemplate
|
||||
{
|
||||
public class LevelTemplate
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public JsonValue Id { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public int Type { get; set; }
|
||||
public int? Type { get; set; }
|
||||
|
||||
[JsonPropertyName("object-id")]
|
||||
public ulong? ObjectId { get; set; }
|
||||
@@ -21,9 +21,8 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
public Point4 Rotation { get; set; }
|
||||
|
||||
[JsonPropertyName("scale")]
|
||||
public float Scale { get; set; }
|
||||
public float? Scale { get; set; }
|
||||
|
||||
[JsonPropertyName("data")]
|
||||
public DataDictionary Data { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class Particle
|
||||
{
|
||||
public class Particle
|
||||
{
|
||||
[JsonPropertyName("position")]
|
||||
public Point3 Position { get; set; }
|
||||
|
||||
@@ -12,5 +12,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class PathData
|
||||
{
|
||||
public class PathData
|
||||
{
|
||||
public class Waypoint
|
||||
{
|
||||
public class Config
|
||||
@@ -131,5 +131,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
|
||||
[JsonPropertyName("activate-on-load")]
|
||||
public bool? ActivateOnLoad { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public struct Point3
|
||||
{
|
||||
public struct Point3
|
||||
{
|
||||
[JsonPropertyName("x")]
|
||||
public float X { get; set; }
|
||||
|
||||
@@ -19,5 +19,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
{
|
||||
return new Vector3(point.X, point.Y, point.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public struct Point4
|
||||
{
|
||||
public struct Point4
|
||||
{
|
||||
[JsonPropertyName("x")]
|
||||
public float X { get; set; }
|
||||
|
||||
@@ -22,5 +22,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
{
|
||||
return new Quaternion(point.X, point.Y, point.Z, point.W);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using InfectedRose.Triggers;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class Scene
|
||||
{
|
||||
public class Scene
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
@@ -73,5 +73,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
|
||||
[JsonPropertyName("scene-audio")]
|
||||
public SceneAudio SceneAudio { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
[XmlRoot("SceneAudioAttributes")]
|
||||
public class SceneAudio
|
||||
{
|
||||
[XmlRoot("SceneAudioAttributes")]
|
||||
public class SceneAudio
|
||||
{
|
||||
[JsonPropertyName("music-cue")]
|
||||
[XmlAttribute("musicCue")]
|
||||
public string MusicCue { get; set; }
|
||||
@@ -37,5 +37,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
[JsonPropertyName("boredom-time")]
|
||||
[XmlAttribute("boredomTime")]
|
||||
public string BoredomTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class Transition
|
||||
{
|
||||
public class Transition
|
||||
{
|
||||
public class Point
|
||||
{
|
||||
[JsonPropertyName("scene")]
|
||||
@@ -18,5 +18,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
|
||||
[JsonPropertyName("points")]
|
||||
public Point[] Points { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
namespace InfectedRose.Interface.Templates.ValueTypes;
|
||||
|
||||
public class Zone
|
||||
{
|
||||
public class Zone
|
||||
{
|
||||
[JsonPropertyName("spawn-point")]
|
||||
public Point3 SpawnPoint { get; set; }
|
||||
|
||||
@@ -21,5 +21,4 @@ namespace InfectedRose.Interface.Templates.ValueTypes
|
||||
|
||||
[JsonPropertyName("paths")]
|
||||
public PathData[] Paths { get; set; }
|
||||
}
|
||||
}
|
||||
251
InfectedRose.Interface/Templates/World/WorldInstances.cs
Normal file
251
InfectedRose.Interface/Templates/World/WorldInstances.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using InfectedRose.Database.Generic;
|
||||
using InfectedRose.Luz;
|
||||
using InfectedRose.Lvl;
|
||||
using RakDotNet.IO;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.World;
|
||||
|
||||
public class WorldInstance
|
||||
{
|
||||
public static Dictionary<string, WorldInstance> WorldInstances { get; } = new();
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public LuzFile Zone { get; set; }
|
||||
|
||||
public Dictionary<string, LvlFile> Scenes { get; set; }
|
||||
|
||||
public HashSet<ulong> ClaimedIds { get; }
|
||||
|
||||
public WorldInstance(string name, LuzFile zone, Dictionary<string, LvlFile> scenes, HashSet<ulong> claimedIds)
|
||||
{
|
||||
Name = name;
|
||||
Zone = zone;
|
||||
Scenes = scenes;
|
||||
ClaimedIds = claimedIds;
|
||||
}
|
||||
|
||||
public ulong ClaimId()
|
||||
{
|
||||
var lowest = 0x38B40AUL;
|
||||
|
||||
while (ClaimedIds.Contains(lowest))
|
||||
{
|
||||
lowest++;
|
||||
}
|
||||
|
||||
ClaimedIds.Add(lowest);
|
||||
|
||||
return lowest;
|
||||
}
|
||||
|
||||
public static void Await(string id, Action<WorldInstance> callback)
|
||||
{
|
||||
if (WorldInstances.TryGetValue(id, out var instance))
|
||||
{
|
||||
callback(instance);
|
||||
}
|
||||
|
||||
ModContext.AwaitId(id, zoneId =>
|
||||
{
|
||||
var zoneTable = ModContext.Database["ZoneTable"]!;
|
||||
|
||||
if (!zoneTable.Seek(zoneId, out var zoneEntry))
|
||||
{
|
||||
throw new Exception($"Zone {zoneId} not found");
|
||||
}
|
||||
|
||||
var zoneName = zoneEntry.Value<string>("zoneName").ToLower();
|
||||
|
||||
var path = Path.Combine(ModContext.Root, "../res/maps/", zoneName);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new Exception($"Zone file for {zoneName} not found: {path}");
|
||||
}
|
||||
|
||||
var zone = new LuzFile();
|
||||
|
||||
{
|
||||
using var zoneStream = File.OpenRead(path);
|
||||
using var zoneReader = new ByteReader(zoneStream);
|
||||
|
||||
zone.Deserialize(zoneReader);
|
||||
}
|
||||
|
||||
var scenes = new Dictionary<string, LvlFile>();
|
||||
|
||||
var claimedIds = new HashSet<ulong>();
|
||||
|
||||
var zoneRoot = Path.GetDirectoryName(path)!;
|
||||
|
||||
foreach (var scene in zone.Scenes)
|
||||
{
|
||||
var scenePath = Path.Combine(zoneRoot, scene.FileName.ToLower());
|
||||
|
||||
if (!File.Exists(scenePath))
|
||||
{
|
||||
throw new Exception($"Scene file for {scene.SceneName} not found: {scenePath}");
|
||||
}
|
||||
|
||||
var lvl = new LvlFile();
|
||||
|
||||
using var lvlStream = File.OpenRead(scenePath);
|
||||
using var lvlReader = new ByteReader(lvlStream);
|
||||
|
||||
lvl.Deserialize(lvlReader);
|
||||
|
||||
if (lvl.LevelObjects != null)
|
||||
{
|
||||
foreach (var template in lvl.LevelObjects.Templates)
|
||||
{
|
||||
claimedIds.Add(template.ObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
scenes.Add(scene.FileName.ToLower(), lvl);
|
||||
}
|
||||
|
||||
instance = new WorldInstance(zoneName, zone, scenes, claimedIds);
|
||||
|
||||
WorldInstances.Add(id, instance);
|
||||
|
||||
callback(instance);
|
||||
});
|
||||
}
|
||||
|
||||
public static WorldInstance Get(string id)
|
||||
{
|
||||
if (!WorldInstances.TryGetValue(id, out var instance))
|
||||
{
|
||||
throw new Exception($"World instance {id} not found");
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void AwaitZone(string id, Action<LuzFile> callback)
|
||||
{
|
||||
Await(id, instance => callback(instance.Zone));
|
||||
}
|
||||
|
||||
public static void AwaitScene(string id, string sceneName, Action<LvlFile> callback)
|
||||
{
|
||||
Await(id, instance =>
|
||||
{
|
||||
var sceneId = -1;
|
||||
var layerId = -1;
|
||||
|
||||
if (sceneName.Contains(":"))
|
||||
{
|
||||
var split = sceneName.Split(':');
|
||||
|
||||
if (split.Length != 3)
|
||||
{
|
||||
throw new Exception($"Invalid scene id {id}");
|
||||
}
|
||||
|
||||
sceneName = split[0];
|
||||
sceneId = int.Parse(split[1]);
|
||||
layerId = int.Parse(split[2]);
|
||||
}
|
||||
|
||||
foreach (var scene in instance.Zone.Scenes)
|
||||
{
|
||||
if (sceneId == -1 && layerId == -1 && sceneName == scene.SceneName)
|
||||
{
|
||||
callback(instance.Scenes[scene.FileName.ToLower()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (sceneId != scene.SceneId || layerId != scene.LayerId || sceneName != scene.SceneName) continue;
|
||||
|
||||
callback(instance.Scenes[scene.FileName.ToLower()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception($"Scene {sceneName} not found");
|
||||
});
|
||||
}
|
||||
|
||||
public static void SaveWorldInstances()
|
||||
{
|
||||
foreach (var (name, instance) in WorldInstances)
|
||||
{
|
||||
SaveWorldInstance(name, instance.Zone, instance.Scenes);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveWorldInstance(string name, LuzFile luzFile, Dictionary<string, LvlFile> scenes)
|
||||
{
|
||||
var zoneTable = ModContext.Database["ZoneTable"]!;
|
||||
|
||||
var zoneId = ModContext.AssertId(name);
|
||||
|
||||
if (!zoneTable.Seek(zoneId, out var zoneEntry))
|
||||
{
|
||||
throw new Exception($"Zone {name} not found");
|
||||
}
|
||||
|
||||
var zoneName = zoneEntry.Value<string>("zoneName").ToLower();
|
||||
|
||||
var root = Path.Combine(
|
||||
ModContext.Root,
|
||||
ModContext.Configuration.ResourceFolder,
|
||||
$"./compiled/{zoneId}/",
|
||||
Path.GetDirectoryName(zoneName)!
|
||||
);
|
||||
|
||||
if (!Directory.Exists(root))
|
||||
{
|
||||
Directory.CreateDirectory(root);
|
||||
}
|
||||
|
||||
var path = Path.Combine(root, Path.GetFileName(zoneName));
|
||||
|
||||
{
|
||||
using var zoneStream = File.Create(path);
|
||||
using var zoneWriter = new ByteWriter(zoneStream);
|
||||
|
||||
luzFile.Serialize(zoneWriter);
|
||||
}
|
||||
|
||||
foreach (var (fileName, scene) in scenes)
|
||||
{
|
||||
var scenePath = Path.Combine(root, fileName);
|
||||
|
||||
using var sceneStream = File.Create(scenePath);
|
||||
using var sceneWriter = new ByteWriter(sceneStream);
|
||||
|
||||
scene.Serialize(sceneWriter);
|
||||
}
|
||||
|
||||
var originalPath = Path.Combine(ModContext.Root, "../res/maps/", zoneName);
|
||||
|
||||
// Copy all ".raw", ".lutriggers", ".evc", and ".ast" files
|
||||
foreach (var file in Directory.GetFiles(Path.GetDirectoryName(originalPath)!))
|
||||
{
|
||||
var extension = Path.GetExtension(file);
|
||||
|
||||
if (extension == ".raw" || extension == ".lutriggers" || extension == ".evc" || extension == ".ast")
|
||||
{
|
||||
File.Copy(file, Path.Combine(root, Path.GetFileName(file)), true);
|
||||
|
||||
// Remove read-only flag
|
||||
var attributes = File.GetAttributes(Path.Combine(root, Path.GetFileName(file)));
|
||||
attributes &= ~FileAttributes.ReadOnly;
|
||||
File.SetAttributes(Path.Combine(root, Path.GetFileName(file)), attributes);
|
||||
}
|
||||
}
|
||||
|
||||
var link = new Uri(Path.Combine("../", ModContext.Configuration.ResourceFolder, $"./compiled/{zoneId}/", zoneName), UriKind.Relative);
|
||||
|
||||
zoneEntry["zoneName"].Value = link.ToString();
|
||||
}
|
||||
}
|
||||
144
InfectedRose.Interface/Templates/World/WorldObjectMod.cs
Normal file
144
InfectedRose.Interface/Templates/World/WorldObjectMod.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using InfectedRose.Interface.Templates.ValueTypes;
|
||||
using InfectedRose.Lvl;
|
||||
|
||||
namespace InfectedRose.Interface.Templates.World;
|
||||
|
||||
[ModType("world-object")]
|
||||
public class WorldObjectMod : ModType
|
||||
{
|
||||
public override void Apply(Mod mod)
|
||||
{
|
||||
var zoneId = mod.GetValue<string>("zone");
|
||||
var sceneId = mod.GetValue<string>("scene");
|
||||
|
||||
WorldInstance.AwaitScene(zoneId, sceneId, scene => ApplyToZone(mod, scene));
|
||||
}
|
||||
|
||||
private void ApplyToZone(Mod mod, LvlFile scene)
|
||||
{
|
||||
if (scene.LevelObjects == null)
|
||||
{
|
||||
scene.LevelObjects = new LevelObjects(scene.LvlVersion);
|
||||
}
|
||||
|
||||
var zoneId = mod.GetValue<string>("zone");
|
||||
|
||||
var instance = WorldInstance.Get(zoneId);
|
||||
|
||||
LevelObjectTemplate levelObjectTemplate;
|
||||
|
||||
switch (mod.Action)
|
||||
{
|
||||
case "add":
|
||||
{
|
||||
levelObjectTemplate = new LevelObjectTemplate(scene.LvlVersion);
|
||||
|
||||
levelObjectTemplate.ObjectId = instance.ClaimId();
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
throw new Exception($"Unknown action {mod.Action} for world-object");
|
||||
} break;
|
||||
}
|
||||
|
||||
var template = mod.GetValue<LevelTemplate>("template");
|
||||
|
||||
ModContext.AwaitId(template.Id, lot =>
|
||||
{
|
||||
levelObjectTemplate.Lot = lot;
|
||||
});
|
||||
|
||||
levelObjectTemplate.Position = template.Position;
|
||||
levelObjectTemplate.Rotation = template.Rotation;
|
||||
levelObjectTemplate.Scale = template.Scale ?? 1;
|
||||
levelObjectTemplate.AssetType = (uint) (template.Type ?? 1);
|
||||
levelObjectTemplate.GlomId = 1;
|
||||
|
||||
var dataString = new StringBuilder();
|
||||
|
||||
var ids = new List<string>();
|
||||
|
||||
foreach (var (_, dataValue) in template.Data)
|
||||
{
|
||||
var value = dataValue.ObjectValue ?? "";
|
||||
|
||||
if (dataValue.Type == FieldType.Object)
|
||||
{
|
||||
ids.Add(value.ToString()!);
|
||||
}
|
||||
}
|
||||
|
||||
ModContext.AwaitMultiple(ids.ToArray(), () =>
|
||||
{
|
||||
foreach (var (dataKey, dataValue) in template.Data)
|
||||
{
|
||||
var type = dataValue.TypeId;
|
||||
var value = dataValue.ObjectValue ?? "";
|
||||
|
||||
if (value is true) value = 1;
|
||||
if (value is false) value = 0;
|
||||
if (dataValue.Type == FieldType.Object)
|
||||
{
|
||||
value = ModContext.AssertId(value.ToString()!);
|
||||
type = 1;
|
||||
}
|
||||
|
||||
var strValue = value.ToString()!;
|
||||
|
||||
if (dataValue.Type == FieldType.Position)
|
||||
{
|
||||
if (dataValue.Value.TryGetValue(out string? _))
|
||||
{
|
||||
strValue = strValue.Replace("<", "").Replace(">", "").Replace(" ", "");
|
||||
var split = strValue.Split(',');
|
||||
strValue = $"{split[0]}\u001F{split[1]}\u001F{split[2]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = dataValue.Value.ToDictionary();
|
||||
strValue = $"{obj["X"]}\u001F{obj["Y"]}\u001F{obj["Z"]}";
|
||||
}
|
||||
}
|
||||
else if (dataValue.Type == FieldType.Rotation)
|
||||
{
|
||||
if (dataValue.Value.TryGetValue(out string? _))
|
||||
{
|
||||
strValue = strValue.Replace("<", "").Replace(">", "").Replace(" ", "");
|
||||
var split = strValue.Split(',');
|
||||
strValue = $"{split[0]}\u001F{split[1]}\u001F{split[2]}\u001F{split[3]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = dataValue.Value.ToDictionary();
|
||||
strValue = $"{obj["X"]}\u001F{obj["Y"]}\u001F{obj["Z"]}\u001F{obj["W"]}";
|
||||
}
|
||||
}
|
||||
|
||||
if (strValue == "True") strValue = "1";
|
||||
else if (strValue == "False") strValue = "0";
|
||||
|
||||
dataString.Append($"{dataKey}={type}:{strValue}\n");
|
||||
}
|
||||
|
||||
if (dataString.Length > 0)
|
||||
{
|
||||
dataString.Length -= 1;
|
||||
|
||||
levelObjectTemplate.LegoInfo = LegoDataDictionary.FromString(dataString.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
levelObjectTemplate.LegoInfo = new LegoDataDictionary();
|
||||
}
|
||||
|
||||
// Resize and add the object
|
||||
var levelObjectsTemplates = scene.LevelObjects.Templates;
|
||||
Array.Resize(ref levelObjectsTemplates, levelObjectsTemplates.Length + 1);
|
||||
levelObjectsTemplates[^1] = levelObjectTemplate;
|
||||
scene.LevelObjects.Templates = levelObjectsTemplates;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using InfectedRose.Core;
|
||||
@@ -14,11 +16,11 @@ using InfectedRose.Lvl;
|
||||
using InfectedRose.Triggers;
|
||||
using RakDotNet.IO;
|
||||
|
||||
namespace InfectedRose.Interface.Templates
|
||||
namespace InfectedRose.Interface.Templates;
|
||||
|
||||
[ModType("zone")]
|
||||
public class ZoneMod : ModType
|
||||
{
|
||||
[ModType("zone")]
|
||||
public class ZoneMod : ModType
|
||||
{
|
||||
public void InsertZone(Mod mod)
|
||||
{
|
||||
var zoneTable = ModContext.Database["ZoneTable"];
|
||||
@@ -129,7 +131,9 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
var fileName = mod.Id.Replace("-", "_").Replace(":", "_");
|
||||
|
||||
var root = $"./compiled/{fileName}/";
|
||||
var root = Path.Combine(ModContext.Root, ModContext.Configuration.ResourceFolder, $"./compiled/{fileName}/");
|
||||
|
||||
root = new Uri(root).LocalPath;
|
||||
|
||||
if (!Directory.Exists(root))
|
||||
{
|
||||
@@ -145,7 +149,7 @@ namespace InfectedRose.Interface.Templates
|
||||
|
||||
var terrainFile = $"{fileName}.raw";
|
||||
|
||||
File.CreateSymbolicLink(Path.Combine(root, terrainFile), Path.Combine("../../", zone.TerrainFile));
|
||||
File.Copy(zone.TerrainFile, Path.Combine(root, terrainFile));
|
||||
|
||||
var luzFile = new LuzFile();
|
||||
|
||||
@@ -414,8 +418,8 @@ namespace InfectedRose.Interface.Templates
|
||||
levelObjectTemplate.Lot = ModContext.AssertId(template.Id);
|
||||
levelObjectTemplate.Position = template.Position;
|
||||
levelObjectTemplate.Rotation = template.Rotation;
|
||||
levelObjectTemplate.Scale = template.Scale;
|
||||
levelObjectTemplate.AssetType = (uint)template.Type;
|
||||
levelObjectTemplate.Scale = template.Scale ?? 1;
|
||||
levelObjectTemplate.AssetType = (uint) (template.Type ?? 1);
|
||||
levelObjectTemplate.GlomId = 1;
|
||||
|
||||
var objectId = template.ObjectId ?? objId++;
|
||||
@@ -433,25 +437,51 @@ namespace InfectedRose.Interface.Templates
|
||||
foreach (var (dataKey, dataValue) in template.Data)
|
||||
{
|
||||
var type = dataValue.TypeId;
|
||||
var value = dataValue.Value;
|
||||
var value = dataValue.ObjectValue ?? "";
|
||||
|
||||
if (value is true) value = 1;
|
||||
if (value is false) value = 0;
|
||||
if (dataValue.Type == "id")
|
||||
if (dataValue.Type == FieldType.Object)
|
||||
{
|
||||
value = ModContext.AssertId(value.ToString()!);
|
||||
type = 1;
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
value = value.ToString();
|
||||
var strValue = value.ToString()!;
|
||||
|
||||
if (value == "True") value = "1";
|
||||
else if (value == "False") value = "0";
|
||||
if (dataValue.Type == FieldType.Position)
|
||||
{
|
||||
if (dataValue.Value.TryGetValue(out string? _))
|
||||
{
|
||||
strValue = strValue.Replace("<", "").Replace(">", "").Replace(" ", "");
|
||||
var split = strValue.Split(',');
|
||||
strValue = $"{split[0]}\u001F{split[1]}\u001F{split[2]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = dataValue.Value.ToDictionary();
|
||||
strValue = $"{obj["X"]}\u001F{obj["Y"]}\u001F{obj["Z"]}";
|
||||
}
|
||||
}
|
||||
else if (dataValue.Type == FieldType.Rotation)
|
||||
{
|
||||
if (dataValue.Value.TryGetValue(out string? _))
|
||||
{
|
||||
strValue = strValue.Replace("<", "").Replace(">", "").Replace(" ", "");
|
||||
var split = strValue.Split(',');
|
||||
strValue = $"{split[0]}\u001F{split[1]}\u001F{split[2]}\u001F{split[3]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var obj = dataValue.Value.ToDictionary();
|
||||
strValue = $"{obj["X"]}\u001F{obj["Y"]}\u001F{obj["Z"]}\u001F{obj["W"]}";
|
||||
}
|
||||
}
|
||||
|
||||
dataString.Append($"{dataKey}={type}:{value}\n");
|
||||
if (strValue == "True") strValue = "1";
|
||||
else if (strValue == "False") strValue = "0";
|
||||
|
||||
dataString.Append($"{dataKey}={type}:{strValue}\n");
|
||||
}
|
||||
|
||||
if (dataString.Length > 0)
|
||||
@@ -583,7 +613,13 @@ namespace InfectedRose.Interface.Templates
|
||||
ModContext.AddToLocale($"ZoneTable_{entry.Key}_DisplayDescription", mod.Locale);
|
||||
}
|
||||
|
||||
entry["zoneName"].Value = ModContext.ParseValue($"ASSET:MAP:{luzFilename}");
|
||||
var relativeTo = Path.Combine(ModContext.Root, "../res", "maps/");
|
||||
|
||||
var uri = new Uri(relativeTo);
|
||||
|
||||
var relativePath = uri.MakeRelativeUri(new Uri(luzFilename)).ToString();
|
||||
|
||||
entry["zoneName"].Value = relativePath;
|
||||
}
|
||||
|
||||
public override void Apply(Mod mod)
|
||||
@@ -606,5 +642,4 @@ namespace InfectedRose.Interface.Templates
|
||||
"Action " + mod.Action + " is not supported for zone mods"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,8 +172,8 @@ namespace InfectedRose.Lvl
|
||||
var str = value switch
|
||||
{
|
||||
Vector2 vec2 => $"{vec2.X}{InfoSeparator}{vec2.Y}",
|
||||
Vector3 vec3 => $"{vec3.X}{InfoSeparator}{vec3.Z}{InfoSeparator}{vec3.Y}",
|
||||
Vector4 vec4 => $"{vec4.X}{InfoSeparator}{vec4.Z}{InfoSeparator}{vec4.Y}{InfoSeparator}{vec4.W}",
|
||||
Vector3 vec3 => $"{vec3.X}{InfoSeparator}{vec3.Y}{InfoSeparator}{vec3.Z}",
|
||||
Vector4 vec4 => $"{vec4.X}{InfoSeparator}{vec4.Y}{InfoSeparator}{vec4.Z}{InfoSeparator}{vec4.W}",
|
||||
LegoDataList list => list.ToString(),
|
||||
_ => value.ToString()
|
||||
};
|
||||
@@ -198,8 +198,8 @@ namespace InfectedRose.Lvl
|
||||
var val = v switch
|
||||
{
|
||||
Vector2 vec2 => $"{vec2.X}{InfoSeparator}{vec2.Y}",
|
||||
Vector3 vec3 => $"{vec3.X}{InfoSeparator}{vec3.Z}{InfoSeparator}{vec3.Y}",
|
||||
Vector4 vec4 => $"{vec4.X}{InfoSeparator}{vec4.Z}{InfoSeparator}{vec4.Y}{InfoSeparator}{vec4.W}",
|
||||
Vector3 vec3 => $"{vec3.X}{InfoSeparator}{vec3.Y}{InfoSeparator}{vec3.Z}",
|
||||
Vector4 vec4 => $"{vec4.X}{InfoSeparator}{vec4.Y}{InfoSeparator}{vec4.Z}{InfoSeparator}{vec4.W}",
|
||||
LegoDataList list => list.ToString(),
|
||||
false => "0",
|
||||
true => "1",
|
||||
|
||||
@@ -113,11 +113,11 @@ namespace InfectedRose.Lvl
|
||||
break;
|
||||
|
||||
case Vector3 vec3:
|
||||
val = $"{vec3.X}{InfoSeparator}{vec3.Z}{InfoSeparator}{vec3.Y}";
|
||||
val = $"{vec3.X}{InfoSeparator}{vec3.Y}{InfoSeparator}{vec3.Z}";
|
||||
break;
|
||||
|
||||
case Vector4 vec4:
|
||||
val = $"{vec4.X}{InfoSeparator}{vec4.Z}{InfoSeparator}{vec4.Y}{InfoSeparator}{vec4.W}";
|
||||
val = $"{vec4.X}{InfoSeparator}{vec4.Y}{InfoSeparator}{vec4.Z}{InfoSeparator}{vec4.W}";
|
||||
break;
|
||||
|
||||
case LegoDataList list:
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace InfectedRose.Lvl
|
||||
{
|
||||
writer.Write<ushort>(0);
|
||||
}
|
||||
|
||||
writer.WriteNiString(Config, true);
|
||||
}
|
||||
|
||||
public void Deserialize(BitReader reader)
|
||||
@@ -59,6 +61,8 @@ namespace InfectedRose.Lvl
|
||||
{
|
||||
reader.Read<ushort>();
|
||||
}
|
||||
|
||||
Config = reader.ReadNiString(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@ using InfectedRose.Luz;
|
||||
using InfectedRose.Lvl;
|
||||
using RakDotNet.IO;
|
||||
|
||||
namespace InfectedRose.Utilities
|
||||
namespace InfectedRose.Utilities;
|
||||
|
||||
public static class Checksum
|
||||
{
|
||||
public static class Checksum
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculate the checksum for a LEGO Universe zone
|
||||
/// </summary>
|
||||
@@ -220,6 +220,4 @@ namespace InfectedRose.Utilities
|
||||
|
||||
return (uint) (upper << 16 | lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace InfectedRose.Utilities
|
||||
namespace InfectedRose.Utilities;
|
||||
|
||||
internal struct ChecksumLayer
|
||||
{
|
||||
internal struct ChecksumLayer
|
||||
{
|
||||
internal uint Id { get; set; }
|
||||
|
||||
internal uint Layer { get; set; }
|
||||
@@ -21,5 +21,4 @@ namespace InfectedRose.Utilities
|
||||
total += value; // Add to total
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using InfectedRose.Luz;
|
||||
using InfectedRose.Lvl;
|
||||
|
||||
namespace InfectedRose.Utilities
|
||||
namespace InfectedRose.Utilities;
|
||||
|
||||
public static class LuzFileExtensions
|
||||
{
|
||||
public static class LuzFileExtensions
|
||||
{
|
||||
public static uint GenerateChecksum(this LuzFile @this, List<LvlFile> scenes)
|
||||
{
|
||||
if (@this.Scenes.Length != scenes.Count)
|
||||
@@ -64,5 +64,4 @@ namespace InfectedRose.Utilities
|
||||
|
||||
return (uint) (upper << 16 | lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user