7555e80855ba5f3d79a12f1681e7bf22294c04e9
[supertux.git] / tools / tilemanager / Tile.cs
1 //  $Id$
2 using System;
3 using System.Collections;
4 using System.IO;
5 using System.Drawing;
6 using Lisp;
7
8 public class ImageRegion {
9     public String ImageFile;
10     public Rectangle Region;
11 }
12
13 public class Attribute {
14         /// <summary>solid tile that is indestructible by Tux</summary>
15         public const int SOLID     = 0x0001;
16         /// <summary>uni-directional solid tile</summary>
17         public const int UNISOLID  = 0x0002;
18         /// <summary>a brick that can be destroyed by jumping under it</summary>
19         public const int BRICK     = 0x0004;
20         /// <summary>the level should be finished when touching a goaltile.</summary>
21         /// <remarks>
22         /// if <see cref="Data">data</see> is 0 then the endsequence should be
23         /// triggered, if <see cref="Data">data</see> is 1 then we can finish
24         /// the level instantly.
25         /// </remarks>
26         public const int GOAL      = 0x0008;
27         /// <summary>slope tile</summary>
28         public const int SLOPE     = 0x0010;
29         /// <summary>Bonusbox, content is stored in <see cref="Data">data</see></summary>
30         public const int FULLBOX   = 0x0020;
31         /// <summary>Tile is a coin</summary>
32         public const int COIN      = 0x0040;
33         /// <summary>an ice brick that makes tux sliding more than usual</summary>
34         public const int ICE       = 0x0100;
35         /// <summary>a water tile in which tux starts to swim</summary>
36         public const int WATER     = 0x0200;
37         /// <summary>a tile that hurts the player if he touches it</summary>
38         public const int HURTS     = 0x0400;
39         /// <summary>for lava: WATER, HURTS, FIRE</summary>
40         public const int FIRE      = 0x0800;
41
42
43         // TODO: Find out why are worldmap tile attributes stored in data(s)
44         // worldmap flags
45         public const int WORLDMAP_NORTH = 0x0001;
46         public const int WORLDMAP_SOUTH = 0x0002;
47         public const int WORLDMAP_EAST  = 0x0004;
48         public const int WORLDMAP_WEST  = 0x0008;
49
50         public const int WORLDMAP_STOP  = 0x0010;
51 }
52
53 public class Tile {
54         public int ID;
55         public bool Hidden;
56         public int NextTile;
57         public int Attributes;
58         public int Data;
59         public float AnimFps;
60         public string EditorImage;
61         public ArrayList Images = new ArrayList();
62
63         public Tile() {
64                 ID = -1;
65                 NextTile = -1;
66                 AnimFps = 1;
67         }
68
69         public bool HasAttribute (int Attrib)
70         {
71                 return (Attributes & Attrib) != 0;
72         }
73
74         public void SetAttribute (int Attrib, bool Value)
75         {
76                 if (Value)
77                         Attributes |= Attrib;
78                 else
79                         Attributes &= (~Attrib);        //NOTE: "~" stands for bitwise negation
80         }
81
82     public void Write(LispWriter writer) {
83         writer.StartList("tile");
84         writer.Write("id", ID);
85
86         if(Images.Count > 0) {
87             writer.StartList("images");
88             foreach(ImageRegion region in Images) {
89                 if(region.Region.Width != 0) {
90                     writer.WriteVerbatimLine(
91                             String.Format("(region \"{0}\" {1} {2} {3} {4})",
92                                 region.ImageFile, region.Region.Left,
93                                 region.Region.Top, region.Region.Width,
94                                 region.Region.Height));
95                 } else {
96                     writer.WriteVerbatimLine(
97                             "\"" + region.ImageFile + "\"");
98                 }
99             }
100             writer.EndList("images");
101         } else {
102             Console.WriteLine("no images on tile " + ID);
103         }
104
105         if(HasAttribute(Attribute.SOLID))
106             writer.Write("solid", true);
107         if(HasAttribute(Attribute.UNISOLID))
108             writer.Write("unisolid", true);
109         if(HasAttribute(Attribute.ICE))
110             writer.Write("ice", true);
111         if(HasAttribute(Attribute.WATER))
112             writer.Write("water", true);
113         if(HasAttribute(Attribute.SLOPE))
114             writer.Write("slope-type", Data);
115         if(HasAttribute(Attribute.HURTS))
116             writer.Write("hurts", true);
117         if(HasAttribute(Attribute.COIN))
118             writer.Write("coin", true);
119         if(HasAttribute(Attribute.FULLBOX))
120             writer.Write("fullbox", true);
121         if(HasAttribute(Attribute.BRICK))
122             writer.Write("brick", true);
123         if(HasAttribute(Attribute.GOAL))
124             writer.Write("goal", true);
125
126         if(Hidden)
127             writer.Write("hidden", true);
128         if(NextTile >= 0)
129             writer.Write("next-tile", NextTile);
130         if(EditorImage != null)
131             writer.Write("editor-images", EditorImage);
132         if(Data != 0)
133             writer.Write("data", Data);
134         if(Images.Count > 1) {
135             if(AnimFps == 1.0)
136               AnimFps = 40;
137             writer.Write("anim-fps", AnimFps);
138         }
139         writer.EndList("tile");
140     }
141
142     public void Parse(Lisp.Parser parser) {
143         int d = parser.Depth;
144         while(parser.Parse() && parser.Depth >= d) {
145             if(parser.Depth == d+1) {
146                 if(parser.Type != Parser.LispType.SYMBOL)
147                     throw new Exception("expected SYMBOL");
148                 string symbol = parser.SymbolValue;
149                 parser.Parse();
150                 switch(symbol) {
151                     case "id":
152                         ID = parser.IntegerValue;
153                     break;
154                     case "images":
155                         ParseTileImages(parser);
156                         break;
157                     case "editor-images":
158                         EditorImage = parser.StringValue;
159                         break;
160                     case "anim-fps":
161                         AnimFps = parser.FloatValue;
162                         break;
163                     case "data":
164                         Data = parser.IntegerValue;
165                         break;
166                     case "next-tile":
167                         NextTile = parser.IntegerValue;
168                         break;
169                     case "hidden":
170                         Hidden = parser.BoolValue;
171                         break;
172                     case "solid":
173                         SetAttribute(Attribute.SOLID, parser.BoolValue);
174                         break;
175                     case "unisolid":
176                         SetAttribute(Attribute.UNISOLID, parser.BoolValue);
177                         break;
178                     case "ice":
179                         SetAttribute(Attribute.ICE, parser.BoolValue);
180                         break;
181                     case "water":
182                         SetAttribute(Attribute.WATER, parser.BoolValue);
183                         break;
184                     case "slope-type":
185                         SetAttribute(Attribute.SLOPE, true);
186                         Data = parser.IntegerValue;
187                         break;
188                     case "hurts":
189                         SetAttribute(Attribute.HURTS, parser.BoolValue);
190                         break;
191                     case "brick":
192                         SetAttribute(Attribute.BRICK, parser.BoolValue);
193                         break;
194                     case "fullbox":
195                         SetAttribute(Attribute.FULLBOX, parser.BoolValue);
196                         break;
197                     case "coin":
198                         SetAttribute(Attribute.COIN, parser.BoolValue);
199                         break;
200                     case "goal":
201                         SetAttribute(Attribute.GOAL, parser.BoolValue);
202                         break;
203                     default:
204                         Console.WriteLine("Unknown tile element " + symbol);
205                         break;
206                 }
207             }
208         }
209     }
210
211     private void ParseTileImages(Lisp.Parser parser) {
212         if(parser.Type == Parser.LispType.END_LIST)
213             return;
214
215         int d = parser.Depth;
216         do {
217             ImageRegion region = new ImageRegion();
218             if(parser.Type == Parser.LispType.STRING) {
219                 region.ImageFile = parser.StringValue;
220             } else if(parser.Type == Parser.LispType.START_LIST) {
221                 ParseImageRegion(parser, region);
222             } else {
223                 throw new Exception("unexpected lisp data: " + parser.Type);
224             }
225             Images.Add(region);
226         } while(parser.Parse() && parser.Depth >= d);
227     }
228
229     private void ParseImageRegion(Lisp.Parser parser, ImageRegion region) {
230         parser.Parse();
231         if(parser.Type != Parser.LispType.SYMBOL)
232             throw new Exception("expected symbol");
233         if(parser.SymbolValue != "region")
234             throw new Exception("expected region symbol");
235         parser.Parse();
236         if(parser.Type != Parser.LispType.STRING)
237             throw new Exception("expected string");
238         region.ImageFile = parser.StringValue;
239
240         parser.Parse();
241         if(parser.Type != Parser.LispType.INTEGER)
242             throw new Exception("expected integer");
243         region.Region.X = parser.IntegerValue;
244
245         parser.Parse();
246         if(parser.Type != Parser.LispType.INTEGER)
247             throw new Exception("expected integer");
248         region.Region.Y = parser.IntegerValue;
249
250         parser.Parse();
251         if(parser.Type != Parser.LispType.INTEGER)
252             throw new Exception("expected integer");
253         region.Region.Width = parser.IntegerValue;
254
255         parser.Parse();
256         if(parser.Type != Parser.LispType.INTEGER)
257             throw new Exception("expected integer");
258         region.Region.Height = parser.IntegerValue;
259
260         parser.Parse();
261         if(parser.Type != Parser.LispType.END_LIST)
262             throw new Exception("expected END_LIST");
263     }
264 }