Esempio n. 1
0
        public MapPlayers(Ruleset rules, int playerCount)
        {
            var firstFaction = rules.Actors["world"].TraitInfos<FactionInfo>()
                .First(f => f.Selectable).InternalName;

            Players = new Dictionary<string, PlayerReference>
            {
                {
                    "Neutral", new PlayerReference
                    {
                        Name = "Neutral",
                        Faction = firstFaction,
                        OwnsWorld = true,
                        NonCombatant = true
                    }
                },
                {
                    "Creeps", new PlayerReference
                    {
                        Name = "Creeps",
                        Faction = firstFaction,
                        NonCombatant = true,
                        Enemies = Exts.MakeArray(playerCount, i => "Multi{0}".F(i))
                    }
                }
            };

            for (var index = 0; index < playerCount; index++)
            {
                var p = new PlayerReference
                {
                    Name = "Multi{0}".F(index),
                    Faction = "Random",
                    Playable = true,
                    Enemies = new[] { "Creeps" }
                };
                Players.Add(p.Name, p);
            }
        }
Esempio n. 2
0
        public void FixOpenAreas(Ruleset rules)
        {
            var r = new Random();
            var tileset = rules.TileSets[Tileset];

            for (var j = Bounds.Top; j < Bounds.Bottom; j++)
            {
                for (var i = Bounds.Left; i < Bounds.Right; i++)
                {
                    var type = MapTiles.Value[new MPos(i, j)].Type;
                    var index = MapTiles.Value[new MPos(i, j)].Index;
                    if (!tileset.Templates.ContainsKey(type))
                    {
                        Console.WriteLine("Unknown Tile ID {0}".F(type));
                        continue;
                    }

                    var template = tileset.Templates[type];
                    if (!template.PickAny)
                        continue;

                    index = (byte)r.Next(0, template.TilesCount);
                    MapTiles.Value[new MPos(i, j)] = new TerrainTile(type, index);
                }
            }
        }
Esempio n. 3
0
        // Returns true if played successfully
        public bool PlayPredefined(Ruleset ruleset, Player p, Actor voicedActor, 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 (ruleset.Voices == null || ruleset.Notifications == null)
                return false;

            var rules = (voicedActor != null) ? ruleset.Voices[type] : ruleset.Notifications[type];
            if (rules == null)
                return false;

            var id = voicedActor != null ? voicedActor.ActorID : 0;

            string clip;
            var suffix = rules.DefaultVariant;
            var prefix = rules.DefaultPrefix;

            if (voicedActor != null)
            {
                if (!rules.VoicePools.Value.ContainsKey(definition))
                    throw new InvalidOperationException("Can't find {0} in voice pool.".F(definition));

                clip = rules.VoicePools.Value[definition].GetNext();
            }
            else
            {
                if (!rules.NotificationsPools.Value.ContainsKey(definition))
                    throw new InvalidOperationException("Can't find {0} in notification pool.".F(definition));

                clip = rules.NotificationsPools.Value[definition].GetNext();
            }

            if (string.IsNullOrEmpty(clip))
                return false;

            if (variant != null)
            {
                if (rules.Variants.ContainsKey(variant) && !rules.DisableVariants.Contains(definition))
                    suffix = rules.Variants[variant][id % rules.Variants[variant].Length];
                if (rules.Prefixes.ContainsKey(variant) && !rules.DisablePrefixes.Contains(definition))
                    prefix = rules.Prefixes[variant][id % rules.Prefixes[variant].Length];
            }

            var name = prefix + clip + suffix;

            if (!string.IsNullOrEmpty(name) && (p == null || p == p.World.LocalPlayer))
            {
                var sound = soundEngine.Play2D(sounds[name],
                    false, relative, pos,
                    InternalSoundVolume * volumeModifier, attenuateVolume);
                if (id != 0)
                {
                    if (currentSounds.ContainsKey(id))
                        soundEngine.StopSound(currentSounds[id]);

                    currentSounds[id] = sound;
                }
            }

            return true;
        }
Esempio n. 4
0
        public NewMapLogic(Action onExit, Action<string> onSelect, Ruleset modRules, Widget widget, World world)
        {
            panel = widget;

            panel.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };

            var tilesetDropDown = panel.Get<DropDownButtonWidget>("TILESET");
            var tilesets = modRules.TileSets.Select(t => t.Key).ToList();
            Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
            {
                var item = ScrollItemWidget.Setup(template,
                    () => tilesetDropDown.Text == option,
                    () => { tilesetDropDown.Text = option; });
                item.Get<LabelWidget>("LABEL").GetText = () => option;
                return item;
            };
            tilesetDropDown.Text = tilesets.First();
            tilesetDropDown.OnClick = () =>
                tilesetDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, tilesets, setupItem);

            var widthTextField = panel.Get<TextFieldWidget>("WIDTH");
            var heightTextField = panel.Get<TextFieldWidget>("HEIGHT");

            panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = () =>
            {
                var tileset = modRules.TileSets[tilesetDropDown.Text];
                var map = Map.FromTileset(tileset);

                int width, height;
                int.TryParse(widthTextField.Text, out width);
                int.TryParse(heightTextField.Text, out height);

                // Require at least a 2x2 playable area so that the
                // ground is visible through the edge shroud
                width = Math.Max(2, width);
                height = Math.Max(2, height);

                map.Resize(width + 2, height + tileset.MaxGroundHeight + 2);
                map.ResizeCordon(1, 1, width + 1, height + tileset.MaxGroundHeight + 1);
                map.PlayerDefinitions = new MapPlayers(map.Rules, map.SpawnPoints.Value.Length).ToMiniYaml();
                map.FixOpenAreas(modRules);

                var userMapFolder = Game.ModData.Manifest.MapFolders.First(f => f.Value == "User").Key;

                // Ignore optional flag
                if (userMapFolder.StartsWith("~"))
                    userMapFolder = userMapFolder.Substring(1);

                var mapDir = Platform.ResolvePath(userMapFolder);
                Directory.CreateDirectory(mapDir);
                var tempLocation = Path.Combine(mapDir, "temp") + ".oramap";
                map.Save(tempLocation); // TODO: load it right away and save later properly

                var newMap = new Map(tempLocation);
                Game.ModData.MapCache[newMap.Uid].UpdateFromMap(newMap, MapClassification.User);

                ConnectionLogic.Connect(System.Net.IPAddress.Loopback.ToString(),
                    Game.CreateLocalServer(newMap.Uid),
                    "",
                    () => { Game.LoadEditor(newMap.Uid); },
                    () => { Game.CloseServer(); onExit(); });
                onSelect(newMap.Uid);
            };
        }
Esempio n. 5
0
        public NewMapLogic(Action onExit, Action<string> onSelect, Ruleset modRules, Widget widget, World world)
        {
            panel = widget;

            panel.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); };

            var tilesetDropDown = panel.Get<DropDownButtonWidget>("TILESET");
            var tilesets = modRules.TileSets.Select(t => t.Key).ToList();
            Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
            {
                var item = ScrollItemWidget.Setup(template,
                    () => tilesetDropDown.Text == option,
                    () => { tilesetDropDown.Text = option; });
                item.Get<LabelWidget>("LABEL").GetText = () => option;
                return item;
            };
            tilesetDropDown.Text = tilesets.First();
            tilesetDropDown.OnClick = () =>
                tilesetDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, tilesets, setupItem);

            var widthTextField = panel.Get<TextFieldWidget>("WIDTH");
            var heightTextField = panel.Get<TextFieldWidget>("HEIGHT");

            panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = () =>
            {
                int width, height;
                int.TryParse(widthTextField.Text, out width);
                int.TryParse(heightTextField.Text, out height);

                // Require at least a 2x2 playable area so that the
                // ground is visible through the edge shroud
                width = Math.Max(2, width);
                height = Math.Max(2, height);

                var maxTerrainHeight = Game.ModData.Manifest.MaximumTerrainHeight;
                var tileset = modRules.TileSets[tilesetDropDown.Text];
                var map = new Map(tileset, width + 2, height + maxTerrainHeight + 2);

                var tl = new MPos(1, 1);
                var br = new MPos(width, height + maxTerrainHeight);
                map.SetBounds(tl, br);

                map.PlayerDefinitions = new MapPlayers(map.Rules, map.SpawnPoints.Value.Length).ToMiniYaml();
                map.FixOpenAreas(modRules);

                Action<string> afterSave = uid =>
                {
                    // HACK: Work around a synced-code change check.
                    // It's not clear why this is needed here, but not in the other places that load maps.
                    Game.RunAfterTick(() =>
                    {
                        ConnectionLogic.Connect(System.Net.IPAddress.Loopback.ToString(),
                            Game.CreateLocalServer(uid), "",
                            () => Game.LoadEditor(uid),
                            () => { Game.CloseServer(); onExit(); });
                    });

                    Ui.CloseWindow();
                    onSelect(uid);
                };

                Ui.OpenWindow("SAVE_MAP_PANEL", new WidgetArgs()
                {
                    { "onSave", afterSave },
                    { "onExit", () => { Ui.CloseWindow(); onExit(); } },
                    { "map", map },
                    { "playerDefinitions", map.PlayerDefinitions },
                    { "actorDefinitions", map.ActorDefinitions }
                });
            };
        }
Esempio n. 6
0
        public bool PlayNotification(Ruleset 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(rules, player, null, type.ToLowerInvariant(), notification, variant, true, WPos.Zero, 1f, false);
        }
Esempio n. 7
0
        public ModData(Manifest mod, InstalledMods mods, bool useLoadScreen = false)
        {
            Languages = new string[0];

            // Take a local copy of the manifest
            Manifest       = new Manifest(mod.Id, mod.Package);
            ObjectCreator  = new ObjectCreator(Manifest, mods);
            PackageLoaders = ObjectCreator.GetLoaders <IPackageLoader>(Manifest.PackageFormats, "package");

            ModFiles = new FS(mod.Id, mods, PackageLoaders);
            ModFiles.LoadFromManifest(Manifest);
            Manifest.LoadCustomData(ObjectCreator);

            if (useLoadScreen)
            {
                LoadScreen = ObjectCreator.CreateObject <ILoadScreen>(Manifest.LoadScreen.Value);
                LoadScreen.Init(this, Manifest.LoadScreen.ToDictionary(my => my.Value));
                LoadScreen.Display();
            }

            WidgetLoader = new WidgetLoader(this);
            MapCache     = new MapCache(this);

            SoundLoaders  = ObjectCreator.GetLoaders <ISoundLoader>(Manifest.SoundFormats, "sound");
            SpriteLoaders = ObjectCreator.GetLoaders <ISpriteLoader>(Manifest.SpriteFormats, "sprite");
            VideoLoaders  = ObjectCreator.GetLoaders <IVideoLoader>(Manifest.VideoFormats, "video");

            var terrainFormat = Manifest.Get <TerrainFormat>();
            var terrainLoader = ObjectCreator.FindType(terrainFormat.Type + "Loader");
            var terrainCtor   = terrainLoader?.GetConstructor(new[] { typeof(ModData) });

            if (terrainLoader == null || !terrainLoader.GetInterfaces().Contains(typeof(ITerrainLoader)) || terrainCtor == null)
            {
                throw new InvalidOperationException("Unable to find a terrain loader for type '{0}'.".F(terrainFormat.Type));
            }

            TerrainLoader = (ITerrainLoader)terrainCtor.Invoke(new[] { this });

            var sequenceFormat = Manifest.Get <SpriteSequenceFormat>();
            var sequenceLoader = ObjectCreator.FindType(sequenceFormat.Type + "Loader");
            var sequenceCtor   = sequenceLoader != null?sequenceLoader.GetConstructor(new[] { typeof(ModData) }) : null;

            if (sequenceLoader == null || !sequenceLoader.GetInterfaces().Contains(typeof(ISpriteSequenceLoader)) || sequenceCtor == null)
            {
                throw new InvalidOperationException("Unable to find a sequence loader for type '{0}'.".F(sequenceFormat.Type));
            }

            SpriteSequenceLoader = (ISpriteSequenceLoader)sequenceCtor.Invoke(new[] { this });

            var modelFormat = Manifest.Get <ModelSequenceFormat>();
            var modelLoader = ObjectCreator.FindType(modelFormat.Type + "Loader");
            var modelCtor   = modelLoader != null?modelLoader.GetConstructor(new[] { typeof(ModData) }) : null;

            if (modelLoader == null || !modelLoader.GetInterfaces().Contains(typeof(IModelSequenceLoader)) || modelCtor == null)
            {
                throw new InvalidOperationException("Unable to find a model loader for type '{0}'.".F(modelFormat.Type));
            }

            ModelSequenceLoader = (IModelSequenceLoader)modelCtor.Invoke(new[] { this });
            ModelSequenceLoader.OnMissingModelError = s => Log.Write("debug", s);

            Hotkeys = new HotkeyManager(ModFiles, Game.Settings.Keys, Manifest);

            defaultRules       = Exts.Lazy(() => Ruleset.LoadDefaults(this));
            defaultTerrainInfo = Exts.Lazy(() =>
            {
                var items = new Dictionary <string, ITerrainInfo>();

                foreach (var file in Manifest.TileSets)
                {
                    var t = TerrainLoader.ParseTerrain(DefaultFileSystem, file);
                    items.Add(t.Id, t);
                }

                return((IReadOnlyDictionary <string, ITerrainInfo>)(new ReadOnlyDictionary <string, ITerrainInfo>(items)));
            });

            defaultSequences = Exts.Lazy(() =>
            {
                var items = DefaultTerrainInfo.ToDictionary(t => t.Key, t => new SequenceProvider(DefaultFileSystem, this, t.Key, null));
                return((IReadOnlyDictionary <string, SequenceProvider>)(new ReadOnlyDictionary <string, SequenceProvider>(items)));
            });

            initialThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
        }
Esempio n. 8
0
        // Returns true if played successfully
        public bool PlayPredefined(SoundType soundType, Ruleset ruleset, Player p, Actor voicedActor, string type, string definition, string variant,
                                   bool relative, WPos pos, float volumeModifier, bool attenuateVolume)
        {
            if (ruleset == null)
            {
                throw new ArgumentNullException("ruleset");
            }

            if (definition == null || (DisableWorldSounds && soundType == SoundType.World))
            {
                return(false);
            }

            if (ruleset.Voices == null || ruleset.Notifications == null)
            {
                return(false);
            }

            var rules = (voicedActor != null) ? ruleset.Voices[type] : ruleset.Notifications[type];

            if (rules == null)
            {
                return(false);
            }

            var id = voicedActor != null ? voicedActor.ActorID : 0;

            string clip;
            var    suffix = rules.DefaultVariant;
            var    prefix = rules.DefaultPrefix;

            if (voicedActor != null)
            {
                if (!rules.VoicePools.Value.ContainsKey(definition))
                {
                    throw new InvalidOperationException("Can't find {0} in voice pool.".F(definition));
                }

                clip = rules.VoicePools.Value[definition].GetNext();
            }
            else
            {
                if (!rules.NotificationsPools.Value.ContainsKey(definition))
                {
                    throw new InvalidOperationException("Can't find {0} in notification pool.".F(definition));
                }

                clip = rules.NotificationsPools.Value[definition].GetNext();
            }

            if (string.IsNullOrEmpty(clip))
            {
                return(false);
            }

            if (variant != null)
            {
                if (rules.Variants.ContainsKey(variant) && !rules.DisableVariants.Contains(definition))
                {
                    suffix = rules.Variants[variant][id % rules.Variants[variant].Length];
                }
                if (rules.Prefixes.ContainsKey(variant) && !rules.DisablePrefixes.Contains(definition))
                {
                    prefix = rules.Prefixes[variant][id % rules.Prefixes[variant].Length];
                }
            }

            var name = prefix + clip + suffix;

            if (!string.IsNullOrEmpty(name) && (p == null || p == p.World.LocalPlayer))
            {
                var sound = soundEngine.Play2D(sounds[name],
                                               false, relative, pos,
                                               InternalSoundVolume * volumeModifier, attenuateVolume);
                if (id != 0)
                {
                    if (currentSounds.ContainsKey(id))
                    {
                        soundEngine.StopSound(currentSounds[id]);
                    }

                    currentSounds[id] = sound;
                }
            }

            return(true);
        }
Esempio n. 9
0
        public static Ruleset Load(ModData modData, IReadOnlyFileSystem fileSystem, string tileSet,
                                   MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications,
                                   MiniYaml mapMusic, MiniYaml mapSequences, MiniYaml mapModelSequences)
        {
            var m  = modData.Manifest;
            var dr = modData.DefaultRules;

            Ruleset ruleset = null;
            Action  f       = () =>
            {
                var actors = MergeOrDefault("Rules", fileSystem, m.Rules, mapRules, dr.Actors,
                                            k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value),
                                            filterNode: n => n.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal));

                var weapons = MergeOrDefault("Weapons", fileSystem, m.Weapons, mapWeapons, dr.Weapons,
                                             k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));

                var voices = MergeOrDefault("Voices", fileSystem, m.Voices, mapVoices, dr.Voices,
                                            k => new SoundInfo(k.Value));

                var notifications = MergeOrDefault("Notifications", fileSystem, m.Notifications, mapNotifications, dr.Notifications,
                                                   k => new SoundInfo(k.Value));

                var music = MergeOrDefault("Music", fileSystem, m.Music, mapMusic, dr.Music,
                                           k => new MusicInfo(k.Key, k.Value));

                // TODO: Add support for merging custom terrain modifications
                var terrainInfo = modData.DefaultTerrainInfo[tileSet];

                // TODO: Top-level dictionary should be moved into the Ruleset instead of in its own object
                var sequences = mapSequences == null ? modData.DefaultSequences[tileSet] :
                                new SequenceProvider(fileSystem, modData, tileSet, mapSequences);

                var modelSequences = dr.ModelSequences;
                if (mapModelSequences != null)
                {
                    modelSequences = MergeOrDefault("ModelSequences", fileSystem, m.ModelSequences, mapModelSequences, dr.ModelSequences,
                                                    k => k);
                }

                ruleset = new Ruleset(actors, weapons, voices, notifications, music, terrainInfo, sequences, modelSequences);
            };

            if (modData.IsOnMainThread)
            {
                modData.HandleLoadingProgress();

                var loader = new Task(f);
                loader.Start();

                // Animate the loadscreen while we wait
                while (!loader.Wait(40))
                {
                    modData.HandleLoadingProgress();
                }
            }
            else
            {
                f();
            }

            return(ruleset);
        }
Esempio n. 10
0
        public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml, Action <MapPreview> parseMetadata = null)
        {
            var newData = innerData.Clone();

            newData.Status = status;
            newData.Class  = MapClassification.Remote;

            if (status == MapStatus.DownloadAvailable)
            {
                try
                {
                    var r = FieldLoader.Load <RemoteMapData>(yaml);

                    // Map download has been disabled server side
                    if (!r.downloading)
                    {
                        newData.Status = MapStatus.Unavailable;
                        return;
                    }

                    newData.Title       = r.title;
                    newData.Categories  = r.categories;
                    newData.Author      = r.author;
                    newData.PlayerCount = r.players;
                    newData.Bounds      = r.bounds;
                    newData.TileSet     = r.tileset;

                    var spawns = new CPos[r.spawnpoints.Length / 2];
                    for (var j = 0; j < r.spawnpoints.Length; j += 2)
                    {
                        spawns[j / 2] = new CPos(r.spawnpoints[j], r.spawnpoints[j + 1]);
                    }
                    newData.SpawnPoints = spawns;
                    newData.GridType    = r.map_grid_type;
                    try
                    {
                        newData.Preview = new Bitmap(new MemoryStream(Convert.FromBase64String(r.minimap)));
                    }
                    catch (Exception e)
                    {
                        Log.Write("debug", "Failed parsing mapserver minimap response: {0}", e);
                        newData.Preview = null;
                    }

                    var playersString = Encoding.UTF8.GetString(Convert.FromBase64String(r.players_block));
                    newData.Players = new MapPlayers(MiniYaml.FromString(playersString));

                    newData.SetRulesetGenerator(modData, () =>
                    {
                        var rulesString             = Encoding.UTF8.GetString(Convert.FromBase64String(r.rules));
                        var rulesYaml               = new MiniYaml("", MiniYaml.FromString(rulesString)).ToDictionary();
                        var ruleDefinitions         = LoadRuleSection(rulesYaml, "Rules");
                        var weaponDefinitions       = LoadRuleSection(rulesYaml, "Weapons");
                        var voiceDefinitions        = LoadRuleSection(rulesYaml, "Voices");
                        var musicDefinitions        = LoadRuleSection(rulesYaml, "Music");
                        var notificationDefinitions = LoadRuleSection(rulesYaml, "Notifications");
                        var sequenceDefinitions     = LoadRuleSection(rulesYaml, "Sequences");
                        var rules = Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
                                                 voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions);
                        var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
                                                                       weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
                        return(Pair.New(rules, flagged));
                    });
                }
                catch (Exception e)
                {
                    Log.Write("debug", "Failed parsing mapserver response: {0}", e);
                }

                // Commit updated data before running the callbacks
                innerData = newData;

                if (innerData.Preview != null)
                {
                    cache.CacheMinimap(this);
                }

                if (parseMetadata != null)
                {
                    parseMetadata(this);
                }
            }

            // Update the status and class unconditionally
            innerData = newData;
        }
Esempio n. 11
0
        public void UpdateFromMap(IReadOnlyPackage p, IReadOnlyPackage parent, MapClassification classification, string[] mapCompatibility, MapGridType gridType)
        {
            Dictionary <string, MiniYaml> yaml;

            using (var yamlStream = p.GetStream("map.yaml"))
            {
                if (yamlStream == null)
                {
                    throw new FileNotFoundException("Required file map.yaml not present in this map");
                }

                yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, "map.yaml")).ToDictionary();
            }

            Package       = p;
            parentPackage = parent;

            var newData = innerData.Clone();

            newData.GridType = gridType;
            newData.Class    = classification;

            MiniYaml temp;

            if (yaml.TryGetValue("MapFormat", out temp))
            {
                var format = FieldLoader.GetValue <int>("MapFormat", temp.Value);
                if (format != Map.SupportedMapFormat)
                {
                    throw new InvalidDataException("Map format {0} is not supported.".F(format));
                }
            }

            if (yaml.TryGetValue("Title", out temp))
            {
                newData.Title = temp.Value;
            }

            if (yaml.TryGetValue("Categories", out temp))
            {
                newData.Categories = FieldLoader.GetValue <string[]>("Categories", temp.Value);
            }

            if (yaml.TryGetValue("Tileset", out temp))
            {
                newData.TileSet = temp.Value;
            }

            if (yaml.TryGetValue("Author", out temp))
            {
                newData.Author = temp.Value;
            }

            if (yaml.TryGetValue("Bounds", out temp))
            {
                newData.Bounds = FieldLoader.GetValue <Rectangle>("Bounds", temp.Value);
            }

            if (yaml.TryGetValue("Visibility", out temp))
            {
                newData.Visibility = FieldLoader.GetValue <MapVisibility>("Visibility", temp.Value);
            }

            string requiresMod = string.Empty;

            if (yaml.TryGetValue("RequiresMod", out temp))
            {
                requiresMod = temp.Value;
            }

            newData.Status = mapCompatibility == null || mapCompatibility.Contains(requiresMod) ?
                             MapStatus.Available : MapStatus.Unavailable;

            try
            {
                // Actor definitions may change if the map format changes
                MiniYaml actorDefinitions;
                if (yaml.TryGetValue("Actors", out actorDefinitions))
                {
                    var spawns = new List <CPos>();
                    foreach (var kv in actorDefinitions.Nodes.Where(d => d.Value.Value == "mpspawn"))
                    {
                        var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
                        spawns.Add(s.InitDict.Get <LocationInit>().Value(null));
                    }

                    newData.SpawnPoints = spawns.ToArray();
                }
                else
                {
                    newData.SpawnPoints = new CPos[0];
                }
            }
            catch (Exception)
            {
                newData.SpawnPoints = new CPos[0];
                newData.Status      = MapStatus.Unavailable;
            }

            try
            {
                // Player definitions may change if the map format changes
                MiniYaml playerDefinitions;
                if (yaml.TryGetValue("Players", out playerDefinitions))
                {
                    newData.Players     = new MapPlayers(playerDefinitions.Nodes);
                    newData.PlayerCount = newData.Players.Players.Count(x => x.Value.Playable);
                }
            }
            catch (Exception)
            {
                newData.Status = MapStatus.Unavailable;
            }

            newData.SetRulesetGenerator(modData, () =>
            {
                var ruleDefinitions         = LoadRuleSection(yaml, "Rules");
                var weaponDefinitions       = LoadRuleSection(yaml, "Weapons");
                var voiceDefinitions        = LoadRuleSection(yaml, "Voices");
                var musicDefinitions        = LoadRuleSection(yaml, "Music");
                var notificationDefinitions = LoadRuleSection(yaml, "Notifications");
                var sequenceDefinitions     = LoadRuleSection(yaml, "Sequences");
                var rules = Ruleset.Load(modData, this, TileSet, ruleDefinitions, weaponDefinitions,
                                         voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions);
                var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
                                                               weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
                return(Pair.New(rules, flagged));
            });

            if (p.Contains("map.png"))
            {
                using (var dataStream = p.GetStream("map.png"))
                    newData.Preview = new Bitmap(dataStream);
            }

            // Assign the new data atomically
            innerData = newData;
        }