Lots of changes, too late to describe it all.

This commit is contained in:
wincent
2023-07-15 10:51:25 +02:00
parent c27aa174c0
commit 9599be8db4
60 changed files with 5550 additions and 4258 deletions

View File

@@ -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++)

View File

@@ -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;

View File

@@ -285,7 +285,25 @@ 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)

View File

@@ -1,14 +1,13 @@
using System.Collections.Generic;
namespace InfectedRose.Interface
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>
{
/// <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>
{
}
}

View 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;
}
}

View File

@@ -1,58 +1,57 @@
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; }
[XmlAttribute("count")]
public int Count { get; set; }
[XmlElement("locale")]
public string[] Locale { get; set; }
}
[XmlElement("locale")]
public string[] Locale { get; set; }
}
[XmlRoot("translation")]
public class Translation
{
[XmlAttribute("locale")]
public string Locale { get; set; }
[XmlRoot("translation")]
public class Translation
{
[XmlAttribute("locale")]
public string Locale { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlText]
public string Text { get; set; }
}
[XmlRoot("phrase")]
public class Phrase
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlRoot("phrase")]
public class Phrase
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlElement("translation")]
public List<Translation> Translations { get; set; }
}
[XmlElement("translation")]
public List<Translation> Translations { get; set; }
}
[XmlRoot("phrases")]
public class Phrases
{
[XmlAttribute("count")]
public int Count { get; set; }
[XmlRoot("phrases")]
public class Phrases
{
[XmlAttribute("count")]
public int Count { get; set; }
[XmlElement("phrase")]
public List<Phrase> Phrase { get; set; }
}
[XmlElement("phrase")]
public List<Phrase> Phrase { get; set; }
}
[XmlRoot("localization")]
public class Localization
{
[XmlAttribute("version")]
public float Version { get; set; } = 1.200000f;
[XmlRoot("localization")]
public class Localization
{
[XmlAttribute("version")]
public float Version { get; set; } = 1.200000f;
[XmlElement("locales")]
public Locales Locales { get; set; }
[XmlElement("locales")]
public Locales Locales { get; set; }
[XmlElement("phrases")]
public Phrases Phrases { get; set; }
}
[XmlElement("phrases")]
public Phrases Phrases { get; set; }
}

View File

@@ -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>
{
}
}

View File

@@ -1,13 +1,15 @@
using System.Text.Json.Serialization;
namespace InfectedRose.Interface
{
public class Manifest
{
[JsonPropertyName("name")]
public string Name { get; set; } = "mod-name";
namespace InfectedRose.Interface;
[JsonPropertyName("files")]
public string[] Files { get; set; } = {"mod.json"};
}
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";
}

View File

@@ -6,110 +6,106 @@ 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; } = "";
[JsonPropertyName("explicit-id")]
public int? ExplicitId { get; set; }
[JsonPropertyName("old-ids")]
public string[]? OldIds { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; } = "";
[JsonPropertyName("action")]
public string Action { get; set; }
[JsonPropertyName("show-defaults")]
public bool? ShowDefaults { get; set; }
[JsonPropertyName("components")]
public string[]? Components { get; set; }
[JsonPropertyName("table")]
public string? Table { get; set; }
[JsonPropertyName("items")]
public JsonValue[]? Items { get; set; }
[JsonPropertyName("skills")]
public ObjectSkillEntry[]? Skills { get; set; }
[JsonPropertyName("tasks")]
public MissionModTask[]? Tasks { get; set; }
[JsonPropertyName("missions")]
public MissionOffer[]? MissionOffers { get; set; }
[JsonPropertyName("behaviors")]
public Dictionary<string, Behavior> Behaviors { get; set; }
[JsonPropertyName("zone")]
public Zone? Zone { get; set; }
[JsonPropertyName("locale")]
public Dictionary<string, string>? Locale { get; set; }
[JsonPropertyName("values")]
public Dictionary<string, object?> Values { get; set; } = new Dictionary<string, object?>();
[JsonIgnore]
public Dictionary<string, object?> Defaults { get; set; } = new Dictionary<string, object?>();
public T GetValue<T>(string id)
{
[JsonPropertyName("id")]
public string Id { get; set; } = "";
[JsonPropertyName("explicit-id")]
public int? ExplicitId { get; set; }
[JsonPropertyName("old-ids")]
public string[]? OldIds { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; } = "";
[JsonPropertyName("action")]
public string Action { get; set; } = "add";
[JsonPropertyName("show-defaults")]
public bool? ShowDefaults { get; set; }
[JsonPropertyName("components")]
public string[]? Components { get; set; }
[JsonPropertyName("table")]
public string? Table { get; set; }
[JsonPropertyName("items")]
public JsonValue[]? Items { get; set; }
[JsonPropertyName("skills")]
public ObjectSkillEntry[]? Skills { get; set; }
[JsonPropertyName("tasks")]
public MissionModTask[]? Tasks { get; set; }
[JsonPropertyName("missions")]
public MissionOffer[]? MissionOffers { get; set; }
[JsonPropertyName("behaviors")]
public Dictionary<string, Behavior> Behaviors { get; set; }
[JsonPropertyName("zone")]
public Zone? Zone { get; set; }
[JsonPropertyName("locale")]
public Dictionary<string, string>? Locale { get; set; } = new Dictionary<string, string>
if (Values[id] is JsonElement jsonElement)
{
{"en_US", ""}
};
[JsonPropertyName("values")]
public Dictionary<string, object?> Values { get; set; } = new Dictionary<string, object?>();
[JsonIgnore]
public Dictionary<string, object?> Defaults { get; set; } = new Dictionary<string, object?>();
public T GetValue<T>(string id)
{
if (Values[id] is JsonElement jsonElement)
{
return jsonElement.Deserialize<T>()!;
}
return jsonElement.Deserialize<T>()!;
}
if (Values[id] is T value)
{
return value;
}
if (Values[id] is null)
{
return default;
}
return (T) Convert.ChangeType(Values[id], typeof(T));
}
public bool HasValue(string id)
if (Values[id] is T value)
{
return Values.ContainsKey(id);
return value;
}
public void Default<T>(string id, T value)
if (Values[id] is null)
{
Defaults[id] = value;
if (HasValue(id)) return;
Values[id] = value;
return default;
}
public void DefaultNull(string id)
{
Defaults[id] = null;
if (HasValue(id)) return;
return (T) Convert.ChangeType(Values[id], typeof(T));
}
Values[id] = null;
}
public bool HasValue(string id)
{
return Values.ContainsKey(id);
}
public void Default<T>(string id, T value)
{
Defaults[id] = value;
public int GetComponentType()
{
return (int) Enum.Parse(typeof(ComponentId), Type);
}
if (HasValue(id)) return;
Values[id] = value;
}
public void DefaultNull(string id)
{
Defaults[id] = null;
if (HasValue(id)) return;
Values[id] = null;
}
public int GetComponentType()
{
return (int) Enum.Parse(typeof(ComponentId), Type);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
namespace InfectedRose.Interface
namespace InfectedRose.Interface;
public abstract class ModType
{
public abstract class ModType
{
public abstract void Apply(Mod mod);
}
public abstract void Apply(Mod mod);
}

View File

@@ -1,15 +1,14 @@
using System;
namespace InfectedRose.Interface
{
[AttributeUsage(AttributeTargets.Class)]
public class ModTypeAttribute : Attribute
{
public string Type { get; set; }
namespace InfectedRose.Interface;
public ModTypeAttribute(string type)
{
Type = type;
}
[AttributeUsage(AttributeTargets.Class)]
public class ModTypeAttribute : Attribute
{
public string Type { get; set; }
public ModTypeAttribute(string type)
{
Type = type;
}
}

View File

@@ -1,35 +1,34 @@
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("directory")]
public string Directory { get; set; }
[JsonPropertyName("priority")]
public int Priority { get; set; }
}
[JsonPropertyName("priority")]
public int Priority { get; set; }
}
public class Mods
{
[JsonPropertyName("version")]
public string Version { get; set; } = "";
public class Mods
{
[JsonPropertyName("version")]
public string Version { get; set; } = "";
[JsonPropertyName("database")]
public string Database { get; set; } = "cdclient.fdb";
[JsonPropertyName("database")]
public string Database { get; set; } = "cdclient.fdb";
[JsonPropertyName("sqlite")]
public string Sqlite { get; set; } = "CDServer.sqlite";
[JsonPropertyName("sqlite")]
public string Sqlite { get; set; } = "CDServer.sqlite";
[JsonPropertyName("copy-mods")]
public string? Copy { get; set; }
[JsonPropertyName("resource-folder")]
public string ResourceFolder { get; set; }
[JsonPropertyName("copy-mods")]
public string? Copy { get; set; }
[JsonPropertyName("priorities")]
public List<ModPriority> Priorities { get; set; } = new List<ModPriority>();
}
[JsonPropertyName("resource-folder")]
public string ResourceFolder { get; set; } = "../res/maps/__mods__";
[JsonPropertyName("priorities")]
public List<ModPriority> Priorities { get; set; } = new List<ModPriority>();
}

View File

@@ -2,179 +2,178 @@ 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"}},
{"AccessoryDefaultLoc", new[] {"GroupID"}},
{"Activities", new[] {"ActivityID"}},
{"ActivityRewards", new[] {"objectTemplate", "ActivityRewardIndex"}},
{"ActivityText", new[] {"activityID", "type"}},
{"AnimationIndex", new[] {"animationGroupID"}},
{"Animations", new[] {"animationGroupID", "animation_type"}},
{"BaseCombatAIComponent", new[] {"id"}},
{"BehaviorEffect", new[] {"effectID", "effectType"}},
{"BehaviorParameter", new[] {"behaviorID", "parameterID"}},
{"BehaviorTemplate", new[] {"behaviorID"}},
{"BehaviorTemplateName", new[] {"templateID"}},
{"Blueprints", new[] {"id"}},
{"BrickColors", new[] {"id"}},
{"BrickIDTable", new[] {"NDObjectID"}},
{"BuffDefinitions", new[] {"ID"}},
{"BuffParameters", new[] {"BuffID", "ParameterName"}},
{"Camera", new[] {"camera_name"}},
{"CelebrationParameters", new[] {"id"}},
{"ChoiceBuildComponent", new[] {"id"}},
{"CollectibleComponent", new[] {"id"}},
{"ComponentsRegistry", new[] {"id", "component_type"}},
{"ControlSchemes", new[] {"control_scheme"}},
{"CurrencyDenominations", new[] {"value"}},
{"CurrencyTable", new[] {"currencyIndex", "npcminlevel"}},
{"DBExclude", new[] {"table", "column"}},
{"DeletionRestrictions", new[] {"id"}},
{"DestructibleComponent", new[] {"id"}},
{"DevModelBehaviors", new[] {"ModelID"}},
{"Emotes", new[] {"id"}},
{"EventGating", new[] {"eventName"}},
{"ExhibitComponent", new[] {"id"}},
{"Factions", new[] {"faction"}},
{"FeatureGating", new[] {"featureName"}},
{"FlairTable", new[] {"id"}},
{"Icons", new[] {"IconID"}},
{"InventoryComponent", new[] {"id", "itemid"}},
{"ItemComponent", new[] {"id"}},
{"ItemEggData", new[] {"id"}},
{"ItemFoodData", new[] {"id"}},
{"ItemSetSkills", new[] {"SkillSetID", "SkillID"}},
{"ItemSets", new[] {"setID"}},
{"JetPackPadComponent", new[] {"id"}},
{"LUPExhibitComponent", new[] {"id"}},
{"LUPExhibitModelData", new[] {"LOT"}},
{"LUPZoneIDs", new[] {"zoneID"}},
{"LanguageType", new[] {"LanguageID"}},
{"LevelProgressionLookup", new[] {"id"}},
{"LootMatrix", new[] {"LootMatrixIndex", "LootTableIndex", "RarityTableIndex"}},
{"LootMatrixIndex", new[] {"LootMatrixIndex"}},
{"LootTable", new[] {"itemid", "LootTableIndex", "id"}},
{"LootTableIndex", new[] {"LootTableIndex"}},
{"MinifigComponent", new[] {"id"}},
{"MinifigDecals_Eyebrows", new[] {"ID"}},
{"MinifigDecals_Eyes", new[] {"ID"}},
{"MinifigDecals_Legs", new[] {"ID"}},
{"MinifigDecals_Mouths", new[] {"ID"}},
{"MinifigDecals_Torsos", new[] {"ID"}},
{"MissionEmail", new[] {"ID"}},
{"MissionNPCComponent", new[] {"id", "missionID"}},
{"MissionTasks", new[] {"id", "uid"}},
{"MissionText", new[] {"id"}},
{"Missions", new[] {"id"}},
{"ModelBehavior", new[] {"id"}},
{"ModularBuildComponent", new[] {"id"}},
{"ModuleComponent", new[] {"id"}},
{"MotionFX", new[] {"id"}},
{"MovementAIComponent", new[] {"id"}},
{"MovingPlatforms", new[] {"id"}},
{"NpcIcons", new[] {"id"}},
{"ObjectBehaviorXREF", new[] {"LOT"}},
{"ObjectBehaviors", new[] {"BehaviorID"}},
{"ObjectSkills", new[] {"objectTemplate", "skillID"}},
{"Objects", new[] {"id"}},
{"PackageComponent", new[] {"id"}},
{"PetAbilities", new[] {"id"}},
{"PetComponent", new[] {"id"}},
{"PetNestComponent", new[] {"id"}},
{"PhysicsComponent", new[] {"id"}},
{"PlayerFlags", new[] {"id"}},
{"PlayerStatistics", new[] {"statID"}},
{"PossessableComponent", new[] {"id"}},
{"Preconditions", new[] {"id"}},
{"PropertyEntranceComponent", new[] {"id"}},
{"PropertyTemplate", new[] {"id"}},
{"ProximityMonitorComponent", new[] {"id"}},
{"ProximityTypes", new[] {"id"}},
{"RacingModuleComponent", new[] {"id"}},
{"RailActivatorComponent", new[] {"id"}},
{"RarityTable", new[] {"id"}},
{"RarityTableIndex", new[] {"RarityTableIndex"}},
{"RebuildComponent", new[] {"id"}},
{"RebuildSections", new[] {"id"}},
{"Release_Version", new[] {"ReleaseVersion"}},
{"RenderComponent", new[] {"id"}},
{"RenderComponentFlash", new[] {"id", "interactive", "animated", "nodeName", "flashPath", "elementName", "_uid"}},
{"RenderComponentWrapper", new[] {"id"}},
{"RenderIconAssets", new[] {"id"}},
{"ReputationRewards", new[] {"repLevel"}},
{"RewardCodes", new[] {"id"}},
{"Rewards", new[] {"id"}},
{"RocketLaunchpadControlComponent", new[] {"id"}},
{"SceneTable", new[] {"sceneID"}},
{"ScriptComponent", new[] {"id"}},
{"SkillBehavior", new[] {"skillID"}},
{"SmashableChain", new[] {"chainIndex", "chainLevel"}},
{"SmashableChainIndex", new[] {"id"}},
{"SmashableComponent", new[] {"id"}},
{"SmashableElements", new[] {"elementID"}},
{"SpeedchatMenu", new[] {"id"}},
{"SubscriptionPricing", new[] {"id"}},
{"SurfaceType", new[] {"SurfaceType"}},
{"TamingBuildPuzzles", new[] {"id"}},
{"TextDescription", new[] {"TextID"}},
{"TextLanguage", new[] {"TextID"}},
{"TrailEffects", new[] {"trailID"}},
{"UGBehaviorSounds", new[] {"id"}},
{"VehiclePhysics", new[] {"id"}},
{"VehicleStatMap", new[] {"id", "ModuleStat", "HavokStat"}},
{"VendorComponent", new[] {"id"}},
{"WhatsCoolItemSpotlight", new[] {"id"}},
{"WhatsCoolNewsAndTips", new[] {"id"}},
{"WorldConfig", new[] {"WorldConfigID"}},
{"ZoneLoadingTips", new[] {"id"}},
{"ZoneSummary", new[] {"zoneID", "type", "value", "_uniqueID"}},
{"ZoneTable", new[] {"zoneID"}},
{"brickAttributes", new[] {"ID"}},
{"dtproperties", new[] {"id"}},
{"mapAnimationPriorities", new[] {"id"}},
{"mapAssetType", new[] {"id"}},
{"mapIcon", new[] {"LOT", "iconID", "iconState"}},
{"mapItemTypes", new[] {"id"}},
{"mapRenderEffects", new[] {"id"}},
{"mapShaders", new[] {"id"}},
{"mapTextureResource", new[] {"id"}},
{"map_BlueprintCategory", new[] {"id"}},
{"sysdiagrams", new[] {"name"}}
};
public void Generate(AccessDatabase accessDatabase)
{
public Dictionary<string, string[]> PrimaryKeys { get; } = new Dictionary<string, string[]>
{
{"AICombatRoles", new[] {"id"}},
{"AccessoryDefaultLoc", new[] {"GroupID"}},
{"Activities", new[] {"ActivityID"}},
{"ActivityRewards", new[] {"objectTemplate", "ActivityRewardIndex"}},
{"ActivityText", new[] {"activityID", "type"}},
{"AnimationIndex", new[] {"animationGroupID"}},
{"Animations", new[] {"animationGroupID", "animation_type"}},
{"BaseCombatAIComponent", new[] {"id"}},
{"BehaviorEffect", new[] {"effectID", "effectType"}},
{"BehaviorParameter", new[] {"behaviorID", "parameterID"}},
{"BehaviorTemplate", new[] {"behaviorID"}},
{"BehaviorTemplateName", new[] {"templateID"}},
{"Blueprints", new[] {"id"}},
{"BrickColors", new[] {"id"}},
{"BrickIDTable", new[] {"NDObjectID"}},
{"BuffDefinitions", new[] {"ID"}},
{"BuffParameters", new[] {"BuffID", "ParameterName"}},
{"Camera", new[] {"camera_name"}},
{"CelebrationParameters", new[] {"id"}},
{"ChoiceBuildComponent", new[] {"id"}},
{"CollectibleComponent", new[] {"id"}},
{"ComponentsRegistry", new[] {"id", "component_type"}},
{"ControlSchemes", new[] {"control_scheme"}},
{"CurrencyDenominations", new[] {"value"}},
{"CurrencyTable", new[] {"currencyIndex", "npcminlevel"}},
{"DBExclude", new[] {"table", "column"}},
{"DeletionRestrictions", new[] {"id"}},
{"DestructibleComponent", new[] {"id"}},
{"DevModelBehaviors", new[] {"ModelID"}},
{"Emotes", new[] {"id"}},
{"EventGating", new[] {"eventName"}},
{"ExhibitComponent", new[] {"id"}},
{"Factions", new[] {"faction"}},
{"FeatureGating", new[] {"featureName"}},
{"FlairTable", new[] {"id"}},
{"Icons", new[] {"IconID"}},
{"InventoryComponent", new[] {"id", "itemid"}},
{"ItemComponent", new[] {"id"}},
{"ItemEggData", new[] {"id"}},
{"ItemFoodData", new[] {"id"}},
{"ItemSetSkills", new[] {"SkillSetID", "SkillID"}},
{"ItemSets", new[] {"setID"}},
{"JetPackPadComponent", new[] {"id"}},
{"LUPExhibitComponent", new[] {"id"}},
{"LUPExhibitModelData", new[] {"LOT"}},
{"LUPZoneIDs", new[] {"zoneID"}},
{"LanguageType", new[] {"LanguageID"}},
{"LevelProgressionLookup", new[] {"id"}},
{"LootMatrix", new[] {"LootMatrixIndex", "LootTableIndex", "RarityTableIndex"}},
{"LootMatrixIndex", new[] {"LootMatrixIndex"}},
{"LootTable", new[] {"itemid", "LootTableIndex", "id"}},
{"LootTableIndex", new[] {"LootTableIndex"}},
{"MinifigComponent", new[] {"id"}},
{"MinifigDecals_Eyebrows", new[] {"ID"}},
{"MinifigDecals_Eyes", new[] {"ID"}},
{"MinifigDecals_Legs", new[] {"ID"}},
{"MinifigDecals_Mouths", new[] {"ID"}},
{"MinifigDecals_Torsos", new[] {"ID"}},
{"MissionEmail", new[] {"ID"}},
{"MissionNPCComponent", new[] {"id", "missionID"}},
{"MissionTasks", new[] {"id", "uid"}},
{"MissionText", new[] {"id"}},
{"Missions", new[] {"id"}},
{"ModelBehavior", new[] {"id"}},
{"ModularBuildComponent", new[] {"id"}},
{"ModuleComponent", new[] {"id"}},
{"MotionFX", new[] {"id"}},
{"MovementAIComponent", new[] {"id"}},
{"MovingPlatforms", new[] {"id"}},
{"NpcIcons", new[] {"id"}},
{"ObjectBehaviorXREF", new[] {"LOT"}},
{"ObjectBehaviors", new[] {"BehaviorID"}},
{"ObjectSkills", new[] {"objectTemplate", "skillID"}},
{"Objects", new[] {"id"}},
{"PackageComponent", new[] {"id"}},
{"PetAbilities", new[] {"id"}},
{"PetComponent", new[] {"id"}},
{"PetNestComponent", new[] {"id"}},
{"PhysicsComponent", new[] {"id"}},
{"PlayerFlags", new[] {"id"}},
{"PlayerStatistics", new[] {"statID"}},
{"PossessableComponent", new[] {"id"}},
{"Preconditions", new[] {"id"}},
{"PropertyEntranceComponent", new[] {"id"}},
{"PropertyTemplate", new[] {"id"}},
{"ProximityMonitorComponent", new[] {"id"}},
{"ProximityTypes", new[] {"id"}},
{"RacingModuleComponent", new[] {"id"}},
{"RailActivatorComponent", new[] {"id"}},
{"RarityTable", new[] {"id"}},
{"RarityTableIndex", new[] {"RarityTableIndex"}},
{"RebuildComponent", new[] {"id"}},
{"RebuildSections", new[] {"id"}},
{"Release_Version", new[] {"ReleaseVersion"}},
{"RenderComponent", new[] {"id"}},
{"RenderComponentFlash", new[] {"id", "interactive", "animated", "nodeName", "flashPath", "elementName", "_uid"}},
{"RenderComponentWrapper", new[] {"id"}},
{"RenderIconAssets", new[] {"id"}},
{"ReputationRewards", new[] {"repLevel"}},
{"RewardCodes", new[] {"id"}},
{"Rewards", new[] {"id"}},
{"RocketLaunchpadControlComponent", new[] {"id"}},
{"SceneTable", new[] {"sceneID"}},
{"ScriptComponent", new[] {"id"}},
{"SkillBehavior", new[] {"skillID"}},
{"SmashableChain", new[] {"chainIndex", "chainLevel"}},
{"SmashableChainIndex", new[] {"id"}},
{"SmashableComponent", new[] {"id"}},
{"SmashableElements", new[] {"elementID"}},
{"SpeedchatMenu", new[] {"id"}},
{"SubscriptionPricing", new[] {"id"}},
{"SurfaceType", new[] {"SurfaceType"}},
{"TamingBuildPuzzles", new[] {"id"}},
{"TextDescription", new[] {"TextID"}},
{"TextLanguage", new[] {"TextID"}},
{"TrailEffects", new[] {"trailID"}},
{"UGBehaviorSounds", new[] {"id"}},
{"VehiclePhysics", new[] {"id"}},
{"VehicleStatMap", new[] {"id", "ModuleStat", "HavokStat"}},
{"VendorComponent", new[] {"id"}},
{"WhatsCoolItemSpotlight", new[] {"id"}},
{"WhatsCoolNewsAndTips", new[] {"id"}},
{"WorldConfig", new[] {"WorldConfigID"}},
{"ZoneLoadingTips", new[] {"id"}},
{"ZoneSummary", new[] {"zoneID", "type", "value", "_uniqueID"}},
{"ZoneTable", new[] {"zoneID"}},
{"brickAttributes", new[] {"ID"}},
{"dtproperties", new[] {"id"}},
{"mapAnimationPriorities", new[] {"id"}},
{"mapAssetType", new[] {"id"}},
{"mapIcon", new[] {"LOT", "iconID", "iconState"}},
{"mapItemTypes", new[] {"id"}},
{"mapRenderEffects", new[] {"id"}},
{"mapShaders", new[] {"id"}},
{"mapTextureResource", new[] {"id"}},
{"map_BlueprintCategory", new[] {"id"}},
{"sysdiagrams", new[] {"name"}}
};
public void Generate(AccessDatabase accessDatabase)
foreach (var table in accessDatabase)
{
foreach (var table in accessDatabase)
{
var info = table.TableInfo;
var rows = table.ToArray();
var info = table.TableInfo;
var rows = table.ToArray();
var primaryKey = new List<string>();
var primaryKey = new List<string>();
for (var i = 0; i < info.Count; ++i)
for (var i = 0; i < info.Count; ++i)
{
primaryKey.Add(info[i].Name);
// Check if there are duplicates of this value in the table.
var index = i;
var values = rows.Where(r => r[index].Value != null).Select(r => r[index].Value).ToArray();
var containsDuplicates = values.Count() != values.Distinct().Count();
if (!containsDuplicates)
{
primaryKey.Add(info[i].Name);
// Check if there are duplicates of this value in the table.
var index = i;
var values = rows.Where(r => r[index].Value != null).Select(r => r[index].Value).ToArray();
var containsDuplicates = values.Count() != values.Distinct().Count();
if (!containsDuplicates)
{
break;
}
break;
}
PrimaryKeys.Add(table.Name, primaryKey.ToArray());
}
PrimaryKeys.Add(table.Name, primaryKey.ToArray());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,42 @@
using System.Collections.Generic;
using InfectedRose.Database;
namespace InfectedRose.Interface
{
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);
}
public static Row FromLookup(this Table @this, Mod mod)
{
if (mod.Id.StartsWith("lego-universe:"))
{
@this.Seek(int.Parse(mod.Id[14..]), out var row);
namespace InfectedRose.Interface;
return row;
}
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);
}
public static Row FromLookup(this Table @this, Mod mod)
{
if (mod.Id.StartsWith("lego-universe:"))
{
@this.Seek(int.Parse(mod.Id[14..]), out var row);
return row;
}
if (ModContext.Lookup.TryGetValue(mod.Id, out var value))
if (ModContext.Lookup.TryGetValue(mod.Id, out var value))
{
return @this.Create(value);
}
if (mod.OldIds == null)
{
return @this.CreateWithFilter(ModContext.Lookup.Values);
}
foreach (var oldId in mod.OldIds)
{
if (ModContext.Lookup.TryGetValue(oldId, out value))
{
return @this.Create(value);
}
if (mod.OldIds == null)
{
return @this.CreateWithFilter(ModContext.Lookup.Values);
}
foreach (var oldId in mod.OldIds)
{
if (ModContext.Lookup.TryGetValue(oldId, out value))
{
return @this.Create(value);
}
}
throw new KeyNotFoundException($"Could not determine the ID of {mod.Id}, no old ids match lookup.json.");
}
throw new KeyNotFoundException($"Could not determine the ID of {mod.Id}, no old ids match lookup.json.");
}
}
}

View File

@@ -3,77 +3,93 @@ 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")]
public string Template { get; set; }
[JsonPropertyName("template")]
public string Template { get; set; }
[JsonPropertyName("effect")]
public Effect? Effect { get; set; }
[JsonPropertyName("effect")]
public Effect? Effect { get; set; }
[JsonPropertyName("effect-handle")]
public string? EffectHandle { get; set; }
[JsonPropertyName("effect-handle")]
public string? EffectHandle { get; set; }
[JsonPropertyName("parameters")]
public Dictionary<string, object> Parameters { get; set; }
[JsonPropertyName("parameters")]
public Dictionary<string, object>? Parameters { get; set; }
public int Apply(string id)
public int Apply(string id)
{
var behaviorTemplateTable = ModContext.Database["BehaviorTemplate"]!;
var behaviorParameterTable = ModContext.Database["BehaviorParameter"]!;
var behaviorTemplateNameTable = ModContext.Database["BehaviorTemplateName"]!;
var templateName = behaviorTemplateNameTable.FirstOrDefault(t => (string) t["name"].Value == Template);
if (templateName == null)
{
var behaviorTemplateTable = ModContext.Database["BehaviorTemplate"]!;
var behaviorParameterTable = ModContext.Database["BehaviorParameter"]!;
var behaviorTemplateNameTable = ModContext.Database["BehaviorTemplateName"]!;
var templateName = behaviorTemplateNameTable.FirstOrDefault(t => (string) t["name"].Value == Template);
if (templateName == null)
{
throw new Exception($"Invalid behavior template {Template}!");
}
var behaviorTemplate = behaviorTemplateTable.FromLookup(id);
ModContext.RegisterId(id, behaviorTemplate.Key);
behaviorTemplate["templateID"].Value = (int) templateName["templateID"].Value;
var effectId = behaviorTemplate["effectID"];
if (Effect == null)
{
effectId.Value = 0;
}
else if (Effect.Id.StartsWith("lego-universe"))
{
effectId.Value = Effect.Id.Split(":")[1];
}
else
{
effectId.Value = Effect.Apply();
}
behaviorTemplate["effectHandle"].Value = EffectHandle;
foreach (var (key, value) in Parameters)
{
var parameter = behaviorParameterTable.Create(behaviorTemplate.Key);
parameter["parameterID"].Value = key;
var valueField = parameter["value"];
if (float.TryParse(value.ToString(), out var f))
{
valueField.Value = f;
continue;
}
ModContext.AwaitId(value.ToString()!, i =>
{
valueField.Value = (float) i;
});
}
return behaviorTemplate.Key;
throw new Exception($"Invalid behavior template {Template}!");
}
var behaviorTemplate = behaviorTemplateTable.FromLookup(id);
ModContext.RegisterId(id, behaviorTemplate.Key);
behaviorTemplate["templateID"].Value = (int) templateName["templateID"].Value;
var effectId = behaviorTemplate["effectID"];
if (Effect == null)
{
effectId.Value = 0;
}
else if (Effect.Id.StartsWith("lego-universe"))
{
effectId.Value = Effect.Id.Split(":")[1];
}
else
{
effectId.Value = Effect.Apply();
}
behaviorTemplate["effectHandle"].Value = EffectHandle;
if (Parameters == null) return behaviorTemplate.Key;
foreach (var (key, value) in Parameters)
{
var parameter = behaviorParameterTable.Create(behaviorTemplate.Key);
parameter["parameterID"].Value = key;
var valueField = parameter["value"];
if (float.TryParse(value.ToString(), out var f))
{
valueField.Value = f;
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;
});
}
return behaviorTemplate.Key;
}
}

View File

@@ -1,109 +1,108 @@
namespace InfectedRose.Interface.Templates
namespace InfectedRose.Interface.Templates;
public enum ComponentId
{
public enum ComponentId
{
ControllablePhysicsComponent = 1,
RenderComponent = 2,
SimplePhysicsComponent = 3,
CharacterComponent = 4,
ScriptComponent = 5,
BouncerComponent = 6,
DestructibleComponent = 7,
GhostComponent = 8,
SkillComponent = 9,
SpawnerComponent = 10,
ItemComponent = 11,
RebuildComponent = 12,
RebuildStartComponent = 13,
RebuildActivatorComponent = 14,
IconOnlyComponent = 15,
VendorComponent = 16,
InventoryComponent = 17,
ProjectilePhysicsComponent = 18,
ShootingGalleryComponent = 19,
RigidBodyPhantomPhysicsComponent = 20,
DropEffectComponent = 21,
ChestComponent = 22,
CollectibleComponent = 23,
BlueprintComponent = 24,
MovingPlatformComponent = 25,
PetComponent = 26,
PlatformBoundaryComponent = 27,
ModuleComponent = 28,
ArcadeComponent = 29,
VehiclePhysicsComponent = 30,
MovementAIComponent = 31,
ExhibitComponent = 32,
OverheadIconComponent = 33,
PetControlComponent = 34,
MinifigComponent = 35,
PropertyComponent = 36,
PetCreatorComponent = 37,
ModelBuilderComponent = 38,
ScriptedActivityComponent = 39,
PhantomPhysicsComponent = 40,
SpringpadComponent = 41,
B3BehaviorsComponent = 42,
PropertyEntranceComponent = 43,
FXComponent = 44,
PropertyManagementComponent = 45,
SecondVehiclePhysicsComponent = 46,
PhysicsSystemComponent = 47,
QuickBuildComponent = 48,
SwitchComponent = 49,
MinigameComponent = 50,
ChanglingComponent = 51,
ChoiceBuildComponent = 52,
PackageComponent = 53,
SoundRepeaterComponent = 54,
SoundAmbient2DComponent = 55,
SoundAmbient3DComponent = 56,
PreconditionComponent = 57,
PlayerFlagsComponent = 58,
CustomBuildAssemblyComponent = 59,
BaseCombatAIComponent = 60,
ModuleAssemblyComponent = 61,
ShowcaseModelHandlerComponent = 62,
RacingModuleComponent = 63,
GenericActivatorComponent = 64,
PropertyVendorComponent = 65,
HFLightDirectionGadgetComponent = 66,
RocketLaunchComponent = 67,
RocketLandingComponent = 68,
TriggerComponent = 69,
DroppedLootComponent = 70,
RacingControlComponent = 71,
FactionTriggerComponent = 72,
MissionNPCComponent = 73,
RacingStatsComponent = 74,
LUPExhibitComponent = 75,
BBBComponent = 76,
SoundTriggerComponent = 77,
ProximityMonitorComponent = 78,
RacingSoundTriggerComponent = 79,
ChatComponent = 80,
FriendsListComponent = 81,
GuildComponent = 82,
LocalSystemComponent = 83,
MissionComponent = 84,
MutableModelBehaviorsComponent = 85,
PathfindingControlComponent = 86,
PetTamingControlComponent = 87,
PropertyEditorComponent = 88,
SkinnedRenderComponent = 89,
SlashCommandComponent = 90,
StatusEffectComponent = 91,
TeamsComponent = 92,
TextEffectComponent = 93,
TradeComponent = 94,
UserControlComponent = 95,
IgnoreListComponent = 96,
LUPLaunchpadComponent = 97,
BuffComponent = 98,
InteractionManagerComponent = 98,
DonationVendorComponent = 100,
CombatMediatorComponent = 101,
Component107 = 107,
Possesable = 108
}
ControllablePhysicsComponent = 1,
RenderComponent = 2,
SimplePhysicsComponent = 3,
CharacterComponent = 4,
ScriptComponent = 5,
BouncerComponent = 6,
DestructibleComponent = 7,
GhostComponent = 8,
SkillComponent = 9,
SpawnerComponent = 10,
ItemComponent = 11,
RebuildComponent = 12,
RebuildStartComponent = 13,
RebuildActivatorComponent = 14,
IconOnlyComponent = 15,
VendorComponent = 16,
InventoryComponent = 17,
ProjectilePhysicsComponent = 18,
ShootingGalleryComponent = 19,
RigidBodyPhantomPhysicsComponent = 20,
DropEffectComponent = 21,
ChestComponent = 22,
CollectibleComponent = 23,
BlueprintComponent = 24,
MovingPlatformComponent = 25,
PetComponent = 26,
PlatformBoundaryComponent = 27,
ModuleComponent = 28,
ArcadeComponent = 29,
VehiclePhysicsComponent = 30,
MovementAIComponent = 31,
ExhibitComponent = 32,
OverheadIconComponent = 33,
PetControlComponent = 34,
MinifigComponent = 35,
PropertyComponent = 36,
PetCreatorComponent = 37,
ModelBuilderComponent = 38,
ScriptedActivityComponent = 39,
PhantomPhysicsComponent = 40,
SpringpadComponent = 41,
B3BehaviorsComponent = 42,
PropertyEntranceComponent = 43,
FXComponent = 44,
PropertyManagementComponent = 45,
SecondVehiclePhysicsComponent = 46,
PhysicsSystemComponent = 47,
QuickBuildComponent = 48,
SwitchComponent = 49,
MinigameComponent = 50,
ChanglingComponent = 51,
ChoiceBuildComponent = 52,
PackageComponent = 53,
SoundRepeaterComponent = 54,
SoundAmbient2DComponent = 55,
SoundAmbient3DComponent = 56,
PreconditionComponent = 57,
PlayerFlagsComponent = 58,
CustomBuildAssemblyComponent = 59,
BaseCombatAIComponent = 60,
ModuleAssemblyComponent = 61,
ShowcaseModelHandlerComponent = 62,
RacingModuleComponent = 63,
GenericActivatorComponent = 64,
PropertyVendorComponent = 65,
HFLightDirectionGadgetComponent = 66,
RocketLaunchComponent = 67,
RocketLandingComponent = 68,
TriggerComponent = 69,
DroppedLootComponent = 70,
RacingControlComponent = 71,
FactionTriggerComponent = 72,
MissionNPCComponent = 73,
RacingStatsComponent = 74,
LUPExhibitComponent = 75,
BBBComponent = 76,
SoundTriggerComponent = 77,
ProximityMonitorComponent = 78,
RacingSoundTriggerComponent = 79,
ChatComponent = 80,
FriendsListComponent = 81,
GuildComponent = 82,
LocalSystemComponent = 83,
MissionComponent = 84,
MutableModelBehaviorsComponent = 85,
PathfindingControlComponent = 86,
PetTamingControlComponent = 87,
PropertyEditorComponent = 88,
SkinnedRenderComponent = 89,
SlashCommandComponent = 90,
StatusEffectComponent = 91,
TeamsComponent = 92,
TextEffectComponent = 93,
TradeComponent = 94,
UserControlComponent = 95,
IgnoreListComponent = 96,
LUPLaunchpadComponent = 97,
BuffComponent = 98,
InteractionManagerComponent = 98,
DonationVendorComponent = 100,
CombatMediatorComponent = 101,
Component107 = 107,
Possesable = 108
}

View File

@@ -1,60 +1,59 @@
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; }
[JsonPropertyName("type")]
public string EffectType { get; set; }
[JsonPropertyName("name")]
public string EffectName { get; set; }
[JsonPropertyName("animation")]
public string AnimationName { get; set; }
[JsonPropertyName("bone")]
public string BoneName { get; set; }
[JsonPropertyName("values")]
public Dictionary<string, object> Values { get; set; }
public int Apply()
{
[JsonPropertyName("id")]
public string Id { get; set; }
var behaviorEffectTable = ModContext.Database["BehaviorEffect"];
[JsonPropertyName("type")]
public string EffectType { get; set; }
var behaviorEffect = behaviorEffectTable.FromLookup(Id);
[JsonPropertyName("name")]
public string EffectName { get; set; }
ModContext.RegisterId(Id, behaviorEffect.Key);
[JsonPropertyName("animation")]
public string AnimationName { get; set; }
[JsonPropertyName("bone")]
public string BoneName { get; set; }
[JsonPropertyName("values")]
public Dictionary<string, object> Values { get; set; }
public int Apply()
foreach (var field in behaviorEffect)
{
var behaviorEffectTable = ModContext.Database["BehaviorEffect"];
var behaviorEffect = behaviorEffectTable.FromLookup(Id);
ModContext.RegisterId(Id, behaviorEffect.Key);
foreach (var field in behaviorEffect)
switch (field.Name)
{
switch (field.Name)
{
case "effectID":
break;
case "effectType":
field.Value = EffectType;
break;
case "effectName":
field.Value = EffectName;
break;
case "animationName":
field.Value = AnimationName;
break;
case "boneName":
field.Value = BoneName;
break;
}
case "effectID":
break;
case "effectType":
field.Value = EffectType;
break;
case "effectName":
field.Value = EffectName;
break;
case "animationName":
field.Value = AnimationName;
break;
case "boneName":
field.Value = BoneName;
break;
}
ModContext.ApplyValues(Values, behaviorEffect, behaviorEffectTable, true);
return behaviorEffect.Key;
}
ModContext.ApplyValues(Values, behaviorEffect, behaviorEffectTable, true);
return behaviorEffect.Key;
}
}

View File

@@ -1,104 +1,103 @@
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)
{
public override void Apply(Mod mod)
if (mod.Action != "add")
{
if (mod.Action != "add")
{
return;
}
// Controller
mod.Default("physics_asset", @"miscellaneous\standard_enemy.hkx");
mod.Default("static", 0);
mod.Default("jump", 4);
mod.Default("doublejump", 0);
mod.Default("speed", 8);
mod.Default("rotSpeed", 720);
mod.Default("playerHeight", 4.4f);
mod.Default("playerRadius", 1.7f);
mod.Default("pcShapeType", 0);
mod.Default("collisionGroup", 12);
mod.Default("airSpeed", 5);
mod.Default("jumpAirSpeed", 25);
// Render
mod.Default("render_asset", @"animations\\creatures\\cre_strombie.kfm");
mod.Default("animationGroupIDs", "513,535");
mod.Default("shader_id", 66);
mod.DefaultNull("interactionDistance");
mod.DefaultNull("chatBubbleOffset");
mod.Default("fade", true);
mod.Default("fadeInTime", 0.1f);
mod.DefaultNull("billboardHeight");
mod.DefaultNull("AudioMetaEventSet");
mod.Default("usedropshadow", false);
mod.Default("preloadAnimations", false);
mod.Default("ignoreCameraCollision", false);
mod.Default("gradualSnap", false);
mod.Default("staticBillboard", false);
mod.Default("attachIndicatorsToNode", false);
// Destroyable
mod.Default("life", 1);
mod.Default("armor", 0);
mod.Default("imagination", 0);
mod.Default("level", 1);
mod.Default("faction", 4);
mod.Default("factionList", "4");
mod.Default("isnpc", true);
mod.Default("isSmashable", true);
mod.Default("attack_priority", 1);
mod.Default("death_behavior", 2);
mod.Default("CurrencyIndex", 1);
mod.Default("LootMatrixIndex", 160);
mod.DefaultNull("difficultyLevel");
// Movement
mod.Default("MovementType", "Wander");
mod.Default("WanderChance", 90);
mod.Default("WanderDelayMin", 3);
mod.Default("WanderDelayMax", 6);
mod.Default("WanderSpeed", 0.5f);
mod.Default("WanderRadius", 8);
mod.DefaultNull("attachedPath");
// BaseCombatAI
mod.Default("behaviorType", 1);
mod.Default("minRoundLength", 3);
mod.Default("maxRoundLength", 5);
mod.Default("pursuitSpeed", 2);
mod.Default("spawnTimer", 1);
mod.Default("tetherSpeed", 4);
mod.Default("softTetherRadius", 25);
mod.Default("hardTetherRadius", 101);
mod.Default("tetherEffectID", 6270);
mod.Default("combatRoundLength", 4);
mod.Default("combatRole", 5);
mod.Default("combatStartDelay", 1.5f);
mod.Default("aggroRadius", 25);
mod.Default("ignoreMediator", true);
mod.Default("ignoreStatReset", false);
mod.Default("ignoreParent", false);
// Object
mod.DefaultNull("npcTemplateID");
mod.Default("nametag", true);
mod.Default("placeable", true);
mod.Default("localize", true);
mod.Default("locStatus", 2);
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "Enemies";
ObjectMod.AddComponent(mod, obj, ComponentId.ControllablePhysicsComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.DestructibleComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.MovementAIComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.BaseCombatAIComponent);
return;
}
// Controller
mod.Default("physics_asset", @"miscellaneous\standard_enemy.hkx");
mod.Default("static", 0);
mod.Default("jump", 4);
mod.Default("doublejump", 0);
mod.Default("speed", 8);
mod.Default("rotSpeed", 720);
mod.Default("playerHeight", 4.4f);
mod.Default("playerRadius", 1.7f);
mod.Default("pcShapeType", 0);
mod.Default("collisionGroup", 12);
mod.Default("airSpeed", 5);
mod.Default("jumpAirSpeed", 25);
// Render
mod.Default("render_asset", @"animations\\creatures\\cre_strombie.kfm");
mod.Default("animationGroupIDs", "513,535");
mod.Default("shader_id", 66);
mod.DefaultNull("interactionDistance");
mod.DefaultNull("chatBubbleOffset");
mod.Default("fade", true);
mod.Default("fadeInTime", 0.1f);
mod.DefaultNull("billboardHeight");
mod.DefaultNull("AudioMetaEventSet");
mod.Default("usedropshadow", false);
mod.Default("preloadAnimations", false);
mod.Default("ignoreCameraCollision", false);
mod.Default("gradualSnap", false);
mod.Default("staticBillboard", false);
mod.Default("attachIndicatorsToNode", false);
// Destroyable
mod.Default("life", 1);
mod.Default("armor", 0);
mod.Default("imagination", 0);
mod.Default("level", 1);
mod.Default("faction", 4);
mod.Default("factionList", "4");
mod.Default("isnpc", true);
mod.Default("isSmashable", true);
mod.Default("attack_priority", 1);
mod.Default("death_behavior", 2);
mod.Default("CurrencyIndex", 1);
mod.Default("LootMatrixIndex", 160);
mod.DefaultNull("difficultyLevel");
// Movement
mod.Default("MovementType", "Wander");
mod.Default("WanderChance", 90);
mod.Default("WanderDelayMin", 3);
mod.Default("WanderDelayMax", 6);
mod.Default("WanderSpeed", 0.5f);
mod.Default("WanderRadius", 8);
mod.DefaultNull("attachedPath");
// BaseCombatAI
mod.Default("behaviorType", 1);
mod.Default("minRoundLength", 3);
mod.Default("maxRoundLength", 5);
mod.Default("pursuitSpeed", 2);
mod.Default("spawnTimer", 1);
mod.Default("tetherSpeed", 4);
mod.Default("softTetherRadius", 25);
mod.Default("hardTetherRadius", 101);
mod.Default("tetherEffectID", 6270);
mod.Default("combatRoundLength", 4);
mod.Default("combatRole", 5);
mod.Default("combatStartDelay", 1.5f);
mod.Default("aggroRadius", 25);
mod.Default("ignoreMediator", true);
mod.Default("ignoreStatReset", false);
mod.Default("ignoreParent", false);
// Object
mod.DefaultNull("npcTemplateID");
mod.Default("nametag", true);
mod.Default("placeable", true);
mod.Default("localize", true);
mod.Default("locStatus", 2);
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "Enemies";
ObjectMod.AddComponent(mod, obj, ComponentId.ControllablePhysicsComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.DestructibleComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.MovementAIComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.BaseCombatAIComponent);
}
}

View File

@@ -1,24 +1,23 @@
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)
{
public override void Apply(Mod mod)
if (mod.Action != "add")
{
if (mod.Action != "add")
{
return;
}
mod.Default("static", 1);
mod.Default("shader_id", 1);
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "Environmental";
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.SimplePhysicsComponent);
return;
}
mod.Default("static", 1);
mod.Default("shader_id", 1);
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "Environmental";
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.SimplePhysicsComponent);
}
}

View 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);
}
}

View File

@@ -1,26 +1,25 @@
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)
{
public override void Apply(Mod mod)
if (mod.Action != "add")
{
if (mod.Action != "add")
{
return;
}
mod.Default("SessionOnly", false);
mod.Default("OnlySetByServer", false);
mod.Default("SessionZoneOnly", false);
var flags = ModContext.Database["PlayerFlags"]!;
var row = flags.Create();
ModContext.ApplyValues(mod, row, flags);
ModContext.RegisterId(mod.Id, row.Key);
return;
}
mod.Default("SessionOnly", false);
mod.Default("OnlySetByServer", false);
mod.Default("SessionZoneOnly", false);
var flags = ModContext.Database["PlayerFlags"]!;
var row = flags.Create();
ModContext.ApplyValues(mod, row, flags);
ModContext.RegisterId(mod.Id, row.Key);
}
}

View File

@@ -1,32 +1,47 @@
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)
{
public override void Apply(Mod mod)
if (mod.Action != "add")
{
if (mod.Action != "add")
{
return;
}
throw new Exception("ItemMod can only be used with the add action");
}
mod.Default("nametag", false);
mod.Default("placeable", false);
mod.Default("localize", true);
mod.Default("locStatus", 2);
mod.Default("offsetGroupID", 78);
mod.Default("itemInfo", 0);
mod.Default("fade", true);
mod.Default("fadeInTime", 1);
mod.Default("shader_id", 23);
mod.Default("audioEquipMetaEventSet", "Weapon_Hammer_Generic");
mod.Default("nametag", false);
mod.Default("placeable", false);
mod.Default("localize", true);
mod.Default("locStatus", 2);
mod.Default("offsetGroupID", 78);
mod.Default("itemInfo", 0);
mod.Default("fade", true);
mod.Default("fadeInTime", 1);
mod.Default("shader_id", 23);
mod.Default("audioEquipMetaEventSet", "Weapon_Hammer_Generic");
if (mod.HasValue("icon"))
{
var iconId = ModContext.AddIcon(mod.GetValue<string>("icon"), out var path);
var obj = ObjectMod.CreateObject(mod);
mod.Values["icon_asset"] = path;
mod.Values["IconID"] = iconId;
}
obj["type"].Value = "Loot";
var obj = ObjectMod.CreateObject(mod, false);
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.ItemComponent);
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");
}
}
}

View 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);
}
}
}

View 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;
});
}
});
}
}
}

View 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;
}
});
}
}
}

View File

@@ -2,353 +2,339 @@ 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)
{
// Proud of this one :D
public static void ParsePrerequisites(StringBuilder builder, StringBuilder missionId, string str, int i, Action<string> done)
if (i >= str.Length)
{
if (i >= str.Length)
{
done(builder.ToString());
done(builder.ToString());
return;
}
return;
}
var c = str[i];
var c = str[i];
switch (c)
{
case '(':
break;
case ')':
break;
case '&' or ',' or ';':
break;
case '|':
break;
case ' ':
ParsePrerequisites(builder, missionId, str, i + 1, done);
return;
default:
missionId.Append(c);
ParsePrerequisites(builder, missionId, str, i + 1, done);
return;
}
if (missionId.Length == 0)
{
builder.Append(c);
switch (c)
{
case '(':
break;
case ')':
break;
case '&' or ',' or ';':
break;
case '|':
break;
case ' ':
ParsePrerequisites(builder, missionId, str, i + 1, done);
return;
}
var missionIdString = missionId.ToString();
if (string.IsNullOrEmpty(missionIdString))
{
builder.Append(c);
default:
missionId.Append(c);
ParsePrerequisites(builder, missionId, str, i + 1, done);
return;
}
}
missionIdString = missionIdString.Trim();
if (missionId.Length == 0)
{
builder.Append(c);
ParsePrerequisites(builder, missionId, str, i + 1, done);
return;
}
var missionIdString = missionId.ToString();
if (string.IsNullOrEmpty(missionIdString))
{
builder.Append(c);
ParsePrerequisites(builder, missionId, str, i + 1, done);
return;
}
missionIdString = missionIdString.Trim();
// If the string ends with ":2" state is true
var state = missionIdString.EndsWith(":2");
// If the string ends with ":2" state is true
var state = missionIdString.EndsWith(":2");
// Remove the ":2" from the string
if (state)
{
missionIdString = missionIdString.Substring(0, missionIdString.Length - 2);
}
// Get the mission ID
ModContext.AwaitId(missionIdString, id =>
{
builder.Append(id);
// Remove the ":2" from the string
if (state)
{
missionIdString = missionIdString.Substring(0, missionIdString.Length - 2);
builder.Append(":2");
}
missionId.Clear();
// Get the mission ID
ModContext.AwaitId(missionIdString, id =>
builder.Append(c);
ParsePrerequisites(builder, missionId, str, i + 1, done);
});
}
public override void Apply(Mod mod)
{
mod.Default("locStatus", 2);
mod.DefaultNull("UIPrereqID");
mod.Default("localize", true);
mod.Default("isMission", false);
mod.Default("isChoiceReward", false);
mod.DefaultNull("missionIconID");
if (mod.HasValue("repeatable") && mod.GetValue<bool>("repeatable"))
{
mod.Default("time_limit", 1300);
}
else
{
mod.DefaultNull("time_limit");
}
mod.Default("reward_item1", -1);
mod.Default("reward_item2", -1);
mod.Default("reward_item3", -1);
mod.Default("reward_item4", -1);
mod.Default("reward_item1_repeatable", -1);
mod.Default("reward_item2_repeatable", -1);
mod.Default("reward_item3_repeatable", -1);
mod.Default("reward_item4_repeatable", -1);
mod.Default("reward_emote", -1);
mod.Default("reward_emote2", -1);
mod.Default("reward_emote3", -1);
mod.Default("reward_emote4", -1);
mod.Default("reward_maxwallet", 0);
mod.Default("reward_reputation", 0);
mod.Default("reward_currency_repeatable", 0);
var missionsTable = ModContext.Database["Missions"];
var missionTasksTable = ModContext.Database["MissionTasks"];
var mission = missionsTable.FromLookup(mod);
foreach (var (locale, text) in mod.Locale!)
{
ModContext.AddToLocale($"Missions_{mission.Key}_name", text, locale);
}
ModContext.AddToLocale($"MissionText_{mission.Key}_accept_chat_bubble", "accept_chat_bubble", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_accept_chat_bubble", "chat_accept", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_1", "chat_state_1", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_2", "chat_state_2", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_3", "chat_state_3", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_4", "chat_state_4", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_1", "chat_available", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_2", "chat_active", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_3", "chat_ready_to_complete", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_4", "chat_complete", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_completion_succeed_tip", "completion_succeed_tip", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_in_progress", "in_progress", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_offer", "offer", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_ready_to_complete", "ready_to_complete", mod);
var missionTextTable = ModContext.Database["MissionText"];
var missionText = missionTextTable.FromLookup(mod)!;
missionText["localize"].Value = true;
missionText["locStatus"].Value = 2;
missionText["IconID"].Value = null;
if (mod.HasValue("icon-turn-in"))
{
var turnInIconAsset = ModContext.ParseValue(mod.GetValue<string>("icon-turn-in"), true);
var turnInIconId = ModContext.AddIcon(turnInIconAsset);
missionText["turnInIconID"].Value = turnInIconId;
}
else
{
missionText["turnInIconID"].Value = null;
}
ModContext.ApplyValues(mod, mission, missionsTable);
if (mod.HasValue("reward_item1"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item1"), id =>
{
builder.Append(id);
if (state)
{
builder.Append(":2");
}
missionId.Clear();
builder.Append(c);
ParsePrerequisites(builder, missionId, str, i + 1, done);
mission["reward_item1"].Value = id == 0 ? -1 : id;
});
}
public override void Apply(Mod mod)
if (mod.HasValue("reward_item2"))
{
mod.Default("locStatus", 2);
mod.DefaultNull("UIPrereqID");
mod.Default("localize", true);
mod.Default("isMission", false);
mod.Default("isChoiceReward", false);
mod.DefaultNull("missionIconID");
ModContext.AwaitId(mod.GetValue<string>("reward_item2"), id =>
{
mission["reward_item2"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_item3"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item3"), id =>
{
mission["reward_item3"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_item4"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item4"), id =>
{
mission["reward_item4"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_item1_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item1_repeatable"), id =>
{
mission["reward_item1_repeatable"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_item2_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item2_repeatable"), id =>
{
mission["reward_item2_repeatable"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_item3_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item3_repeatable"), id =>
{
mission["reward_item3_repeatable"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_item4_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item4_repeatable"), id =>
{
mission["reward_item4_repeatable"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_emote"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote"), id =>
{
mission["reward_emote"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_emote2"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote2"), id =>
{
mission["reward_emote2"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_emote3"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote3"), id =>
{
mission["reward_emote3"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("reward_emote4"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote4"), id =>
{
mission["reward_emote4"].Value = id == 0 ? -1 : id;
});
}
if (mod.HasValue("repeatable") && mod.GetValue<bool>("repeatable"))
{
mod.Default("time_limit", 1300);
}
else
{
mod.DefaultNull("time_limit");
}
mod.Default("reward_item1", -1);
mod.Default("reward_item2", -1);
mod.Default("reward_item3", -1);
mod.Default("reward_item4", -1);
mod.Default("reward_item1_repeatable", -1);
mod.Default("reward_item2_repeatable", -1);
mod.Default("reward_item3_repeatable", -1);
mod.Default("reward_item4_repeatable", -1);
mod.Default("reward_emote", -1);
mod.Default("reward_emote2", -1);
mod.Default("reward_emote3", -1);
mod.Default("reward_emote4", -1);
mod.Default("reward_maxwallet", 0);
mod.Default("reward_reputation", 0);
mod.Default("reward_currency_repeatable", 0);
if (mod.HasValue("prereqMissionID"))
{
var prereqMissionId = mod.GetValue<string>("prereqMissionID");
var missionsTable = ModContext.Database["Missions"];
var missionTasksTable = ModContext.Database["MissionTasks"];
var mission = missionsTable.FromLookup(mod);
foreach (var (locale, text) in mod.Locale!)
{
ModContext.AddToLocale($"Missions_{mission.Key}_name", text, locale);
}
ModContext.AddToLocale($"MissionText_{mission.Key}_accept_chat_bubble", "accept_chat_bubble", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_accept_chat_bubble", "chat_accept", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_1", "chat_state_1", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_2", "chat_state_2", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_3", "chat_state_3", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_4", "chat_state_4", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_1", "chat_available", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_2", "chat_active", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_3", "chat_ready_to_complete", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_chat_state_4", "chat_complete", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_completion_succeed_tip", "completion_succeed_tip", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_in_progress", "in_progress", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_offer", "offer", mod);
ModContext.AddToLocale($"MissionText_{mission.Key}_ready_to_complete", "ready_to_complete", mod);
var missionTextTable = ModContext.Database["MissionText"];
var missionText = missionTextTable.FromLookup(mod)!;
missionText["localize"].Value = true;
missionText["locStatus"].Value = 2;
missionText["IconID"].Value = null;
if (mod.HasValue("icon-turn-in"))
{
var turnInIconAsset = ModContext.ParseValue(mod.GetValue<string>("icon-turn-in"), true);
var turnInIconId = ModContext.AddIcon(turnInIconAsset);
missionText["turnInIconID"].Value = turnInIconId;
}
else
{
missionText["turnInIconID"].Value = null;
}
ModContext.ApplyValues(mod, mission, missionsTable);
if (mod.HasValue("reward_item1"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item1"), id =>
{
mission["reward_item1"].Value = id;
});
}
if (mod.HasValue("reward_item2"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item2"), id =>
{
mission["reward_item2"].Value = id;
});
}
if (mod.HasValue("reward_item3"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item3"), id =>
{
mission["reward_item3"].Value = id;
});
}
if (mod.HasValue("reward_item4"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item4"), id =>
{
mission["reward_item4"].Value = id;
});
}
if (mod.HasValue("reward_item1_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item1_repeatable"), id =>
{
mission["reward_item1_repeatable"].Value = id;
});
}
if (mod.HasValue("reward_item2_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item2_repeatable"), id =>
{
mission["reward_item2_repeatable"].Value = id;
});
}
if (mod.HasValue("reward_item3_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item3_repeatable"), id =>
{
mission["reward_item3_repeatable"].Value = id;
});
}
if (mod.HasValue("reward_item4_repeatable"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_item4_repeatable"), id =>
{
mission["reward_item4_repeatable"].Value = id;
});
}
if (mod.HasValue("reward_emote"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote"), id =>
{
mission["reward_emote"].Value = id;
});
}
if (mod.HasValue("reward_emote2"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote2"), id =>
{
mission["reward_emote2"].Value = id;
});
}
if (mod.HasValue("reward_emote3"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote3"), id =>
{
mission["reward_emote3"].Value = id;
});
}
if (mod.HasValue("reward_emote4"))
{
ModContext.AwaitId(mod.GetValue<string>("reward_emote4"), id =>
{
mission["reward_emote4"].Value = id;
});
}
if (mod.HasValue("prereqMissionID"))
{
var prereqMissionId = mod.GetValue<string>("prereqMissionID");
var builder = new StringBuilder();
var builder = new StringBuilder();
var missionId = new StringBuilder();
var missionId = new StringBuilder();
ParsePrerequisites(builder, missionId, prereqMissionId, 0, done =>
{
mission["prereqMissionID"].Value = done;
});
}
ParsePrerequisites(builder, missionId, prereqMissionId, 0, done =>
{
mission["prereqMissionID"].Value = done;
});
}
ModContext.RegisterId(mod.Id, mission.Key);
ModContext.RegisterId(mod.Id, mission.Key);
if (mod.HasValue("icon") && !ModContext.ShouldBeNull(mod.GetValue<string>("icon")))
{
var missionIconId = ModContext.AddIcon(mod.GetValue<string>("icon"));
mission["missionIconID"].Value = missionIconId;
}
else if (mod.HasValue("missionIconID") && !ModContext.ShouldBeNull(mod.GetValue<string>("missionIconID")))
{
var missionIconId = ModContext.AddIcon(mod.GetValue<string>("missionIconID"));
mission["missionIconID"].Value = missionIconId;
}
else
{
mission["missionIconID"].Value = null;
}
if (mod.HasValue("icon") && !ModContext.ShouldBeNull(mod.GetValue<string>("icon")))
{
var missionIconId = ModContext.AddIcon(mod.GetValue<string>("icon"));
mission["missionIconID"].Value = missionIconId;
}
else if (mod.HasValue("missionIconID") && !ModContext.ShouldBeNull(mod.GetValue<string>("missionIconID")))
{
var missionIconId = ModContext.AddIcon(mod.GetValue<string>("missionIconID"));
mission["missionIconID"].Value = missionIconId;
}
else
{
mission["missionIconID"].Value = null;
}
var uid = (int) missionTasksTable.Max(t => t["uid"].Value)!;
var uid = (int) missionTasksTable.Max(t => t["uid"].Value)!;
foreach (var task in missionTasksTable.SeekMultiple(mission.Key))
{
missionTasksTable.Remove(task);
}
foreach (var task in missionTasksTable.SeekMultiple(mission.Key))
{
missionTasksTable.Remove(task);
}
foreach (var task in mod.Tasks!)
foreach (var task in mod.Tasks!)
{
var missionTask = missionTasksTable.Create(mission.Key)!;
missionTask["locStatus"].Value = 2;
missionTask["localize"].Value = true;
missionTask["taskType"].Value = (int) ModContext.ParseEnum<MissionTaskType>(task.Type);
missionTask["largeTaskIcon"].Value = null;
missionTask["targetValue"].Value = task.Count;
missionTask["uid"].Value = ++uid;
var iconAsset = ModContext.ParseValue(task.Icon, true);
var iconId = ModContext.AddIcon(iconAsset);
missionTask["largeTaskIconID"].Value = iconId;
var smallIcon = string.IsNullOrWhiteSpace(task.SmallIcon) ? task.Icon : task.SmallIcon;
var smallIconAsset = ModContext.ParseValue(smallIcon, true);
var smallIconId = ModContext.AddIcon(smallIconAsset);
missionTask["IconID"].Value = smallIconId;
foreach (var (locale, text) in task.Locale!)
{
var missionTask = missionTasksTable.Create(mission.Key)!;
ModContext.AddToLocale($"MissionTasks_{missionTask["uid"].Value}_description", text, locale);
}
ModContext.AwaitId(task.Target, value => missionTask["target"].Value = value);
missionTask["locStatus"].Value = 2;
missionTask["localize"].Value = true;
missionTask["taskType"].Value = (int) ModContext.ParseEnum<MissionTaskType>(task.Type);
missionTask["largeTaskIcon"].Value = null;
missionTask["targetValue"].Value = task.Count;
missionTask["uid"].Value = ++uid;
var iconAsset = ModContext.ParseValue(task.Icon, true);
var iconId = ModContext.AddIcon(iconAsset);
missionTask["largeTaskIconID"].Value = iconId;
var smallIcon = string.IsNullOrWhiteSpace(task.SmallIcon) ? task.Icon : task.SmallIcon;
var smallIconAsset = ModContext.ParseValue(smallIcon, true);
var smallIconId = ModContext.AddIcon(smallIconAsset);
missionTask["IconID"].Value = smallIconId;
foreach (var (locale, text) in task.Locale!)
foreach (var groupTarget in task.Group)
{
var value = groupTarget!.AsValue();
if (ModContext.IsId(value))
{
ModContext.AddToLocale($"MissionTasks_{missionTask["uid"].Value}_description", text, locale);
}
ModContext.AwaitId(task.Target, value => missionTask["target"].Value = value);
foreach (var groupTarget in task.Group)
{
var value = groupTarget!.AsValue();
if (ModContext.IsId(value))
{
ModContext.AwaitId(groupTarget!.AsValue(), value =>
{
var current = missionTask["targetGroup"].Value?.ToString() ?? "";
if (!string.IsNullOrWhiteSpace(current))
{
current += ",";
}
current += value.ToString();
missionTask["targetGroup"].Value = current;
});
}
else
ModContext.AwaitId(groupTarget!.AsValue(), value =>
{
var current = missionTask["targetGroup"].Value?.ToString() ?? "";
@@ -357,35 +343,35 @@ namespace InfectedRose.Interface.Templates
current += ",";
}
current += groupTarget!.AsValue().ToString();
current += value.ToString();
missionTask["targetGroup"].Value = current;
}
});
}
foreach (var parameter in task.Parameters)
else
{
// taskParam1
var value = parameter!.AsValue();
if (ModContext.IsId(value))
var current = missionTask["targetGroup"].Value?.ToString() ?? "";
if (!string.IsNullOrWhiteSpace(current))
{
ModContext.AwaitId(parameter!.AsValue(), value =>
{
var current = missionTask["taskParam1"].Value?.ToString() ?? "";
if (!string.IsNullOrWhiteSpace(current))
{
current += ",";
}
current += value.ToString();
missionTask["taskParam1"].Value = current;
});
current += ",";
}
else
current += groupTarget!.AsValue().ToString();
missionTask["targetGroup"].Value = current;
}
}
foreach (var parameter in task.Parameters)
{
// taskParam1
var value = parameter!.AsValue();
if (ModContext.IsId(value))
{
ModContext.AwaitId(parameter!.AsValue(), value =>
{
var current = missionTask["taskParam1"].Value?.ToString() ?? "";
@@ -394,10 +380,23 @@ namespace InfectedRose.Interface.Templates
current += ",";
}
current += parameter!.AsValue().ToString();
current += value.ToString();
missionTask["taskParam1"].Value = current;
});
}
else
{
var current = missionTask["taskParam1"].Value?.ToString() ?? "";
if (!string.IsNullOrWhiteSpace(current))
{
current += ",";
}
current += parameter!.AsValue().ToString();
missionTask["taskParam1"].Value = current;
}
}
}

View File

@@ -2,38 +2,37 @@ 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; }
[JsonPropertyName("target")]
public JsonValue Target { get; set; }
[JsonPropertyName("count")]
public int Count { get; set; }
[JsonPropertyName("group")]
public JsonArray Group { get; set; }
[JsonPropertyName("location")]
public string Location { get; set; }
[JsonPropertyName("parameters")]
public JsonArray Parameters { get; set; }
[JsonPropertyName("icon")]
public string Icon { get; set; }
[JsonPropertyName("small-icon")]
public string SmallIcon { get; set; }
[JsonPropertyName("locale")]
public Dictionary<string, string>? Locale { get; set; } = new Dictionary<string, string>
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("target")]
public JsonValue Target { get; set; }
[JsonPropertyName("count")]
public int Count { get; set; }
[JsonPropertyName("group")]
public JsonArray Group { get; set; }
[JsonPropertyName("location")]
public string Location { get; set; }
[JsonPropertyName("parameters")]
public JsonArray Parameters { get; set; }
[JsonPropertyName("icon")]
public string Icon { get; set; }
[JsonPropertyName("small-icon")]
public string SmallIcon { get; set; }
[JsonPropertyName("locale")]
public Dictionary<string, string>? Locale { get; set; } = new Dictionary<string, string>
{
{"en_US", ""}
};
}
{"en_US", ""}
};
}

View File

@@ -1,17 +1,16 @@
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; }
[JsonPropertyName("mission")]
public JsonValue Mission { get; set; }
[JsonPropertyName("accept")]
public bool Accept { get; set; }
[JsonPropertyName("accept")]
public bool Accept { get; set; }
[JsonPropertyName("offer")]
public bool Offer { get; set; }
}
[JsonPropertyName("offer")]
public bool Offer { get; set; }
}

View File

@@ -1,24 +1,23 @@
namespace InfectedRose.Interface.Templates
namespace InfectedRose.Interface.Templates;
public enum MissionTaskType
{
public enum MissionTaskType
{
Smash = 0,
Script = 1,
Activity = 2,
Environment = 3,
MissionInteraction = 4,
Emote = 5,
Food = 9,
Skill = 10,
ItemCollection = 11,
Location = 12,
Minigame = 14,
NonMissionInteraction = 15,
MissionComplete = 16,
Powerup = 21,
PetTaming = 22,
Racing = 23,
PlayerFlag = 24,
VisitProperty = 30
}
Smash = 0,
Script = 1,
Activity = 2,
Environment = 3,
MissionInteraction = 4,
Emote = 5,
Food = 9,
Skill = 10,
ItemCollection = 11,
Location = 12,
Minigame = 14,
NonMissionInteraction = 15,
MissionComplete = 16,
Powerup = 21,
PetTaming = 22,
Racing = 23,
PlayerFlag = 24,
VisitProperty = 30
}

View File

@@ -1,56 +1,55 @@
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)
{
public override void Apply(Mod mod)
if (mod.Action != "add")
{
if (mod.Action != "add")
{
return;
}
mod.Default("render_asset", @"animations\\minifig\\mf_ambient.kfm");
mod.Default("animationGroupIDs", "93");
mod.Default("shader_id", 14);
mod.Default("static", 1);
mod.Default("jump", 0);
mod.Default("doublejump", 0);
mod.Default("speed", 5);
mod.Default("rotSpeed", 360);
mod.Default("playerHeight", 4.4f);
mod.Default("playerRadius", 1);
mod.Default("pcShapeType", 2);
mod.Default("collisionGroup", 3);
mod.Default("airSpeed", 5);
mod.Default("jumpAirSpeed", 25);
mod.DefaultNull("interactionDistance");
mod.DefaultNull("chatBubbleOffset");
mod.Default("fade", true);
mod.Default("fadeInTime", 1);
mod.DefaultNull("billboardHeight");
mod.Default("AudioMetaEventSet", "Emotes_Non_Player");
mod.Default("usedropshadow", false);
mod.Default("preloadAnimations", false);
mod.Default("ignoreCameraCollision", false);
mod.Default("gradualSnap", false);
mod.Default("staticBillboard", false);
mod.Default("attachIndicatorsToNode", false);
mod.Default("npcTemplateID", 14);
mod.Default("nametag", true);
mod.Default("placeable", true);
mod.Default("localize", true);
mod.Default("locStatus", 2);
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "UserGeneratedNPCs";
ObjectMod.AddComponent(mod, obj, ComponentId.SimplePhysicsComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.MinifigComponent);
return;
}
mod.Default("render_asset", @"animations\\minifig\\mf_ambient.kfm");
mod.Default("animationGroupIDs", "93");
mod.Default("shader_id", 14);
mod.Default("static", 1);
mod.Default("jump", 0);
mod.Default("doublejump", 0);
mod.Default("speed", 5);
mod.Default("rotSpeed", 360);
mod.Default("playerHeight", 4.4f);
mod.Default("playerRadius", 1);
mod.Default("pcShapeType", 2);
mod.Default("collisionGroup", 3);
mod.Default("airSpeed", 5);
mod.Default("jumpAirSpeed", 25);
mod.DefaultNull("interactionDistance");
mod.DefaultNull("chatBubbleOffset");
mod.Default("fade", true);
mod.Default("fadeInTime", 1);
mod.DefaultNull("billboardHeight");
mod.Default("AudioMetaEventSet", "Emotes_Non_Player");
mod.Default("usedropshadow", false);
mod.Default("preloadAnimations", false);
mod.Default("ignoreCameraCollision", false);
mod.Default("gradualSnap", false);
mod.Default("staticBillboard", false);
mod.Default("attachIndicatorsToNode", false);
mod.Default("npcTemplateID", 14);
mod.Default("nametag", true);
mod.Default("placeable", true);
mod.Default("localize", true);
mod.Default("locStatus", 2);
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "UserGeneratedNPCs";
ObjectMod.AddComponent(mod, obj, ComponentId.SimplePhysicsComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.MinifigComponent);
}
}

View File

@@ -1,312 +1,353 @@
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, bool checkIcon = true)
{
public static Row CreateObject(Mod mod)
var table = ModContext.Database["Objects"];
var row = table.FromLookup(mod);
row["name"].Value = mod.Id;
row["description"].Value = mod.Id;
ModContext.ApplyValues(mod, row, table);
ModContext.RegisterId(mod.Id, row.Key);
if (!mod.HasValue("interactionDistance") || mod.GetValue<float>("interactionDistance") == 0.0f)
{
var table = ModContext.Database["Objects"];
var row = table.FromLookup(mod);
row["name"].Value = mod.Id;
row["description"].Value = mod.Id;
ModContext.ApplyValues(mod, row, table);
ModContext.RegisterId(mod.Id, row.Key);
if (mod.Components != null)
{
foreach (var component in mod.Components)
{
AwaitComponent(row, component);
}
}
if (mod.Locale != null)
{
foreach (var (locale, text) in mod.Locale)
{
ModContext.AddToLocale($"Objects_{row.Key}_name", text, locale);
}
}
if (mod.Skills != null && mod.Skills.Length > 0)
{
AddComponent(mod, row, ComponentId.SkillComponent);
var objectSkillsTable = ModContext.Database["ObjectSkills"];
foreach (var skill in mod.Skills)
{
var objectSkill = objectSkillsTable.Create(row.Key);
objectSkill["castOnType"].Value = skill.CastOnType;
objectSkill["AICombatWeight"].Value = skill.CombatAiWeight;
if (skill.SkillId.TryGetValue(out int? value))
{
objectSkill["skillID"].Value = value;
}
else if (skill.SkillId.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => objectSkill["skillID"].Value = lot);
}
}
}
if (mod.Items != null && mod.Items.Length > 0)
{
var id = 0;
foreach (var item in mod.Items)
{
var itemComponent = ObjectMod.AddComponent(mod, row, ComponentId.InventoryComponent, id)!;
itemComponent["count"].Value = 1;
itemComponent["equip"].Value = true;
id = itemComponent.Key;
if (item.TryGetValue(out int? value))
{
itemComponent["itemid"].Value = value;
}
else if (item.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => itemComponent["itemid"].Value = lot);
}
}
}
if (mod.MissionOffers != null)
{
var id = 0;
foreach (var missionOffer in mod.MissionOffers)
{
var missionNpcComponent = ObjectMod.AddComponent(mod, row, ComponentId.MissionNPCComponent, id)!;
id = missionNpcComponent.Key;
missionNpcComponent["offersMission"].Value = missionOffer.Offer;
missionNpcComponent["acceptsMission"].Value = missionOffer.Accept;
ModContext.AwaitId(missionOffer.Mission, value => missionNpcComponent["missionID"].Value = value);
}
var mapIconTable = ModContext.Database["mapIcon"];
var mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 1;
mapRow["iconState"].Value = 1;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 3;
mapRow["iconState"].Value = 2;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 4;
mapRow["iconState"].Value = 4;
}
return row;
row["interactionDistance"].Value = 10.0f;
}
public static Row EditObject(Mod mod)
if (checkIcon && mod.HasValue("icon"))
{
var table = ModContext.Database["Objects"];
var row = table.FromLookup(mod);
row["name"].Value = mod.Id;
row["description"].Value = mod.Id;
var iconId = ModContext.AddIcon(mod.GetValue<string>("icon"), out var path);
ModContext.ApplyValues(mod, row, table);
ModContext.RegisterId(mod.Id, row.Key);
var existingComponents = ModContext.Database["ComponentsRegistry"].SeekMultiple(row.Key).ToArray();
if (mod.Components != null)
{
foreach (var component in mod.Components)
{
AwaitComponentWithExistingRows(existingComponents, component);
}
}
if (mod.Locale != null)
{
foreach (var (locale, text) in mod.Locale)
{
ModContext.AddToLocale($"Objects_{row.Key}_name", text, locale);
}
}
if (mod.Skills != null && mod.Skills.Length > 0)
{
var objectSkillsTable = ModContext.Database["ObjectSkills"];
foreach (var objectSkill in objectSkillsTable.SeekMultiple(row.Key))
{
objectSkillsTable.Remove(objectSkill);
}
AddComponent(mod, row, ComponentId.SkillComponent);
foreach (var skill in mod.Skills)
{
var objectSkill = objectSkillsTable.Create(row.Key);
objectSkill["castOnType"].Value = skill.CastOnType;
objectSkill["AICombatWeight"].Value = skill.CombatAiWeight;
if (skill.SkillId.TryGetValue(out int? value))
{
objectSkill["skillID"].Value = value;
}
else if (skill.SkillId.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => objectSkill["skillID"].Value = lot);
}
}
}
if (mod.Items != null && mod.Items.Length > 0)
{
var id = 0;
foreach (var item in mod.Items)
{
var itemComponent = ObjectMod.AddComponent(mod, row, ComponentId.InventoryComponent, id)!;
itemComponent["count"].Value = 1;
itemComponent["equip"].Value = true;
id = itemComponent.Key;
if (item.TryGetValue(out int? value))
{
itemComponent["itemid"].Value = value;
}
else if (item.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => itemComponent["itemid"].Value = lot);
}
}
}
if (mod.MissionOffers != null)
{
var id = 0;
foreach (var missionOffer in mod.MissionOffers)
{
var missionNpcComponent = ObjectMod.AddComponent(mod, row, ComponentId.MissionNPCComponent, id)!;
id = missionNpcComponent.Key;
missionNpcComponent["offersMission"].Value = missionOffer.Offer;
missionNpcComponent["acceptsMission"].Value = missionOffer.Accept;
ModContext.AwaitId(missionOffer.Mission, value => missionNpcComponent["missionID"].Value = value);
}
var mapIconTable = ModContext.Database["mapIcon"];
var mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 1;
mapRow["iconState"].Value = 1;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 3;
mapRow["iconState"].Value = 2;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 4;
mapRow["iconState"].Value = 4;
}
return row;
mod.Values["icon_asset"] = path;
mod.Values["IconID"] = iconId;
}
public static void AwaitComponent(Row obj, string component)
if (mod.Components != null)
{
foreach (var component in mod.Components)
{
AwaitComponent(row, component);
}
}
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)
{
ModContext.AddToLocale($"Objects_{row.Key}_name", text, locale);
}
}
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);
var objectSkillsTable = ModContext.Database["ObjectSkills"];
foreach (var skill in mod.Skills)
{
var objectSkill = objectSkillsTable.Create(row.Key);
objectSkill["castOnType"].Value = skill.CastOnType;
objectSkill["AICombatWeight"].Value = skill.CombatAiWeight;
if (skill.SkillId.TryGetValue(out int? value))
{
objectSkill["skillID"].Value = value;
}
else if (skill.SkillId.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => objectSkill["skillID"].Value = lot);
}
}
}
if (mod.Items != null && mod.Items.Length > 0)
{
var id = 0;
foreach (var item in mod.Items)
{
var itemComponent = ObjectMod.AddComponent(mod, row, ComponentId.InventoryComponent, id)!;
itemComponent["count"].Value = 1;
itemComponent["equip"].Value = true;
id = itemComponent.Key;
if (item.TryGetValue(out int? value))
{
itemComponent["itemid"].Value = value;
}
else if (item.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => itemComponent["itemid"].Value = lot);
}
}
}
if (mod.MissionOffers != null)
{
var id = 0;
foreach (var missionOffer in mod.MissionOffers)
{
var missionNpcComponent = ObjectMod.AddComponent(mod, row, ComponentId.MissionNPCComponent, id)!;
id = missionNpcComponent.Key;
missionNpcComponent["offersMission"].Value = missionOffer.Offer;
missionNpcComponent["acceptsMission"].Value = missionOffer.Accept;
ModContext.AwaitId(missionOffer.Mission, value => missionNpcComponent["missionID"].Value = value);
}
var mapIconTable = ModContext.Database["mapIcon"];
var mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 1;
mapRow["iconState"].Value = 1;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 3;
mapRow["iconState"].Value = 2;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 4;
mapRow["iconState"].Value = 4;
}
return row;
}
public static Row EditObject(Mod mod)
{
var table = ModContext.Database["Objects"];
var row = table.FromLookup(mod);
row["name"].Value = mod.Id;
row["description"].Value = mod.Id;
ModContext.ApplyValues(mod, row, table);
ModContext.RegisterId(mod.Id, row.Key);
var existingComponents = ModContext.Database["ComponentsRegistry"].SeekMultiple(row.Key).ToArray();
if (mod.Components != null)
{
foreach (var component in mod.Components)
{
AwaitComponentWithExistingRows(existingComponents, component);
}
}
if (mod.Locale != null)
{
foreach (var (locale, text) in mod.Locale)
{
ModContext.AddToLocale($"Objects_{row.Key}_name", text, locale);
}
}
if (mod.Skills != null && mod.Skills.Length > 0)
{
var objectSkillsTable = ModContext.Database["ObjectSkills"];
foreach (var objectSkill in objectSkillsTable.SeekMultiple(row.Key))
{
objectSkillsTable.Remove(objectSkill);
}
AddComponent(mod, row, ComponentId.SkillComponent);
foreach (var skill in mod.Skills)
{
var objectSkill = objectSkillsTable.Create(row.Key);
objectSkill["castOnType"].Value = skill.CastOnType;
objectSkill["AICombatWeight"].Value = skill.CombatAiWeight;
if (skill.SkillId.TryGetValue(out int? value))
{
objectSkill["skillID"].Value = value;
}
else if (skill.SkillId.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => objectSkill["skillID"].Value = lot);
}
}
}
if (mod.Items != null && mod.Items.Length > 0)
{
var id = 0;
foreach (var item in mod.Items)
{
var itemComponent = ObjectMod.AddComponent(mod, row, ComponentId.InventoryComponent, id)!;
itemComponent["count"].Value = 1;
itemComponent["equip"].Value = true;
id = itemComponent.Key;
if (item.TryGetValue(out int? value))
{
itemComponent["itemid"].Value = value;
}
else if (item.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => itemComponent["itemid"].Value = lot);
}
}
}
if (mod.MissionOffers != null)
{
var id = 0;
foreach (var missionOffer in mod.MissionOffers)
{
var missionNpcComponent = ObjectMod.AddComponent(mod, row, ComponentId.MissionNPCComponent, id)!;
id = missionNpcComponent.Key;
missionNpcComponent["offersMission"].Value = missionOffer.Offer;
missionNpcComponent["acceptsMission"].Value = missionOffer.Accept;
ModContext.AwaitId(missionOffer.Mission, value => missionNpcComponent["missionID"].Value = value);
}
var mapIconTable = ModContext.Database["mapIcon"];
var mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 1;
mapRow["iconState"].Value = 1;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 3;
mapRow["iconState"].Value = 2;
mapRow = mapIconTable.Create(row.Key);
mapRow["iconID"].Value = 4;
mapRow["iconState"].Value = 4;
}
return row;
}
public static void AwaitComponent(Row obj, string component)
{
var row = ModContext.Database["ComponentsRegistry"].Create(obj.Key);
ModContext.AwaitId(component, id =>
{
row["component_id"].Value = id;
row["component_type"].Value = ModContext.GetMod(component).GetComponentType();
}, true);
}
public static void AwaitComponentWithExistingRows(Row[] rows, string component)
{
ModContext.AwaitId(component, id =>
{
var componentType = ModContext.GetMod(component).GetComponentType();
var row = rows.FirstOrDefault(row => (int) row["component_type"].Value == componentType);
if (row == null)
{
row = ModContext.Database["ComponentsRegistry"].Create(rows[0].Key);
}
row["component_id"].Value = id;
row["component_type"].Value = ModContext.GetMod(component).GetComponentType();
}, true, true);
}
public static Row? AddComponent(Mod mod, Row obj, ComponentId componentId, int id = 0)
{
var table = ModContext.GetComponentTable(componentId);
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);
ModContext.AwaitId(component, id =>
row["component_type"].Value = (int) componentId;
if (table == null)
{
row["component_id"].Value = id;
row["component_type"].Value = ModContext.GetMod(component).GetComponentType();
}, true);
}
public static void AwaitComponentWithExistingRows(Row[] rows, string component)
{
ModContext.AwaitId(component, id =>
{
var componentType = ModContext.GetMod(component).GetComponentType();
row["component_id"].Value = 0;
var row = rows.FirstOrDefault(row => (int) row["component_type"].Value == componentType);
if (row == null)
{
row = ModContext.Database["ComponentsRegistry"].Create(rows[0].Key);
}
row["component_id"].Value = id;
row["component_type"].Value = ModContext.GetMod(component).GetComponentType();
}, true, true);
}
public static Row? AddComponent(Mod mod, Row obj, ComponentId componentId, int id = 0)
{
var table = ModContext.GetComponentTable(componentId);
Row component;
if (id == 0)
{
var row = ModContext.Database["ComponentsRegistry"].Create(obj.Key);
row["component_type"].Value = (int) componentId;
if (table == null)
{
row["component_id"].Value = 0;
return null;
}
component = table.Create();
row["component_id"].Value = component.Key;
ModContext.ApplyValues(mod, component, table);
ModContext.RegisterId(mod.Id + ":" + componentId, component.Key);
return component;
return null;
}
if (table == null) return null;
component = table.Create(id);
component = table.Create();
row["component_id"].Value = component.Key;
ModContext.ApplyValues(mod, component, table);
ModContext.RegisterId(mod.Id + ":" + componentId, component.Key);
return component;
}
if (table == null) return null;
component = table.Create(id);
ModContext.ApplyValues(mod, component, table);
return component;
}
public override void Apply(Mod mod)
public override void Apply(Mod mod)
{
if (mod.Action == "add")
{
if (mod.Action == "add")
{
CreateObject(mod);
}
else if (mod.Action == "edit")
{
EditObject(mod);
}
CreateObject(mod);
}
else if (mod.Action == "edit")
{
EditObject(mod);
}
}
}

View File

@@ -1,17 +1,16 @@
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; }
[JsonPropertyName("skill-id")]
public JsonValue SkillId { get; set; }
[JsonPropertyName("cast-on-type")]
public int CastOnType { get; set; }
[JsonPropertyName("cast-on-type")]
public int CastOnType { get; set; }
[JsonPropertyName("combat-ai-weight")]
public int CombatAiWeight { get; set; }
}
[JsonPropertyName("combat-ai-weight")]
public int CombatAiWeight { get; set; }
}

View File

@@ -1,79 +1,78 @@
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)
{
public override void Apply(Mod mod)
if (mod.Action != "add")
{
if (mod.Action != "add")
{
return;
}
return;
}
mod.Default("render_asset", @"mesh\\re\\re_won_tesla_1-whole.nif");
mod.Default("animationGroupIDs", "93");
mod.Default("shader_id", 1);
mod.Default("render_asset", @"mesh\\re\\re_won_tesla_1-whole.nif");
mod.Default("animationGroupIDs", "93");
mod.Default("shader_id", 1);
mod.Default("static", 1);
mod.Default("jump", 0);
mod.Default("doublejump", 0);
mod.Default("speed", 5);
mod.Default("rotSpeed", 360);
mod.Default("playerHeight", 4.4f);
mod.Default("playerRadius", 1);
mod.Default("pcShapeType", 2);
mod.Default("collisionGroup", 3);
mod.Default("airSpeed", 5);
mod.Default("jumpAirSpeed", 25);
mod.DefaultNull("interactionDistance");
mod.Default("physics_asset", @"re\\re_won_tesla_1-whole.hkx");
mod.Default("static", 1);
mod.Default("jump", 0);
mod.Default("doublejump", 0);
mod.Default("speed", 5);
mod.Default("rotSpeed", 360);
mod.Default("playerHeight", 4.4f);
mod.Default("playerRadius", 1);
mod.Default("pcShapeType", 2);
mod.Default("collisionGroup", 3);
mod.Default("airSpeed", 5);
mod.Default("jumpAirSpeed", 25);
mod.DefaultNull("interactionDistance");
mod.Default("physics_asset", @"re\\re_won_tesla_1-whole.hkx");
mod.DefaultNull("chatBubbleOffset");
mod.Default("fade", true);
mod.Default("fadeInTime", 1);
mod.DefaultNull("billboardHeight");
mod.Default("usedropshadow", false);
mod.Default("preloadAnimations", false);
mod.Default("ignoreCameraCollision", false);
mod.Default("gradualSnap", false);
mod.Default("staticBillboard", false);
mod.Default("attachIndicatorsToNode", false);
mod.DefaultNull("chatBubbleOffset");
mod.Default("fade", true);
mod.Default("fadeInTime", 1);
mod.DefaultNull("billboardHeight");
mod.Default("usedropshadow", false);
mod.Default("preloadAnimations", false);
mod.Default("ignoreCameraCollision", false);
mod.Default("gradualSnap", false);
mod.Default("staticBillboard", false);
mod.Default("attachIndicatorsToNode", false);
mod.Default("npcTemplateID", 14);
mod.Default("nametag", false);
mod.Default("placeable", true);
mod.Default("localize", true);
mod.Default("locStatus", 2);
mod.Default("npcTemplateID", 14);
mod.Default("nametag", false);
mod.Default("placeable", true);
mod.Default("localize", true);
mod.Default("locStatus", 2);
mod.Default("reset_time", 20);
mod.Default("complete_time", 3);
mod.Default("take_imagination", 6);
mod.Default("interruptible", true);
mod.Default("self_activator", false);
mod.Default("activityID", 6209);
mod.Default("time_before_smash", 10);
mod.Default("reset_time", 20);
mod.Default("complete_time", 3);
mod.Default("take_imagination", 6);
mod.Default("interruptible", true);
mod.Default("self_activator", false);
mod.Default("activityID", 6209);
mod.Default("time_before_smash", 10);
mod.Default("level", 1);
mod.Default("life", 1);
mod.Default("isnpc", false);
mod.Default("isSmashable", false);
mod.Default("death_behavior", 2);
mod.Default("faction", 16);
mod.Default("factionList", "16");
mod.Default("level", 1);
mod.Default("life", 1);
mod.Default("isnpc", false);
mod.Default("isSmashable", false);
mod.Default("death_behavior", 2);
mod.Default("faction", 16);
mod.Default("factionList", "16");
var obj = ObjectMod.CreateObject(mod);
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "Rebuildables";
obj["type"].Value = "Rebuildables";
ObjectMod.AddComponent(mod, obj, ComponentId.SimplePhysicsComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.DestructibleComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.QuickBuildComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.SimplePhysicsComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.DestructibleComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.QuickBuildComponent);
if (mod.HasValue("lxfml"))
{
ModContext.ParseValue(mod.GetValue<string>("lxfml"));
}
if (mod.HasValue("lxfml"))
{
ModContext.ParseValue(mod.GetValue<string>("lxfml"));
}
}
}

View 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;
});
}
});
}
}
}

View File

@@ -1,35 +1,51 @@
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)
{
public override void Apply(Mod mod)
if (mod.Action != "add")
{
if (mod.Action != "add")
{
return;
}
return;
}
var skillBehaviorTable = ModContext.Database["SkillBehavior"]!;
var skillBehaviorTable = ModContext.Database["SkillBehavior"]!;
var skillBehavior = skillBehaviorTable.FromLookup(mod);
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)
{
behavior.Apply(key);
}
if (mod.HasValue("icon"))
{
var iconId = ModContext.AddIcon(mod.GetValue<string>("icon"), out var path);
ModContext.RegisterId(mod.Id, skillBehavior.Key);
mod.Values["skillIcon"] = iconId;
}
foreach (var (key, behavior) in mod.Behaviors)
{
behavior.Apply(key);
}
ModContext.ApplyValues(mod, skillBehavior, skillBehaviorTable);
ModContext.ApplyValues(mod, skillBehavior, skillBehaviorTable);
if (mod.HasValue("root-behavior"))
if (mod.HasValue("icon"))
{
mod.Values.Remove("skillIcon");
}
if (mod.HasValue("root-behavior"))
{
ModContext.AwaitId(mod.GetValue<string>("root-behavior"), i =>
{
ModContext.AwaitId(mod.GetValue<string>("root-behavior"), i =>
{
skillBehavior["behaviorID"].Value = i;
});
}
skillBehavior["behaviorID"].Value = i;
});
}
}
}

View File

@@ -1,19 +1,18 @@
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)
{
public override void Apply(Mod mod)
switch (mod.Action)
{
switch (mod.Action)
{
case "run":
ModContext.GeneralSql.Add(ModContext.ParseValue(mod.GetValue<string>("sql")));
break;
case "run-server":
ModContext.ServerSql.Add(ModContext.ParseValue(mod.GetValue<string>("sql")));
break;
}
case "run":
ModContext.GeneralSql.Add(ModContext.ParseValue(mod.GetValue<string>("sql")));
break;
case "run-server":
ModContext.ServerSql.Add(ModContext.ParseValue(mod.GetValue<string>("sql")));
break;
}
}
}

View File

@@ -1,17 +1,16 @@
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; }
[JsonPropertyName("r")]
public float R { get; set; }
[JsonPropertyName("g")]
public float G { get; set; }
[JsonPropertyName("g")]
public float G { get; set; }
[JsonPropertyName("b")]
public float B { get; set; }
}
[JsonPropertyName("b")]
public float B { get; set; }
}

View File

@@ -1,9 +1,8 @@
using System.Collections.Generic;
namespace InfectedRose.Interface.Templates.ValueTypes
namespace InfectedRose.Interface.Templates.ValueTypes;
public class DataDictionary : Dictionary<string, DataValue>
{
public class DataDictionary : Dictionary<string, DataValue>
{
}
}

View File

@@ -1,81 +1,188 @@
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("value")]
public JsonValue Value { get; set; }
[JsonPropertyName("type")]
public string? TypeInternal { get; set; }
[JsonIgnore]
public FieldType Type
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("value")]
public object Value { get; set; }
public static int ParseTypeString(string type)
get
{
return type switch
if (TypeInternal == null)
{
"int" => 1,
"float" => 3,
"double" => 4,
"uint" => 5,
"bool" => 7,
"long" => 8,
"blob" => 13,
"string" => 0,
"id" => 100,
_ => 0
};
}
public static string ParseType(int type)
{
return type switch
{
1 => "int",
3 => "float",
4 => "double",
5 => "uint",
7 => "bool",
8 => "long",
13 => "blob",
0 => "string",
100 => "id",
_ => "string"
};
}
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;
}
[JsonIgnore]
public int TypeId
{
get => ParseTypeString(Type);
set => Type = ParseType(value);
}
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;
return FieldType.String;
}
if (value != null)
{
value = value.ToString();
if (value == "True") value = "1";
else if (value == "False") value = "0";
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
{
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}")
};
}
public static string ParseType(int type)
{
return type switch
{
1 => "int",
3 => "float",
4 => "double",
5 => "uint",
7 => "bool",
8 => "long",
13 => "blob",
0 => "string",
100 => "id",
_ => "string"
};
}
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
{
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();
}
return $"{type}:{value}";
}
}
public override string ToString()
{
return $"{TypeId}:{Value}";
}
}

View 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,
}

View File

@@ -1,29 +1,28 @@
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("id")]
public JsonValue Id { get; set; }
[JsonPropertyName("type")]
public int? Type { get; set; }
[JsonPropertyName("type")]
public int Type { get; set; }
[JsonPropertyName("object-id")]
public ulong? ObjectId { get; set; }
[JsonPropertyName("object-id")]
public ulong? ObjectId { get; set; }
[JsonPropertyName("position")]
public Point3 Position { get; set; }
[JsonPropertyName("position")]
public Point3 Position { get; set; }
[JsonPropertyName("rotation")]
public Point4 Rotation { get; set; }
[JsonPropertyName("rotation")]
public Point4 Rotation { get; set; }
[JsonPropertyName("scale")]
public float? Scale { get; set; }
[JsonPropertyName("scale")]
public float Scale { get; set; }
[JsonPropertyName("data")]
public DataDictionary Data { get; set; }
}
[JsonPropertyName("data")]
public DataDictionary Data { get; set; }
}

View File

@@ -1,16 +1,15 @@
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; }
[JsonPropertyName("position")]
public Point3 Position { get; set; }
[JsonPropertyName("rotation")]
public Point4 Rotation { get; set; }
[JsonPropertyName("rotation")]
public Point4 Rotation { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
[JsonPropertyName("name")]
public string Name { get; set; }
}

View File

@@ -1,135 +1,134 @@
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 Waypoint
public class Config
{
public class Config
{
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("value")]
public DataValue Value { get; set; }
}
[JsonPropertyName("position")]
public Point3 Position { get; set; }
[JsonPropertyName("rotation")]
public Point4? Rotation { get; set; }
// Camera
[JsonPropertyName("time")]
public float? Time { get; set; }
[JsonPropertyName("tension")]
public float? Tension { get; set; }
[JsonPropertyName("continuity")]
public float? Continuity { get; set; }
[JsonPropertyName("bias")]
public float? Bias { get; set; }
[JsonPropertyName("field-of-view")]
public float? FieldOfView { get; set; }
[JsonPropertyName("config")]
public Config[]? Configuration { get; set; }
// Moving platform
[JsonPropertyName("lock-player")]
public bool? LockPlayer { get; set; }
[JsonPropertyName("speed")]
public float? Speed { get; set; }
[JsonPropertyName("wait")]
public float? Wait { get; set; }
[JsonPropertyName("depart-sound")]
public string? DepartSound { get; set; }
[JsonPropertyName("arrive-sound")]
public string? ArriveSound { get; set; }
[JsonPropertyName("value")]
public DataValue Value { get; set; }
}
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("behavior")]
public string Behavior { get; set; }
[JsonPropertyName("waypoints")]
public Waypoint[] Waypoints { get; set; }
[JsonPropertyName("position")]
public Point3 Position { get; set; }
[JsonPropertyName("rotation")]
public Point4? Rotation { get; set; }
// Camera
[JsonPropertyName("time")]
public float? Time { get; set; }
[JsonPropertyName("tension")]
public float? Tension { get; set; }
[JsonPropertyName("continuity")]
public float? Continuity { get; set; }
[JsonPropertyName("bias")]
public float? Bias { get; set; }
[JsonPropertyName("field-of-view")]
public float? FieldOfView { get; set; }
[JsonPropertyName("config")]
public Config[]? Configuration { get; set; }
// Moving platform
[JsonPropertyName("next-path")]
public string? NextPath { get; set; }
[JsonPropertyName("sound")]
public string? Sound { get; set; }
[JsonPropertyName("time-based")]
public bool? TimeBased { get; set; }
// Property
[JsonPropertyName("price")]
public int? Price { get; set; }
[JsonPropertyName("lock-player")]
public bool? LockPlayer { get; set; }
[JsonPropertyName("rental-time")]
public int? RentalTime { get; set; }
[JsonPropertyName("speed")]
public float? Speed { get; set; }
[JsonPropertyName("associative-zone")]
public string? AssociativeZone { get; set; }
[JsonPropertyName("wait")]
public float? Wait { get; set; }
[JsonPropertyName("display-name")]
public string? DisplayName { get; set; }
[JsonPropertyName("depart-sound")]
public string? DepartSound { get; set; }
[JsonPropertyName("display-description")]
public string? DisplayDescription { get; set; }
[JsonPropertyName("clone-limit")]
public int? CloneLimit { get; set; }
[JsonPropertyName("reputation-multiplier")]
public float? ReputationMultiplier { get; set; }
[JsonPropertyName("time-unit")]
public string? TimeUnit { get; set; }
[JsonPropertyName("achievement")]
public string? Achievement { get; set; }
[JsonPropertyName("player-zone-point")]
public Point3? PlayerZonePoint { get; set; }
[JsonPropertyName("max-build-height")]
public float? MaxBuildHeight { get; set; }
// Spawner
[JsonPropertyName("spawned-id")]
public string? SpawnedId { get; set; }
[JsonPropertyName("respawn-time")]
public float? RespawnTime { get; set; }
[JsonPropertyName("max-spawn-count")]
public int? MaxSpawnCount { get; set; }
[JsonPropertyName("number-to-maintain")]
public int? NumberToMaintain { get; set; }
[JsonPropertyName("spawner-object")]
public string? SpawnerObject { get; set; }
[JsonPropertyName("activate-on-load")]
public bool? ActivateOnLoad { get; set; }
[JsonPropertyName("arrive-sound")]
public string? ArriveSound { get; set; }
}
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("behavior")]
public string Behavior { get; set; }
[JsonPropertyName("waypoints")]
public Waypoint[] Waypoints { get; set; }
// Moving platform
[JsonPropertyName("next-path")]
public string? NextPath { get; set; }
[JsonPropertyName("sound")]
public string? Sound { get; set; }
[JsonPropertyName("time-based")]
public bool? TimeBased { get; set; }
// Property
[JsonPropertyName("price")]
public int? Price { get; set; }
[JsonPropertyName("rental-time")]
public int? RentalTime { get; set; }
[JsonPropertyName("associative-zone")]
public string? AssociativeZone { get; set; }
[JsonPropertyName("display-name")]
public string? DisplayName { get; set; }
[JsonPropertyName("display-description")]
public string? DisplayDescription { get; set; }
[JsonPropertyName("clone-limit")]
public int? CloneLimit { get; set; }
[JsonPropertyName("reputation-multiplier")]
public float? ReputationMultiplier { get; set; }
[JsonPropertyName("time-unit")]
public string? TimeUnit { get; set; }
[JsonPropertyName("achievement")]
public string? Achievement { get; set; }
[JsonPropertyName("player-zone-point")]
public Point3? PlayerZonePoint { get; set; }
[JsonPropertyName("max-build-height")]
public float? MaxBuildHeight { get; set; }
// Spawner
[JsonPropertyName("spawned-id")]
public string? SpawnedId { get; set; }
[JsonPropertyName("respawn-time")]
public float? RespawnTime { get; set; }
[JsonPropertyName("max-spawn-count")]
public int? MaxSpawnCount { get; set; }
[JsonPropertyName("number-to-maintain")]
public int? NumberToMaintain { get; set; }
[JsonPropertyName("spawner-object")]
public string? SpawnerObject { get; set; }
[JsonPropertyName("activate-on-load")]
public bool? ActivateOnLoad { get; set; }
}

View File

@@ -1,23 +1,22 @@
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; }
[JsonPropertyName("y")]
public float Y { get; set; }
[JsonPropertyName("z")]
public float Z { get; set; }
// Implicit conversion from Point3 to Vector3
public static implicit operator Vector3(Point3 point)
{
[JsonPropertyName("x")]
public float X { get; set; }
[JsonPropertyName("y")]
public float Y { get; set; }
[JsonPropertyName("z")]
public float Z { get; set; }
// Implicit conversion from Point3 to Vector3
public static implicit operator Vector3(Point3 point)
{
return new Vector3(point.X, point.Y, point.Z);
}
return new Vector3(point.X, point.Y, point.Z);
}
}

View File

@@ -1,26 +1,25 @@
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; }
[JsonPropertyName("y")]
public float Y { get; set; }
[JsonPropertyName("z")]
public float Z { get; set; }
[JsonPropertyName("w")]
public float W { get; set; }
// Implicit conversion from Point4 to Quaternion
public static implicit operator Quaternion(Point4 point)
{
[JsonPropertyName("x")]
public float X { get; set; }
[JsonPropertyName("y")]
public float Y { get; set; }
[JsonPropertyName("z")]
public float Z { get; set; }
[JsonPropertyName("w")]
public float W { get; set; }
// Implicit conversion from Point4 to Quaternion
public static implicit operator Quaternion(Point4 point)
{
return new Quaternion(point.X, point.Y, point.Z, point.W);
}
return new Quaternion(point.X, point.Y, point.Z, point.W);
}
}

View File

@@ -1,77 +1,76 @@
using System.Text.Json.Serialization;
using InfectedRose.Triggers;
namespace InfectedRose.Interface.Templates.ValueTypes
{
public class Scene
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("layer")]
public int Layer { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("skybox")]
public string Skybox { get; set; }
[JsonPropertyName("blend-time")]
public float BlendTime { get; set; }
[JsonPropertyName("ambient-color")]
public Color3 AmbientColor { get; set; }
[JsonPropertyName("specular-color")]
public Color3 SpecularColor { get; set; }
[JsonPropertyName("upper-hemi-color")]
public Color3 UpperHemiColor { get; set; }
[JsonPropertyName("light-direction")]
public Point3 LightDirection { get; set; }
[JsonPropertyName("fog-near-min")]
public float FogNearMin { get; set; }
[JsonPropertyName("fog-far-min")]
public float FogFarMin { get; set; }
[JsonPropertyName("post-fog-solid-min")]
public float PostFogSolidMin { get; set; }
[JsonPropertyName("post-fog-fade-min")]
public float PostFogFadeMin { get; set; }
[JsonPropertyName("fog-near-max")]
public float FogNearMax { get; set; }
[JsonPropertyName("fog-far-max")]
public float FogFarMax { get; set; }
[JsonPropertyName("post-fog-solid-max")]
public float PostFogSolidMax { get; set; }
[JsonPropertyName("post-fog-fade-max")]
public float PostFogFadeMax { get; set; }
[JsonPropertyName("fog-color")]
public Color3 FogColor { get; set; }
[JsonPropertyName("directional-light-color")]
public Color3 DirectionalLightColor { get; set; }
namespace InfectedRose.Interface.Templates.ValueTypes;
[JsonPropertyName("templates")]
public LevelTemplate[] Templates { get; set; }
public class Scene
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("particles")]
public Particle[] Particles { get; set; }
[JsonPropertyName("layer")]
public int Layer { get; set; }
[JsonPropertyName("triggers")]
public Trigger[] Triggers { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("scene-audio")]
public SceneAudio SceneAudio { get; set; }
}
[JsonPropertyName("skybox")]
public string Skybox { get; set; }
[JsonPropertyName("blend-time")]
public float BlendTime { get; set; }
[JsonPropertyName("ambient-color")]
public Color3 AmbientColor { get; set; }
[JsonPropertyName("specular-color")]
public Color3 SpecularColor { get; set; }
[JsonPropertyName("upper-hemi-color")]
public Color3 UpperHemiColor { get; set; }
[JsonPropertyName("light-direction")]
public Point3 LightDirection { get; set; }
[JsonPropertyName("fog-near-min")]
public float FogNearMin { get; set; }
[JsonPropertyName("fog-far-min")]
public float FogFarMin { get; set; }
[JsonPropertyName("post-fog-solid-min")]
public float PostFogSolidMin { get; set; }
[JsonPropertyName("post-fog-fade-min")]
public float PostFogFadeMin { get; set; }
[JsonPropertyName("fog-near-max")]
public float FogNearMax { get; set; }
[JsonPropertyName("fog-far-max")]
public float FogFarMax { get; set; }
[JsonPropertyName("post-fog-solid-max")]
public float PostFogSolidMax { get; set; }
[JsonPropertyName("post-fog-fade-max")]
public float PostFogFadeMax { get; set; }
[JsonPropertyName("fog-color")]
public Color3 FogColor { get; set; }
[JsonPropertyName("directional-light-color")]
public Color3 DirectionalLightColor { get; set; }
[JsonPropertyName("templates")]
public LevelTemplate[] Templates { get; set; }
[JsonPropertyName("particles")]
public Particle[] Particles { get; set; }
[JsonPropertyName("triggers")]
public Trigger[] Triggers { get; set; }
[JsonPropertyName("scene-audio")]
public SceneAudio SceneAudio { get; set; }
}

View File

@@ -1,41 +1,40 @@
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; }
[JsonPropertyName("music-cue")]
[XmlAttribute("musicCue")]
public string MusicCue { get; set; }
[JsonPropertyName("music-param-name")]
[XmlAttribute("musicParamName")]
public string MusicParamName { get; set; }
[JsonPropertyName("music-param-name")]
[XmlAttribute("musicParamName")]
public string MusicParamName { get; set; }
[JsonPropertyName("guid-2d")]
[XmlAttribute("guid2D")]
public string Guid2D { get; set; }
[JsonPropertyName("guid-2d")]
[XmlAttribute("guid2D")]
public string Guid2D { get; set; }
[JsonPropertyName("guid-3d")]
[XmlAttribute("guid3D")]
public string Guid3D { get; set; }
[JsonPropertyName("guid-3d")]
[XmlAttribute("guid3D")]
public string Guid3D { get; set; }
[JsonPropertyName("group-name")]
[XmlAttribute("groupName")]
public string GroupName { get; set; }
[JsonPropertyName("group-name")]
[XmlAttribute("groupName")]
public string GroupName { get; set; }
[JsonPropertyName("program-name")]
[XmlAttribute("programName")]
public string ProgramName { get; set; }
[JsonPropertyName("program-name")]
[XmlAttribute("programName")]
public string ProgramName { get; set; }
[JsonPropertyName("music-param-value")]
[XmlAttribute("musicParamValue")]
public string MusicParamValue { get; set; }
[JsonPropertyName("music-param-value")]
[XmlAttribute("musicParamValue")]
public string MusicParamValue { get; set; }
[JsonPropertyName("boredom-time")]
[XmlAttribute("boredomTime")]
public string BoredomTime { get; set; }
}
[JsonPropertyName("boredom-time")]
[XmlAttribute("boredomTime")]
public string BoredomTime { get; set; }
}

View File

@@ -1,22 +1,21 @@
using System.Text.Json.Serialization;
namespace InfectedRose.Interface.Templates.ValueTypes
namespace InfectedRose.Interface.Templates.ValueTypes;
public class Transition
{
public class Transition
public class Point
{
public class Point
{
[JsonPropertyName("scene")]
public string Scene { get; set; }
[JsonPropertyName("scene")]
public string Scene { get; set; }
[JsonPropertyName("position")]
public Point3 Position { get; set; }
}
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("points")]
public Point[] Points { get; set; }
[JsonPropertyName("position")]
public Point3 Position { get; set; }
}
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("points")]
public Point[] Points { get; set; }
}

View File

@@ -1,25 +1,24 @@
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; }
[JsonPropertyName("spawn-point")]
public Point3 SpawnPoint { get; set; }
[JsonPropertyName("spawn-rotation")]
public Point4 SpawnRotation { get; set; }
[JsonPropertyName("spawn-rotation")]
public Point4 SpawnRotation { get; set; }
[JsonPropertyName("terrain-file")]
public string TerrainFile { get; set; }
[JsonPropertyName("terrain-file")]
public string TerrainFile { get; set; }
[JsonPropertyName("scenes")]
public Scene[] Scenes { get; set; }
[JsonPropertyName("scenes")]
public Scene[] Scenes { get; set; }
[JsonPropertyName("transitions")]
public Transition[] Transitions { get; set; }
[JsonPropertyName("transitions")]
public Transition[] Transitions { get; set; }
[JsonPropertyName("paths")]
public PathData[] Paths { get; set; }
}
[JsonPropertyName("paths")]
public PathData[] Paths { get; set; }
}

View 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();
}
}

View 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;
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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:

View File

@@ -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);
}
}
}

View File

@@ -4,222 +4,220 @@ 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>
/// <param name="zone">Path to zone file</param>
/// <returns>Calculated checksum</returns>
public static uint Generate(string zone)
{
/// <summary>
/// Calculate the checksum for a LEGO Universe zone
/// </summary>
/// <param name="zone">Path to zone file</param>
/// <returns>Calculated checksum</returns>
public static uint Generate(string zone)
{
uint value = ushort.MaxValue; // For checksum calculations
uint total = ushort.MaxValue; // Sum of all changes applied to value
uint value = ushort.MaxValue; // For checksum calculations
uint total = ushort.MaxValue; // Sum of all changes applied to value
//
// Read zone file
//
//
// Read zone file
//
var luz = new LuzFile();
var luz = new LuzFile();
using (var stream = File.OpenRead(zone))
using (var stream = File.OpenRead(zone))
{
using var reader = new BitReader(stream);
luz.Deserialize(reader);
}
//
// Apply zone layer
//
var zoneLayer = new ChecksumLayer
{
Id = uint.MaxValue,
Layer = default,
Revision = luz.RevisionNumber
};
zoneLayer.Apply(ref value, ref total);
//
// Get layers for scenes
//
var root = Path.GetDirectoryName(zone);
var sceneLayers = new ChecksumLayer[luz.Scenes.Length];
for (var index = 0; index < luz.Scenes.Length; index++)
{
var scene = luz.Scenes[index];
//
// Read scene (level) file
//
var lvl = new LvlFile();
using (var stream = File.OpenRead(Path.Combine(root, scene.FileName)))
{
using var reader = new BitReader(stream);
luz.Deserialize(reader);
lvl.Deserialize(reader);
}
//
// Apply zone layer
// Get revision
//
var zoneLayer = new ChecksumLayer
var revision = lvl.LevelInfo?.RevisionNumber ?? lvl.OldLevelHeader.Revision;
//
// Get layer
//
sceneLayers[index] = new ChecksumLayer
{
Id = uint.MaxValue,
Layer = default,
Revision = luz.RevisionNumber
Id = scene.SceneId,
Layer = scene.LayerId,
Revision = revision
};
zoneLayer.Apply(ref value, ref total);
//
// Get layers for scenes
//
var root = Path.GetDirectoryName(zone);
var sceneLayers = new ChecksumLayer[luz.Scenes.Length];
for (var index = 0; index < luz.Scenes.Length; index++)
{
var scene = luz.Scenes[index];
//
// Read scene (level) file
//
var lvl = new LvlFile();
using (var stream = File.OpenRead(Path.Combine(root, scene.FileName)))
{
using var reader = new BitReader(stream);
lvl.Deserialize(reader);
}
//
// Get revision
//
var revision = lvl.LevelInfo?.RevisionNumber ?? lvl.OldLevelHeader.Revision;
//
// Get layer
//
sceneLayers[index] = new ChecksumLayer
{
Id = scene.SceneId,
Layer = scene.LayerId,
Revision = revision
};
}
//
// Apply scene layers
//
foreach (var layer in sceneLayers)
{
layer.Apply(ref value, ref total);
}
//
// Get final checksum
//
var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16));
var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16));
//
// The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value
//
return (uint) (upper << 16 | lower);
}
/// <summary>
/// Calculate the checksum for a LEGO Universe zone
/// </summary>
/// <param name="zone">Path to zone file</param>
/// <returns>Calculated checksum</returns>
public static async Task<uint> GenerateAsync(string zone)
//
// Apply scene layers
//
foreach (var layer in sceneLayers)
{
uint value = ushort.MaxValue; // For checksum calculations
uint total = ushort.MaxValue; // Sum of all changes applied to value
//
// Read zone file
//
var luz = new LuzFile();
using (var stream = File.OpenRead(zone))
{
using var reader = new BitReader(stream);
luz.Deserialize(reader);
}
//
// Apply zone layer
//
var zoneLayer = new ChecksumLayer
{
Id = uint.MaxValue,
Layer = default,
Revision = luz.RevisionNumber
};
zoneLayer.Apply(ref value, ref total);
//
// Get layers for scenes
//
var root = Path.GetDirectoryName(zone);
var sceneLayers = new ChecksumLayer[luz.Scenes.Length];
var tasks = new Task[luz.Scenes.Length];
for (var index = 0; index < luz.Scenes.Length; index++)
{
var sceneIndex = index;
tasks[index] = Task.Run(() =>
{
var scene = luz.Scenes[sceneIndex];
//
// Read scene (level) file
//
var lvl = new LvlFile();
using (var stream = File.OpenRead(Path.Combine(root, scene.FileName)))
{
using var reader = new BitReader(stream);
lvl.Deserialize(reader);
}
//
// Get revision
//
var revision = lvl.LevelInfo?.RevisionNumber ?? lvl.OldLevelHeader.Revision;
//
// Get layer
//
sceneLayers[sceneIndex] = new ChecksumLayer
{
Id = scene.SceneId,
Layer = scene.LayerId,
Revision = revision
};
});
}
await Task.WhenAll(tasks);
//
// Apply scene layers
//
foreach (var layer in sceneLayers)
{
layer.Apply(ref value, ref total);
}
//
// Get final checksum
//
var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16));
var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16));
//
// The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value
//
return (uint) (upper << 16 | lower);
layer.Apply(ref value, ref total);
}
//
// Get final checksum
//
var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16));
var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16));
//
// The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value
//
return (uint) (upper << 16 | lower);
}
}
/// <summary>
/// Calculate the checksum for a LEGO Universe zone
/// </summary>
/// <param name="zone">Path to zone file</param>
/// <returns>Calculated checksum</returns>
public static async Task<uint> GenerateAsync(string zone)
{
uint value = ushort.MaxValue; // For checksum calculations
uint total = ushort.MaxValue; // Sum of all changes applied to value
//
// Read zone file
//
var luz = new LuzFile();
using (var stream = File.OpenRead(zone))
{
using var reader = new BitReader(stream);
luz.Deserialize(reader);
}
//
// Apply zone layer
//
var zoneLayer = new ChecksumLayer
{
Id = uint.MaxValue,
Layer = default,
Revision = luz.RevisionNumber
};
zoneLayer.Apply(ref value, ref total);
//
// Get layers for scenes
//
var root = Path.GetDirectoryName(zone);
var sceneLayers = new ChecksumLayer[luz.Scenes.Length];
var tasks = new Task[luz.Scenes.Length];
for (var index = 0; index < luz.Scenes.Length; index++)
{
var sceneIndex = index;
tasks[index] = Task.Run(() =>
{
var scene = luz.Scenes[sceneIndex];
//
// Read scene (level) file
//
var lvl = new LvlFile();
using (var stream = File.OpenRead(Path.Combine(root, scene.FileName)))
{
using var reader = new BitReader(stream);
lvl.Deserialize(reader);
}
//
// Get revision
//
var revision = lvl.LevelInfo?.RevisionNumber ?? lvl.OldLevelHeader.Revision;
//
// Get layer
//
sceneLayers[sceneIndex] = new ChecksumLayer
{
Id = scene.SceneId,
Layer = scene.LayerId,
Revision = revision
};
});
}
await Task.WhenAll(tasks);
//
// Apply scene layers
//
foreach (var layer in sceneLayers)
{
layer.Apply(ref value, ref total);
}
//
// Get final checksum
//
var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16));
var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16));
//
// The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value
//
return (uint) (upper << 16 | lower);
}
}

View File

@@ -1,25 +1,24 @@
namespace InfectedRose.Utilities
namespace InfectedRose.Utilities;
internal struct ChecksumLayer
{
internal struct ChecksumLayer
internal uint Id { get; set; }
internal uint Layer { get; set; }
internal uint Revision { get; set; }
internal void Apply(ref uint value, ref uint total)
{
internal uint Id { get; set; }
internal uint Layer { get; set; }
internal uint Revision { get; set; }
internal void Apply(ref uint value, ref uint total)
foreach (var reference in new[] {Id, Layer, Revision})
{
foreach (var reference in new[] {Id, Layer, Revision})
{
value += reference >> 16; // Apply reference
value += reference >> 16; // Apply reference
total += value; // Add to total
total += value; // Add to total
value += reference & ushort.MaxValue; // Make ushort
value += reference & ushort.MaxValue; // Make ushort
total += value; // Add to total
}
total += value; // Add to total
}
}
}

View File

@@ -3,66 +3,65 @@ 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)
{
public static uint GenerateChecksum(this LuzFile @this, List<LvlFile> scenes)
if (@this.Scenes.Length != scenes.Count)
{
if (@this.Scenes.Length != scenes.Count)
{
throw new ArgumentOutOfRangeException(nameof(scenes), "The count of scenes has to equal the count of scenes in this luz file.");
}
throw new ArgumentOutOfRangeException(nameof(scenes), "The count of scenes has to equal the count of scenes in this luz file.");
}
uint value = ushort.MaxValue; // For checksum calculations
uint total = ushort.MaxValue; // Sum of all changes applied to value
uint value = ushort.MaxValue; // For checksum calculations
uint total = ushort.MaxValue; // Sum of all changes applied to value
var zoneLayer = new ChecksumLayer
var zoneLayer = new ChecksumLayer
{
Id = uint.MaxValue,
Layer = default,
Revision = @this.RevisionNumber
};
zoneLayer.Apply(ref value, ref total);
for (var index = 0; index < scenes.Count; index++)
{
var scene = scenes[index];
var lvl = @this.Scenes[index];
//
// Get revision
//
var revision = scene.LevelInfo?.RevisionNumber ?? scene.OldLevelHeader.Revision;
//
// Get layer
//
var sceneLayer = new ChecksumLayer
{
Id = uint.MaxValue,
Layer = default,
Revision = @this.RevisionNumber
Id = lvl.SceneId,
Layer = lvl.LayerId,
Revision = revision
};
zoneLayer.Apply(ref value, ref total);
for (var index = 0; index < scenes.Count; index++)
{
var scene = scenes[index];
var lvl = @this.Scenes[index];
//
// Get revision
//
var revision = scene.LevelInfo?.RevisionNumber ?? scene.OldLevelHeader.Revision;
//
// Get layer
//
var sceneLayer = new ChecksumLayer
{
Id = lvl.SceneId,
Layer = lvl.LayerId,
Revision = revision
};
sceneLayer.Apply(ref value, ref total);
}
//
// Get final checksum
//
var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16));
var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16));
//
// The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value
//
return (uint) (upper << 16 | lower);
sceneLayer.Apply(ref value, ref total);
}
//
// Get final checksum
//
var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16));
var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16));
//
// The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value
//
return (uint) (upper << 16 | lower);
}
}