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