Added mod types:

+ NPC
+ Item
This commit is contained in:
wincent
2022-01-03 17:33:31 +01:00
parent a529fd2524
commit b4592b93b1
8 changed files with 462 additions and 45 deletions

View File

@@ -0,0 +1,24 @@
namespace InfectedRose.Interface
{
[ModType("environmental")]
public class EnvironmentalMod : ModType
{
public override void Apply(Mod mod)
{
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);
}
}
}

View File

@@ -0,0 +1,55 @@
namespace InfectedRose.Interface
{
[ModType("item")]
public class ItemMod : ModType
{
public override void Apply(Mod mod)
{
if (mod.Action != "add")
{
return;
}
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");
var obj = ObjectMod.CreateObject(mod);
obj["type"].Value = "Loot";
ObjectMod.AddComponent(mod, obj, ComponentId.RenderComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.ItemComponent);
ObjectMod.AddComponent(mod, obj, ComponentId.SkillComponent);
if (mod.Skills != null)
{
var objectSkillsTable = ModContext.Database["ObjectSkills"];
foreach (var skill in mod.Skills)
{
var objectSkill = objectSkillsTable.Create(obj.Key);
objectSkill["castOnType"].Value = mod.HasValue("castOnType") ? mod.GetValue<int>("castOnType") : 0;
objectSkill["AICombatWeight"].Value = 0;
if (skill.TryGetValue(out int? value))
{
objectSkill["skillID"].Value = value;
}
else if (skill.TryGetValue(out string? itemId))
{
ModContext.AwaitId(itemId, lot => objectSkill["skillID"].Value = lot);
}
}
}
}
}
}

View File

@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Xml.Serialization;
namespace InfectedRose.Interface
{
[XmlRoot("locales")]
public class Locales
{
[XmlAttribute("count")]
public int Count { get; set; }
[XmlElement("locale")]
public string[] Locale { get; set; }
}
[XmlRoot("translation")]
public class Translation
{
[XmlAttribute("locale")]
public string Locale { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot("phrase")]
public class Phrase
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlElement("translation")]
public List<Translation> Translations { get; set; }
}
[XmlRoot("phrases")]
public class Phrases
{
[XmlAttribute("count")]
public int Count { get; set; }
[XmlElement("phrase")]
public List<Phrase> Phrase { get; set; }
}
[XmlRoot("localization")]
public class Localization
{
[XmlAttribute("version")]
public float Version { get; set; } = 1.200000f;
[XmlElement("locales")]
public Locales Locales { get; set; }
[XmlElement("phrases")]
public Phrases Phrases { get; set; }
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace InfectedRose.Interface
@@ -8,26 +9,68 @@ namespace InfectedRose.Interface
public class Mod
{
[JsonPropertyName("id")]
public string Id { get; set; }
public string Id { get; set; } = "";
[JsonPropertyName("type")]
public string Type { get; set; }
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; }
public string[]? Components { get; set; }
[JsonPropertyName("table")]
public string Table { get; set; }
public string? Table { get; set; }
[JsonPropertyName("items")]
public JsonValue[]? Items { get; set; }
[JsonPropertyName("skills")]
public JsonValue[]? Skills { get; set; }
[JsonPropertyName("locale")]
public Dictionary<string, string>? Locale { get; set; } = new Dictionary<string, string>
{
{"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)
{
return ((JsonElement) Values[id]).Deserialize<T>()!;
if (Values[id] is JsonElement jsonElement)
{
return jsonElement.Deserialize<T>()!;
}
if (Values[id] is T value)
{
return value;
}
return (T) Convert.ChangeType(Values[id], typeof(T));
}
public bool HasValue(string id)
{
return Values.ContainsKey(id);
}
public void Default<T>(string id, T value)
{
Defaults[id] = value;
if (HasValue(id)) return;
Values[id] = value;
}
public int GetComponentType()

View File

@@ -23,6 +23,8 @@ namespace InfectedRose.Interface
public static AccessDatabase Database { get; set; }
public static Localization Localization { get; set; }
public static Dictionary<string, int> Ids { get; set; } = new Dictionary<string, int>();
public static Dictionary<string, Mod> Mods { get; set; } = new Dictionary<string, Mod>();
@@ -59,6 +61,60 @@ namespace InfectedRose.Interface
{
return Mods[id];
}
public static Table? GetComponentTable(ComponentId component)
{
return Database[GetComponentTableName(component)];
}
public static Table? GetComponentTable(string component)
{
return Database[GetComponentTableName(component)];
}
public static string GetComponentTableName(ComponentId component)
{
return GetComponentTableName(component.ToString());
}
public static string GetComponentTableName(string component)
{
if (component.Contains("PhysicsComponent"))
{
return "PhysicsComponent";
}
return component;
}
public static void AddToLocale(string id, string text, string locale)
{
var phrase = Localization.Phrases.Phrase.FirstOrDefault(p => p.Id == id);
if (phrase == null)
{
phrase = new Phrase();
phrase.Id = id;
phrase.Translations = new List<Translation>();
Localization.Phrases.Phrase.Add(phrase);
}
var translation = phrase.Translations.FirstOrDefault(t => t.Locale == locale);
if (translation == null)
{
translation = new Translation();
translation.Locale = locale;
phrase.Translations.Add(translation);
}
translation.Text = text;
}
public static string ParseValue(string value)
{
@@ -84,6 +140,12 @@ namespace InfectedRose.Interface
value = value.Substring(8);
}
else if (value.StartsWith("ICON:"))
{
root = "../../res/mesh/bricks/";
value = value.Substring(5);
}
root = Root + root;
@@ -97,16 +159,20 @@ namespace InfectedRose.Interface
{
foreach (var (key, objValue) in mod.Values)
{
var value = (JsonElement) objValue;
var info = table.TableInfo.FirstOrDefault(column => column.Name == key);
if (info == null)
{
continue;
}
var field = row[key];
var info = table.TableInfo.First(column => column.Name == key);
switch (info.Type)
{
case DataType.Integer:
{
var str = value.ToString();
var str = objValue.ToString();
if (str.Contains(':'))
{
@@ -117,23 +183,23 @@ namespace InfectedRose.Interface
}
else
{
field.Value = value.GetInt32();
field.Value = mod.GetValue<int>(key);
}
break;
}
case DataType.Float:
field.Value = value.GetSingle();
field.Value = mod.GetValue<float>(key);
break;
case DataType.Varchar:
case DataType.Text:
field.Value = ParseValue(value.GetString()!);
field.Value = ParseValue(objValue.ToString()!);
break;
case DataType.Boolean:
field.Value = value.GetBoolean();
field.Value = mod.GetValue<bool>(key);
break;
case DataType.Bigint:
field.Value = value.GetInt64();
field.Value = mod.GetValue<long>(key);
break;
}
}

View File

@@ -0,0 +1,79 @@
namespace InfectedRose.Interface
{
[ModType("npc")]
public class NpcMod : ModType
{
public override void Apply(Mod mod)
{
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.Default("chatBubbleOffset", 6);
mod.Default("fade", true);
mod.Default("fadeInTime", 1);
mod.Default("billboardHeight", 6);
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", 17);
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);
if (mod.Items != null)
{
var id = 0;
foreach (var item in mod.Items)
{
var itemComponent = ObjectMod.AddComponent(mod, obj, 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);
}
}
}
}
}
}

View File

@@ -1,8 +1,90 @@
using InfectedRose.Database;
namespace InfectedRose.Interface
{
[ModType("object")]
public class ObjectMod : ModType
{
public static Row CreateObject(Mod mod)
{
var table = ModContext.Database["Objects"];
var row = table.Create();
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);
}
}
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();
});
}
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);
return component;
}
if (table == null) return null;
component = table.Create(id);
ModContext.ApplyValues(mod, component, table);
return component;
}
public override void Apply(Mod mod)
{
if (mod.Action != "add")
@@ -10,24 +92,7 @@ namespace InfectedRose.Interface
return;
}
var table = ModContext.Database["Objects"];
var obj = table.Create();
ModContext.ApplyValues(mod, obj, table);
ModContext.RegisterId(mod.Id, obj.Key);
foreach (var component in mod.Components)
{
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();
});
}
CreateObject(mod);
}
}
}

View File

@@ -7,6 +7,7 @@ using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.Xml.Serialization;
using CommandLine;
using InfectedRose.Database;
using InfectedRose.Database.Fdb;
@@ -71,16 +72,6 @@ namespace InfectedRose.Interface
return JsonSerializer.Deserialize<T>(stream) ?? new T();
}
public static string GetComponentTable(string component)
{
if (component.Contains("PhysicsComponent"))
{
return "PhysicsComponent";
}
return component;
}
public static void ApplyRow(Mod mod)
{
if (mod.Action != "add")
@@ -88,7 +79,7 @@ namespace InfectedRose.Interface
return;
}
var tableName = GetComponentTable(mod.Type);
var tableName = ModContext.GetComponentTableName(mod.Type);
if (!string.IsNullOrWhiteSpace(mod.Table))
{
@@ -147,6 +138,17 @@ namespace InfectedRose.Interface
ModContext.Mods[mod.Id] = mod;
ApplyMod(mod);
if (!mod.ShowDefaults.HasValue || mod.ShowDefaults.Value == false)
{
foreach (var (key, value) in mod.Defaults)
{
if (mod.HasValue(key) && mod.Values[key].ToString() == value.ToString())
{
mod.Values.Remove(key);
}
}
}
}
Directory.SetCurrentDirectory(directory);
@@ -259,7 +261,7 @@ namespace InfectedRose.Interface
components.Add(componentMod.Id);
if (!ModContext.Database[GetComponentTable(componentMod.Type)].Seek(componentId, out var componentRow))
if (!ModContext.Database[ModContext.GetComponentTableName(componentMod.Type)].Seek(componentId, out var componentRow))
{
continue;
}
@@ -308,6 +310,22 @@ namespace InfectedRose.Interface
ModContext.Configuration = ReadOrCreateJson<Mods>(CommandLineOptions.Input);
// Load locale
XmlSerializer localeSerializer = new XmlSerializer(typeof(Localization));
var localeSourcePath = Path.Combine(Path.GetDirectoryName(CommandLineOptions.Input)!, "./locale.xml");
var localeDestinationPath = Path.Combine(Path.GetDirectoryName(CommandLineOptions.Input)!, "../locale/locale.xml");
if (!File.Exists(localeSourcePath))
{
File.Copy(localeDestinationPath, localeSourcePath);
}
using (var stream = File.OpenRead(localeSourcePath))
{
ModContext.Localization = (Localization) localeSerializer.Deserialize(stream)!;
}
// Check version
if (string.IsNullOrWhiteSpace(ModContext.Configuration.Version))
{
@@ -324,9 +342,10 @@ namespace InfectedRose.Interface
// Open database
if (!File.Exists(ModContext.Configuration.Database))
{
Console.WriteLine("Please specify a valid path to the base fdb database in mods.json.");
var databaseSourcePath = Path.Combine(Path.GetDirectoryName(CommandLineOptions.Input)!, "./cdclient.fdb");
var databaseDestinationPath = Path.Combine(Path.GetDirectoryName(CommandLineOptions.Input)!, "../res/cdclient.fdb");
return;
File.Copy(databaseDestinationPath, databaseSourcePath);
}
var origin = Console.GetCursorPosition();
@@ -426,6 +445,14 @@ namespace InfectedRose.Interface
Console.SetCursorPosition(origin.Left, origin.Top);
using (var stream = File.Create(localeDestinationPath))
{
ModContext.Localization.Locales.Count = ModContext.Localization.Locales.Locale.Length;
ModContext.Localization.Phrases.Count = ModContext.Localization.Phrases.Phrase.Count;
localeSerializer.Serialize(stream, ModContext.Localization);
}
Console.WriteLine("Complete!");
}
}