From 8c15a4f6f48286e511e4a4df97ccd75d477ccc27 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 2 Apr 2010 00:44:51 +1300 Subject: [PATCH] Convert overlays (wall, field, crate) to actors. Untested cnc support. --- MapConverter/MapConverter.cs | 149 ++++++++++------- OpenRA.Game/Actor.cs | 4 + OpenRA.Game/OpenRA.Game.csproj | 3 +- OpenRA.Game/Traits/World/WallLoadHook.cs | 32 ---- mods/cnc/system.yaml | 18 --- mods/ra/system.yaml | 18 --- mods/ra/testmap.bin | Bin 81925 -> 81925 bytes mods/ra/testmap.yaml | 197 ++++++++++++++++++----- 8 files changed, 255 insertions(+), 166 deletions(-) delete mode 100644 OpenRA.Game/Traits/World/WallLoadHook.cs diff --git a/MapConverter/MapConverter.cs b/MapConverter/MapConverter.cs index 42b6bc6f98..7ac247e12f 100644 --- a/MapConverter/MapConverter.cs +++ b/MapConverter/MapConverter.cs @@ -30,22 +30,7 @@ namespace MapConverter { public class MapConverter { - public readonly int INIFormat; - - public readonly int MapSize; - public readonly int XOffset; - public readonly int YOffset; - - public readonly int Width; - public readonly int Height; - public Map Map = new Map(); - - static string Truncate( string s, int maxLength ) - { - return s.Length <= maxLength ? s : s.Substring(0,maxLength ); - } - - + // Mapping from ra overlay index to type string static string[] raOverlayNames = { "sbag", "cycl", "brik", "fenc", "wood", @@ -55,7 +40,9 @@ namespace MapConverter "fpls", "wcrate", "scrate", "barb", "sbag", }; - Dictionary< string, Pair > resourceMapping = new Dictionary>() { + Dictionary< string, Pair > overlayResourceMapping = new Dictionary>() + { + // RA Gems, Gold { "gold01", new Pair(1,0) }, { "gold02", new Pair(1,1) }, { "gold03", new Pair(1,2) }, @@ -66,32 +53,62 @@ namespace MapConverter { "gem03", new Pair(2,2) }, { "gem04", new Pair(2,3) }, - // TODO Add cnc tiberium + // cnc tiberium + { "ti1", new Pair(1,0) }, + { "ti2", new Pair(1,1) }, + { "ti3", new Pair(1,2) }, + { "ti4", new Pair(1,3) }, + { "ti5", new Pair(1,4) }, + { "ti6", new Pair(1,5) }, + { "ti7", new Pair(1,6) }, + { "ti8", new Pair(1,7) }, + { "ti9", new Pair(1,8) }, + { "ti10", new Pair(1,9) }, + { "ti11", new Pair(1,10) }, + { "ti12", new Pair(1,11) }, }; + Dictionary overlayActorMapping = new Dictionary() { + // Fences + {"sbag","sbag"}, + {"cycl","cycl"}, + {"brik","brik"}, + {"fenc","fenc"}, + {"wood","wood"}, + + // Fields + {"v12","v12"}, + {"v13","v13"}, + {"v14","v14"}, + {"v15","v15"}, + {"v16","v16"}, + {"v17","v17"}, + {"v18","v18"}, + + // Crates + {"wcrate","crate"}, + {"scrate","crate"}, + }; + int MapSize; + int ActorCount = 0; + public Map Map = new Map(); + public MapConverter(string filename) - { - Map.Author = "Westwood Studios"; - + { IniFile file = new IniFile(FileSystem.Open(filename)); - IniSection basic = file.GetSection("Basic"); - Map.Title = basic.GetValue("Name", "(null)"); - - - INIFormat = int.Parse(basic.GetValue("NewINIFormat", "0")); - IniSection map = file.GetSection("Map"); - Map.Tileset = Truncate(map.GetValue("Theater", "TEMPERAT"), 8); - - XOffset = int.Parse(map.GetValue("X", "0")); - YOffset = int.Parse(map.GetValue("Y", "0")); - - Width = int.Parse(map.GetValue("Width", "0")); - Height = int.Parse(map.GetValue("Height", "0")); + var INIFormat = int.Parse(basic.GetValue("NewINIFormat", "0")); + var XOffset = int.Parse(map.GetValue("X", "0")); + var YOffset = int.Parse(map.GetValue("Y", "0")); + var Width = int.Parse(map.GetValue("Width", "0")); + var Height = int.Parse(map.GetValue("Height", "0")); MapSize = (INIFormat == 3) ? 128 : 64; + Map.Title = basic.GetValue("Name", "(null)"); + Map.Author = "Westwood Studios"; + Map.Tileset = Truncate(map.GetValue("Theater", "TEMPERAT"), 8); Map.MapSize.X = MapSize; Map.MapSize.Y = MapSize; Map.TopLeft = new int2 (XOffset, YOffset); @@ -185,8 +202,8 @@ namespace MapConverter void UnpackRATileData( MemoryStream ms ) { Map.MapTiles = new TileReference[ MapSize, MapSize ]; - for( int j = 0 ; j < MapSize ; j++ ) - for( int i = 0 ; i < MapSize ; i++ ) + for( int i = 0 ; i < MapSize ; i++ ) + for( int j = 0 ; j < MapSize ; j++ ) Map.MapTiles[i,j] = new TileReference(); for( int j = 0 ; j < MapSize ; j++ ) @@ -211,10 +228,13 @@ namespace MapConverter byte o = ReadByte( ms ); var res = Pair.New((byte)0,(byte)0); - if (o != 255 && resourceMapping.ContainsKey(raOverlayNames[o])) - res = resourceMapping[raOverlayNames[o]]; - + if (o != 255 && overlayResourceMapping.ContainsKey(raOverlayNames[o])) + res = overlayResourceMapping[raOverlayNames[o]]; + Map.MapResources[i,j] = new TileReference(res.First, res.Second); + + if (o != 255 && overlayActorMapping.ContainsKey(raOverlayNames[o])) + Map.Actors.Add("Actor"+ActorCount++, new ActorReference( overlayActorMapping[raOverlayNames[o]], new int2(i,j), "Neutral")); } } @@ -223,39 +243,53 @@ namespace MapConverter IniSection terrain = file.GetSection( "TERRAIN", true ); if( terrain == null ) return; - int a = 0; + foreach( KeyValuePair kv in terrain ) { var loc = int.Parse( kv.Key ); - Map.Actors.Add("Actor"+a++, new ActorReference(kv.Value, new int2(loc % MapSize, loc / MapSize), "Neutral" ) ); + Map.Actors.Add("Actor"+ActorCount++, new ActorReference(kv.Value.ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), "Neutral" ) ); } } void UnpackCncTileData( Stream ms ) { - /*for( int i = 0 ; i < MapSize ; i++ ) + Map.MapTiles = new TileReference[ MapSize, MapSize ]; + for( int i = 0 ; i < MapSize ; i++ ) + for( int j = 0 ; j < MapSize ; j++ ) + Map.MapTiles[i,j] = new TileReference(); + + for( int i = 0 ; i < MapSize ; i++ ) for( int j = 0 ; j < MapSize ; j++ ) { - MapTiles[j, i].tile = (byte)ms.ReadByte(); - MapTiles[j, i].image = (byte)ms.ReadByte(); + Map.MapTiles[i,j].type = (ushort)ms.ReadByte(); + Map.MapTiles[i,j].image = (byte)ms.ReadByte(); - if( MapTiles[ j, i ].tile == 0xff ) - MapTiles[ j, i ].image = (byte)( i % 4 + ( j % 4 ) * 4 ); - }*/ + if( Map.MapTiles[i,j].type == 0xff || Map.MapTiles[i,j].type == 0xffff ) + Map.MapTiles[i,j].image = (byte)( i % 4 + ( j % 4 ) * 4 ); + } } void ReadCncOverlay( IniFile file ) { - /*IniSection overlay = file.GetSection( "OVERLAY", true ); + IniSection overlay = file.GetSection( "OVERLAY", true ); if( overlay == null ) return; - + + Map.MapResources = new TileReference[ MapSize, MapSize ]; foreach( KeyValuePair kv in overlay ) { var loc = int.Parse( kv.Key ); int2 cell = new int2(loc % MapSize, loc / MapSize); - MapTiles[ cell.X, cell.Y ].overlay = kv.Value.ToLower(); - }*/ + + var res = Pair.New((byte)0,(byte)0); + if (overlayResourceMapping.ContainsKey(kv.Value.ToLower())) + res = overlayResourceMapping[kv.Value.ToLower()]; + + Map.MapResources[ cell.X, cell.Y ] = new TileReference(res.First, res.Second); + + if (overlayActorMapping.ContainsKey(kv.Value.ToLower())) + Map.Actors.Add("Actor"+ActorCount++, new ActorReference( overlayActorMapping[kv.Value.ToLower()], new int2(cell.X,cell.Y), "Neutral")); + } } @@ -264,18 +298,16 @@ namespace MapConverter IniSection terrain = file.GetSection( "TERRAIN", true ); if( terrain == null ) return; - - int a = 0; + foreach( KeyValuePair kv in terrain ) { var loc = int.Parse( kv.Key ); - Map.Actors.Add("Actor"+a++, new ActorReference( kv.Value.Split(',')[0], new int2(loc % MapSize, loc / MapSize),"Neutral")); + Map.Actors.Add("Actor"+ActorCount++, new ActorReference( kv.Value.Split(',')[0].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize),"Neutral")); } } void LoadActors(IniFile file, string section) { - int a = 0; foreach (var s in file.GetSection(section, true)) { //num=owner,type,health,location,facing,... @@ -283,10 +315,15 @@ namespace MapConverter var loc = int.Parse(parts[3]); if (parts[0] == "") parts[0] = "Neutral"; - Map.Actors.Add("Actor"+a++, new ActorReference( parts[1].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), parts[0])); + Map.Actors.Add("Actor"+ActorCount++, new ActorReference( parts[1].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), parts[0])); } } + static string Truncate( string s, int maxLength ) + { + return s.Length <= maxLength ? s : s.Substring(0,maxLength ); + } + public void Save(string filepath) { Map.Tiledata = filepath+".bin"; diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 3407537aa4..28159f5ceb 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using System; using OpenRA.FileFormats; using OpenRA.GameRules; using OpenRA.Traits; @@ -55,6 +56,9 @@ namespace OpenRA if (name != null) { + if (!Rules.Info.ContainsKey(name.ToLowerInvariant())) + throw new NotImplementedException("No rules definition for unit {0}".F(name.ToLowerInvariant())); + Info = Rules.Info[name.ToLowerInvariant()]; Health = this.GetMaxHP(); diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 4cecc9c0fd..3902bb7a13 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -1,4 +1,4 @@ - + Debug @@ -262,7 +262,6 @@ - diff --git a/OpenRA.Game/Traits/World/WallLoadHook.cs b/OpenRA.Game/Traits/World/WallLoadHook.cs deleted file mode 100644 index 2ab0dad7c5..0000000000 --- a/OpenRA.Game/Traits/World/WallLoadHook.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OpenRA.Traits -{ - class WallLoadHookInfo : ITraitInfo - { - public readonly string[] OverlayTypes = { }; - public readonly string ActorType = "brik"; - - public object Create(Actor self) { return new WallLoadHook( self, this ); } - } - - class WallLoadHook // : IGameStarted - { - WallLoadHookInfo info; - public WallLoadHook(Actor self, WallLoadHookInfo info) { this.info = info; } - /* - public void GameStarted(World w) - { - var map = w.Map; - - for (int y = map.YOffset; y < map.YOffset + map.Height; y++) - for (int x = map.XOffset; x < map.XOffset + map.Width; x++) - if (info.OverlayTypes.Contains(w.Map.MapTiles[x, y].overlay)) - w.CreateActor(info.ActorType, new int2(x, y), w.NeutralPlayer); - } - */ - } -} diff --git a/mods/cnc/system.yaml b/mods/cnc/system.yaml index 9dc036983e..abb7a5b003 100644 --- a/mods/cnc/system.yaml +++ b/mods/cnc/system.yaml @@ -189,24 +189,6 @@ World: SellButton: RepairButton: ChoosePaletteOnSelect: - WallLoadHook@sbag: - ActorType: sbag - OverlayTypes: sbag - WallLoadHook@cycl: - ActorType: cycl - OverlayTypes: cycl - WallLoadHook@brik: - ActorType: brik - OverlayTypes: brik - WallLoadHook@fenc: - ActorType: fenc - OverlayTypes: fenc - WallLoadHook@wood: - ActorType: wood - OverlayTypes: wood - WallLoadHook@barb: - ActorType: barb - OverlayTypes: barb ResourceLayer: ResourceType@green-tib: ResourceType: 1 diff --git a/mods/ra/system.yaml b/mods/ra/system.yaml index 672d879acf..a23dc9e83f 100644 --- a/mods/ra/system.yaml +++ b/mods/ra/system.yaml @@ -212,24 +212,6 @@ World: SellButton: RepairButton: PowerDownButton: - WallLoadHook@sbag: - ActorType: sbag - OverlayTypes: sbag - WallLoadHook@cycl: - ActorType: cycl - OverlayTypes: cycl - WallLoadHook@brik: - ActorType: brik - OverlayTypes: brik - WallLoadHook@fenc: - ActorType: fenc - OverlayTypes: fenc - WallLoadHook@wood: - ActorType: wood - OverlayTypes: wood - WallLoadHook@barb: - ActorType: barb - OverlayTypes: barb ResourceLayer: ResourceType@ore: ResourceType: 1 diff --git a/mods/ra/testmap.bin b/mods/ra/testmap.bin index 464010b961b94939ac6cd97d568d40a9309f4288..dab3d1318aed240650b695ed3669b158fd9b6549 100644 GIT binary patch literal 81925 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 N3Q&Lo6rcbF@IU9twub-! literal 81925 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