Rewrite map generator settings UI logic.
This commit is contained in:
committed by
Gustas Kažukauskas
parent
8f46247dc9
commit
78f660124c
@@ -13,389 +13,231 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA.Mods.Common.MapGenerator
|
||||
{
|
||||
public sealed class MapGeneratorSettings
|
||||
public abstract class MapGeneratorOption
|
||||
{
|
||||
/// <summary>How an option should be treated for UI purposes.</summary>
|
||||
public enum UiType
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string Id;
|
||||
public readonly string Label = null;
|
||||
public readonly int Priority = 0;
|
||||
|
||||
protected MapGeneratorOption(string id, MiniYaml yaml)
|
||||
{
|
||||
Hidden,
|
||||
DropDown,
|
||||
Checkbox,
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
Id = id;
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
|
||||
/// <summary>Represents a bunch of settings, along with UI information.</summary>
|
||||
public sealed class Choice
|
||||
{
|
||||
/// <summary>Uniquely identifies a Choice within an Option.</summary>
|
||||
public readonly string Id;
|
||||
public abstract IReadOnlyCollection<MiniYamlNode> GetSettings(ITerrainInfo terrainInfo);
|
||||
|
||||
/// <summary>The label to use for UI selection. Post-fluent.</summary>
|
||||
public virtual IEnumerable<string> GetFluentReferences()
|
||||
{
|
||||
if (Label != null)
|
||||
yield return Label;
|
||||
}
|
||||
}
|
||||
|
||||
public class MapGeneratorBooleanOption : MapGeneratorOption
|
||||
{
|
||||
[FieldLoader.Require]
|
||||
public readonly string Parameter = null;
|
||||
public readonly bool Default = false;
|
||||
public bool Value;
|
||||
|
||||
public MapGeneratorBooleanOption(string id, MiniYaml yaml)
|
||||
: base(id, yaml)
|
||||
{
|
||||
Value = Default;
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<MiniYamlNode> GetSettings(ITerrainInfo terrainInfo)
|
||||
{
|
||||
return [new MiniYamlNode(Parameter, FieldSaver.FormatValue(Value))];
|
||||
}
|
||||
}
|
||||
|
||||
public class MapGeneratorIntegerOption : MapGeneratorOption
|
||||
{
|
||||
[FieldLoader.Require]
|
||||
public readonly string Parameter = null;
|
||||
public readonly int Default = 0;
|
||||
public int Value;
|
||||
|
||||
public MapGeneratorIntegerOption(string id, MiniYaml yaml)
|
||||
: base(id, yaml)
|
||||
{
|
||||
Value = Default;
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<MiniYamlNode> GetSettings(ITerrainInfo terrainInfo)
|
||||
{
|
||||
return [new MiniYamlNode(Parameter, FieldSaver.FormatValue(Value))];
|
||||
}
|
||||
}
|
||||
|
||||
public class MapGeneratorMultiChoiceOption : MapGeneratorOption
|
||||
{
|
||||
public class MapGeneratorDropdownChoice
|
||||
{
|
||||
public readonly string Label = null;
|
||||
public readonly string[] Tileset = null;
|
||||
|
||||
/// <summary>The tooltip to use for UI selection. Post-fluent.</summary>
|
||||
public readonly string Description = null;
|
||||
[FieldLoader.LoadUsing(nameof(LoadSettings))]
|
||||
[FieldLoader.Require]
|
||||
public readonly ImmutableList<MiniYamlNode> Settings = null;
|
||||
|
||||
/// <summary>
|
||||
/// Only offer the Choice for these tilesets. (If null, show for all.)
|
||||
/// </summary>
|
||||
public readonly IReadOnlySet<string> Tileset = null;
|
||||
|
||||
/// <summary>(Partial) settings to combine into the final overall settings.</summary>
|
||||
public readonly MiniYaml Settings;
|
||||
|
||||
public Choice(string id, MiniYaml my)
|
||||
static ImmutableList<MiniYamlNode> LoadSettings(MiniYaml yaml)
|
||||
{
|
||||
Id = id;
|
||||
var label = my.NodeWithKeyOrDefault("Label")?.Value.Value;
|
||||
if (label != null)
|
||||
{
|
||||
Label = FluentProvider.GetMessage($"{label}.label");
|
||||
FluentProvider.TryGetMessage($"{label}.description", out Description);
|
||||
}
|
||||
|
||||
Tileset = my.NodeWithKeyOrDefault("Tileset")?.Value.Value
|
||||
?.Split(',')
|
||||
.ToImmutableHashSet();
|
||||
Settings = my.NodeWithKey("Settings").Value;
|
||||
}
|
||||
|
||||
/// <summary>Create a choice that represents a top-level setting with a given value.</summary>
|
||||
public Choice(string setting, string value)
|
||||
{
|
||||
Id = value;
|
||||
Label = value;
|
||||
Description = null;
|
||||
Tileset = null;
|
||||
Settings = new MiniYaml(null, [new MiniYamlNode(setting, value)]);
|
||||
}
|
||||
|
||||
public static void DumpFluent(MiniYaml my, List<string> references)
|
||||
{
|
||||
var label = my.NodeWithKeyOrDefault("Label")?.Value.Value;
|
||||
if (label != null)
|
||||
{
|
||||
references.Add($"{label}.label");
|
||||
|
||||
// Descriptions are optional.
|
||||
if (FluentProvider.TryGetMessage($"{label}.description", out _))
|
||||
references.Add($"{label}.description");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Check whether this choice is permitted for this map.</summary>
|
||||
public bool Allowed(ITerrainInfo terrainInfo)
|
||||
{
|
||||
if (Tileset != null && !Tileset.Contains(terrainInfo.Id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>For single-setting choices, creates a new choice for that setting with a given value.</summary>
|
||||
public Choice NewValue(string value)
|
||||
{
|
||||
if (Settings.Nodes.Length != 1)
|
||||
throw new InvalidOperationException("NewValue can only be used on single-setting Choices");
|
||||
return new(Settings.Nodes[0].Key, value);
|
||||
return yaml.NodeWithKey("Settings").Value.Nodes.ToImmutableList();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Option
|
||||
[FieldLoader.LoadUsing(nameof(LoadChoices))]
|
||||
public readonly Dictionary<string, MapGeneratorDropdownChoice> Choices = null;
|
||||
|
||||
static Dictionary<string, MapGeneratorDropdownChoice> LoadChoices(MiniYaml yaml)
|
||||
{
|
||||
/// <summary>Unique identifier for this Option.</summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string Id;
|
||||
|
||||
/// <summary>The label to use for UI selection. Post-fluent.</summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string Label = null;
|
||||
|
||||
/// <summary>
|
||||
/// <para>For multiple-choice options (dropdowns/checkboxes) holds the allowed Choices.</para>
|
||||
/// <para>For text entry Options, contains the default value.</para>
|
||||
/// <para>If this is empty, the map is not compatible with the generator.</para>
|
||||
/// </summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly IReadOnlyList<Choice> Choices;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The default Choice for this option.</para>
|
||||
/// <para>Default will be ignored if it is not valid for the map.</para>
|
||||
/// </summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly Choice Default;
|
||||
|
||||
public readonly double Min = double.NegativeInfinity;
|
||||
public readonly double Max = double.PositiveInfinity;
|
||||
|
||||
/// <summary>Whether the Option can be randomized.</summary>
|
||||
public readonly bool Random = false;
|
||||
|
||||
/// <summary>How an option should be treated for UI purposes.</summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly UiType Ui;
|
||||
|
||||
/// <summary>Settings layering priority. Higher overrides lower.</summary>
|
||||
public readonly int Priority = 0;
|
||||
|
||||
public Option(string id, MiniYaml my, ITerrainInfo terrainInfo)
|
||||
{
|
||||
Id = id;
|
||||
FieldLoader.Load(this, my);
|
||||
var label = my.NodeWithKeyOrDefault("Label")?.Value.Value;
|
||||
if (label != null)
|
||||
Label = FluentProvider.GetMessage(label);
|
||||
|
||||
var choices = new List<Choice>();
|
||||
Choices = choices;
|
||||
|
||||
var integerSetting = my.NodeWithKeyOrDefault("Integer");
|
||||
var floatSetting = my.NodeWithKeyOrDefault("Float");
|
||||
var stringSetting = my.NodeWithKeyOrDefault("String");
|
||||
var checkboxSetting = my.NodeWithKeyOrDefault("Checkbox");
|
||||
var simpleChoiceSetting = my.NodeWithKeyOrDefault("SimpleChoice");
|
||||
var textSetting = integerSetting ?? floatSetting ?? stringSetting;
|
||||
if (textSetting != null)
|
||||
{
|
||||
var setting = textSetting.Value.Value;
|
||||
var value = my.NodeWithKeyOrDefault("Default")?.Value.Value ?? "";
|
||||
var choice = new Choice(setting, value);
|
||||
choices.Add(choice);
|
||||
Default = choice;
|
||||
Ui =
|
||||
integerSetting != null ? UiType.Integer :
|
||||
floatSetting != null ? UiType.Float :
|
||||
UiType.String;
|
||||
|
||||
if (Ui == UiType.Integer)
|
||||
{
|
||||
if (Min == double.NegativeInfinity)
|
||||
Min = int.MinValue;
|
||||
if (Max == double.PositiveInfinity)
|
||||
Max = int.MaxValue;
|
||||
}
|
||||
}
|
||||
else if (checkboxSetting != null)
|
||||
{
|
||||
var setting = checkboxSetting.Value.Value;
|
||||
var falseChoice = new Choice(setting, "False");
|
||||
var trueChoice = new Choice(setting, "True");
|
||||
choices.Add(falseChoice);
|
||||
choices.Add(trueChoice);
|
||||
var enabled = (my.NodeWithKeyOrDefault("Default")?.Value.Value ?? "False") == "True";
|
||||
Default = enabled ? trueChoice : falseChoice;
|
||||
Ui = UiType.Checkbox;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (simpleChoiceSetting != null)
|
||||
{
|
||||
var setting = simpleChoiceSetting.Value.Value;
|
||||
var values = simpleChoiceSetting.Value
|
||||
.NodeWithKey("Values").Value.Value
|
||||
.Split(',');
|
||||
foreach (var value in values)
|
||||
choices.Add(new Choice(setting, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var node in my.Nodes)
|
||||
{
|
||||
var split = node.Key.Split('@');
|
||||
if (split[0] == "Choice")
|
||||
{
|
||||
string choiceId = null;
|
||||
if (split.Length >= 2)
|
||||
choiceId = split[1];
|
||||
var choice = new Choice(choiceId, node.Value);
|
||||
if (choice.Allowed(terrainInfo))
|
||||
choices.Add(choice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Choices.Count > 0)
|
||||
{
|
||||
var defaultOrder = my.NodeWithKeyOrDefault("Default")?.Value.Value;
|
||||
if (defaultOrder != null)
|
||||
{
|
||||
foreach (var defaultChoice in defaultOrder.Split(','))
|
||||
{
|
||||
Default = Choices.FirstOrDefault(choice => choice.Id == defaultChoice);
|
||||
if (Default != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (Default == null)
|
||||
throw new YamlException($"None of option `{id}`'s default choices `{defaultOrder}` are not valid");
|
||||
}
|
||||
else
|
||||
{
|
||||
Default = Choices[0];
|
||||
}
|
||||
}
|
||||
|
||||
Ui = Label == null ? UiType.Hidden : UiType.DropDown;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DumpFluent(MiniYaml my, List<string> references)
|
||||
{
|
||||
var label = my.NodeWithKeyOrDefault("Label")?.Value.Value;
|
||||
if (label != null)
|
||||
references.Add(label);
|
||||
foreach (var node in my.Nodes)
|
||||
if (node.Key.Split('@')[0] == "Choice")
|
||||
Choice.DumpFluent(node.Value, references);
|
||||
}
|
||||
|
||||
public bool ValidateChoice(Choice choice)
|
||||
{
|
||||
switch (Ui)
|
||||
{
|
||||
case UiType.Integer:
|
||||
{
|
||||
var valid = long.TryParse(choice.Id, out var value);
|
||||
if (value < Min || value > Max)
|
||||
valid = false;
|
||||
return valid;
|
||||
}
|
||||
|
||||
case UiType.Float:
|
||||
{
|
||||
var valid = double.TryParse(choice.Id, out var value);
|
||||
if (value < Min || value > Max)
|
||||
valid = false;
|
||||
return valid;
|
||||
}
|
||||
|
||||
case UiType.String:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return Choices.Contains(choice);
|
||||
}
|
||||
}
|
||||
|
||||
public Choice RandomChoice()
|
||||
{
|
||||
if (Random)
|
||||
{
|
||||
var random = (long)Guid.NewGuid().GetHashCode() - int.MinValue;
|
||||
switch (Ui)
|
||||
{
|
||||
case UiType.Integer:
|
||||
var intRandom = (int)(random % (long)(Max - Min + 1) + (long)Min);
|
||||
return Default.NewValue(intRandom.ToStringInvariant());
|
||||
case UiType.Float:
|
||||
var floatRandom = (float)((double)0xffffffff * random / (Max - Min) + Min);
|
||||
return Default.NewValue(floatRandom.ToStringInvariant());
|
||||
case UiType.Checkbox:
|
||||
case UiType.DropDown:
|
||||
if (Choices.Count == 0)
|
||||
throw new InvalidOperationException($"Map is not compatible with Option `{Id}`");
|
||||
return Choices[(int)(random % Choices.Count)];
|
||||
default:
|
||||
throw new InvalidOperationException($"Option `{Id}` does not have a randomizable type");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Default;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFreeform()
|
||||
{
|
||||
switch (Ui)
|
||||
{
|
||||
case UiType.Hidden:
|
||||
case UiType.DropDown:
|
||||
case UiType.Checkbox:
|
||||
return false;
|
||||
case UiType.Integer:
|
||||
case UiType.Float:
|
||||
case UiType.String:
|
||||
return true;
|
||||
default:
|
||||
throw new InvalidOperationException("Bad UiType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public readonly IReadOnlyList<Option> Options;
|
||||
|
||||
/// <summary>
|
||||
/// Parse settings from a MiniYaml definition. Returns null if the map isn't compatible.
|
||||
/// </summary>
|
||||
public static MapGeneratorSettings LoadSettings(MiniYaml my, ITerrainInfo terrainInfo)
|
||||
{
|
||||
var options = new List<Option>();
|
||||
|
||||
foreach (var node in my.Nodes)
|
||||
var ret = new Dictionary<string, MapGeneratorDropdownChoice>();
|
||||
foreach (var node in yaml.Nodes)
|
||||
{
|
||||
var split = node.Key.Split('@');
|
||||
if (split[0] == "Option")
|
||||
{
|
||||
string id = null;
|
||||
if (split.Length >= 2)
|
||||
id = split[1];
|
||||
var option = new Option(id, node.Value, terrainInfo);
|
||||
if (option.Choices.Count == 0)
|
||||
return null;
|
||||
options.Add(option);
|
||||
}
|
||||
if (split.Length == 2 && split[0] == "Choice")
|
||||
ret.Add(split[1], FieldLoader.Load<MapGeneratorDropdownChoice>(node.Value));
|
||||
}
|
||||
|
||||
return new MapGeneratorSettings(options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static List<string> DumpFluent(MiniYaml my)
|
||||
public readonly string[] Default = null;
|
||||
string value = null;
|
||||
|
||||
public MapGeneratorMultiChoiceOption(string id, MiniYaml yaml)
|
||||
: base(id, yaml) { }
|
||||
|
||||
public override IReadOnlyCollection<MiniYamlNode> GetSettings(ITerrainInfo terrainInfo)
|
||||
{
|
||||
var references = new List<string>();
|
||||
DumpFluent(my, references);
|
||||
return references;
|
||||
var validChoices = ValidChoices(terrainInfo);
|
||||
if (validChoices.Contains(value))
|
||||
return Choices[value].Settings;
|
||||
|
||||
var fallback = Default?.FirstOrDefault(validChoices.Contains) ?? validChoices.FirstOrDefault();
|
||||
return fallback != null ? Choices[fallback].Settings : [];
|
||||
}
|
||||
|
||||
public static void DumpFluent(MiniYaml my, List<string> references)
|
||||
public string Value
|
||||
{
|
||||
foreach (var node in my.Nodes)
|
||||
if (node.Key.Split('@')[0] == "Option")
|
||||
Option.DumpFluent(node.Value, references);
|
||||
}
|
||||
|
||||
MapGeneratorSettings(IReadOnlyList<Option> options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public Dictionary<Option, Choice> DefaultChoices()
|
||||
{
|
||||
return Options.ToDictionary(option => option, option => option.Default);
|
||||
}
|
||||
|
||||
/// <summary>Merge all choices into a complete settings MiniYaml.</summary>
|
||||
public MiniYaml Compile(IReadOnlyDictionary<Option, Choice> choices)
|
||||
{
|
||||
var layers = new List<IReadOnlyCollection<MiniYamlNode>>();
|
||||
|
||||
// Apply the choices in their canonical order.
|
||||
foreach (var option in Options.OrderBy(option => option.Priority))
|
||||
get => value;
|
||||
set
|
||||
{
|
||||
var choice = choices[option];
|
||||
if (!option.ValidateChoice(choice))
|
||||
throw new ArgumentException($"Option `{option.Id}` has illegal choice");
|
||||
layers.Add(choice.Settings.Nodes);
|
||||
}
|
||||
if (!Choices.ContainsKey(value))
|
||||
throw new ArgumentException($"{value} is not in the list of valid choices");
|
||||
|
||||
var settingsNodes = MiniYaml.Merge(layers);
|
||||
return new MiniYaml(null, settingsNodes);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> ValidChoices(ITerrainInfo terrainInfo)
|
||||
{
|
||||
return Choices
|
||||
.Where(kv => kv.Value.Tileset == null || kv.Value.Tileset.Contains(terrainInfo.Id))
|
||||
.Select(kv => kv.Key)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override IEnumerable<string> GetFluentReferences()
|
||||
{
|
||||
if (Label != null)
|
||||
yield return Label;
|
||||
|
||||
foreach (var c in Choices.Values)
|
||||
{
|
||||
if (c.Label == null)
|
||||
continue;
|
||||
|
||||
yield return c.Label + ".label";
|
||||
|
||||
// Descriptions are optional
|
||||
if (FluentProvider.TryGetMessage(c.Label + ".description", out _))
|
||||
yield return c.Label + ".description";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MapGeneratorMultiIntegerChoiceOption : MapGeneratorOption
|
||||
{
|
||||
[FieldLoader.Require]
|
||||
public readonly string Parameter = null;
|
||||
|
||||
[FieldLoader.Require]
|
||||
public readonly int[] Choices = null;
|
||||
|
||||
public readonly int? Default = null;
|
||||
int value;
|
||||
|
||||
public MapGeneratorMultiIntegerChoiceOption(string id, MiniYaml yaml)
|
||||
: base(id, yaml)
|
||||
{
|
||||
Value = Default ?? Choices?.First() ?? 0;
|
||||
}
|
||||
|
||||
public int Value
|
||||
{
|
||||
get => value;
|
||||
set
|
||||
{
|
||||
if (!Choices.Contains(value))
|
||||
throw new ArgumentException($"{value} is not in the list of valid choices");
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyCollection<MiniYamlNode> GetSettings(ITerrainInfo terrainInfo)
|
||||
{
|
||||
return [new MiniYamlNode(Parameter, FieldSaver.FormatValue(Value))];
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MapGeneratorSettings : IMapGeneratorSettings
|
||||
{
|
||||
public MapGeneratorSettings(MiniYaml yaml)
|
||||
{
|
||||
foreach (var node in yaml.Nodes)
|
||||
{
|
||||
var split = node.Key.Split('@');
|
||||
if (split.Length != 2)
|
||||
continue;
|
||||
|
||||
if (split[0] == "BooleanOption")
|
||||
Options.Add(new MapGeneratorBooleanOption(split[1], node.Value));
|
||||
else if (split[0] == "IntegerOption")
|
||||
Options.Add(new MapGeneratorIntegerOption(split[1], node.Value));
|
||||
else if (split[0] == "MultiIntegerChoiceOption")
|
||||
Options.Add(new MapGeneratorMultiIntegerChoiceOption(split[1], node.Value));
|
||||
else if (split[0] == "MultiChoiceOption")
|
||||
Options.Add(new MapGeneratorMultiChoiceOption(split[1], node.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public List<MapGeneratorOption> Options { get; } = [];
|
||||
|
||||
public void Randomize(MersenneTwister random)
|
||||
{
|
||||
if (Options.FirstOrDefault(o => o.Id == "Seed") is MapGeneratorIntegerOption seed)
|
||||
seed.Value = random.Next();
|
||||
}
|
||||
|
||||
public MiniYaml Compile(ITerrainInfo terrainInfo)
|
||||
{
|
||||
// Apply the choices in their canonical order.
|
||||
var layers = Options
|
||||
.OrderBy(option => option.Priority)
|
||||
.Select(o => o.GetSettings(terrainInfo));
|
||||
|
||||
return new MiniYaml(null, MiniYaml.Merge(layers));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.MapGenerator;
|
||||
using OpenRA.Mods.Common.Terrain;
|
||||
using OpenRA.Support;
|
||||
@@ -50,12 +51,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
static List<string> FluentReferencesLoader(MiniYaml my)
|
||||
{
|
||||
return MapGeneratorSettings.DumpFluent(my.NodeWithKey("Settings").Value);
|
||||
return new MapGeneratorSettings(my.NodeWithKey("Settings").Value)
|
||||
.Options.SelectMany(o => o.GetFluentReferences()).ToList();
|
||||
}
|
||||
|
||||
public MapGeneratorSettings GetSettings(ITerrainInfo terrainInfo)
|
||||
public IMapGeneratorSettings GetSettings()
|
||||
{
|
||||
return MapGeneratorSettings.LoadSettings(Settings, terrainInfo);
|
||||
return new MapGeneratorSettings(Settings);
|
||||
}
|
||||
|
||||
public void Generate(Map map, MiniYaml settings)
|
||||
|
||||
@@ -50,7 +50,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
static List<string> FluentReferencesLoader(MiniYaml my)
|
||||
{
|
||||
return MapGeneratorSettings.DumpFluent(my.NodeWithKey("Settings").Value);
|
||||
return new MapGeneratorSettings(my.NodeWithKey("Settings").Value)
|
||||
.Options.SelectMany(o => o.GetFluentReferences()).ToList();
|
||||
}
|
||||
|
||||
const int FractionMax = 1000;
|
||||
@@ -481,9 +482,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public MapGeneratorSettings GetSettings(ITerrainInfo terrainInfo)
|
||||
public IMapGeneratorSettings GetSettings()
|
||||
{
|
||||
return MapGeneratorSettings.LoadSettings(Settings, terrainInfo);
|
||||
return new MapGeneratorSettings(Settings);
|
||||
}
|
||||
|
||||
public void Generate(Map map, MiniYaml settings)
|
||||
|
||||
@@ -969,16 +969,23 @@ namespace OpenRA.Mods.Common.Traits
|
||||
: base(message, inner) { }
|
||||
}
|
||||
|
||||
public interface IMapGeneratorSettings
|
||||
{
|
||||
/// <summary>Returns the options that allow users to customise the result.</summary>
|
||||
List<MapGeneratorOption> Options { get; }
|
||||
|
||||
void Randomize(MersenneTwister random);
|
||||
|
||||
/// <summary>Merge all choices into a complete settings MiniYaml.</summary>
|
||||
MiniYaml Compile(ITerrainInfo terrainInfo);
|
||||
}
|
||||
|
||||
public interface IMapGeneratorInfo : ITraitInfoInterface
|
||||
{
|
||||
string Type { get; }
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the generator settings available for this tileset.
|
||||
/// Returns null if not compatible with the given tileset.
|
||||
/// </summary>
|
||||
MapGeneratorSettings GetSettings(ITerrainInfo terrainInfo);
|
||||
IMapGeneratorSettings GetSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Generate or manipulate a supplied map in-place.
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenRA.Mods.Common.MapGenerator;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.UtilityCommands
|
||||
@@ -204,9 +205,9 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
.Select(variable => config.Choices[variable].Length)
|
||||
.ToImmutableArray();
|
||||
|
||||
var generator =
|
||||
modData.DefaultRules.Actors[SystemActors.EditorWorld].TraitInfos<IMapGeneratorInfo>()
|
||||
.FirstOrDefault(info => info.Type == config.MapGeneratorType);
|
||||
var generator = modData.DefaultRules.Actors[SystemActors.EditorWorld]
|
||||
.TraitInfos<IMapGeneratorInfo>()
|
||||
.FirstOrDefault(info => info.Type == config.MapGeneratorType);
|
||||
if (generator == null)
|
||||
throw new ArgumentException($"No map generator with type `{config.MapGeneratorType}`");
|
||||
|
||||
@@ -246,7 +247,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
.Select(kv => $"{kv.Key}={kv.Value}")
|
||||
.ToHashSet();
|
||||
|
||||
var tileset = modData.DefaultTerrainInfo[iterationChoices[Configuration.TilesetVariable]];
|
||||
var terrainInfo = modData.DefaultTerrainInfo[iterationChoices[Configuration.TilesetVariable]];
|
||||
iterationChoices.Remove(Configuration.TilesetVariable);
|
||||
|
||||
var size = iterationChoices[Configuration.SizeVariable]
|
||||
@@ -263,26 +264,31 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
if (size.Length != 2)
|
||||
throw new ArgumentException($"bad map size `{iterationChoices[Configuration.SizeVariable]}`");
|
||||
|
||||
var map = new Map(modData, tileset, size[0], size[1]);
|
||||
var map = new Map(modData, terrainInfo, size[0], size[1]);
|
||||
var maxTerrainHeight = map.Grid.MaximumTerrainHeight;
|
||||
var tl = new PPos(1, 1 + maxTerrainHeight);
|
||||
var br = new PPos(size[0], size[1] + maxTerrainHeight);
|
||||
map.SetBounds(tl, br);
|
||||
|
||||
var settings = generator.GetSettings(tileset);
|
||||
var choices = settings.DefaultChoices();
|
||||
foreach (var option in choices.Keys)
|
||||
if (iterationChoices.TryGetValue(option.Id, out var choice))
|
||||
var settings = generator.GetSettings();
|
||||
foreach (var o in settings.Options)
|
||||
{
|
||||
if (iterationChoices.TryGetValue(o.Id, out var choice))
|
||||
{
|
||||
choices[option] = option.IsFreeform()
|
||||
? choices[option].NewValue(choice)
|
||||
: option.Choices.First(c => c.Id == choice);
|
||||
iterationChoices.Remove(option.Id);
|
||||
if (o is MapGeneratorBooleanOption bo)
|
||||
bo.Value = FieldLoader.GetValue<bool>("choice", choice);
|
||||
else if (o is MapGeneratorIntegerOption io)
|
||||
io.Value = FieldLoader.GetValue<int>("choice", choice);
|
||||
else if (o is MapGeneratorMultiIntegerChoiceOption mio)
|
||||
mio.Value = FieldLoader.GetValue<int>("choice", choice);
|
||||
else if (o is MapGeneratorMultiChoiceOption mo)
|
||||
mo.Value = choice;
|
||||
|
||||
iterationChoices.Remove(o.Id);
|
||||
}
|
||||
else if (config.NoDefaults)
|
||||
{
|
||||
throw new ArgumentException($"No choices specified for option `{option.Id}`");
|
||||
}
|
||||
throw new ArgumentException($"No choices specified for option `{o.Id}`");
|
||||
}
|
||||
|
||||
if (iterationChoices.Count != 0)
|
||||
throw new ArgumentException($"Unknown options: {string.Join(", ", iterationChoices.Keys)}");
|
||||
@@ -292,8 +298,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
tests++;
|
||||
try
|
||||
{
|
||||
var my = settings.Compile(choices);
|
||||
generator.Generate(map, my);
|
||||
generator.Generate(map, settings.Compile(terrainInfo));
|
||||
}
|
||||
catch (Exception e) when (e is MapGenerationException || e is YamlException)
|
||||
{
|
||||
|
||||
@@ -27,24 +27,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
[FluentReference("name")]
|
||||
const string StrGenerated = "notification-map-generator-generated";
|
||||
[FluentReference]
|
||||
const string StrBadOption = "notification-map-generator-bad-option";
|
||||
[FluentReference]
|
||||
const string StrFailed = "notification-map-generator-failed";
|
||||
[FluentReference]
|
||||
const string StrFailedCancel = "label-map-generator-failed-cancel";
|
||||
|
||||
readonly EditorActionManager editorActionManager;
|
||||
readonly ButtonWidget generateButtonWidget;
|
||||
readonly ButtonWidget generateRandomButtonWidget;
|
||||
readonly World world;
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly ModData modData;
|
||||
|
||||
// nullable
|
||||
IMapGeneratorInfo selectedGenerator;
|
||||
|
||||
readonly Dictionary<IMapGeneratorInfo, MapGeneratorSettings> generatorsToSettings;
|
||||
readonly Dictionary<IMapGeneratorInfo, Dictionary<MapGeneratorSettings.Option, MapGeneratorSettings.Choice>> generatorsToSettingsChoices;
|
||||
IMapGeneratorSettings selectedSettings;
|
||||
|
||||
readonly ScrollPanelWidget settingsPanel;
|
||||
readonly Widget checkboxSettingTemplate;
|
||||
@@ -60,42 +54,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
this.worldRenderer = worldRenderer;
|
||||
this.modData = modData;
|
||||
|
||||
selectedGenerator = null;
|
||||
generatorsToSettings = [];
|
||||
generatorsToSettingsChoices = [];
|
||||
|
||||
var mapGenerators = new List<IMapGeneratorInfo>();
|
||||
var terrainInfo = modData.DefaultTerrainInfo[world.Map.Tileset];
|
||||
foreach (var generator in world.Map.Rules.Actors[SystemActors.EditorWorld].TraitInfos<IMapGeneratorInfo>())
|
||||
{
|
||||
var settings = generator.GetSettings(terrainInfo);
|
||||
if (settings == null)
|
||||
continue;
|
||||
|
||||
var choices = settings.DefaultChoices();
|
||||
mapGenerators.Add(generator);
|
||||
generatorsToSettingsChoices.Add(generator, choices);
|
||||
generatorsToSettings.Add(generator, settings);
|
||||
}
|
||||
|
||||
generateButtonWidget = widget.Get<ButtonWidget>("GENERATE_BUTTON");
|
||||
generateRandomButtonWidget = widget.Get<ButtonWidget>("GENERATE_RANDOM_BUTTON");
|
||||
|
||||
settingsPanel = widget.Get<ScrollPanelWidget>("SETTINGS_PANEL");
|
||||
checkboxSettingTemplate = settingsPanel.Get<Widget>("CHECKBOX_TEMPLATE");
|
||||
textSettingTemplate = settingsPanel.Get<Widget>("TEXT_TEMPLATE");
|
||||
dropDownSettingTemplate = settingsPanel.Get<Widget>("DROPDOWN_TEMPLATE");
|
||||
|
||||
var generateButtonWidget = widget.Get<ButtonWidget>("GENERATE_BUTTON");
|
||||
generateButtonWidget.OnClick = GenerateMap;
|
||||
|
||||
var generateRandomButtonWidget = widget.Get<ButtonWidget>("GENERATE_RANDOM_BUTTON");
|
||||
generateRandomButtonWidget.OnClick = () =>
|
||||
{
|
||||
Randomize();
|
||||
selectedSettings?.Randomize(world.LocalRandom);
|
||||
UpdateSettingsUi();
|
||||
GenerateMap();
|
||||
};
|
||||
|
||||
var generatorDropDown = widget.Get<DropDownButtonWidget>("GENERATOR");
|
||||
ChangeGenerator(mapGenerators.FirstOrDefault());
|
||||
if (selectedGenerator != null)
|
||||
var mapGenerators = world.Map.Rules.Actors[SystemActors.EditorWorld].TraitInfos<IMapGeneratorInfo>();
|
||||
if (mapGenerators.Count > 0)
|
||||
{
|
||||
var label = new CachedTransform<IMapGeneratorInfo, string>(g => FluentProvider.GetMessage(g.Name));
|
||||
generatorDropDown.GetText = () => label.Update(selectedGenerator);
|
||||
@@ -104,7 +81,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
ScrollItemWidget SetupItem(IMapGeneratorInfo g, ScrollItemWidget template)
|
||||
{
|
||||
bool IsSelected() => g.Type == selectedGenerator.Type;
|
||||
void OnClick() => ChangeGenerator(mapGenerators.First(generator => generator.Type == g.Type));
|
||||
void OnClick() => ChangeGenerator(g);
|
||||
var item = ScrollItemWidget.Setup(template, IsSelected, OnClick);
|
||||
var label = FluentProvider.GetMessage(g.Name);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => label;
|
||||
@@ -120,6 +97,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
generateRandomButtonWidget.IsDisabled = () => true;
|
||||
generatorDropDown.IsDisabled = () => true;
|
||||
}
|
||||
|
||||
ChangeGenerator(mapGenerators.FirstOrDefault());
|
||||
}
|
||||
|
||||
sealed class RandomMapEditorAction : IEditorAction
|
||||
@@ -155,6 +134,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
void ChangeGenerator(IMapGeneratorInfo newGenerator)
|
||||
{
|
||||
selectedGenerator = newGenerator;
|
||||
selectedSettings = newGenerator?.GetSettings();
|
||||
|
||||
UpdateSettingsUi();
|
||||
}
|
||||
@@ -166,89 +146,119 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
if (selectedGenerator == null)
|
||||
return;
|
||||
|
||||
var settings = generatorsToSettings[selectedGenerator];
|
||||
var choices = generatorsToSettingsChoices[selectedGenerator];
|
||||
foreach (var option in settings.Options)
|
||||
foreach (var o in selectedSettings.Options)
|
||||
{
|
||||
Widget settingWidget;
|
||||
|
||||
switch (option.Ui)
|
||||
Widget settingWidget = null;
|
||||
switch (o)
|
||||
{
|
||||
case MapGeneratorSettings.UiType.Hidden:
|
||||
continue;
|
||||
case MapGeneratorBooleanOption bo:
|
||||
{
|
||||
settingWidget = checkboxSettingTemplate.Clone();
|
||||
var checkboxWidget = settingWidget.Get<CheckboxWidget>("CHECKBOX");
|
||||
var label = FluentProvider.GetMessage(bo.Label);
|
||||
checkboxWidget.GetText = () => label;
|
||||
checkboxWidget.IsChecked = () => bo.Value;
|
||||
checkboxWidget.OnClick = () => bo.Value ^= true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MapGeneratorSettings.UiType.DropDown:
|
||||
case MapGeneratorIntegerOption io:
|
||||
{
|
||||
settingWidget = textSettingTemplate.Clone();
|
||||
var labelWidget = settingWidget.Get<LabelWidget>("LABEL");
|
||||
var label = FluentProvider.GetMessage(io.Label);
|
||||
labelWidget.GetText = () => label;
|
||||
var textFieldWidget = settingWidget.Get<TextFieldWidget>("INPUT");
|
||||
textFieldWidget.Type = TextFieldType.Integer;
|
||||
textFieldWidget.Text = FieldSaver.FormatValue(io.Value);
|
||||
textFieldWidget.OnTextEdited = () =>
|
||||
{
|
||||
var valid = int.TryParse(textFieldWidget.Text, out io.Value);
|
||||
textFieldWidget.IsValid = () => valid;
|
||||
};
|
||||
|
||||
textFieldWidget.OnEscKey = _ => { textFieldWidget.YieldKeyboardFocus(); return true; };
|
||||
textFieldWidget.OnEnterKey = _ => { textFieldWidget.YieldKeyboardFocus(); return true; };
|
||||
break;
|
||||
}
|
||||
|
||||
case MapGeneratorMultiIntegerChoiceOption mio:
|
||||
{
|
||||
settingWidget = dropDownSettingTemplate.Clone();
|
||||
var label = settingWidget.Get<LabelWidget>("LABEL");
|
||||
label.GetText = () => option.Label;
|
||||
var dropDown = settingWidget.Get<DropDownButtonWidget>("DROPDOWN");
|
||||
dropDown.GetText = () => choices[option].Label;
|
||||
dropDown.OnMouseDown = _ =>
|
||||
{
|
||||
ScrollItemWidget SetupItem(MapGeneratorSettings.Choice choice, ScrollItemWidget template)
|
||||
{
|
||||
bool IsSelected() => choice == choices[option];
|
||||
void OnClick() => choices[option] = choice;
|
||||
var item = ScrollItemWidget.Setup(template, IsSelected, OnClick);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => choice.Label;
|
||||
item.GetTooltipText =
|
||||
choice.Description != null
|
||||
? () => choice.Description
|
||||
: null;
|
||||
var labelWidget = settingWidget.Get<LabelWidget>("LABEL");
|
||||
var label = FluentProvider.GetMessage(mio.Label);
|
||||
labelWidget.GetText = () => label;
|
||||
|
||||
var labelCache = new CachedTransform<int, string>(v => FieldSaver.FormatValue(v));
|
||||
var dropDownWidget = settingWidget.Get<DropDownButtonWidget>("DROPDOWN");
|
||||
dropDownWidget.GetText = () => labelCache.Update(mio.Value);
|
||||
dropDownWidget.OnMouseDown = _ =>
|
||||
{
|
||||
ScrollItemWidget SetupItem(int choice, ScrollItemWidget template)
|
||||
{
|
||||
bool IsSelected() => choice == mio.Value;
|
||||
void OnClick() => mio.Value = choice;
|
||||
var item = ScrollItemWidget.Setup(template, IsSelected, OnClick);
|
||||
|
||||
var itemLabel = FieldSaver.FormatValue(choice);
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => itemLabel;
|
||||
item.GetTooltipText = null;
|
||||
return item;
|
||||
}
|
||||
|
||||
dropDown.ShowDropDown("LABEL_DROPDOWN_WITH_TOOLTIP_TEMPLATE", option.Choices.Count * 30, option.Choices, SetupItem);
|
||||
dropDownWidget.ShowDropDown("LABEL_DROPDOWN_WITH_TOOLTIP_TEMPLATE", mio.Choices.Length * 30, mio.Choices, SetupItem);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
case MapGeneratorSettings.UiType.Checkbox:
|
||||
case MapGeneratorMultiChoiceOption mo:
|
||||
{
|
||||
if (option.Choices.Count != 2)
|
||||
throw new InvalidOperationException("Checkbox option that does not have two choices");
|
||||
settingWidget = checkboxSettingTemplate.Clone();
|
||||
var checkbox = settingWidget.Get<CheckboxWidget>("CHECKBOX");
|
||||
checkbox.GetText = () => option.Label;
|
||||
checkbox.IsChecked = () => choices[option] == option.Choices[1];
|
||||
checkbox.OnClick = () =>
|
||||
choices[option] =
|
||||
choices[option] == option.Choices[1]
|
||||
? option.Choices[0]
|
||||
: option.Choices[1];
|
||||
break;
|
||||
}
|
||||
var validChoices = mo.ValidChoices(world.Map.Rules.TerrainInfo);
|
||||
if (!validChoices.Contains(mo.Value))
|
||||
mo.Value = mo.Default?.FirstOrDefault(validChoices.Contains) ?? validChoices.FirstOrDefault();
|
||||
|
||||
case MapGeneratorSettings.UiType.Integer:
|
||||
case MapGeneratorSettings.UiType.Float:
|
||||
case MapGeneratorSettings.UiType.String:
|
||||
{
|
||||
if (option.Choices.Count != 1)
|
||||
throw new InvalidOperationException("Text option that does not have one choice");
|
||||
settingWidget = textSettingTemplate.Clone();
|
||||
var label = settingWidget.Get<LabelWidget>("LABEL");
|
||||
label.GetText = () => option.Label;
|
||||
var input = settingWidget.Get<TextFieldWidget>("INPUT");
|
||||
input.Text = choices[option].Id;
|
||||
input.OnTextEdited = () =>
|
||||
if (mo.Label != null && validChoices.Count > 0)
|
||||
{
|
||||
var choice = choices[option].NewValue(input.Text);
|
||||
choices[option] = choice;
|
||||
var valid = option.ValidateChoice(choice);
|
||||
input.IsValid = () => valid;
|
||||
};
|
||||
settingWidget = dropDownSettingTemplate.Clone();
|
||||
var labelWidget = settingWidget.Get<LabelWidget>("LABEL");
|
||||
var label = FluentProvider.GetMessage(mo.Label);
|
||||
labelWidget.GetText = () => label;
|
||||
|
||||
var labelCache = new CachedTransform<string, string>(v => FluentProvider.GetMessage(mo.Choices[v].Label + ".label"));
|
||||
var dropDownWidget = settingWidget.Get<DropDownButtonWidget>("DROPDOWN");
|
||||
dropDownWidget.GetText = () => labelCache.Update(mo.Value);
|
||||
dropDownWidget.OnMouseDown = _ =>
|
||||
{
|
||||
ScrollItemWidget SetupItem(string choice, ScrollItemWidget template)
|
||||
{
|
||||
bool IsSelected() => choice == mo.Value;
|
||||
void OnClick() => mo.Value = choice;
|
||||
var item = ScrollItemWidget.Setup(template, IsSelected, OnClick);
|
||||
|
||||
var itemLabel = FluentProvider.GetMessage(mo.Choices[choice].Label + ".label");
|
||||
item.Get<LabelWidget>("LABEL").GetText = () => itemLabel;
|
||||
if (FluentProvider.TryGetMessage(mo.Choices[choice].Label + ".description", out var desc))
|
||||
item.GetTooltipText = () => desc;
|
||||
else
|
||||
item.GetTooltipText = null;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
dropDownWidget.ShowDropDown("LABEL_DROPDOWN_WITH_TOOLTIP_TEMPLATE", validChoices.Count * 30, validChoices, SetupItem);
|
||||
};
|
||||
}
|
||||
|
||||
input.OnEscKey = _ => { input.YieldKeyboardFocus(); return true; };
|
||||
input.OnEnterKey = _ => { input.YieldKeyboardFocus(); return true; };
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported MapGeneratorSettings.UiType");
|
||||
throw new NotImplementedException($"Unhandled MapGeneratorOption type {o.GetType().Name}");
|
||||
}
|
||||
|
||||
if (settingWidget == null)
|
||||
continue;
|
||||
|
||||
settingWidget.IsVisible = () => true;
|
||||
settingsPanel.AddChild(settingWidget);
|
||||
}
|
||||
@@ -266,18 +276,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
cancelText: StrFailedCancel);
|
||||
}
|
||||
|
||||
void Randomize()
|
||||
{
|
||||
var choices = generatorsToSettingsChoices[selectedGenerator];
|
||||
foreach (var option in choices.Keys)
|
||||
{
|
||||
if (option.Random)
|
||||
choices[option] = option.RandomChoice();
|
||||
}
|
||||
|
||||
UpdateSettingsUi();
|
||||
}
|
||||
|
||||
void GenerateMap()
|
||||
{
|
||||
try
|
||||
@@ -293,22 +291,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
void GenerateMapMayThrow()
|
||||
{
|
||||
var map = world.Map;
|
||||
var tileset = modData.DefaultTerrainInfo[map.Tileset];
|
||||
var generatedMap = new Map(modData, tileset, map.MapSize.X, map.MapSize.Y);
|
||||
var terrainInfo = map.Rules.TerrainInfo;
|
||||
var generatedMap = new Map(modData, terrainInfo, map.MapSize.X, map.MapSize.Y);
|
||||
var bounds = map.Bounds;
|
||||
generatedMap.SetBounds(new PPos(bounds.Left, bounds.Top), new PPos(bounds.Right - 1, bounds.Bottom - 1));
|
||||
var choices = generatorsToSettingsChoices[selectedGenerator];
|
||||
|
||||
foreach (var optionChoice in choices)
|
||||
{
|
||||
var option = optionChoice.Key;
|
||||
var choice = optionChoice.Value;
|
||||
if (!option.ValidateChoice(choice))
|
||||
throw new MapGenerationException(
|
||||
FluentProvider.GetMessage(StrBadOption, "option", option.Label));
|
||||
}
|
||||
|
||||
var settings = generatorsToSettings[selectedGenerator].Compile(choices);
|
||||
var settings = selectedSettings.Compile(terrainInfo);
|
||||
|
||||
// Run main generator logic. May throw.
|
||||
var generateStopwatch = Stopwatch.StartNew();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Type: experimental
|
||||
Name: map-generator-experimental
|
||||
Settings:
|
||||
Option@hidden_defaults:
|
||||
MultiChoiceOption@hidden_defaults:
|
||||
Choice@hidden_defaults:
|
||||
Settings:
|
||||
TerrainFeatureSize: 20480
|
||||
@@ -71,7 +71,7 @@
|
||||
ForestObstacles: Trees
|
||||
UnplayableObstacles: Obstructions
|
||||
CivilianBuildingsObstacles: CivilianBuildings
|
||||
Option@hidden_tileset_overrides:
|
||||
MultiChoiceOption@hidden_tileset_overrides:
|
||||
Choice@common:
|
||||
Tileset: DESERT,SNOW,TEMPERAT
|
||||
Settings:
|
||||
@@ -87,12 +87,11 @@
|
||||
RepaintTiles:
|
||||
255: Snow
|
||||
1: Water
|
||||
Option@Seed:
|
||||
IntegerOption@Seed:
|
||||
Label: label-cnc-map-generator-option-seed
|
||||
Random: True
|
||||
Parameter: Seed
|
||||
Default: 0
|
||||
Integer: Seed
|
||||
Option@TerrainType:
|
||||
MultiChoiceOption@TerrainType:
|
||||
Label: label-cnc-map-generator-option-terrain-type
|
||||
Priority: 2
|
||||
Default: Gardens,Rocky
|
||||
@@ -170,13 +169,13 @@
|
||||
Mountains: 1000
|
||||
Roughness: 850
|
||||
MinimumTerrainContourSpacing: 5
|
||||
Option@Rotations:
|
||||
MultiIntegerChoiceOption@Rotations:
|
||||
Label: label-cnc-map-generator-option-rotations
|
||||
SimpleChoice: Rotations
|
||||
Values: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
|
||||
Parameter: Rotations
|
||||
Choices: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
|
||||
Default: 2
|
||||
Priority: 1
|
||||
Option@Mirror:
|
||||
MultiChoiceOption@Mirror:
|
||||
Label: label-cnc-map-generator-option-mirror
|
||||
Default: None
|
||||
Priority: 1
|
||||
@@ -200,7 +199,7 @@
|
||||
Label: label-cnc-map-generator-choice-mirror-top-right-matches-bottom-left
|
||||
Settings:
|
||||
Mirror: TopRightMatchesBottomLeft
|
||||
Option@Shape:
|
||||
MultiChoiceOption@Shape:
|
||||
Label: label-cnc-map-generator-option-shape
|
||||
Default: Square
|
||||
Priority: 1
|
||||
@@ -217,13 +216,13 @@
|
||||
Tileset: DESERT
|
||||
Settings:
|
||||
ExternalCircularBias: -1
|
||||
Option@Players:
|
||||
MultiIntegerChoiceOption@Players:
|
||||
Label: label-cnc-map-generator-option-players
|
||||
SimpleChoice: Players
|
||||
Values: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
|
||||
Parameter: Players
|
||||
Choices: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
|
||||
Default: 1
|
||||
Priority: 1
|
||||
Option@Resources:
|
||||
MultiChoiceOption@Resources:
|
||||
Label: label-cnc-map-generator-option-resources
|
||||
Default: Medium
|
||||
Choice@None:
|
||||
@@ -285,7 +284,7 @@
|
||||
ResourceSpawnWeights:
|
||||
MaximumExpansionResourceSpawns: 0
|
||||
MaximumResourceSpawnsPerExpansion: 1
|
||||
Option@Buildings:
|
||||
MultiChoiceOption@Buildings:
|
||||
Label: label-cnc-map-generator-option-buildings
|
||||
Default: Standard
|
||||
Choice@None:
|
||||
@@ -327,7 +326,7 @@
|
||||
MaximumBuildings: 10
|
||||
BuildingWeights:
|
||||
v19: 1
|
||||
Option@Density:
|
||||
MultiChoiceOption@Density:
|
||||
Label: label-cnc-map-generator-option-density
|
||||
Default: Players
|
||||
Priority: 1
|
||||
@@ -366,17 +365,17 @@
|
||||
Settings:
|
||||
AreaEntityBonus: 800
|
||||
PlayerCountEntityBonus: 0
|
||||
Option@DenyWalledArea:
|
||||
BooleanOption@DenyWalledArea:
|
||||
Label: label-cnc-map-generator-option-deny-walled-areas
|
||||
Checkbox: DenyWalledAreas
|
||||
Parameter: DenyWalledAreas
|
||||
Default: True
|
||||
Priority: 1
|
||||
Option@Roads:
|
||||
BooleanOption@Roads:
|
||||
Label: label-cnc-map-generator-option-roads
|
||||
Checkbox: Roads
|
||||
Parameter: Roads
|
||||
Default: True
|
||||
Priority: 1
|
||||
Option@CivilianDensity:
|
||||
MultiChoiceOption@CivilianDensity:
|
||||
Label: label-cnc-map-generator-option-civilian-density
|
||||
Default: Default
|
||||
Priority: 3
|
||||
@@ -411,7 +410,7 @@
|
||||
Type: clear
|
||||
Name: map-generator-clear
|
||||
Settings:
|
||||
Option@Tile:
|
||||
MultiChoiceOption@Tile:
|
||||
Label: label-clear-map-generator-option-tile
|
||||
Choice@CommonClear:
|
||||
Label: label-clear-map-generator-choice-tile-clear
|
||||
|
||||
@@ -1142,5 +1142,4 @@ keycode =
|
||||
## MapGeneratorToolLogic
|
||||
label-map-generator-failed-cancel = Dismiss
|
||||
notification-map-generator-generated = Generated using { $name }
|
||||
notification-map-generator-bad-option = Option "{ $option }" is invalid
|
||||
notification-map-generator-failed = Map generation failed
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Type: experimental
|
||||
Name: map-generator-experimental
|
||||
Settings:
|
||||
Option@hidden_defaults:
|
||||
MultiChoiceOption@hidden_defaults:
|
||||
Choice@hidden_defaults:
|
||||
Settings:
|
||||
TerrainFeatureSize: 20480
|
||||
@@ -70,7 +70,7 @@
|
||||
ForestObstacles: Trees
|
||||
UnplayableObstacles: Obstructions
|
||||
CivilianBuildingsObstacles: CivilianBuildings
|
||||
Option@hidden_tileset_overrides:
|
||||
MultiChoiceOption@hidden_tileset_overrides:
|
||||
Choice@desert:
|
||||
Tileset: DESERT
|
||||
Settings:
|
||||
@@ -86,12 +86,11 @@
|
||||
Settings:
|
||||
LandTile: 255
|
||||
WaterTile: 1
|
||||
Option@Seed:
|
||||
IntegerOption@Seed:
|
||||
Label: label-ra-map-generator-option-seed
|
||||
Random: True
|
||||
Parameter: Seed
|
||||
Default: 0
|
||||
Integer: Seed
|
||||
Option@TerrainType:
|
||||
MultiChoiceOption@TerrainType:
|
||||
Label: label-ra-map-generator-option-terrain-type
|
||||
Priority: 2
|
||||
Default: Gardens
|
||||
@@ -193,13 +192,13 @@
|
||||
Forests: 0
|
||||
SpawnBuildSize: 6
|
||||
MinimumSpawnRadius: 4
|
||||
Option@Rotations:
|
||||
MultiIntegerChoiceOption@Rotations:
|
||||
Label: label-ra-map-generator-option-rotations
|
||||
SimpleChoice: Rotations
|
||||
Values: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
|
||||
Parameter: Rotations
|
||||
Choices: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
|
||||
Default: 2
|
||||
Priority: 1
|
||||
Option@Mirror:
|
||||
MultiChoiceOption@Mirror:
|
||||
Label: label-ra-map-generator-option-mirror
|
||||
Default: None
|
||||
Priority: 1
|
||||
@@ -223,7 +222,7 @@
|
||||
Label: label-ra-map-generator-choice-mirror-top-right-matches-bottom-left
|
||||
Settings:
|
||||
Mirror: TopRightMatchesBottomLeft
|
||||
Option@Shape:
|
||||
MultiChoiceOption@Shape:
|
||||
Label: label-ra-map-generator-option-shape
|
||||
Default: Square
|
||||
Priority: 1
|
||||
@@ -239,13 +238,13 @@
|
||||
Label: label-ra-map-generator-choice-shape-circle-water
|
||||
Settings:
|
||||
ExternalCircularBias: -1
|
||||
Option@Players:
|
||||
MultiIntegerChoiceOption@Players:
|
||||
Label: label-ra-map-generator-option-players
|
||||
SimpleChoice: Players
|
||||
Values: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
|
||||
Parameter: Players
|
||||
Choices: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
|
||||
Default: 1
|
||||
Priority: 1
|
||||
Option@Resources:
|
||||
MultiChoiceOption@Resources:
|
||||
Label: label-ra-map-generator-option-resources
|
||||
Default: Medium
|
||||
Choice@None:
|
||||
@@ -303,7 +302,7 @@
|
||||
ResourceSpawnWeights:
|
||||
MaximumExpansionResourceSpawns: 0
|
||||
MaximumResourceSpawnsPerExpansion: 1
|
||||
Option@Buildings:
|
||||
MultiChoiceOption@Buildings:
|
||||
Label: label-ra-map-generator-option-buildings
|
||||
Default: Standard
|
||||
Choice@None:
|
||||
@@ -346,7 +345,7 @@
|
||||
MaximumBuildings: 10
|
||||
BuildingWeights:
|
||||
oilb: 1
|
||||
Option@Density:
|
||||
MultiChoiceOption@Density:
|
||||
Label: label-ra-map-generator-option-density
|
||||
Default: Players
|
||||
Priority: 1
|
||||
@@ -385,17 +384,17 @@
|
||||
Settings:
|
||||
AreaEntityBonus: 800
|
||||
PlayerCountEntityBonus: 0
|
||||
Option@DenyWalledArea:
|
||||
BooleanOption@DenyWalledArea:
|
||||
Label: label-ra-map-generator-option-deny-walled-areas
|
||||
Checkbox: DenyWalledAreas
|
||||
Parameter: DenyWalledAreas
|
||||
Default: True
|
||||
Priority: 1
|
||||
Option@Roads:
|
||||
BooleanOption@Roads:
|
||||
Label: label-ra-map-generator-option-roads
|
||||
Checkbox: Roads
|
||||
Parameter: Roads
|
||||
Default: True
|
||||
Priority: 1
|
||||
Option@CivilianDensity:
|
||||
MultiChoiceOption@CivilianDensity:
|
||||
Label: label-ra-map-generator-option-civilian-density
|
||||
Default: Default
|
||||
Priority: 3
|
||||
@@ -430,7 +429,7 @@
|
||||
Type: clear
|
||||
Name: map-generator-clear
|
||||
Settings:
|
||||
Option@Tile:
|
||||
MultiChoiceOption@Tile:
|
||||
Label: label-clear-map-generator-option-tile
|
||||
Choice@DesertClear:
|
||||
Label: label-clear-map-generator-choice-tile-clear
|
||||
|
||||
Reference in New Issue
Block a user