Refactoring to remove static Rules & SequenceProvider

This commit is contained in:
Pavlos Touboulidis
2014-05-05 02:43:08 +03:00
parent c68427eaa6
commit 63ec6d60e7
114 changed files with 914 additions and 615 deletions

View File

@@ -69,10 +69,10 @@ namespace OpenRA
if (name != null)
{
if (!Rules.Info.ContainsKey(name.ToLowerInvariant()))
if (!world.Map.Rules.Actors.ContainsKey(name.ToLowerInvariant()))
throw new NotImplementedException("No rules definition for unit {0}".F(name.ToLowerInvariant()));
Info = Rules.Info[name.ToLowerInvariant()];
Info = world.Map.Rules.Actors[name.ToLowerInvariant()];
foreach (var trait in Info.TraitsInConstructOrder())
AddTrait(trait.Create(init));
}

View File

@@ -117,6 +117,7 @@ namespace OpenRA
}, parent, id);
}
// TODO: Delayed actions shouldn't be static or at least should be cleared when the map/world is changed
static ActionQueue delayedActions = new ActionQueue();
public static void RunAfterTick(Action a) { delayedActions.Add(a); }
public static void RunAfterDelay(int delay, Action a) { delayedActions.Add(a, delay); }
@@ -151,8 +152,11 @@ namespace OpenRA
using (new PerfSample("render_widgets"))
{
Ui.Draw();
var cursorName = Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default";
CursorProvider.DrawCursor(Renderer, cursorName, Viewport.LastMousePos, (int)cursorFrame);
if (modData != null && modData.CursorProvider != null)
{
var cursorName = Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default";
modData.CursorProvider.DrawCursor(Renderer, cursorName, Viewport.LastMousePos, (int)cursorFrame);
}
}
using (new PerfSample("render_flip"))
@@ -248,6 +252,7 @@ namespace OpenRA
BeforeGameStart();
Map map;
using (new PerfTimer("PrepareMap"))
map = modData.PrepareMap(mapUID);
using (new PerfTimer("NewWorld"))
@@ -354,7 +359,7 @@ namespace OpenRA
}
Console.WriteLine("Available mods:");
foreach (var mod in Mod.AllMods)
foreach (var mod in ModInformation.AllMods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version);
InitializeWithMod(Settings.Game.Mod, args.GetValue("Launch.Replay", null));
@@ -379,7 +384,7 @@ namespace OpenRA
orderManager.Dispose();
// Fall back to default if the mod doesn't exist
if (!Mod.AllMods.ContainsKey(mod))
if (!ModInformation.AllMods.ContainsKey(mod))
mod = new GameSettings().Mod;
Console.WriteLine("Loading mod: {0}", mod);

View File

@@ -17,34 +17,10 @@ using OpenRA.Support;
namespace OpenRA
{
public static class Rules
{
public static Dictionary<string, ActorInfo> Info { get { return Game.modData.Rules.Actors; } }
public static Dictionary<string, WeaponInfo> Weapons { get { return Game.modData.Rules.Weapons; } }
public static Dictionary<string, SoundInfo> Voices { get { return Game.modData.Rules.Voices; } }
public static Dictionary<string, SoundInfo> Notifications { get { return Game.modData.Rules.Notifications; } }
public static Dictionary<string, MusicInfo> Music { get { return Game.modData.Rules.Music; } }
public static Dictionary<string, string> Movies { get { return Game.modData.Rules.Movies; } }
public static Dictionary<string, TileSet> TileSets { get { return Game.modData.Rules.TileSets; } }
public static void LoadRules(Manifest m, Map map)
{
// HACK: Fallback for code that hasn't been updated yet
Game.modData.Rules = new ModRules(Game.modData);
Game.modData.Rules.ActivateMap(map);
}
public static IEnumerable<KeyValuePair<string, MusicInfo>> InstalledMusic { get { return Music.Where(m => m.Value.Exists); } }
}
public class ModRules
public class RulesetCache
{
readonly ModData modData;
//
// These contain all unique instances created from each mod/map combination
//
readonly Dictionary<string, ActorInfo> actorCache = new Dictionary<string, ActorInfo>();
readonly Dictionary<string, WeaponInfo> weaponCache = new Dictionary<string, WeaponInfo>();
readonly Dictionary<string, SoundInfo> voiceCache = new Dictionary<string, SoundInfo>();
@@ -53,40 +29,60 @@ namespace OpenRA
readonly Dictionary<string, string> movieCache = new Dictionary<string, string>();
readonly Dictionary<string, TileSet> tileSetCache = new Dictionary<string, TileSet>();
//
// These are the instances needed for the current map
//
public Dictionary<string, ActorInfo> Actors { get; private set; }
public Dictionary<string, WeaponInfo> Weapons { get; private set; }
public Dictionary<string, SoundInfo> Voices { get; private set; }
public Dictionary<string, SoundInfo> Notifications { get; private set; }
public Dictionary<string, MusicInfo> Music { get; private set; }
public Dictionary<string, string> Movies { get; private set; }
public Dictionary<string, TileSet> TileSets { get; private set; }
public Action OnProgress = () => { if (Game.modData != null && Game.modData.LoadScreen != null) Game.modData.LoadScreen.Display(); };
public ModRules(ModData modData)
public RulesetCache(ModData modData)
{
this.modData = modData;
}
public void ActivateMap(Map map)
public ModRuleset LoadModRules()
{
var m = modData.Manifest;
using (new PerfTimer("Actors"))
Actors = LoadYamlRules(actorCache, m.Rules, map.Rules, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
using (new PerfTimer("Weapons"))
Weapons = LoadYamlRules(weaponCache, m.Weapons, map.Weapons, (k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
using (new PerfTimer("Voices"))
Voices = LoadYamlRules(voiceCache, m.Voices, map.Voices, (k, _) => new SoundInfo(k.Value));
using (new PerfTimer("Notifications"))
Notifications = LoadYamlRules(notificationCache, m.Notifications, map.Notifications, (k, _) => new SoundInfo(k.Value));
Dictionary<string, MusicInfo> music;
Dictionary<string, string> movies;
Dictionary<string, TileSet> tileSets;
using (new PerfTimer("Music"))
Music = LoadYamlRules(musicCache, m.Music, new List<MiniYamlNode>(), (k, _) => new MusicInfo(k.Key, k.Value));
music = LoadYamlRules(musicCache, m.Music, new List<MiniYamlNode>(), (k, _) => new MusicInfo(k.Key, k.Value));
using (new PerfTimer("Movies"))
Movies = LoadYamlRules(movieCache, m.Movies, new List<MiniYamlNode>(), (k, v) => k.Value.Value);
movies = LoadYamlRules(movieCache, m.Movies, new List<MiniYamlNode>(), (k, v) => k.Value.Value);
using (new PerfTimer("TileSets"))
TileSets = LoadTileSets(tileSetCache, m.TileSets);
tileSets = LoadTileSets(tileSetCache, m.TileSets);
return new ModRuleset(music, movies, tileSets);
}
public MapRuleset LoadDefaultRules()
{
return LoadMapRules(new Map());
}
public MapRuleset LoadMapRules(Map map)
{
var m = modData.Manifest;
Dictionary<string, ActorInfo> actors;
Dictionary<string, WeaponInfo> weapons;
Dictionary<string, SoundInfo> voices;
Dictionary<string, SoundInfo> notifications;
OnProgress();
using (new PerfTimer("Actors"))
actors = LoadYamlRules(actorCache, m.Rules, map.RuleDefinitions, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
OnProgress();
using (new PerfTimer("Weapons"))
weapons = LoadYamlRules(weaponCache, m.Weapons, map.WeaponDefinitions, (k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
OnProgress();
using (new PerfTimer("Voices"))
voices = LoadYamlRules(voiceCache, m.Voices, map.VoiceDefinitions, (k, _) => new SoundInfo(k.Value));
OnProgress();
using (new PerfTimer("Notifications"))
notifications = LoadYamlRules(notificationCache, m.Notifications, map.NotificationDefinitions, (k, _) => new SoundInfo(k.Value));
OnProgress();
return new MapRuleset(LoadModRules(), actors, weapons, voices, notifications);
}
Dictionary<string, T> LoadYamlRules<T>(
@@ -131,7 +127,7 @@ namespace OpenRA
}
else
{
t = new TileSet(file);
t = new TileSet(modData, file);
itemCache.Add(file, t);
items.Add(t.Id, t);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -28,6 +28,9 @@ namespace OpenRA.Graphics
public string Name { get { return name; } }
readonly SequenceProvider sequenceProvider;
static SequenceProvider lastSequenceProvider;
public Animation(string name)
: this(name, () => 0) {}
@@ -36,6 +39,17 @@ namespace OpenRA.Graphics
this.name = name.ToLowerInvariant();
this.tickFunc = () => {};
this.facingFunc = facingFunc;
// TODO: This is wrong, don't use the static
if (Game.orderManager != null && Game.orderManager.world != null && Game.orderManager.world.Map != null)
sequenceProvider = Game.orderManager.world.Map.SequenceProvider;
// HACK: This just makes sure we have a sequence provider in between map changes for delayed actions
// It sucks but it can only be removed when we don't use the statics above but replace them with
// a possible parameter on this constructor.
if (sequenceProvider == null)
sequenceProvider = lastSequenceProvider;
else
lastSequenceProvider = sequenceProvider;
}
int CurrentFrame { get { return backwards ? CurrentSequence.Start + CurrentSequence.Length - frame - 1 : frame; } }
@@ -66,7 +80,7 @@ namespace OpenRA.Graphics
{
backwards = false;
tickAlways = false;
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame = 0;
tickFunc = () =>
{
@@ -81,7 +95,7 @@ namespace OpenRA.Graphics
if (!HasSequence(sequenceName))
return false;
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame %= CurrentSequence.Length;
return true;
}
@@ -90,7 +104,7 @@ namespace OpenRA.Graphics
{
backwards = false;
tickAlways = false;
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame = 0;
tickFunc = () =>
{
@@ -114,7 +128,7 @@ namespace OpenRA.Graphics
{
backwards = false;
tickAlways = true;
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
frame = func();
tickFunc = () => frame = func();
}
@@ -128,7 +142,7 @@ namespace OpenRA.Graphics
Tick(40); // tick one frame
}
public bool HasSequence(string seq) { return SequenceProvider.HasSequence(name, seq); }
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(name, seq); }
public void Tick(int t)
{
@@ -159,7 +173,7 @@ namespace OpenRA.Graphics
public Sequence GetSequence(string sequenceName)
{
return SequenceProvider.GetSequence(name, sequenceName);
return sequenceProvider.GetSequence(name, sequenceName);
}
}
}

View File

@@ -16,23 +16,18 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public static class CursorProvider
public class CursorProvider
{
static HardwarePalette palette;
static Dictionary<string, CursorSequence> cursors;
static Cache<string, PaletteReference> palettes;
HardwarePalette palette;
Dictionary<string, CursorSequence> cursors;
Cache<string, PaletteReference> palettes;
static PaletteReference CreatePaletteReference(string name)
public Action OnProgress = () => { if (Game.modData != null && Game.modData.LoadScreen != null) Game.modData.LoadScreen.Display(); };
public CursorProvider(ModData modData)
{
var pal = palette.GetPalette(name);
if (pal == null)
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
var sequenceFiles = modData.Manifest.Cursors;
return new PaletteReference(name, palette.GetPaletteIndex(name), pal);
}
public static void Initialize(string[] sequenceFiles)
{
cursors = new Dictionary<string, CursorSequence>();
palettes = new Cache<string, PaletteReference>(CreatePaletteReference);
var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
@@ -49,26 +44,37 @@ namespace OpenRA.Graphics
foreach (var p in sequences.NodesDict["Palettes"].Nodes)
palette.AddPalette(p.Key, new Palette(GlobalFileSystem.Open(p.Value.Value), shadowIndex), false);
var spriteLoader = new SpriteLoader(new string[0], new SheetBuilder(SheetType.Indexed));
foreach (var s in sequences.NodesDict["Cursors"].Nodes)
LoadSequencesForCursor(s.Key, s.Value);
LoadSequencesForCursor(spriteLoader, s.Key, s.Value);
palette.Initialize();
}
static void LoadSequencesForCursor(string cursorSrc, MiniYaml cursor)
PaletteReference CreatePaletteReference(string name)
{
Game.modData.LoadScreen.Display();
var pal = palette.GetPalette(name);
if (pal == null)
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
foreach (var sequence in cursor.Nodes)
cursors.Add(sequence.Key, new CursorSequence(cursorSrc, cursor.Value, sequence.Value));
return new PaletteReference(name, palette.GetPaletteIndex(name), pal);
}
public static bool HasCursorSequence(string cursor)
void LoadSequencesForCursor(SpriteLoader loader, string cursorSrc, MiniYaml cursor)
{
OnProgress();
foreach (var sequence in cursor.Nodes)
cursors.Add(sequence.Key, new CursorSequence(loader, cursorSrc, cursor.Value, sequence.Value));
}
public bool HasCursorSequence(string cursor)
{
return cursors.ContainsKey(cursor);
}
public static void DrawCursor(Renderer renderer, string cursorName, int2 lastMousePos, int cursorFrame)
public void DrawCursor(Renderer renderer, string cursorName, int2 lastMousePos, int cursorFrame)
{
var cursorSequence = GetCursorSequence(cursorName);
var cursorSprite = cursorSequence.GetSprite(cursorFrame);
@@ -80,13 +86,12 @@ namespace OpenRA.Graphics
cursorSprite.size);
}
public static CursorSequence GetCursorSequence(string cursor)
public CursorSequence GetCursorSequence(string cursor)
{
try { return cursors[cursor]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Cursor does not have a sequence `{0}`".F(cursor));
throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor));
}
}
}

View File

@@ -25,9 +25,9 @@ namespace OpenRA.Graphics
Sprite[] sprites;
public CursorSequence(string cursorSrc, string palette, MiniYaml info)
public CursorSequence(SpriteLoader loader, string cursorSrc, string palette, MiniYaml info)
{
sprites = Game.modData.SpriteLoader.LoadAllSprites(cursorSrc);
sprites = loader.LoadAllSprites(cursorSrc);
var d = info.NodesDict;
start = Exts.ParseIntegerInvariant(d["start"].Value);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -19,14 +19,8 @@ namespace OpenRA.Graphics
{
public class Minimap
{
public static Bitmap TerrainBitmap(Map map)
public static Bitmap TerrainBitmap(TileSet tileset, Map map, bool actualSize = false)
{
return TerrainBitmap(map, false);
}
public static Bitmap TerrainBitmap(Map map, bool actualSize)
{
var tileset = Rules.TileSets[map.Tileset];
var width = map.Bounds.Width;
var height = map.Bounds.Height;
@@ -61,10 +55,9 @@ namespace OpenRA.Graphics
// Add the static resources defined in the map; if the map lives
// in a world use AddCustomTerrain instead
public static Bitmap AddStaticResources(Map map, Bitmap terrainBitmap)
public static Bitmap AddStaticResources(TileSet tileset, Map map, Bitmap terrainBitmap)
{
Bitmap terrain = new Bitmap(terrainBitmap);
var tileset = Rules.TileSets[map.Tileset];
var bitmapData = terrain.LockBits(terrain.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
@@ -81,7 +74,7 @@ namespace OpenRA.Graphics
if (map.MapResources.Value[mapX, mapY].Type == 0)
continue;
var res = Rules.Info["world"].Traits.WithInterface<ResourceTypeInfo>()
var res = map.Rules.Actors["world"].Traits.WithInterface<ResourceTypeInfo>()
.Where(t => t.ResourceType == map.MapResources.Value[mapX, mapY].Type)
.Select(t => t.TerrainType).FirstOrDefault();
if (res == null)
@@ -185,10 +178,10 @@ namespace OpenRA.Graphics
return bitmap;
}
public static Bitmap RenderMapPreview(Map map, bool actualSize)
public static Bitmap RenderMapPreview(TileSet tileset, Map map, bool actualSize)
{
Bitmap terrain = TerrainBitmap(map, actualSize);
return AddStaticResources(map, terrain);
Bitmap terrain = TerrainBitmap(tileset, map, actualSize);
return AddStaticResources(tileset, map, terrain);
}
}
}

View File

@@ -29,7 +29,7 @@ namespace OpenRA.Graphics
public readonly int ShadowZOffset;
public readonly int[] Frames;
public Sequence(string unit, string name, MiniYaml info)
public Sequence(SpriteLoader loader, string unit, string name, MiniYaml info)
{
var srcOverride = info.Value;
Name = name;
@@ -50,7 +50,7 @@ namespace OpenRA.Graphics
// Apply offset to each sprite in the sequence
// Different sequences may apply different offsets to the same frame
sprites = Game.modData.SpriteLoader.LoadAllSprites(srcOverride ?? unit).Select(
sprites = loader.LoadAllSprites(srcOverride ?? unit).Select(
s => new Sprite(s.sheet, s.bounds, s.offset + offset, s.channel, blendMode)).ToArray();
if (!d.ContainsKey("Length"))

View File

@@ -17,102 +17,101 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public static class SequenceProvider
public class SequenceProvider
{
public static Sequence GetSequence(string unitName, string sequenceName)
readonly Lazy<IReadOnlyDictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>>> sequences;
public SequenceProvider(Map map)
{
return Game.modData.SequenceProvider.GetSequence(unitName, sequenceName);
this.sequences = Exts.Lazy(() => map.Rules.TileSets[map.Tileset].Data.SequenceCache.LoadSequences(map));
}
public static bool HasSequence(string unitName, string sequenceName)
public Sequence GetSequence(string unitName, string sequenceName)
{
return Game.modData.SequenceProvider.HasSequence(unitName, sequenceName);
try
{
return sequences.Value[unitName].Value[sequenceName];
}
catch (KeyNotFoundException)
{
if (sequences.Value.ContainsKey(unitName))
throw new InvalidOperationException("Unit `{0}` does not have a sequence `{1}`".F(unitName, sequenceName));
else
throw new InvalidOperationException("Unit `{0}` does not have all sequences defined.".F(unitName));
}
}
public static IEnumerable<string> Sequences(string unitName)
public bool HasSequence(string unitName, string sequenceName)
{
return Game.modData.SequenceProvider.Sequences(unitName);
if (!sequences.Value.ContainsKey(unitName))
throw new InvalidOperationException("Unit `{0}` does not have sequence `{1}` defined.".F(unitName, sequenceName));
return sequences.Value[unitName].Value.ContainsKey(sequenceName);
}
public IEnumerable<string> Sequences(string unitName)
{
if (!sequences.Value.ContainsKey(unitName))
throw new InvalidOperationException("Unit `{0}` does not have all sequences defined.".F(unitName));
return sequences.Value[unitName].Value.Keys;
}
}
public class ModSequenceProvider
public class SequenceCache
{
readonly ModData modData;
readonly TileSet tileSet;
readonly Dictionary<string, Lazy<Dictionary<string, Sequence>>> sequenceCache = new Dictionary<string, Lazy<Dictionary<string, Sequence>>>();
Dictionary<string, Lazy<Dictionary<string, Sequence>>> sequences;
readonly Dictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>> sequenceCache = new Dictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>>();
public ModSequenceProvider(ModData modData)
public Action OnProgress = () => { if (Game.modData != null && Game.modData.LoadScreen != null) Game.modData.LoadScreen.Display(); };
public SequenceCache(ModData modData, TileSet tileSet)
{
this.modData = modData;
this.tileSet = tileSet;
}
public void ActivateMap(Map map)
public IReadOnlyDictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>> LoadSequences(Map map)
{
sequences = Load(modData.Manifest.Sequences, map.Tileset, map.Sequences);
using (new Support.PerfTimer("LoadSequences"))
return Load(map.SequenceDefinitions);
}
public Dictionary<string, Lazy<Dictionary<string, Sequence>>> Load(string[] sequenceFiles, string tileset, List<MiniYamlNode> sequenceNodes)
IReadOnlyDictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>> Load(List<MiniYamlNode> sequenceNodes)
{
Game.modData.LoadScreen.Display();
OnProgress();
var sequenceFiles = modData.Manifest.Sequences;
var nodes = sequenceFiles
.Select(s => MiniYaml.FromFile(s))
.Aggregate(sequenceNodes, MiniYaml.MergeLiberal);
var items = new Dictionary<string, Lazy<Dictionary<string, Sequence>>>();
var items = new Dictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>>();
foreach (var node in nodes)
{
// Sequence loading uses the active SpriteLoader that depends on the current map's tileset
var key = node.Value.ToLines(node.Key).JoinWith("|");
var key = tileset + node.Value.ToLines(node.Key).JoinWith("|");
Lazy<Dictionary<string, Sequence>> t;
Lazy<IReadOnlyDictionary<string, Sequence>> t;
if (sequenceCache.TryGetValue(key, out t))
{
items.Add(node.Key, t);
}
else
{
t = Exts.Lazy(() => node.Value.NodesDict.ToDictionary(x => x.Key, x => new Sequence(node.Key, x.Key, x.Value)));
t = Exts.Lazy(() => (IReadOnlyDictionary<string, Sequence>)new ReadOnlyDictionary<string, Sequence>(
node.Value.NodesDict.ToDictionary(x => x.Key, x =>
new Sequence(tileSet.Data.SpriteLoader, node.Key, x.Key, x.Value))));
sequenceCache.Add(key, t);
items.Add(node.Key, t);
}
OnProgress();
}
return items;
}
public Sequence GetSequence(string unitName, string sequenceName)
{
try { return sequences[unitName].Value[sequenceName]; }
catch (KeyNotFoundException)
{
if (sequences.ContainsKey(unitName))
throw new InvalidOperationException(
"Unit `{0}` does not have a sequence `{1}`".F(unitName, sequenceName));
else
throw new InvalidOperationException(
"Unit `{0}` does not have all sequences defined.".F(unitName));
}
}
public bool HasSequence(string unitName, string sequenceName)
{
if (!sequences.ContainsKey(unitName))
throw new InvalidOperationException(
"Unit `{0}` does not have sequence `{1}` defined.".F(unitName, sequenceName));
return sequences[unitName].Value.ContainsKey(sequenceName);
}
public IEnumerable<string> Sequences(string unitName)
{
if (!sequences.ContainsKey(unitName))
throw new InvalidOperationException(
"Unit `{0}` does not have all sequences defined.".F(unitName));
return sequences[unitName].Value.Keys;
return new ReadOnlyDictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>>(items);
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -18,7 +18,7 @@ namespace OpenRA.Graphics
{
public class SpriteLoader
{
readonly SheetBuilder SheetBuilder;
public readonly SheetBuilder SheetBuilder;
readonly Cache<string, Sprite[]> sprites;
readonly string[] exts;

View File

@@ -19,7 +19,7 @@ namespace OpenRA
// Describes what is to be loaded in order to run a mod
public class Manifest
{
public readonly Mod Mod;
public readonly ModInformation Mod;
public readonly string[]
Folders, MapFolders, Rules, ServerTraits,
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
@@ -38,7 +38,7 @@ namespace OpenRA
var path = new[] { "mods", mod, "mod.yaml" }.Aggregate(Path.Combine);
var yaml = new MiniYaml(null, MiniYaml.FromFile(path)).NodesDict;
Mod = FieldLoader.Load<Mod>(yaml["Metadata"]);
Mod = FieldLoader.Load<ModInformation>(yaml["Metadata"]);
Mod.Id = mod;
// TODO: Use fieldloader

View File

@@ -18,6 +18,7 @@ using System.Text;
using OpenRA.FileSystem;
using OpenRA.Network;
using OpenRA.Traits;
using OpenRA.Graphics;
namespace OpenRA
{
@@ -94,13 +95,13 @@ namespace OpenRA
[FieldLoader.Ignore] public Dictionary<string, PlayerReference> Players = new Dictionary<string, PlayerReference>();
[FieldLoader.Ignore] public Lazy<List<SmudgeReference>> Smudges;
[FieldLoader.Ignore] public List<MiniYamlNode> Rules = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Sequences = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> VoxelSequences = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Notifications = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Translations = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> RuleDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> SequenceDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> VoxelSequenceDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> WeaponDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> VoiceDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> NotificationDefinitions = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> TranslationDefinitions = new List<MiniYamlNode>();
// Binary map data
[FieldLoader.Ignore] public byte TileFormat = 1;
@@ -110,6 +111,10 @@ namespace OpenRA
[FieldLoader.Ignore] public Lazy<TileReference<byte, byte>[,]> MapResources;
[FieldLoader.Ignore] public string[,] CustomTerrain;
[FieldLoader.Ignore] Lazy<MapRuleset> rules;
public MapRuleset Rules { get { return rules != null ? rules.Value : null; } }
public SequenceProvider SequenceProvider { get; private set; }
public static Map FromTileset(TileSet tileset)
{
var tile = tileset.Templates.First();
@@ -128,6 +133,7 @@ namespace OpenRA
Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()),
Smudges = Exts.Lazy(() => new List<SmudgeReference>())
};
map.PostInit();
return map;
}
@@ -210,13 +216,13 @@ namespace OpenRA
return ret;
});
Rules = MiniYaml.NodesOrEmpty(yaml, "Rules");
Sequences = MiniYaml.NodesOrEmpty(yaml, "Sequences");
VoxelSequences = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences");
Weapons = MiniYaml.NodesOrEmpty(yaml, "Weapons");
Voices = MiniYaml.NodesOrEmpty(yaml, "Voices");
Notifications = MiniYaml.NodesOrEmpty(yaml, "Notifications");
Translations = MiniYaml.NodesOrEmpty(yaml, "Translations");
RuleDefinitions = MiniYaml.NodesOrEmpty(yaml, "Rules");
SequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Sequences");
VoxelSequenceDefinitions = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences");
WeaponDefinitions = MiniYaml.NodesOrEmpty(yaml, "Weapons");
VoiceDefinitions = MiniYaml.NodesOrEmpty(yaml, "Voices");
NotificationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Notifications");
TranslationDefinitions = MiniYaml.NodesOrEmpty(yaml, "Translations");
CustomTerrain = new string[MapSize.X, MapSize.Y];
@@ -233,6 +239,19 @@ namespace OpenRA
if (Container.Exists("map.png"))
CustomPreview = new Bitmap(Container.GetContent("map.png"));
PostInit();
}
void PostInit()
{
rules = Exts.Lazy(() => Game.modData.RulesetCache.LoadMapRules(this));
SequenceProvider = new SequenceProvider(this);
}
public MapRuleset PreloadRules()
{
return rules.Value;
}
public CPos[] GetSpawnPoints()
@@ -282,13 +301,13 @@ namespace OpenRA
);
root.Add(new MiniYamlNode("Smudges", MiniYaml.FromList<SmudgeReference>(Smudges.Value)));
root.Add(new MiniYamlNode("Rules", null, Rules));
root.Add(new MiniYamlNode("Sequences", null, Sequences));
root.Add(new MiniYamlNode("VoxelSequences", null, VoxelSequences));
root.Add(new MiniYamlNode("Weapons", null, Weapons));
root.Add(new MiniYamlNode("Voices", null, Voices));
root.Add(new MiniYamlNode("Notifications", null, Notifications));
root.Add(new MiniYamlNode("Translations", null, Translations));
root.Add(new MiniYamlNode("Rules", null, RuleDefinitions));
root.Add(new MiniYamlNode("Sequences", null, SequenceDefinitions));
root.Add(new MiniYamlNode("VoxelSequences", null, VoxelSequenceDefinitions));
root.Add(new MiniYamlNode("Weapons", null, WeaponDefinitions));
root.Add(new MiniYamlNode("Voices", null, VoiceDefinitions));
root.Add(new MiniYamlNode("Notifications", null, NotificationDefinitions));
root.Add(new MiniYamlNode("Translations", null, TranslationDefinitions));
var entries = new Dictionary<string, byte[]>();
entries.Add("map.bin", SaveBinaryData());
@@ -452,7 +471,7 @@ namespace OpenRA
public void MakeDefaultPlayers()
{
var firstRace = OpenRA.Rules.Info["world"].Traits
var firstRace = Rules.Actors["world"].Traits
.WithInterface<CountryInfo>().First(c => c.Selectable).Race;
if (!Players.ContainsKey("Neutral"))
@@ -489,10 +508,10 @@ namespace OpenRA
});
}
public void FixOpenAreas()
public void FixOpenAreas(MapRuleset rules)
{
var r = new Random();
var tileset = OpenRA.Rules.TileSets[Tileset];
var tileset = rules.TileSets[Tileset];
for (var j = Bounds.Top; j < Bounds.Bottom; j++)
{

14
OpenRA.Game/Map/MapCache.cs Executable file → Normal file
View File

@@ -27,30 +27,30 @@ namespace OpenRA
{
public static readonly MapPreview UnknownMap = new MapPreview(null, null);
readonly Cache<string, MapPreview> previews;
readonly Manifest manifest;
readonly ModData modData;
readonly SheetBuilder sheetBuilder;
Thread previewLoaderThread;
object syncRoot = new object();
Queue<MapPreview> generateMinimap = new Queue<MapPreview>();
public MapCache(Manifest m)
public MapCache(ModData modData)
{
manifest = m;
this.modData = modData;
previews = new Cache<string, MapPreview>(uid => new MapPreview(uid, this));
sheetBuilder = new SheetBuilder(SheetType.BGRA);
}
public void LoadMaps()
{
var paths = manifest.MapFolders.SelectMany(f => FindMapsIn(f));
var paths = modData.Manifest.MapFolders.SelectMany(f => FindMapsIn(f));
foreach (var path in paths)
{
try
{
using (new Support.PerfTimer(path))
{
var map = new Map(path, manifest.Mod.Id);
if (manifest.MapCompatibility.Contains(map.RequiresMod))
var map = new Map(path, modData.Manifest.Mod.Id);
if (modData.Manifest.MapCompatibility.Contains(map.RequiresMod))
previews[map.Uid].UpdateFromMap(map);
}
}
@@ -149,7 +149,7 @@ namespace OpenRA
// the next render cycle.
// (d) Any partially written bytes from the next minimap is in an
// unallocated area, and will be committed in the next cycle.
var bitmap = p.CustomPreview ?? Minimap.RenderMapPreview(p.Map, true);
var bitmap = p.CustomPreview ?? Minimap.RenderMapPreview(modData.ModRules.TileSets[p.Map.Tileset], p.Map, true);
p.Minimap = sheetBuilder.Add(bitmap);
lock (syncRoot)

View File

@@ -0,0 +1,65 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.GameRules;
namespace OpenRA
{
public class ModRuleset
{
public readonly IReadOnlyDictionary<string, MusicInfo> Music;
public readonly IReadOnlyDictionary<string, string> Movies;
public readonly IReadOnlyDictionary<string, TileSet> TileSets;
public ModRuleset(ModRuleset other)
{
this.Music = other.Music;
this.Movies = other.Movies;
this.TileSets = other.TileSets;
}
public ModRuleset(
IDictionary<string, MusicInfo> music,
IDictionary<string, string> movies,
IDictionary<string, TileSet> tileSets)
{
this.Music = new ReadOnlyDictionary<string, MusicInfo>(music);
this.Movies = new ReadOnlyDictionary<string, string>(movies);
this.TileSets = new ReadOnlyDictionary<string, TileSet>(tileSets);
}
public IEnumerable<KeyValuePair<string, MusicInfo>> InstalledMusic { get { return Music.Where(m => m.Value.Exists); } }
}
public class MapRuleset : ModRuleset
{
public readonly IReadOnlyDictionary<string, ActorInfo> Actors;
public readonly IReadOnlyDictionary<string, WeaponInfo> Weapons;
public readonly IReadOnlyDictionary<string, SoundInfo> Voices;
public readonly IReadOnlyDictionary<string, SoundInfo> Notifications;
public MapRuleset(
ModRuleset modRuleset,
IDictionary<string, ActorInfo> actors,
IDictionary<string, WeaponInfo> weapons,
IDictionary<string, SoundInfo> voices,
IDictionary<string, SoundInfo> notifications)
: base(modRuleset)
{
this.Actors = new ReadOnlyDictionary<string, ActorInfo>(actors);
this.Weapons = new ReadOnlyDictionary<string, WeaponInfo>(weapons);
this.Voices = new ReadOnlyDictionary<string, SoundInfo>(voices);
this.Notifications = new ReadOnlyDictionary<string, SoundInfo>(notifications);
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -8,21 +8,23 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using OpenRA.Graphics;
namespace OpenRA
{
public class TerrainTypeInfo
{
public string Type;
public string[] TargetTypes = { };
public string[] AcceptsSmudgeType = { };
public bool IsWater = false; // TODO: Remove this
public Color Color;
public string CustomCursor;
public readonly string Type;
public readonly string[] TargetTypes = { };
public readonly string[] AcceptsSmudgeType = { };
public readonly bool IsWater = false; // TODO: Remove this
public readonly Color Color;
public readonly string CustomCursor;
public TerrainTypeInfo() { }
public TerrainTypeInfo(MiniYaml my) { FieldLoader.Load(this, my); }
@@ -32,19 +34,26 @@ namespace OpenRA
public class TileTemplate
{
public ushort Id;
public string Image;
public int[] Frames;
public int2 Size;
public bool PickAny;
public string Category;
public readonly ushort Id;
public readonly string Image;
public readonly int[] Frames;
public readonly int2 Size;
public readonly bool PickAny;
public readonly string Category;
[FieldLoader.LoadUsing("LoadTiles")]
public Dictionary<byte, string> Tiles = new Dictionary<byte, string>();
public readonly Dictionary<byte, string> Tiles = new Dictionary<byte, string>();
public TileTemplate() { }
public TileTemplate(MiniYaml my) { FieldLoader.Load(this, my); }
public TileTemplate(ushort id, string image, int2 size)
{
this.Id = id;
this.Image = image;
this.Size = size;
}
static object LoadTiles(MiniYaml y)
{
return y.NodesDict["Tiles"].NodesDict.ToDictionary(
@@ -73,24 +82,39 @@ namespace OpenRA
}
}
public class TileSetData
{
Lazy<SpriteLoader> spriteLoader;
public SpriteLoader SpriteLoader { get { return spriteLoader.Value; } }
public readonly SequenceCache SequenceCache;
public TileSetData(ModData modData, TileSet tileSet)
{
spriteLoader = Exts.Lazy(() => new SpriteLoader(tileSet.Extensions, new SheetBuilder(SheetType.Indexed)));
SequenceCache = new SequenceCache(modData, tileSet);
}
}
public class TileSet
{
public string Name;
public string Id;
public int SheetSize = 512;
public string Palette;
public string PlayerPalette;
public string[] Extensions;
public int WaterPaletteRotationBase = 0x60;
public Dictionary<string, TerrainTypeInfo> Terrain = new Dictionary<string, TerrainTypeInfo>();
public Dictionary<ushort, TileTemplate> Templates = new Dictionary<ushort, TileTemplate>();
public string[] EditorTemplateOrder;
public readonly string Name;
public readonly string Id;
public readonly int SheetSize = 512;
public readonly string Palette;
public readonly string PlayerPalette;
public readonly string[] Extensions;
public readonly int WaterPaletteRotationBase = 0x60;
public readonly Dictionary<string, TerrainTypeInfo> Terrain = new Dictionary<string, TerrainTypeInfo>();
public readonly Dictionary<ushort, TileTemplate> Templates = new Dictionary<ushort, TileTemplate>();
public readonly string[] EditorTemplateOrder;
static readonly string[] Fields = { "Name", "Id", "SheetSize", "Palette", "Extensions" };
public TileSet() { }
[FieldLoader.IgnoreAttribute]
public readonly TileSetData Data;
public TileSet(string filepath)
public TileSet(ModData modData, string filepath)
{
var yaml = MiniYaml.DictFromFile(filepath);
@@ -104,6 +128,16 @@ namespace OpenRA
// Templates
Templates = yaml["Templates"].NodesDict.Values
.Select(y => new TileTemplate(y)).ToDictionary(t => t.Id);
Data = new TileSetData(modData, this);
}
public TileSet(string name, string id, string palette, string[] extensions)
{
this.Name = name;
this.Id = id;
this.Palette = palette;
this.Extensions = extensions;
}
public void Save(string filepath)
@@ -139,5 +173,7 @@ namespace OpenRA
return ret;
}
}
}

37
OpenRA.Game/ModData.cs Executable file → Normal file
View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -25,11 +25,14 @@ namespace OpenRA
public readonly WidgetLoader WidgetLoader;
public readonly MapCache MapCache;
public ILoadScreen LoadScreen = null;
public SheetBuilder SheetBuilder;
public SpriteLoader SpriteLoader;
public VoxelLoader VoxelLoader;
public ModSequenceProvider SequenceProvider;
public ModRules Rules;
public readonly RulesetCache RulesetCache;
public CursorProvider CursorProvider { get; private set; }
Lazy<ModRuleset> modRules;
public ModRuleset ModRules { get { return modRules.Value; } }
Lazy<MapRuleset> defaultRules;
public MapRuleset DefaultRules { get { return defaultRules.Value; } }
public ModData(string mod)
{
@@ -40,14 +43,16 @@ namespace OpenRA
LoadScreen.Init(Manifest, Manifest.LoadScreen.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value));
LoadScreen.Display();
WidgetLoader = new WidgetLoader(this);
MapCache = new MapCache(Manifest);
SequenceProvider = new ModSequenceProvider(this);
Rules = new ModRules(this);
RulesetCache = new RulesetCache(this);
MapCache = new MapCache(this);
// HACK: Mount only local folders so we have a half-working environment for the asset installer
GlobalFileSystem.UnmountAll();
foreach (var dir in Manifest.Folders)
GlobalFileSystem.Mount(dir);
modRules = Exts.Lazy(() => RulesetCache.LoadModRules());
defaultRules = Exts.Lazy(() => RulesetCache.LoadDefaultRules());
}
public void InitializeLoaders()
@@ -56,10 +61,9 @@ namespace OpenRA
// horribly when you use ModData in unexpected ways.
ChromeMetrics.Initialize(Manifest.ChromeMetrics);
ChromeProvider.Initialize(Manifest.Chrome);
SheetBuilder = new SheetBuilder(SheetType.Indexed);
SpriteLoader = new SpriteLoader(new string[0], SheetBuilder);
VoxelLoader = new VoxelLoader();
CursorProvider.Initialize(Manifest.Cursors);
CursorProvider = new CursorProvider(this);
}
public IEnumerable<string> Languages { get; private set; }
@@ -79,7 +83,7 @@ namespace OpenRA
var yaml = Manifest.Translations.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal);
Languages = yaml.Select(t => t.Key).ToArray();
yaml = MiniYaml.MergeLiberal(map.Translations, yaml);
yaml = MiniYaml.MergeLiberal(map.TranslationDefinitions, yaml);
foreach (var y in yaml)
{
@@ -122,14 +126,11 @@ namespace OpenRA
// Mount map package so custom assets can be used. TODO: check priority.
GlobalFileSystem.Mount(GlobalFileSystem.OpenPackage(map.Path, null, int.MaxValue));
using (new Support.PerfTimer("Rules.ActivateMap"))
Rules.ActivateMap(map);
SpriteLoader = new SpriteLoader(Rules.TileSets[map.Tileset].Extensions, SheetBuilder);
using (new Support.PerfTimer("Map.LoadRules"))
map.PreloadRules();
using (new Support.PerfTimer("SequenceProvider.ActivateMap"))
SequenceProvider.ActivateMap(map);
VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequenceDefinitions);
VoxelProvider.Initialize(Manifest.VoxelSequences, map.VoxelSequences);
return map;
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -14,9 +14,9 @@ using System.Linq;
namespace OpenRA
{
public class Mod
public class ModInformation
{
public static readonly Dictionary<string, Mod> AllMods = ValidateMods(Directory.GetDirectories("mods").Select(x => x.Substring(5)).ToArray());
public static readonly Dictionary<string, ModInformation> AllMods = ValidateMods(Directory.GetDirectories("mods").Select(x => x.Substring(5)).ToArray());
public string Id;
public string Title;
@@ -24,9 +24,9 @@ namespace OpenRA
public string Version;
public string Author;
public static Dictionary<string, Mod> ValidateMods(string[] mods)
public static Dictionary<string, ModInformation> ValidateMods(string[] mods)
{
var ret = new Dictionary<string, Mod>();
var ret = new Dictionary<string, ModInformation>();
foreach (var m in mods)
{
var yamlPath = new[] { "mods", m, "mod.yaml" }.Aggregate(Path.Combine);
@@ -37,7 +37,7 @@ namespace OpenRA
if (!yaml.NodesDict.ContainsKey("Metadata"))
continue;
var mod = FieldLoader.Load<Mod>(yaml.NodesDict["Metadata"]);
var mod = FieldLoader.Load<ModInformation>(yaml.NodesDict["Metadata"]);
mod.Id = m;
ret.Add(m, mod);

View File

@@ -99,7 +99,6 @@
<Compile Include="Game.cs" />
<Compile Include="GameRules\ActorInfo.cs" />
<Compile Include="GameRules\MusicInfo.cs" />
<Compile Include="GameRules\Rules.cs" />
<Compile Include="GameRules\SoundInfo.cs" />
<Compile Include="GameRules\WeaponInfo.cs" />
<Compile Include="Graphics\Animation.cs" />
@@ -244,6 +243,10 @@
<Compile Include="Scripting\ScriptMemberExts.cs" />
<Compile Include="Scripting\ScriptPlayerInterface.cs" />
<Compile Include="Traits\Player\FixedColorPalette.cs" />
<Compile Include="Primitives\ReadOnlyDictionary.cs" />
<Compile Include="ModInformation.cs" />
<Compile Include="Map\Ruleset.cs" />
<Compile Include="GameRules\RulesetCache.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="FileSystem\D2kSoundResources.cs" />
@@ -303,7 +306,6 @@
<Compile Include="Hotkey.cs" />
<Compile Include="Keycode.cs" />
<Compile Include="MiniYaml.cs" />
<Compile Include="Mod.cs" />
<Compile Include="Platform.cs" />
<Compile Include="StreamExts.cs" />
<Compile Include="Map\Map.cs" />

View File

@@ -47,7 +47,7 @@ namespace OpenRA
static CountryInfo ChooseCountry(World world, string name)
{
var selectableCountries = Rules.Info["world"].Traits
var selectableCountries = world.Map.Rules.Actors["world"].Traits
.WithInterface<CountryInfo>().Where( c => c.Selectable )
.ToArray();

View File

@@ -0,0 +1,90 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
namespace OpenRA
{
/// <summary>
/// A minimal read only dictionary interface for .NET 4
/// </summary>
/// <remarks>
/// .NET 4.5 has an implementation built-in, this code is not meant to
/// duplicate it but provide a compatible interface that can be replaced
/// when we switch to .NET 4.5 or higher.
/// </remarks>
public interface IReadOnlyDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
int Count { get; }
TValue this[TKey key] { get; }
IEnumerable<TKey> Keys { get; }
IEnumerable<TValue> Values { get; }
bool ContainsKey(TKey key);
bool TryGetValue(TKey key, out TValue value);
}
/// <summary>
/// A minimal read only dictionary for .NET 4 implemented as a wrapper
/// around an IDictionary.
/// </summary>
public class ReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> dict;
public ReadOnlyDictionary()
: this(new Dictionary<TKey, TValue>())
{
}
public ReadOnlyDictionary(IDictionary<TKey, TValue> dict)
{
if (dict == null)
throw new ArgumentNullException("dict");
this.dict = dict;
}
#region IReadOnlyDictionary implementation
public bool ContainsKey(TKey key)
{
return dict.ContainsKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return dict.TryGetValue(key, out value);
}
public int Count { get { return dict.Count; } }
public TValue this[TKey key] { get { return dict[key]; } }
public IEnumerable<TKey> Keys { get { return dict.Keys; } }
public IEnumerable<TValue> Values { get { return dict.Values; } }
#endregion
#region IEnumerable implementation
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return dict.GetEnumerator();
}
#endregion
#region IEnumerable implementation
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return dict.GetEnumerator();
}
#endregion
}
}

View File

@@ -296,15 +296,18 @@ namespace OpenRA
}
// Returns true if played successfully
public static bool PlayPredefined(Player p, Actor voicedUnit, string type, string definition, string variant, bool relative, WPos pos, float volumeModifier, bool attenuateVolume)
public static bool PlayPredefined(MapRuleset ruleset, Player p, Actor voicedUnit, string type, string definition, string variant, bool relative, WPos pos, float volumeModifier, bool attenuateVolume)
{
if (ruleset == null)
throw new ArgumentNullException("ruleset");
if (definition == null)
return false;
if (Rules.Voices == null || Rules.Notifications == null)
if (ruleset.Voices == null || ruleset.Notifications == null)
return false;
var rules = (voicedUnit != null) ? Rules.Voices[type] : Rules.Notifications[type];
var rules = (voicedUnit != null) ? ruleset.Voices[type] : ruleset.Notifications[type];
if (rules == null)
return false;
@@ -366,7 +369,7 @@ namespace OpenRA
return false;
var type = mi.Voice.ToLowerInvariant();
return PlayPredefined(null, voicedUnit, type, phrase, variant, true, WPos.Zero, 1f, true);
return PlayPredefined(voicedUnit.World.Map.Rules, null, voicedUnit, type, phrase, variant, true, WPos.Zero, 1f, true);
}
public static bool PlayVoiceLocal(string phrase, Actor voicedUnit, string variant, WPos pos, float volume)
@@ -379,15 +382,18 @@ namespace OpenRA
return false;
var type = mi.Voice.ToLowerInvariant();
return PlayPredefined(null, voicedUnit, type, phrase, variant, false, pos, volume, true);
return PlayPredefined(voicedUnit.World.Map.Rules, null, voicedUnit, type, phrase, variant, false, pos, volume, true);
}
public static bool PlayNotification(Player player, string type, string notification, string variant)
public static bool PlayNotification(MapRuleset rules, Player player, string type, string notification, string variant)
{
if (rules == null)
throw new ArgumentNullException("rules");
if (type == null || notification == null)
return false;
return PlayPredefined(player, null, type.ToLowerInvariant(), notification, variant, true, WPos.Zero, 1f, false);
return PlayPredefined(rules, player, null, type.ToLowerInvariant(), notification, variant, true, WPos.Zero, 1f, false);
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2012 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -118,7 +118,7 @@ namespace OpenRA.Traits
{
if (Ore > 0.8 * OreCapacity)
{
Sound.PlayNotification(Owner, "Speech", "SilosNeeded", Owner.Country.Race);
Sound.PlayNotification(self.World.Map.Rules, Owner, "Speech", "SilosNeeded", Owner.Country.Race);
AlertSilo = true;
}
else
@@ -163,14 +163,14 @@ namespace OpenRA.Traits
public void playCashTickUp(Actor self)
{
if (Game.Settings.Sound.CashTicks)
Sound.PlayNotification(self.Owner, "Sounds", "CashTickUp", self.Owner.Country.Race);
Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", "CashTickUp", self.Owner.Country.Race);
}
public void playCashTickDown(Actor self)
{
if (Game.Settings.Sound.CashTicks && nextCashTickTime == 0)
{
Sound.PlayNotification(self.Owner, "Sounds", "CashTickDown", self.Owner.Country.Race);
Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", "CashTickDown", self.Owner.Country.Race);
nextCashTickTime = 2;
}
}

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Traits
public PipType PipColor = PipType.Yellow;
public object Create(ActorInitializer init) { return new ResourceType(this); }
public object Create(ActorInitializer init) { return new ResourceType(this, init); }
}
public class ResourceType : IWorldLoaded
@@ -41,13 +41,13 @@ namespace OpenRA.Traits
public PaletteReference Palette { get; private set; }
public readonly Dictionary<string, Sprite[]> Variants;
public ResourceType(ResourceTypeInfo info)
public ResourceType(ResourceTypeInfo info, ActorInitializer init)
{
this.Info = info;
Variants = new Dictionary<string, Sprite[]>();
foreach (var v in info.Variants)
{
var seq = SequenceProvider.GetSequence("resources", v);
var seq = init.world.Map.SequenceProvider.GetSequence("resources", v);
var sprites = Exts.MakeArray(seq.Length, x => seq.GetSprite(x));
Variants.Add(v, sprites);
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -11,6 +11,8 @@
using System;
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Network;
namespace OpenRA.Widgets
{
@@ -53,8 +55,13 @@ namespace OpenRA.Widgets
public Action OnDoubleClick = () => {};
public Action<KeyInput> OnKeyPress = _ => {};
public ButtonWidget()
readonly MapRuleset rules;
[ObjectCreator.UseCtor]
public ButtonWidget(MapRuleset rules)
{
this.rules = rules;
GetText = () => { return Text; };
GetColor = () => TextColor;
GetColorDisabled = () => TextColorDisabled;
@@ -70,6 +77,8 @@ namespace OpenRA.Widgets
protected ButtonWidget(ButtonWidget other)
: base(other)
{
this.rules = other.rules;
Text = other.Text;
Font = other.Font;
TextColor = other.TextColor;
@@ -113,10 +122,10 @@ namespace OpenRA.Widgets
if (!IsDisabled())
{
OnKeyPress(e);
Sound.PlayNotification(null, "Sounds", "ClickSound", null);
Sound.PlayNotification(rules, null, "Sounds", "ClickSound", null);
}
else
Sound.PlayNotification(null, "Sounds", "ClickDisabledSound", null);
Sound.PlayNotification(rules, null, "Sounds", "ClickDisabledSound", null);
return true;
}
@@ -153,12 +162,12 @@ namespace OpenRA.Widgets
{
OnMouseDown(mi);
Depressed = true;
Sound.PlayNotification(null, "Sounds", "ClickSound", null);
Sound.PlayNotification(rules, null, "Sounds", "ClickSound", null);
}
else
{
YieldMouseFocus(mi);
Sound.PlayNotification(null, "Sounds", "ClickDisabledSound", null);
Sound.PlayNotification(rules, null, "Sounds", "ClickDisabledSound", null);
}
}
else if (mi.Event == MouseInputEvent.Move && HasMouseFocus)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -11,6 +11,7 @@
using System;
using System.Drawing;
using OpenRA.Graphics;
using OpenRA.Network;
namespace OpenRA.Widgets
{
@@ -23,7 +24,9 @@ namespace OpenRA.Widgets
public int CheckOffset = 2;
public bool HasPressedState = ChromeMetrics.Get<bool>("CheckboxPressedState");
public CheckboxWidget()
[ObjectCreator.UseCtor]
public CheckboxWidget(MapRuleset rules)
: base(rules)
{
GetCheckType = () => CheckType;
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using OpenRA.Graphics;
using OpenRA.Network;
namespace OpenRA.Widgets
{
@@ -20,7 +21,9 @@ namespace OpenRA.Widgets
Widget panel;
MaskWidget fullscreenMask;
public DropDownButtonWidget() { }
[ObjectCreator.UseCtor]
public DropDownButtonWidget(MapRuleset rules)
: base(rules) { }
protected DropDownButtonWidget(DropDownButtonWidget widget) : base(widget) { }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -9,6 +9,8 @@
#endregion
using System;
using OpenRA.Graphics;
using OpenRA.Network;
namespace OpenRA.Widgets
{
@@ -17,7 +19,9 @@ namespace OpenRA.Widgets
public string ItemKey;
public string BaseName = "scrollitem";
public ScrollItemWidget()
[ObjectCreator.UseCtor]
public ScrollItemWidget(MapRuleset rules)
: base(rules)
{
IsVisible = () => false;
VisualHeight = 0;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -18,10 +18,13 @@ namespace OpenRA
{
public class WidgetLoader
{
Dictionary<string, MiniYamlNode> widgets = new Dictionary<string, MiniYamlNode>();
readonly Dictionary<string, MiniYamlNode> widgets = new Dictionary<string, MiniYamlNode>();
readonly ModData modData;
public WidgetLoader(ModData modData)
{
this.modData = modData;
foreach (var file in modData.Manifest.ChromeLayout.Select(a => MiniYaml.FromFile(a)))
foreach( var w in file )
{
@@ -55,6 +58,8 @@ namespace OpenRA
if (child.Key != "Children")
FieldLoader.LoadField(widget, child.Key, child.Value.Value);
if (!args.ContainsKey("rules"))
args = new WidgetArgs(args) { { "rules", modData.DefaultRules } };
widget.Initialize(args);
foreach (var child in node.Value.Nodes)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* 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. For more information,
@@ -125,7 +125,7 @@ namespace OpenRA
orderGenerator_ = new UnitOrderGenerator();
Map = map;
TileSet = Rules.TileSets[Map.Tileset];
TileSet = map.Rules.TileSets[Map.Tileset];
SharedRandom = new XRandom(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
WorldActor = CreateActor("World", new TypeDictionary());

View File

@@ -151,7 +151,7 @@ namespace OpenRA
var selectable = a.Info.Traits.GetOrDefault<SelectableInfo>();
if (selectable == null) return null;
var v = selectable.Voice;
return (v == null) ? null : Rules.Voices[v.ToLowerInvariant()];
return (v == null) ? null : a.World.Map.Rules.Voices[v.ToLowerInvariant()];
}
public static void PlayVoiceForOrders(this World w, Order[] orders)