Initial commit: OpenRA game engine
Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
322
OpenRA.Mods.Common/MapGenerator/MapGeneratorSettings.cs
Normal file
322
OpenRA.Mods.Common/MapGenerator/MapGeneratorSettings.cs
Normal file
@@ -0,0 +1,322 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.MapGenerator
|
||||
{
|
||||
public abstract class MapGeneratorOption
|
||||
{
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string Id;
|
||||
public readonly string Label = null;
|
||||
public readonly int Priority = 0;
|
||||
|
||||
protected MapGeneratorOption(string id, MiniYaml yaml)
|
||||
{
|
||||
Id = id;
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
|
||||
public abstract IReadOnlyCollection<MiniYamlNode> GetSettings(ITerrainInfo terrainInfo, int playerCount);
|
||||
|
||||
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, int playerCount)
|
||||
{
|
||||
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, int playerCount)
|
||||
{
|
||||
return [new MiniYamlNode(Parameter, FieldSaver.FormatValue(Value))];
|
||||
}
|
||||
}
|
||||
|
||||
public class MapGeneratorMultiChoiceOption : MapGeneratorOption
|
||||
{
|
||||
public class MapGeneratorDropdownChoice
|
||||
{
|
||||
public readonly string Label = null;
|
||||
public readonly ImmutableArray<string> Tileset = default;
|
||||
public readonly ImmutableArray<int> Players = default;
|
||||
|
||||
[FieldLoader.LoadUsing(nameof(LoadSettings))]
|
||||
[FieldLoader.Require]
|
||||
public readonly ImmutableArray<MiniYamlNode> Settings = default;
|
||||
|
||||
static object LoadSettings(MiniYaml yaml)
|
||||
{
|
||||
return yaml.NodeWithKey("Settings").Value.Nodes.ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
[FieldLoader.LoadUsing(nameof(LoadChoices))]
|
||||
public readonly Dictionary<string, MapGeneratorDropdownChoice> Choices = null;
|
||||
|
||||
static Dictionary<string, MapGeneratorDropdownChoice> LoadChoices(MiniYaml yaml)
|
||||
{
|
||||
var ret = new Dictionary<string, MapGeneratorDropdownChoice>();
|
||||
foreach (var node in yaml.Nodes)
|
||||
{
|
||||
var split = node.Key.Split('@');
|
||||
if (split.Length == 2 && split[0] == "Choice")
|
||||
ret.Add(split[1], FieldLoader.Load<MapGeneratorDropdownChoice>(node.Value));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<string> Default = default;
|
||||
string value = null;
|
||||
|
||||
public MapGeneratorMultiChoiceOption(string id, MiniYaml yaml)
|
||||
: base(id, yaml) { }
|
||||
|
||||
public override IReadOnlyCollection<MiniYamlNode> GetSettings(ITerrainInfo terrainInfo, int playerCount)
|
||||
{
|
||||
var validChoices = ValidChoices(terrainInfo, playerCount);
|
||||
if (validChoices.Contains(value))
|
||||
return Choices[value].Settings;
|
||||
|
||||
string fallback = null;
|
||||
if (Default != null)
|
||||
fallback = Default.FirstOrDefault(validChoices.Contains);
|
||||
fallback ??= validChoices.FirstOrDefault();
|
||||
|
||||
return fallback != null ? Choices[fallback].Settings : [];
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get => value;
|
||||
set
|
||||
{
|
||||
if (value != null && !Choices.ContainsKey(value))
|
||||
throw new ArgumentException($"{value} is not in the list of valid choices");
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> ValidChoices(ITerrainInfo terrainInfo, int playerCount)
|
||||
{
|
||||
return Choices
|
||||
.Where(kv =>
|
||||
(kv.Value.Tileset == null || kv.Value.Tileset.Contains(terrainInfo.Id)) &&
|
||||
(kv.Value.Players == null || kv.Value.Players.Contains(playerCount)))
|
||||
.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 ImmutableArray<int> Choices = default;
|
||||
|
||||
public readonly int? Default = null;
|
||||
int value;
|
||||
|
||||
public MapGeneratorMultiIntegerChoiceOption(string id, MiniYaml yaml)
|
||||
: base(id, yaml)
|
||||
{
|
||||
Value = Default ?? (Choices != null ? Choices[0] : 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, int playerCount)
|
||||
{
|
||||
return [new MiniYamlNode(Parameter, FieldSaver.FormatValue(Value))];
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MapGeneratorSettings : IMapGeneratorSettings
|
||||
{
|
||||
sealed class MapGenerationArgsWithOptions : MapGenerationArgs
|
||||
{
|
||||
public FrozenDictionary<string, string> Options = FrozenDictionary<string, string>.Empty;
|
||||
}
|
||||
|
||||
readonly IMapGeneratorInfo generatorInfo;
|
||||
|
||||
public MapGeneratorSettings(IMapGeneratorInfo generatorInfo, MiniYaml yaml)
|
||||
{
|
||||
this.generatorInfo = generatorInfo;
|
||||
|
||||
var options = new List<MapGeneratorOption>();
|
||||
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));
|
||||
}
|
||||
|
||||
Options = options.ToImmutableArray();
|
||||
}
|
||||
|
||||
public ImmutableArray<MapGeneratorOption> Options { get; } = [];
|
||||
|
||||
public void Randomize(MersenneTwister random)
|
||||
{
|
||||
if (Options.FirstOrDefault(o => o.Id == "Seed") is MapGeneratorIntegerOption seed)
|
||||
seed.Value = random.Next();
|
||||
}
|
||||
|
||||
public int PlayerCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var o = Options.FirstOrDefault(o => o.Id == "Players");
|
||||
if (o is MapGeneratorIntegerOption io)
|
||||
return io.Value;
|
||||
|
||||
if (o is MapGeneratorMultiIntegerChoiceOption mio)
|
||||
return mio.Value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(MapGenerationArgs args)
|
||||
{
|
||||
if (args is MapGenerationArgsWithOptions optionArgs)
|
||||
{
|
||||
foreach (var o in Options)
|
||||
{
|
||||
if (!optionArgs.Options.TryGetValue(o.Id, out var value))
|
||||
continue;
|
||||
|
||||
switch (o)
|
||||
{
|
||||
case MapGeneratorBooleanOption bo: bo.Value = FieldLoader.GetValue<bool>(o.Id, value); break;
|
||||
case MapGeneratorIntegerOption io: io.Value = FieldLoader.GetValue<int>(o.Id, value); break;
|
||||
case MapGeneratorMultiIntegerChoiceOption mio: mio.Value = FieldLoader.GetValue<int>(o.Id, value); break;
|
||||
case MapGeneratorMultiChoiceOption mo: mo.Value = value; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MapGenerationArgs Compile(ITerrainInfo terrainInfo, Size size)
|
||||
{
|
||||
// Apply the choices in their canonical order.
|
||||
var playerCount = PlayerCount;
|
||||
var layers = Options
|
||||
.OrderBy(option => option.Priority)
|
||||
.Select(o => o.GetSettings(terrainInfo, playerCount));
|
||||
|
||||
var options = new Dictionary<string, string>();
|
||||
foreach (var o in Options)
|
||||
{
|
||||
switch (o)
|
||||
{
|
||||
case MapGeneratorBooleanOption bo: options[o.Id] = FieldSaver.FormatValue(bo.Value); break;
|
||||
case MapGeneratorIntegerOption io: options[o.Id] = FieldSaver.FormatValue(io.Value); break;
|
||||
case MapGeneratorMultiIntegerChoiceOption mio: options[o.Id] = FieldSaver.FormatValue(mio.Value); break;
|
||||
case MapGeneratorMultiChoiceOption mo: options[o.Id] = mo.Value; break;
|
||||
}
|
||||
}
|
||||
|
||||
return new MapGenerationArgsWithOptions()
|
||||
{
|
||||
Generator = generatorInfo.Type,
|
||||
Tileset = terrainInfo.Id,
|
||||
Size = size,
|
||||
Title = FluentProvider.GetMessage(generatorInfo.MapTitle),
|
||||
Author = FluentProvider.GetMessage(generatorInfo.Name),
|
||||
Settings = new MiniYaml(null, MiniYaml.Merge(layers)),
|
||||
Options = options.ToFrozenDictionary()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user