Tile.cs 11 KB

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