#region Copyright & License Information /* * Copyright 2007-2016 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.Drawing; using System.IO; using System.Linq; using OpenRA.Primitives; namespace OpenRA.Mods.D2k.UtilityCommands { public class D2kMapImporter { const int MapCordonWidth = 2; public static Dictionary> ActorDataByActorCode = new Dictionary> { { 20, Pair.New("wormspawner", "Creeps") }, { 23, Pair.New("mpspawn", "Neutral") }, { 41, Pair.New("spicebloom", "Neutral") }, { 42, Pair.New("spicebloom", "Neutral") }, { 43, Pair.New("spicebloom", "Neutral") }, { 44, Pair.New("spicebloom", "Neutral") }, { 45, Pair.New("spicebloom", "Neutral") }, // Atreides: { 4, Pair.New("wall", "Atreides") }, { 5, Pair.New("wind_trap", "Atreides") }, { 8, Pair.New("construction_yard", "Atreides") }, { 11, Pair.New("barracks", "Atreides") }, { 14, Pair.New("refinery", "Atreides") }, { 17, Pair.New("outpost", "Atreides") }, { 63, Pair.New("light_factory", "Atreides") }, { 69, Pair.New("silo", "Atreides") }, { 72, Pair.New("heavy_factory", "Atreides") }, { 75, Pair.New("repair_pad", "Atreides") }, { 78, Pair.New("medium_gun_turret", "Atreides") }, { 120, Pair.New("high_tech_factory", "Atreides") }, { 123, Pair.New("large_gun_turret", "Atreides") }, { 126, Pair.New("research_centre", "Atreides") }, { 129, Pair.New("starport", "Atreides") }, { 132, Pair.New("palace", "Atreides") }, { 180, Pair.New("light_inf", "Atreides") }, { 181, Pair.New("trooper", "Atreides") }, { 182, Pair.New("fremen", "Atreides") }, { 183, Pair.New("sardaukar", "Atreides") }, { 184, Pair.New("engineer", "Atreides") }, { 185, Pair.New("harvester", "Atreides") }, { 186, Pair.New("mcv", "Atreides") }, { 187, Pair.New("trike", "Atreides") }, { 188, Pair.New("quad", "Atreides") }, { 189, Pair.New("combat_tank_a", "Atreides") }, { 190, Pair.New("missile_tank", "Atreides") }, { 191, Pair.New("siege_tank", "Atreides") }, { 192, Pair.New("carryall", "Atreides") }, { 194, Pair.New("sonic_tank", "Atreides") }, // Harkonnen: { 204, Pair.New("wall", "Harkonnen") }, { 205, Pair.New("wind_trap", "Harkonnen") }, { 208, Pair.New("construction_yard", "Harkonnen") }, { 211, Pair.New("barracks", "Harkonnen") }, { 214, Pair.New("refinery", "Harkonnen") }, { 217, Pair.New("outpost", "Harkonnen") }, { 263, Pair.New("light_factory", "Harkonnen") }, { 269, Pair.New("silo", "Harkonnen") }, { 272, Pair.New("heavy_factory", "Harkonnen") }, { 275, Pair.New("repair_pad", "Harkonnen") }, { 278, Pair.New("medium_gun_turret", "Harkonnen") }, { 320, Pair.New("high_tech_factory", "Harkonnen") }, { 323, Pair.New("large_gun_turret", "Harkonnen") }, { 326, Pair.New("research_centre", "Harkonnen") }, { 329, Pair.New("starport", "Harkonnen") }, { 332, Pair.New("palace", "Harkonnen") }, { 360, Pair.New("light_inf", "Harkonnen") }, { 361, Pair.New("trooper", "Harkonnen") }, { 362, Pair.New("fremen", "Harkonnen") }, { 363, Pair.New("sardaukar", "Harkonnen") }, { 364, Pair.New("engineer", "Harkonnen") }, { 365, Pair.New("harvester", "Harkonnen") }, { 366, Pair.New("mcv", "Harkonnen") }, { 367, Pair.New("trike", "Harkonnen") }, { 368, Pair.New("quad", "Harkonnen") }, { 369, Pair.New("combat_tank_h", "Harkonnen") }, { 370, Pair.New("missile_tank", "Harkonnen") }, { 371, Pair.New("siege_tank", "Harkonnen") }, { 372, Pair.New("carryall", "Harkonnen") }, { 374, Pair.New("devastator", "Harkonnen") }, // Ordos: { 404, Pair.New("wall", "Ordos") }, { 405, Pair.New("wind_trap", "Ordos") }, { 408, Pair.New("construction_yard", "Ordos") }, { 411, Pair.New("barracks", "Ordos") }, { 414, Pair.New("refinery", "Ordos") }, { 417, Pair.New("outpost", "Ordos") }, { 463, Pair.New("light_factory", "Ordos") }, { 469, Pair.New("silo", "Ordos") }, { 472, Pair.New("heavy_factory", "Ordos") }, { 475, Pair.New("repair_pad", "Ordos") }, { 478, Pair.New("medium_gun_turret", "Ordos") }, { 520, Pair.New("high_tech_factory", "Ordos") }, { 523, Pair.New("large_gun_turret", "Ordos") }, { 526, Pair.New("research_centre", "Ordos") }, { 529, Pair.New("starport", "Ordos") }, { 532, Pair.New("palace", "Ordos") }, { 560, Pair.New("light_inf", "Ordos") }, { 561, Pair.New("trooper", "Ordos") }, { 562, Pair.New("saboteur", "Ordos") }, { 563, Pair.New("sardaukar", "Ordos") }, { 564, Pair.New("engineer", "Ordos") }, { 565, Pair.New("harvester", "Ordos") }, { 566, Pair.New("mcv", "Ordos") }, { 567, Pair.New("raider", "Ordos") }, { 568, Pair.New("quad", "Ordos") }, { 569, Pair.New("combat_tank_o", "Ordos") }, { 570, Pair.New("missile_tank", "Ordos") }, { 571, Pair.New("siege_tank", "Ordos") }, { 572, Pair.New("carryall", "Ordos") }, { 574, Pair.New("deviator", "Ordos") }, // Corrino: { 580, Pair.New("wall", "Corrino") }, { 581, Pair.New("wind_trap", "Corrino") }, { 582, Pair.New("construction_yard", "Corrino") }, { 583, Pair.New("barracks", "Corrino") }, { 584, Pair.New("refinery", "Corrino") }, { 585, Pair.New("outpost", "Corrino") }, { 587, Pair.New("light_factory", "Corrino") }, { 588, Pair.New("palace", "Corrino") }, { 589, Pair.New("silo", "Corrino") }, { 590, Pair.New("heavy_factory", "Corrino") }, { 591, Pair.New("repair_pad", "Corrino") }, { 592, Pair.New("medium_gun_turret", "Corrino") }, { 593, Pair.New("high_tech_factory", "Corrino") }, { 594, Pair.New("large_gun_turret", "Corrino") }, { 595, Pair.New("research_centre", "Corrino") }, { 596, Pair.New("starport", "Corrino") }, { 597, Pair.New("sietch", "Corrino") }, { 598, Pair.New("light_inf", "Corrino") }, { 599, Pair.New("trooper", "Corrino") }, { 600, Pair.New("sardaukar", "Corrino") }, { 601, Pair.New("fremen", "Corrino") }, { 602, Pair.New("engineer", "Corrino") }, { 603, Pair.New("harvester", "Corrino") }, { 604, Pair.New("mcv", "Corrino") }, { 605, Pair.New("trike", "Corrino") }, { 606, Pair.New("quad", "Corrino") }, { 607, Pair.New("combat_tank_h", "Corrino") }, { 608, Pair.New("missile_tank", "Corrino") }, { 609, Pair.New("siege_tank", "Corrino") }, { 610, Pair.New("carryall", "Corrino") }, // Fremen: { 620, Pair.New("wall", "Fremen") }, { 621, Pair.New("wind_trap", "Fremen") }, { 622, Pair.New("construction_yard", "Fremen") }, { 623, Pair.New("barracks", "Fremen") }, { 624, Pair.New("refinery", "Fremen") }, { 625, Pair.New("outpost", "Fremen") }, { 627, Pair.New("light_factory", "Fremen") }, { 628, Pair.New("palace", "Fremen") }, { 629, Pair.New("silo", "Fremen") }, { 630, Pair.New("heavy_factory", "Fremen") }, { 631, Pair.New("repair_pad", "Fremen") }, { 632, Pair.New("medium_gun_turret", "Fremen") }, { 633, Pair.New("high_tech_factory", "Fremen") }, { 634, Pair.New("large_gun_turret", "Fremen") }, { 635, Pair.New("research_centre", "Fremen") }, { 636, Pair.New("starport", "Fremen") }, { 637, Pair.New("sietch", "Fremen") }, { 638, Pair.New("light_inf", "Fremen") }, { 639, Pair.New("trooper", "Fremen") }, { 640, Pair.New("fremen", "Fremen") }, { 641, Pair.New("nsfremen", "Fremen") }, { 642, Pair.New("engineer", "Fremen") }, { 643, Pair.New("harvester", "Fremen") }, { 644, Pair.New("mcv", "Fremen") }, { 645, Pair.New("trike", "Fremen") }, { 646, Pair.New("quad", "Fremen") }, { 647, Pair.New("combat_tank_a", "Fremen") }, { 648, Pair.New("missile_tank", "Fremen") }, { 649, Pair.New("siege_tank", "Fremen") }, { 650, Pair.New("carryall", "Fremen") }, { 652, Pair.New("sonic_tank", "Fremen") }, // Smugglers: { 660, Pair.New("wall", "Smugglers") }, { 661, Pair.New("wind_trap", "Smugglers") }, { 662, Pair.New("construction_yard", "Smugglers") }, { 663, Pair.New("barracks", "Smugglers") }, { 664, Pair.New("refinery", "Smugglers") }, { 666, Pair.New("outpost", "Smugglers") }, { 667, Pair.New("light_factory", "Smugglers") }, { 668, Pair.New("silo", "Smugglers") }, { 669, Pair.New("heavy_factory", "Smugglers") }, { 670, Pair.New("repair_pad", "Smugglers") }, { 671, Pair.New("medium_gun_turret", "Smugglers") }, { 672, Pair.New("high_tech_factory", "Smugglers") }, { 673, Pair.New("large_gun_turret", "Smugglers") }, { 674, Pair.New("research_centre", "Smugglers") }, { 675, Pair.New("starport", "Smugglers") }, { 676, Pair.New("palace", "Smugglers") }, { 677, Pair.New("light_inf", "Smugglers") }, { 678, Pair.New("trooper", "Smugglers") }, { 679, Pair.New("saboteur", "Smugglers") }, { 680, Pair.New("engineer", "Smugglers") }, { 681, Pair.New("harvester", "Smugglers") }, { 682, Pair.New("mcv", "Smugglers") }, { 683, Pair.New("trike", "Smugglers") }, { 684, Pair.New("quad", "Smugglers") }, { 685, Pair.New("combat_tank_o", "Smugglers") }, { 686, Pair.New("missile_tank", "Smugglers") }, { 687, Pair.New("siege_tank", "Smugglers") }, { 688, Pair.New("carryall", "Smugglers") }, // Mercenaries: { 700, Pair.New("wall", "Mercenaries") }, { 701, Pair.New("wind_trap", "Mercenaries") }, { 702, Pair.New("construction_yard", "Mercenaries") }, { 703, Pair.New("barracks", "Mercenaries") }, { 704, Pair.New("refinery", "Mercenaries") }, { 705, Pair.New("outpost", "Mercenaries") }, { 707, Pair.New("light_factory", "Mercenaries") }, { 708, Pair.New("silo", "Mercenaries") }, { 709, Pair.New("heavy_factory", "Mercenaries") }, { 710, Pair.New("repair_pad", "Mercenaries") }, { 711, Pair.New("medium_gun_turret", "Mercenaries") }, { 712, Pair.New("high_tech_factory", "Mercenaries") }, { 713, Pair.New("large_gun_turret", "Mercenaries") }, { 714, Pair.New("research_centre", "Mercenaries") }, { 715, Pair.New("starport", "Mercenaries") }, { 716, Pair.New("palace", "Mercenaries") }, { 717, Pair.New("light_inf", "Mercenaries") }, { 718, Pair.New("trooper", "Mercenaries") }, { 719, Pair.New("saboteur", "Mercenaries") }, { 720, Pair.New("harvester", "Mercenaries") }, { 721, Pair.New("harvester", "Mercenaries") }, { 722, Pair.New("mcv", "Mercenaries") }, { 723, Pair.New("trike", "Mercenaries") }, { 724, Pair.New("quad", "Mercenaries") }, { 725, Pair.New("combat_tank_o", "Mercenaries") }, { 726, Pair.New("missile_tank", "Mercenaries") }, { 727, Pair.New("siege_tank", "Mercenaries") }, { 728, Pair.New("carryall", "Mercenaries") }, }; readonly Ruleset rules; readonly FileStream stream; readonly string tilesetName; readonly TerrainTile clearTile; Map map; Size mapSize; TileSet tileSet; List tileSetsFromYaml; D2kMapImporter(string filename, string tileset, Ruleset rules) { tilesetName = tileset; this.rules = rules; try { clearTile = new TerrainTile(0, 0); stream = File.OpenRead(filename); if (stream.Length == 0 || stream.Length % 4 != 0) throw new Exception("The map is in an unrecognized format!"); Initialize(filename); FillMap(); } catch (Exception e) { Console.WriteLine(e); map = null; } finally { stream.Close(); } } public static Map Import(string filename, string mod, string tileset, Ruleset rules) { var map = new D2kMapImporter(filename, tileset, rules).map; if (map == null) return null; map.RequiresMod = mod; var players = new MapPlayers(map.Rules, map.SpawnPoints.Value.Length); map.PlayerDefinitions = players.ToMiniYaml(); return map; } void Initialize(string mapFile) { mapSize = new Size(stream.ReadUInt16(), stream.ReadUInt16()); tileSet = rules.TileSets["ARRAKIS"]; map = new Map(tileSet, mapSize.Width + 2 * MapCordonWidth, mapSize.Height + 2 * MapCordonWidth) { Title = Path.GetFileNameWithoutExtension(mapFile), Author = "Westwood Studios" }; var tl = new PPos(MapCordonWidth, MapCordonWidth); var br = new PPos(MapCordonWidth + mapSize.Width - 1, MapCordonWidth + mapSize.Height - 1); map.SetBounds(tl, br); // Get all templates from the tileset YAML file that have at least one frame and an Image property corresponding to the requested tileset // Each frame is a tile from the Dune 2000 tileset files, with the Frame ID being the index of the tile in the original file tileSetsFromYaml = tileSet.Templates.Where(t => t.Value.Frames != null && t.Value.Images[0].ToLower() == tilesetName.ToLower()).Select(ts => ts.Value).ToList(); } void FillMap() { while (stream.Position < stream.Length) { var tileInfo = stream.ReadUInt16(); var tileSpecialInfo = stream.ReadUInt16(); var tile = GetTile(tileInfo); var locationOnMap = GetCurrentTilePositionOnMap(); map.MapTiles.Value[locationOnMap] = tile; // Spice if (tileSpecialInfo == 1) map.MapResources.Value[locationOnMap] = new ResourceTile(1, 1); if (tileSpecialInfo == 2) map.MapResources.Value[locationOnMap] = new ResourceTile(1, 2); // Actors if (ActorDataByActorCode.ContainsKey(tileSpecialInfo)) { var kvp = ActorDataByActorCode[tileSpecialInfo]; if (!rules.Actors.ContainsKey(kvp.First.ToLower())) throw new InvalidOperationException("Actor with name {0} could not be found in the rules YAML file!".F(kvp.First)); var a = new ActorReference(kvp.First) { new LocationInit(locationOnMap), new OwnerInit(kvp.Second) }; map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, a.Save())); } } } CPos GetCurrentTilePositionOnMap() { var tileIndex = (int)stream.Position / 4 - 2; var x = (tileIndex % mapSize.Width) + MapCordonWidth; var y = (tileIndex / mapSize.Width) + MapCordonWidth; return new CPos(x, y); } TerrainTile GetTile(int tileIndex) { // Get the first tileset template that contains the Frame ID of the original map's tile with the requested index var template = tileSetsFromYaml.FirstOrDefault(x => x.Frames.Contains(tileIndex)); // HACK: The arrakis.yaml tileset file seems to be missing some tiles, so just get a replacement for them // Also used for duplicate tiles that are taken from only tileset if (template == null) { // Just get a template that contains a tile with the same ID as requested var templates = tileSet.Templates.Where(t => t.Value.Frames != null && t.Value.Frames.Contains(tileIndex)); if (templates.Any()) template = templates.First().Value; } if (template == null) { var pos = GetCurrentTilePositionOnMap(); Console.WriteLine("Tile with index {0} could not be found in the tileset YAML file!".F(tileIndex)); Console.WriteLine("Defaulting to a \"clear\" tile for coordinates ({0}, {1})!".F(pos.X, pos.Y)); return clearTile; } var templateIndex = template.Id; var frameIndex = Array.IndexOf(template.Frames, tileIndex); return new TerrainTile(templateIndex, (byte)((frameIndex == -1) ? 0 : frameIndex)); } } }