From: Matthias Braun Date: Mon, 22 Nov 2004 01:49:43 +0000 (+0000) Subject: the app that was used to edit supertux.stgt for the new tiles X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=ddb675d1e52d0a76a3725b81c16f93aba1e31ab7;p=supertux.git the app that was used to edit supertux.stgt for the new tiles SVN-Revision: 2127 --- diff --git a/contrib/tilemanager/Application.cs b/contrib/tilemanager/Application.cs new file mode 100644 index 000000000..49fa912ff --- /dev/null +++ b/contrib/tilemanager/Application.cs @@ -0,0 +1,478 @@ +using System; +using System.IO; +using System.Collections; +using Gtk; +using Gdk; +using Gnome; +using Glade; + +public class Application { + [Glade.Widget] + private Gtk.Window MainWindow; + [Glade.Widget] + private Gtk.DrawingArea DrawingArea; + [Glade.Widget] + private Gtk.CheckButton SolidCheckButton; + [Glade.Widget] + private Gtk.CheckButton UniSolidCheckButton; + [Glade.Widget] + private Gtk.CheckButton IceCheckButton; + [Glade.Widget] + private Gtk.CheckButton WaterCheckButton; + [Glade.Widget] + private Gtk.CheckButton SlopeCheckButton; + [Glade.Widget] + private Gtk.CheckButton DontUseCheckButton; + [Glade.Widget] + private Gtk.Entry DataEntry; + [Glade.Widget] + private Gtk.Entry AnimSpeedEntry; + [Glade.Widget] + private Gtk.Entry IDEntry; + [Glade.Widget] + private Gnome.AppBar AppBar; + [Glade.Widget] + private Gtk.VBox MainLayout; + [Glade.Widget] + private Gtk.TreeView TileList; + [Glade.Widget] + private Gtk.Combo TileGroupComboBox; + [Glade.Widget] + private Gtk.MenuItem AddTileGroupMenu; + + private string tilesetdir; + private string tilesetfile; + private TileSet tileset; + private TileGroup selectedgroup; + + private Tile[] Tiles; + private bool[] SelectionArray; + private ArrayList Selection = new ArrayList(); + private int TilesX; + private int TilesY; + private bool toggling; + private bool selecting; + + private string currentimage; + private Gdk.Pixbuf pixbuf; + + public static int Main(string[] args) { + Program kit = new Program("tiler", "0.0.1", Modules.UI, args); + + Application app = new Application(); + + kit.Run(); + return 0; + } + + public Application() { + Glade.XML gxml = new Glade.XML("tiler.glade", null, null); + gxml.Autoconnect(this); + + if(MainWindow == null || DrawingArea == null || AppBar == null) + throw new Exception("soem widgets not found"); + + DrawingArea.AddEvents((int) Gdk.EventMask.ButtonPressMask); + DrawingArea.AddEvents((int) Gdk.EventMask.ButtonReleaseMask); + DrawingArea.AddEvents((int) Gdk.EventMask.ButtonMotionMask); + + // libglade missed interactivity property :-/ + MainLayout.Remove(AppBar); + AppBar = new AppBar(true, true, PreferencesType.Always); + AppBar.UserResponse += new EventHandler(OnAppBarUserResponse); + MainLayout.PackStart(AppBar, false, false, 0); + AppBar.Show(); + + TileGroupComboBox.Entry.Activated + += new EventHandler (OnTileGroupComboBoxEntryActivated); + + MainWindow.Show(); + } + + private void OnOpen(object o, EventArgs e) { + FileSelection selection = new FileSelection("Select TileSet"); + selection.OkButton.Clicked += new EventHandler(OnSelectTileSetOk); + selection.CancelButton.Clicked += new EventHandler(OnSelectImageCancel); + selection.Show(); + } + + private void OnSelectTileSetOk(object o, EventArgs e) { + FileSelection selection = ((FileSelection.FSButton) o).FileSelection; + string file = selection.Filename; + selection.Destroy(); + + try { + tileset = new TileSet(); + tileset.Parse(file); + tilesetfile = file; + tilesetdir = new FileInfo(file).Directory.ToString(); + } catch(Exception exception) { + ShowException(exception); + } + + Selection.Clear(); + SelectionChanged(); + FillTileGroupComboBox(); + FillTileList(); + } + + private void OnImportImage(object o, EventArgs e) { + FileSelection selection = new FileSelection("Select ImageFile"); + selection.OkButton.Clicked += new EventHandler(OnSelectImageOk); + selection.CancelButton.Clicked += new EventHandler(OnSelectImageCancel); + selection.Show(); + } + + private void OnSelectImageCancel(object o, EventArgs args) { + FileSelection selection = ((FileSelection.FSButton) o).FileSelection; + selection.Destroy(); + } + + private void OnSelectImageOk(object o, EventArgs args) { + FileSelection selection = ((FileSelection.FSButton) o).FileSelection; + string file = selection.Filename; + selection.Destroy(); + + ChangeImage(new FileInfo(file).Name); + + int startid = tileset.Tiles.Count; + for(int y = 0; y < TilesY; ++y) { + for(int x = 0; x < TilesX; ++x) { + int i = y*TilesX+x; + Tile tile = new Tile(); + tile.ID = startid + i; + ImageRegion region = new ImageRegion(); + region.ImageFile = currentimage; + region.Region = new Rectangle(x*32, y*32, 32, 32); + tile.Images.Add(region); + if(Tiles[i] != null) { + Console.WriteLine( + "Warning Tile in this region already existed..."); + } + Tiles[i] = tile; + tileset.Tiles.Add(tile); + } + } + + FillTileList(); + } + + private void ChangeImage(string file) { + if(file == "") { + currentimage = ""; + pixbuf = null; + return; + } + try { + pixbuf = new Pixbuf(tilesetdir + "/" + file); + if(pixbuf.Width % 32 != 0 || pixbuf.Height % 32 != 0) + throw new Exception( + "Image Width or Height is not a multiple of 32"); + } catch(Exception e) { + ShowException(e); + return; + } + currentimage = new FileInfo(file).Name; + TilesX = pixbuf.Width / 32; + TilesY = pixbuf.Height / 32; + SelectionArray = new bool[TilesX * TilesY]; + Tiles = new Tile[TilesX * TilesY]; + + // search tileset for tiles with matching image + foreach(Tile tile in tileset.Tiles) { + if(tile == null) + continue; + if(tile.Images.Count == 0) + continue; + ImageRegion region = (ImageRegion) tile.Images[0]; + if(region.ImageFile == currentimage) { + int px = region.Region.X / 32; + int py = region.Region.Y / 32; + int i = py*TilesX+px; + if(i < 0 || i >= Tiles.Length) { + Console.WriteLine("Invalid Imageregion at tile " + + tile.ID); + continue; + } + if(Tiles[i] != null) { + Console.WriteLine("Multiple tiles for region " + + px*32 + " , " + py*32); + continue; + } + Tiles[i] = tile; + } + } + + /* DrawingArea.Allocation + = new Gdk.Rectangle(0, 0, pixbuf.Width, pixbuf.Height);*/ + DrawingArea.WidthRequest = pixbuf.Width; + DrawingArea.HeightRequest = pixbuf.Height; + DrawingArea.QueueResize(); + } + + private void OnSave(object o, EventArgs e) { + tileset.Write(tilesetfile); + } + + private void OnQuit(object o, EventArgs e) { + Gtk.Application.Quit(); + } + + private void OnAbout(object o, EventArgs e) { + } + + private void OnRemapTiles(object o, EventArgs e) { + AppBar.SetPrompt("Start-ID:", true); + } + + private void OnCreateTileGroup(object o, EventArgs e) { + } + + private void OnRenameTileGroup(object o, EventArgs e) { + } + + private void OnAppBarUserResponse(object o, EventArgs e) { + try { + if(AppBar.Response == null || AppBar.Response == "" + || Tiles == null) + return; + + // remap tiles + int id; + try { + id = Int32.Parse(AppBar.Response); + } catch(Exception exception) { + ShowException(exception); + return; + } + foreach(Tile tile in Selection) { + if(tile.ID != -1) + tile.ID = id++; + } + FillTileList(); + SelectionChanged(); + } finally { + AppBar.ClearPrompt(); + } + } + + private void OnDrawingAreaExpose(object o, ExposeEventArgs e) { + if(pixbuf == null) + return; + + Drawable drawable = e.Event.Window; + Gdk.GC gc = new Gdk.GC(drawable); + drawable.DrawPixbuf(gc, pixbuf, 0, 0, 0, 0, + pixbuf.Width, pixbuf.Height, RgbDither.None, 0, 0); + + gc.RgbFgColor = new Color(0xff, 0, 0); + foreach(Tile tile in Selection) { + System.Drawing.Rectangle rect + = ((ImageRegion) tile.Images[0]).Region; + drawable.DrawRectangle(gc, false, rect.X, rect.Y, rect.Width, + rect.Height); + } + + e.RetVal = false; + } + + private void OnDrawingAreaButtonPress(object o, ButtonPressEventArgs e) { + selecting = true; + + for(int i = 0; i < SelectionArray.Length; ++i) + SelectionArray[i] = false; + select((int) e.Event.X, (int) e.Event.Y); + } + + private void select(int x, int y) { + int tile = y/32 * TilesX + x/32; + if(tile < 0 || tile >= SelectionArray.Length) + return; + + SelectionArray[tile] = true; + SelectionArrayChanged(); + } + + private void OnDrawingAreaMotionNotify(object i, MotionNotifyEventArgs e) { + if(!selecting) + return; + select((int) e.Event.X, (int) e.Event.Y); + } + + private void OnDrawingAreaButtonRelease(object o, ButtonPressEventArgs e) { + selecting = false; + } + + private void OnCheckButtonToggled(object sender, EventArgs e) { + if(toggling) + return; + foreach(Tile tile in Selection) { + if(sender == SolidCheckButton) + tile.Solid = SolidCheckButton.Active; + if(sender == UniSolidCheckButton) + tile.UniSolid = UniSolidCheckButton.Active; + if(sender == IceCheckButton) + tile.Ice = IceCheckButton.Active; + if(sender == WaterCheckButton) + tile.Water = WaterCheckButton.Active; + if(sender == SlopeCheckButton) + tile.Slope = SlopeCheckButton.Active; + if(sender == DontUseCheckButton) + tile.ID = DontUseCheckButton.Active ? -1 : 0; + } + } + + private void OnEntryChanged(object sender, EventArgs e) { + if(toggling) + return; + foreach(Tile tile in Selection) { + try { + if(sender == IDEntry) + tile.ID = Int32.Parse(IDEntry.Text); + if(sender == DataEntry) + tile.Data = Int32.Parse(DataEntry.Text); + if(sender == AnimSpeedEntry) + tile.AnimSpeed = Int32.Parse(AnimSpeedEntry.Text); + } catch(Exception exception) { + // ignore parse errors for now... + } + } + } + + private void SelectionArrayChanged() { + Selection.Clear(); + for(int i = 0; i < SelectionArray.Length; ++i) { + if(!SelectionArray[i]) + continue; + + if(Tiles[i] == null) { + Console.WriteLine("Tile doesn't exist yet"); + // TODO ask user to create new tile... + continue; + } + Selection.Add(Tiles[i]); + } + + SelectionChanged(); + } + + private void SelectionChanged() { + bool first = true; + toggling = true; + string nextimage = ""; + foreach(Tile tile in Selection) { + if(first) { + SolidCheckButton.Active = tile.Solid; + UniSolidCheckButton.Active = tile.UniSolid; + IceCheckButton.Active = tile.Ice; + WaterCheckButton.Active = tile.Water; + SlopeCheckButton.Active = tile.Slope; + DontUseCheckButton.Active = tile.ID == -1; + DataEntry.Text = tile.Data.ToString(); + AnimSpeedEntry.Text = tile.AnimSpeed.ToString(); + IDEntry.Text = tile.ID.ToString(); + IDEntry.Editable = true; + first = false; + + if(tile.Images.Count > 0) { + nextimage = ((ImageRegion) tile.Images[0]).ImageFile; + } + } else { + IDEntry.Text += "," + tile.ID.ToString(); + IDEntry.Editable = false; + if(tile.Images.Count > 0 + && ((ImageRegion) tile.Images[0]).ImageFile != nextimage) { + nextimage = ""; + pixbuf = null; + } + } + } + if(nextimage != currentimage) + ChangeImage(nextimage); + toggling = false; + DrawingArea.QueueDraw(); + } + + private void FillTileList() { + TileList.HeadersVisible = true; + if(TileList.Columns.Length == 0) + TileList.AppendColumn("Tile", new CellRendererText(), "text", 0); + + ListStore store = new ListStore(typeof(string)); + + if(selectedgroup == null) { + foreach(Tile tile in tileset.Tiles) { + if(tile == null) + continue; + store.AppendValues(new object[] { tile.ID.ToString() }); + } + } else { + foreach(int id in selectedgroup.Tiles) { + Tile tile = (Tile) tileset.Tiles[id]; + if(tile == null) { + Console.WriteLine("tilegroup contains deleted tile"); + continue; + } + store.AppendValues(new object[] { id.ToString() }); + } + } + + TileList.Model = store; + TileList.Selection.Mode = SelectionMode.Multiple; + } + + private void FillTileGroupComboBox() { + string[] groups = new string[tileset.TileGroups.Count+1]; + groups[0] = "All"; + + //Submenu submenu = new Submenu(); + for(int i = 0; i < tileset.TileGroups.Count; ++i) { + String tilegroup = ((TileGroup) tileset.TileGroups[i]).Name; + groups[i+1] = tilegroup; + //submenu.Add(new MenuItem(tilegroup)); + } + TileGroupComboBox.PopdownStrings = groups; + TileGroupComboBox.Entry.Editable = false; + + //AddTileGroupMenu.Submenu = submenu; + } + + private void OnTileGroupComboBoxEntryActivated(object o, EventArgs args) { + if(TileGroupComboBox.Entry.Text == "All") { + selectedgroup = null; + } else { + foreach(TileGroup tilegroup in tileset.TileGroups) { + if(tilegroup.Name == TileGroupComboBox.Entry.Text) { + selectedgroup = tilegroup; + break; + } + } + } + FillTileList(); + } + + private void OnTileListCursorChanged(object sender, EventArgs e) { + Console.WriteLine("Cursor changed."); + TreeModel model; + TreePath[] selectpaths = + TileList.Selection.GetSelectedRows(out model); + + Selection.Clear(); + foreach(TreePath path in selectpaths) { + TreeIter iter; + model.GetIter(out iter, path); + int id = Int32.Parse(model.GetValue(iter, 0).ToString()); + Selection.Add(tileset.Tiles[id]); + } + SelectionChanged(); + } + + private void ShowException(Exception e) { + MessageDialog dialog = new MessageDialog(MainWindow, + DialogFlags.Modal | DialogFlags.DestroyWithParent, + MessageType.Error, ButtonsType.Ok, + e.Message); + dialog.Run(); + dialog.Destroy(); + } +} diff --git a/contrib/tilemanager/Lexer.cs b/contrib/tilemanager/Lexer.cs new file mode 100644 index 000000000..28df77a83 --- /dev/null +++ b/contrib/tilemanager/Lexer.cs @@ -0,0 +1,163 @@ +using System; +using System.Text; +using System.IO; + +namespace Lisp { + +public class Lexer { + private StreamReader stream; + private char[] buffer; + private char c; + int bufpos; + int buflen; + + public class EOFException : Exception { + }; + + public enum TokenType { + EOF, + OPEN_PAREN, + CLOSE_PAREN, + SYMBOL, + STRING, + INTEGER, + REAL, + TRUE, + FALSE + }; + + private StringBuilder TokenStringBuilder; + public string TokenString { + get { return TokenStringBuilder.ToString(); } + } + public int LineNumber; + + public Lexer(StreamReader stream) { + this.stream = stream; + buffer = new char[1025]; + NextChar(); + } + + public TokenType GetNextToken() { + try { + while(Char.IsWhiteSpace(c)) { + NextChar(); + if(c == '\n') + LineNumber++; + } + + TokenStringBuilder = new StringBuilder(); + + switch(c) { + case ';': // comment + while(true) { + NextChar(); + if(c == '\n') { + LineNumber++; + break; + } + } + NextChar(); + return GetNextToken(); + case '(': + NextChar(); + return TokenType.OPEN_PAREN; + case ')': + NextChar(); + return TokenType.CLOSE_PAREN; + case '"': { // string + int startline = LineNumber; + while(true) { + NextChar(); + if(c == '"') + break; + + if(c == '\\') { + NextChar(); + switch(c) { + case 'n': + c = '\n'; + break; + case 't': + c = '\t'; + break; + } + } + TokenStringBuilder.Append(c); + } + NextChar(); + return TokenType.STRING; + } + case '#': // constant + NextChar(); + while(Char.IsLetterOrDigit(c) || c == '_') { + TokenStringBuilder.Append(c); + NextChar(); + } + if(TokenString == "t") + return TokenType.TRUE; + if(TokenString == "f") + return TokenType.FALSE; + + throw new Exception("Unknown constant '" + + TokenString + "'"); + default: + if(Char.IsDigit(c) || c == '-') { + bool have_nondigits = false; + bool have_digits = false; + int have_floating_point = 0; + + do { + if(Char.IsDigit(c)) + have_digits = true; + else if(c == '.') + have_floating_point++; + else if(Char.IsLetter(c) || c == '_') + have_nondigits = true; + + TokenStringBuilder.Append(c); + NextChar(); + } while(!Char.IsWhiteSpace(c) && c != '\"' && c != '(' + && c != ')' && c != ';'); + + if(have_nondigits || !have_digits + || have_floating_point > 1) + return TokenType.SYMBOL; + else if(have_floating_point == 1) + return TokenType.REAL; + else + return TokenType.INTEGER; + } else { + do { + TokenStringBuilder.Append(c); + NextChar(); + } while(!Char.IsWhiteSpace(c) && c != '\"' && c != '(' + && c != ')' && c != ';'); + + return TokenType.SYMBOL; + } + } + } catch(EOFException e) { + return TokenType.EOF; + } + } + + private void NextChar() { + if(bufpos >= buflen) { + if(!stream.BaseStream.CanRead) + throw new EOFException(); + buflen = stream.Read(buffer, 0, 1024); + bufpos = 0; + // following hack appends an additional ' ' at the end of the file + // to avoid problems when parsing symbols/elements and a sudden EOF: + // This way we can avoid the need for an unget function. + if(!stream.BaseStream.CanRead) { + buffer[buflen] = ' '; + ++buflen; + } + } + c = buffer[bufpos++]; + } +} + +} diff --git a/contrib/tilemanager/LispWriter.cs b/contrib/tilemanager/LispWriter.cs new file mode 100644 index 000000000..0ec3a3805 --- /dev/null +++ b/contrib/tilemanager/LispWriter.cs @@ -0,0 +1,74 @@ +using System; +using System.IO; +using System.Collections; + +public class LispWriter { + private TextWriter stream; + private int IndentDepth; + private Stack lists = new Stack(); + + public LispWriter(TextWriter stream) { + this.stream = stream; + } + + public void WriteComment(string comment) { + stream.WriteLine("; " + comment); + } + + public void StartList(string name) { + indent(); + stream.WriteLine("(" + name); + IndentDepth += 2; + lists.Push(name); + } + + public void EndList(string name) { + if(lists.Count == 0) + throw new Exception("Trying to close list while none is open"); + string back = (string) lists.Pop(); + if(name != back) + throw new Exception( + String.Format("Trying to close {0} which is not open", name)); + + IndentDepth -= 2; + indent(); + stream.WriteLine(")"); + } + + public void Write(string name, object value) { + indent(); + stream.Write("(" + name); + if(value is string) { + stream.Write(" \"" + value.ToString() + "\""); + } else if(value is IEnumerable) { + foreach(object o in (IEnumerable) value) { + stream.Write(" "); + WriteValue(o); + } + } else { + stream.Write(" "); + WriteValue(value); + } + stream.WriteLine(")"); + } + + private void WriteValue(object val) { + if(val is bool) { + stream.Write((bool) val ? "#t" : "#f"); + } else if(val is int || val is float) { + stream.Write(val.ToString()); + } else { + stream.Write("\"" + val.ToString() + "\""); + } + } + + public void WriteVerbatimLine(string line) { + indent(); + stream.WriteLine(line); + } + + private void indent() { + for(int i = 0; i < IndentDepth; ++i) + stream.Write(" "); + } +} diff --git a/contrib/tilemanager/Parser.cs b/contrib/tilemanager/Parser.cs new file mode 100644 index 000000000..784893f5d --- /dev/null +++ b/contrib/tilemanager/Parser.cs @@ -0,0 +1,90 @@ +using System; +using System.IO; + +namespace Lisp +{ + +public class Parser { + public enum LispType { + START_LIST, + END_LIST, + SYMBOL, + INTEGER, + STRING, + REAL, + BOOLEAN + }; + private Lexer lexer; + private Lexer.TokenType token; + + public Parser(StreamReader stream) { + lexer = new Lexer(stream); + } + + public bool Parse() { + token = lexer.GetNextToken(); + if(delayinc) { + depth++; + delayinc = false; + } + if(token == Lexer.TokenType.EOF) { + depth = 0; + return false; + } + + /* + Console.WriteLine("Token: " + token.ToString() + " - " + + lexer.TokenString); + */ + switch(token) { + case Lexer.TokenType.CLOSE_PAREN: + if(depth == 0) + throw new Exception("Parse Error: unexpected )"); + depth--; + type = LispType.END_LIST; + break; + case Lexer.TokenType.OPEN_PAREN: + type = LispType.START_LIST; + delayinc = true; + break; + case Lexer.TokenType.SYMBOL: + type = LispType.SYMBOL; + break; + case Lexer.TokenType.STRING: + type = LispType.STRING; + break; + case Lexer.TokenType.TRUE: + type = LispType.BOOLEAN; + break; + case Lexer.TokenType.INTEGER: + type = LispType.INTEGER; + break; + } + return true; + } + + private LispType type; + public LispType Type { + get { return type; } + } + private bool delayinc; + private int depth; + public int Depth { + get { return depth; } + } + //public int IntValue + public string SymbolValue { + get { return lexer.TokenString; } + } + public string StringValue { + get { return lexer.TokenString; } + } + public int IntegerValue { + get { return Int32.Parse(lexer.TokenString); } + } + public bool BoolValue { + get { return StringValue == "t" ? true : false; } + } +} + +} diff --git a/contrib/tilemanager/README b/contrib/tilemanager/README new file mode 100644 index 000000000..abc4b0c52 --- /dev/null +++ b/contrib/tilemanager/README @@ -0,0 +1,6 @@ +This Tool allows to edit supertux .stgt files in a comfortable manner. +Warning: This tool is pre-alpha and meant for developers only. + +Compilation: You need to have mono and gtk-sharp (with gnome support) installed +in order to compile and run the app. + diff --git a/contrib/tilemanager/TODO b/contrib/tilemanager/TODO new file mode 100644 index 000000000..9c2987aea --- /dev/null +++ b/contrib/tilemanager/TODO @@ -0,0 +1,4 @@ +-display and allow editing of tilegroups +-show tile images next to tile numbers +-allow removing of tiles +-undo ? diff --git a/contrib/tilemanager/Tile.cs b/contrib/tilemanager/Tile.cs new file mode 100644 index 000000000..5c031476c --- /dev/null +++ b/contrib/tilemanager/Tile.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections; +using System.IO; +using System.Drawing; +using Lisp; + +public class ImageRegion { + public String ImageFile; + public Rectangle Region; +} + +public class Tile { + public int ID; + public bool Solid; + public bool UniSolid; + public bool Ice; + public bool Water; + public bool Slope; + public bool Spike; + public bool FullBox; + public bool Brick; + public bool Coin; + public bool Goal; + public int NextTile; + public int Data; + public int AnimSpeed; + public string EditorImage; + public ArrayList Images = new ArrayList(); + + public Tile() { + ID = -1; + NextTile = -1; + AnimSpeed = 25; + } + + public void Write(LispWriter writer) { + writer.StartList("tile"); + writer.Write("id", ID); + + if(Images.Count > 0) { + writer.StartList("images"); + foreach(ImageRegion region in Images) { + if(region.Region.Width != 0) { + writer.WriteVerbatimLine( + String.Format("(region \"{0}\" {1} {2} {3} {4})", + region.ImageFile, region.Region.Left, + region.Region.Top, region.Region.Width, + region.Region.Height)); + } else { + writer.WriteVerbatimLine( + "\"" + region.ImageFile + "\""); + } + } + writer.EndList("images"); + } else { + Console.WriteLine("no images on tile " + ID); + } + + if(Solid) + writer.Write("solid", true); + if(UniSolid) + writer.Write("unisolid", true); + if(Ice) + writer.Write("ice", true); + if(Water) + writer.Write("water", true); + if(Slope) + writer.Write("slope-type", Data); + if(Spike) + writer.Write("spike", true); + if(Coin) + writer.Write("distro", true); + if(FullBox) + writer.Write("fullbox", true); + if(Brick) + writer.Write("brick", true); + if(NextTile >= 0) + writer.Write("next-tile", NextTile); + if(Goal) + writer.Write("goal", true); + if(EditorImage != null) + writer.Write("editor-images", EditorImage); + if(Data != 0) + writer.Write("data", Data); + if(AnimSpeed != 25) + writer.Write("animspeed", AnimSpeed); + writer.EndList("tile"); + } + + public void Parse(Lisp.Parser parser) { + int d = parser.Depth; + while(parser.Parse() && parser.Depth >= d) { + if(parser.Depth == d+1) { + if(parser.Type != Parser.LispType.SYMBOL) + throw new Exception("expected SYMBOL"); + string symbol = parser.SymbolValue; + parser.Parse(); + switch(symbol) { + case "id": + ID = parser.IntegerValue; + break; + case "images": + ParseTileImages(parser); + break; + case "editor-images": + EditorImage = parser.StringValue; + break; + case "solid": + Solid = parser.BoolValue; + break; + case "unisolid": + UniSolid = parser.BoolValue; + break; + case "ice": + Ice = parser.BoolValue; + break; + case "water": + Water = parser.BoolValue; + break; + case "slope-type": + Slope = true; + Data = parser.IntegerValue; + break; + case "anim-speed": + AnimSpeed = parser.IntegerValue; + break; + case "spike": + Spike = parser.BoolValue; + break; + case "data": + Data = parser.IntegerValue; + break; + case "next-tile": + Data = parser.IntegerValue; + break; + case "brick": + Brick = parser.BoolValue; + break; + case "fullbox": + FullBox = parser.BoolValue; + break; + case "distro": + Coin = parser.BoolValue; + break; + case "goal": + Goal = parser.BoolValue; + break; + default: + Console.WriteLine("Unknown tile element " + symbol); + break; + } + } + } + } + + private void ParseTileImages(Lisp.Parser parser) { + if(parser.Type == Parser.LispType.END_LIST) + return; + + int d = parser.Depth; + do { + ImageRegion region = new ImageRegion(); + if(parser.Type == Parser.LispType.STRING) { + region.ImageFile = parser.StringValue; + } else if(parser.Type == Parser.LispType.START_LIST) { + ParseImageRegion(parser, region); + } else { + throw new Exception("unexpected lisp data: " + parser.Type); + } + Images.Add(region); + } while(parser.Parse() && parser.Depth >= d); + } + + private void ParseImageRegion(Lisp.Parser parser, ImageRegion region) { + parser.Parse(); + if(parser.Type != Parser.LispType.SYMBOL) + throw new Exception("expected symbol"); + if(parser.SymbolValue != "region") + throw new Exception("expected region symbol"); + parser.Parse(); + if(parser.Type != Parser.LispType.STRING) + throw new Exception("expected string"); + region.ImageFile = parser.StringValue; + + parser.Parse(); + if(parser.Type != Parser.LispType.INTEGER) + throw new Exception("expected integer"); + region.Region.X = parser.IntegerValue; + + parser.Parse(); + if(parser.Type != Parser.LispType.INTEGER) + throw new Exception("expected integer"); + region.Region.Y = parser.IntegerValue; + + parser.Parse(); + if(parser.Type != Parser.LispType.INTEGER) + throw new Exception("expected integer"); + region.Region.Width = parser.IntegerValue; + + parser.Parse(); + if(parser.Type != Parser.LispType.INTEGER) + throw new Exception("expected integer"); + region.Region.Height = parser.IntegerValue; + + parser.Parse(); + if(parser.Type != Parser.LispType.END_LIST) + throw new Exception("expected END_LIST"); + } +} + diff --git a/contrib/tilemanager/TileSet.cs b/contrib/tilemanager/TileSet.cs new file mode 100644 index 000000000..ec99ecf80 --- /dev/null +++ b/contrib/tilemanager/TileSet.cs @@ -0,0 +1,137 @@ +using System; +using System.IO; +using System.Collections; +using Lisp; + +public class TileGroup { + public string Name; + public ArrayList Tiles = new ArrayList(); + + public void Write(LispWriter writer) { + writer.StartList("tilegroup"); + + writer.Write("name", Name); + writer.Write("tiles", Tiles); + + writer.EndList("tilegroup"); + } + + public void Parse(Lisp.Parser parser) { + int d = parser.Depth; + while(parser.Parse() && parser.Depth >= d) { + if(parser.Depth == d+1) { + if(parser.Type != Parser.LispType.SYMBOL) + throw new Exception("expected SYMBOL"); + string symbol = parser.SymbolValue; + parser.Parse(); + switch(symbol) { + case "name": + Name = parser.StringValue; + break; + case "tiles": + do { + Tiles.Add(parser.IntegerValue); + } while(parser.Parse() + && parser.Type == Parser.LispType.INTEGER); + break; + default: + Console.WriteLine("Unknown section " + symbol); + break; + } + } + } + } +} + +public class TileSet { + public ArrayList Tiles = new ArrayList(); + public ArrayList TileGroups = new ArrayList(); + + public void Write(string filename) { + FileStream fs = new FileStream(filename, FileMode.Create); + + TextWriter tw = new StreamWriter(fs); + LispWriter writer = new LispWriter(tw); + + writer.WriteComment("Generated by tiler"); + writer.StartList("supertux-tiles"); + foreach(TileGroup tilegroup in TileGroups) { + tilegroup.Write(writer); + } + foreach(Tile tile in Tiles) { + if(tile == null) + continue; + if(tile.ID >= 0) + tile.Write(writer); + } + writer.EndList("supertux-tiles"); + tw.Close(); + fs.Close(); + } + + public void Parse(string filename) { + FileStream fs = new FileStream(filename, FileMode.Open); + StreamReader stream = new StreamReader(fs); + + Lisp.Parser parser = new Lisp.Parser(stream); + parser.Parse(); + if(parser.Type != Parser.LispType.START_LIST) + throw new Exception("Expected START_LIST"); + parser.Parse(); + if(parser.Type != Parser.LispType.SYMBOL) + throw new Exception("Expected symbol"); + if(parser.SymbolValue != "supertux-tiles") + throw new Exception("not a supertux tile files but " + + parser.SymbolValue); + ParseTiles(parser); + + stream.Close(); + fs.Close(); + } + + public void ParseTiles(Lisp.Parser parser) { + Console.WriteLine("ParseTiles..."); + int d = parser.Depth; + while(parser.Parse() && parser.Depth >= d) { + if(parser.Depth == d && parser.Type != Parser.LispType.START_LIST) { + Console.WriteLine("non-cons type in list..."); + continue; + } + + if(parser.Depth == d+1) { + if(parser.Type != Parser.LispType.SYMBOL) { + throw new Exception("Expected symbol in list element"); + } + switch(parser.SymbolValue) { + case "properties": + SkipList(parser); + break; + case "tilegroup": + TileGroup tilegroup = new TileGroup(); + tilegroup.Parse(parser); + TileGroups.Add(tilegroup); + break; + case "tile": + Tile tile = new Tile(); + tile.Parse(parser); + Console.WriteLine("***ID: " + tile.ID); + + while(tile.ID >= Tiles.Count) + Tiles.Add(null); + Tiles[tile.ID] = tile; + break; + default: + throw new Exception("Unexpected listentry: " + + parser.SymbolValue); + } + } + } + } + + private void SkipList(Lisp.Parser parser) { + int d = parser.Depth; + while(parser.Parse() && parser.Depth >= d) + ; + } +} + diff --git a/contrib/tilemanager/tiler.glade b/contrib/tilemanager/tiler.glade new file mode 100644 index 000000000..a84081897 --- /dev/null +++ b/contrib/tilemanager/tiler.glade @@ -0,0 +1,623 @@ + + + + + + + + True + Tile Manager + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER + False + 640 + 480 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + + + + + True + False + 0 + + + + True + + + + True + _Datei + True + + + + + + + True + gtk-open + True + + + + + + + True + gtk-save + True + + + + + + + True + + + + + + True + gtk-quit + True + + + + + + + + + + + True + Edit + True + + + + + + + True + Import Image + True + + + + + + + + True + Remap Tiles + True + + + + + + + + True + + + + + + True + Create TileGroup + True + + + + + + + + True + Rename TileGroup + True + + + + + + + + True + Add Tiles To Group + True + + + + + + + + + + True + _Hilfe + True + + + + + + + True + gtk-dialog-info + True + + + + + + + + + + 0 + False + False + + + + + + True + True + 180 + + + + True + False + 0 + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_NONE + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + True + + + + + + 0 + True + True + + + + + + True + False + True + False + True + False + + + + True + True + True + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + + 0 + False + True + + + + + True + False + + + + + + True + False + 0 + + + + True + + + + + + 0 + True + True + + + + + + True + Flags: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 6 + False + True + + + + + + 6 + True + 6 + 2 + False + 6 + 6 + + + + True + True + Solid + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + True + UniSolid + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + True + Ice + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + True + Water + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + Slope + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + 1 + 2 + 3 + fill + + + + + + + True + Data: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 3 + 4 + fill + + + + + + + True + True + True + True + 0 + + True + * + False + + + + 1 + 2 + 3 + 4 + + + + + + + True + AnimSpeed: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 4 + 5 + fill + + + + + + + True + True + True + True + 0 + + True + * + False + + + + 1 + 2 + 4 + 5 + + + + + + + True + ID + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 5 + 6 + fill + + + + + + + True + True + True + True + 0 + + True + * + False + + + + 1 + 2 + 5 + 6 + + + + + + + True + True + Don't Use + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 1 + 2 + 2 + 3 + fill + + + + + + 0 + True + True + + + + + True + True + + + + + 0 + True + True + + + + + + True + True + True + + + + 0 + False + False + + + + + + +