]> Dogcows Code - chaz/carfire/blob - CarFire/CarFire/CarFire/MapReader.cs
git-svn-id: https://bd85.net/svn/cs3505_group@163 92bb83a3-7c8f-8a45-bc97-515c4e399668
[chaz/carfire] / CarFire / CarFire / CarFire / MapReader.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Runtime.Serialization;
5 using Microsoft.Xna.Framework;
6 using Microsoft.Xna.Framework.Content;
7 using Microsoft.Xna.Framework.Graphics;
8 using System.Reflection;
9
10 namespace CarFire
11 {
12 /// <summary>
13 /// This class will be instantiated by the XNA Framework Content
14 /// Pipeline to read cfmap files from binary .xnb format.
15 /// </summary>
16 public class MapReader : ContentTypeReader<Map>
17 {
18 #region Protected Methods
19
20 protected override Map Read(ContentReader input, Map existingInstance)
21 {
22 mImpl = new Impl(input);
23 return mImpl.GetMap();
24 }
25
26 #endregion
27
28
29 #region Private Types
30
31 /// <summary>
32 /// This private class wraps around ContentReader to make it more
33 /// convenient to use it as an input stream reader.
34 /// </summary>
35 class LineReader
36 {
37 ContentReader mInput;
38 int mLineNumber = 0;
39 int mExpectedNumberOfLines;
40
41 public LineReader(ContentReader input)
42 {
43 mInput = input;
44 mExpectedNumberOfLines = mInput.ReadInt32();
45 }
46
47 public string ReadLine()
48 {
49 mLineNumber++;
50 return mInput.ReadString();
51 }
52
53 public int LineNumber { get { return mLineNumber; } }
54
55 public bool End { get { return mLineNumber >= mExpectedNumberOfLines; } }
56 }
57
58
59 /// <summary>
60 /// This class is the actual implementation. The implementation is wrapped
61 /// in a subclass because the invoker seems to only be able to invoke public
62 /// methods, and this needs to invoke methods that shouldn't be public.
63 /// </summary>
64 class Impl
65 {
66 public Impl(ContentReader input)
67 {
68 mInput = new LineReader(input);
69 ReadSectionHeaders();
70 PostProcess();
71 }
72
73 public Map GetMap()
74 {
75 return new Map(mMetadata, mGrid, mDefaultTile, mEntities, mPlayerPositions);
76 }
77
78
79 public void ReadSectionHeaders()
80 {
81 mMetadata = new Map.Metadata();
82
83 while (!mInput.End)
84 {
85 string line = mInput.ReadLine();
86
87 while (line != null)
88 {
89 if (!IsLineSignificant(line)) break;
90
91 string section = Parse.IniSectionHeader(line);
92 if (section != null)
93 {
94 if (section == "metadata")
95 {
96 line = ReadMetadataSection();
97 }
98 else if (section == "maptable")
99 {
100 line = ReadMapTableSection();
101 }
102 else if (section.Length == 1 && IsValidEntityIdentifier(section[0]))
103 {
104 line = ReadEntitySection(section[0]);
105 }
106 else
107 {
108 throw new Exception("Unexpected section on line " + mInput.LineNumber + ": " + section);
109 }
110 }
111 else
112 {
113 throw new Exception("Unexpected text on line " + mInput.LineNumber + ": " + line);
114 }
115 }
116 }
117 }
118
119 string ReadMetadataSection()
120 {
121 while (!mInput.End)
122 {
123 string line = mInput.ReadLine();
124 if (!IsLineSignificant(line)) continue;
125
126 string[] pair = Parse.KeyValuePair(line);
127 if (pair != null)
128 {
129 try
130 {
131 string methodName = "set_" + pair[0].ToLowerInvariant();
132 object[] args = new object[1];
133 args[0] = pair[1];
134 GetType().InvokeMember(methodName, BindingFlags.InvokeMethod, null, this, args);
135 }
136 #pragma warning disable 0168
137 catch (System.MissingMethodException ex)
138 #pragma warning restore 0168
139 {
140 throw new Exception("Unexpected key on line " + mInput.LineNumber + ": " + pair[0]);
141 }
142 }
143 else
144 {
145 return line;
146 }
147 }
148
149 return null;
150 }
151
152 string ReadMapTableSection()
153 {
154 if (mMetadata == null || mMetadata.GridWidth == 0 || mMetadata.GridHeight == 0)
155 {
156 throw new Exception("Unexpected section on line " + mInput.LineNumber +
157 ": You must define the map dimensions before this section.");
158 }
159
160 mGrid = new char[mMetadata.GridWidth, mMetadata.GridHeight];
161
162 int y;
163 for (y = 0; y < mMetadata.GridHeight && !mInput.End; y++)
164 {
165 string line = mInput.ReadLine();
166
167 if (line.Length < mMetadata.GridWidth)
168 {
169 throw new Exception("Unexpected EOL on line " + mInput.LineNumber +
170 ": The number of characters should match the width dimension (" + mMetadata.GridWidth + ").");
171 }
172
173 for (int x = 0; x < mMetadata.GridWidth; x++)
174 {
175 mGrid[x, y] = line[x];
176 }
177 }
178
179 if (y < mMetadata.GridHeight)
180 {
181 throw new Exception("Unexpected EOF on line " + mInput.LineNumber +
182 ": The number of lines in this section should match the height dimension (" + mMetadata.GridHeight + ").");
183 }
184
185 return null;
186 }
187
188 string ReadEntitySection(char identifier)
189 {
190 Dictionary<string, string> pairs = new Dictionary<string, string>();
191 mEntitySections[identifier] = pairs;
192
193 while (!mInput.End)
194 {
195 string line = mInput.ReadLine();
196
197 string[] pair = Parse.KeyValuePair(line);
198 if (pair != null)
199 {
200 pairs[pair[0]] = pair[1];
201 }
202 else
203 {
204 return line;
205 }
206 }
207
208 return null;
209 }
210
211
212 void PostProcess()
213 {
214 if (mMetadata == null || mGrid == null)
215 {
216 throw new Exception("Missing a required section. Make sure the metadata and grid are there.");
217 }
218
219 mEntities = new List<Map.RawEntity>();
220 mPlayerPositions = new Point[mMetadata.NumPlayers.Max() + 1];
221
222 // create entities defined completely
223 foreach (char identifier in mEntitySections.Keys)
224 {
225 Dictionary<string, string> pairs = mEntitySections[identifier];
226 if (pairs.ContainsKey("create"))
227 {
228 string[] list = Parse.List(pairs["create"]);
229 foreach (string positionString in list)
230 {
231 Point? position = Parse.Coordinates(positionString);
232 if (position != null)
233 {
234 Map.RawEntity createEntity = new Map.RawEntity();
235 createEntity.Id = identifier;
236 createEntity.Position = position.Value;
237 createEntity.Attributes = pairs;
238 mEntities.Add(createEntity);
239 }
240 else
241 {
242 throw new Exception("Unexpected value of key `create' defined for entity " + identifier + ".");
243 }
244 }
245 pairs.Remove("create");
246 }
247 }
248
249 // create entities with positions defined on the grid
250 // and get player starting positions
251 for (int x = 0; x < mMetadata.GridWidth; x++)
252 {
253 for (int y = 0; y < mMetadata.GridHeight; y++)
254 {
255 char identifier = mGrid[x, y];
256 if (IsValidEntityIdentifier(identifier))
257 {
258 if (mEntitySections.ContainsKey(identifier))
259 {
260 Map.RawEntity createEntity = new Map.RawEntity();
261 createEntity.Id = identifier;
262 createEntity.Position = new Point(x, y);
263 createEntity.Attributes = mEntitySections[identifier];
264 mEntities.Add(createEntity);
265 }
266 else
267 {
268 throw new Exception("Unexpected entity (" + identifier +
269 ") placed on the grid at [" + x + "," + y + "] but not defined.");
270 }
271 mGrid[x, y] = mDefaultTile;
272 }
273 else if ('1' <= identifier && identifier <= '9')
274 {
275 int playerNum = identifier - 48;
276 if (playerNum < mPlayerPositions.Count())
277 {
278 mPlayerPositions[playerNum] = new Point(x, y);
279 }
280 mGrid[x, y] = mDefaultTile;
281 }
282 }
283 }
284
285 // check if all needed player positions are defined
286 for (int i = 1; i < mPlayerPositions.Count(); i++)
287 {
288 if (mPlayerPositions[i] == default(Point))
289 {
290 throw new Exception("Not enough player positions were defined on the grid; " +
291 "you are missing a spot for player " + i + ".");
292 }
293 }
294 }
295
296
297 bool IsLineSignificant(string line)
298 {
299 if (line.Trim().Length == 0 || Parse.IniComment(line) != null) return false;
300 return true;
301 }
302
303 bool IsValidEntityIdentifier(char id)
304 {
305 if (('a' <= id && id <= 'z') || ('A' <= id && id <= 'Z')) return true;
306 return false;
307 }
308
309
310 public void set_author(string atom)
311 {
312 string value = Parse.String(atom);
313 if (value != null) mMetadata.Author = value;
314 else throw new Exception("Unexpected value on line " + mInput.LineNumber + ": " + atom);
315 }
316
317 public void set_levelname(string atom)
318 {
319 string value = Parse.String(atom);
320 if (value != null) mMetadata.Name = value;
321 else throw new Exception("Unexpected value on line " + mInput.LineNumber + ": " + atom);
322 }
323
324 public void set_next(string atom)
325 {
326 string value = Parse.String(atom);
327 if (value != null) mMetadata.Next = value;
328 else throw new Exception("Unexpected value on line " + mInput.LineNumber + ": " + atom);
329 }
330
331 public void set_type(string atom)
332 {
333 Map.Mode? value = Parse.Constant<Map.Mode>(atom);
334 if (value != null) mMetadata.Type = value.Value;
335 else throw new Exception("Unexpected type on line " + mInput.LineNumber + ": " + atom);
336 }
337
338 public void set_dimensions(string atom)
339 {
340 Point? dimensions = Parse.Coordinates(atom);
341 if (dimensions != null)
342 {
343 mMetadata.GridWidth = dimensions.Value.X;
344 mMetadata.GridHeight = dimensions.Value.Y;
345 if (mMetadata.GridWidth <= 0 || mMetadata.GridHeight <= 0)
346 {
347 throw new Exception("Invalid dimensions on line " + mInput.LineNumber + ": " + atom);
348 }
349 }
350 else
351 {
352 throw new Exception("Unexpected value on line " + mInput.LineNumber + ": " + atom);
353 }
354 }
355
356 public void set_tileset(string atom)
357 {
358 string value = Parse.String(atom);
359 if (value != null) mMetadata.Tileset = value;
360 else throw new Exception("Unexpected tileset on line " + mInput.LineNumber + ": " + atom);
361 }
362
363 public void set_defaulttile(string atom)
364 {
365 char? value = Parse.Char(atom);
366 if (value != null) mDefaultTile = value.Value;
367 else throw new Exception("Unexpected tile value on line " + mInput.LineNumber + ": " + atom);
368 }
369
370 public void set_numplayers(string atom)
371 {
372 string[] list = Parse.List(atom);
373 if (list != null)
374 {
375 foreach (string item in list)
376 {
377 int[] range = Parse.Range(item);
378 if (range != null)
379 {
380 for (int i = range[0]; i <= range[1]; i++)
381 {
382 mMetadata.NumPlayers.Add(i);
383 }
384 continue;
385 }
386 int? integer = Parse.Integer(item);
387 if (integer != null)
388 {
389 mMetadata.NumPlayers.Add(integer.Value);
390 continue;
391 }
392
393 throw new Exception("Unexpected atom on line " + mInput.LineNumber + ": " + item);
394 }
395 if (mMetadata.NumPlayers.Count == 0)
396 {
397 throw new Exception("No numbers given on line " + mInput.LineNumber + ": " + atom);
398 }
399 }
400 else
401 {
402 throw new Exception("Unexpected value on line " + mInput.LineNumber + ": " + atom);
403 }
404 }
405
406
407 Map.Metadata mMetadata;
408 char[,] mGrid;
409 List<Map.RawEntity> mEntities;
410 Point[] mPlayerPositions;
411 char mDefaultTile = ' ';
412
413 Dictionary<char, Dictionary<string, string>> mEntitySections = new Dictionary<char, Dictionary<string, string>>();
414 LineReader mInput;
415 }
416
417 #endregion
418
419
420 #region Private Variables
421
422 Impl mImpl;
423
424 #endregion
425 }
426 }
This page took 0.053136 seconds and 4 git commands to generate.