1 #include <allegro.h>
2 #ifdef DEMO_USE_ALLEGRO_GL
3 #include <alleggl.h>
4 #endif
5 #include <string.h>
6 #include <math.h>
7 #include "../include/lvlalloc.h"
8 #include "../include/lvlfile.h"
9 #include "../include/token.h"
10
11 /*
12
13 Routines related to the initial reading of level data - heavily connected to
14 tkeniser.c. Defines the grammar that level files should use.
15
16 Also includes a simple function for obtaining the equation of a line and some
17 other bits to do with data initialisation that flows straight from level data
18
19 */
20
21 /*
22 Converts a bitmap using bright pink for transparency to a bitmap using alpha
23 channel.
24 */
25 #ifdef DEMO_USE_ALLEGRO_GL
ConvertFromMaskedToTrans(BITMAP * From,BITMAP * To)26 void ConvertFromMaskedToTrans(BITMAP *From, BITMAP *To)
27 {
28 int x, y;
29
30 for (y = 0; y < From->h; y++) {
31 for (x = 0; x < From->w; x++) {
32 int col = getpixel(From, x, y);
33 /* If the pixel matches mask color (bright pink) write it with alpha value 0
34 (fully transparent), otherwise write at with alpha value 255 (fully
35 opaque). */
36 if (col == bitmap_mask_color(From))
37 putpixel(To, x, y, makeacol32(getr(col), getg(col), getb(col), 0));
38 else
39 putpixel(To, x, y, makeacol32(getr(col), getg(col), getb(col), 255));
40 }
41 }
42 }
43 #endif
44
45
46 /*
47
48 LoadMaterials loads the list of materials according to the following grammar:
49
50 fillname -> string
51 edgename -> string
52 materal -> { fillname, edgename }
53 material list -> { material* }
54
55 */
LoadMaterials(struct Level * NewLev)56 void LoadMaterials(struct Level *NewLev)
57 {
58 struct Material **NewMatPtr = &NewLev->AllMats;
59 #ifdef DEMO_USE_ALLEGRO_GL
60 BITMAP *TmpEdge;
61 #endif
62
63 ExpectToken(TK_OPENBRACE);
64 while (1) {
65 GetToken();
66 switch (Token.Type) {
67 case TK_CLOSEBRACE:
68 return;
69 case TK_OPENBRACE:
70 {
71 *NewMatPtr = NewMaterial();
72
73 ExpectToken(TK_STRING);
74 if (!((*NewMatPtr)->Fill = ObtainBitmap(Token.Text))) {
75 Error = 1;
76 uszprintf(ErrorText, sizeof(ErrorText),
77 "Could not load material fill %s at line %d",
78 Token.Text, Lines);
79 return;
80 }
81
82 ExpectToken(TK_COMMA);
83 ExpectToken(TK_STRING);
84 #ifdef DEMO_USE_ALLEGRO_GL
85 if (!(TmpEdge = ObtainBitmap(Token.Text))) {
86 #else
87 if (!((*NewMatPtr)->Edge = ObtainBitmap(Token.Text))) {
88 #endif
89 Error = 1;
90 uszprintf(ErrorText, sizeof(ErrorText),
91 "Could not load material edge %s at line %d",
92 Token.Text, Lines);
93 return;
94 }
95
96 #ifdef DEMO_USE_ALLEGRO_GL
97 /* The "Edge" texture uses bright pink (also known as magic pink,
98 makecol(255, 0, 255)) for transparency. Since AllegroGL doesn't
99 support rendering of masked 3D polygons we have to convert the
100 texture to a RGBA8 format which will use alpha channel for
101 transparency.*/
102
103 /* Tell AllegroGL to create 32-bit video bitmaps (RGBA8). */
104 allegro_gl_set_video_bitmap_color_depth(32);
105 (*NewMatPtr)->Edge = create_video_bitmap(TmpEdge->w, TmpEdge->h);
106 /* Switch back to defualt video bitmaps color depth (screen color
107 depth). */
108 allegro_gl_set_video_bitmap_color_depth(-1);
109 ConvertFromMaskedToTrans(TmpEdge, (*NewMatPtr)->Edge);
110 destroy_bitmap(TmpEdge);
111 #endif
112
113 ExpectToken(TK_COMMA);
114 ExpectToken(TK_NUMBER);
115 (*NewMatPtr)->Friction = Token.FQuantity;
116
117 ExpectToken(TK_CLOSEBRACE);
118 NewMatPtr = &(*NewMatPtr)->Next;
119 }
120 break;
121 default:
122 Error = 1;
123 return;
124 }
125 }
126 }
127
128 /*
129
130 LoadVertices loads the list of vertices according to the following grammar:
131
132 xpos -> number
133 ypos -> number
134 vertex -> { xpos, ypos }
135 vertex list -> { vertex* }
136
137 */
138 void LoadVertices(struct Level *NewLev)
139 {
140 struct Vertex **NewVertPtr = &NewLev->AllVerts;
141
142 ExpectToken(TK_OPENBRACE);
143 while (1) {
144 GetToken();
145 switch (Token.Type) {
146 case TK_CLOSEBRACE:
147 return;
148 case TK_OPENBRACE:
149 {
150 *NewVertPtr = NewVertex();
151
152 ExpectToken(TK_NUMBER);
153 (*NewVertPtr)->Pos[0] = Token.FQuantity;
154
155 ExpectToken(TK_COMMA);
156 ExpectToken(TK_NUMBER);
157 (*NewVertPtr)->Pos[1] = Token.FQuantity;
158
159 ExpectToken(TK_CLOSEBRACE);
160 NewVertPtr = &(*NewVertPtr)->Next;
161 }
162 break;
163 default:
164 Error = 1;
165 return;
166 }
167 }
168 }
169
170 /*
171
172 GetVert loads a single vertex reference as part of the LoadTriangles routine.
173 Grammar is:
174
175 edge height -> number
176 reference -> number
177 flags -> "edge" | "collidable" | "foreground"
178 vertex reference -> { reference, edge height [, flags] }
179
180 */
181 void GetVert(struct Level *NewLev, struct Triangle *t, int c)
182 {
183 ExpectToken(TK_OPENBRACE);
184
185 ExpectToken(TK_NUMBER);
186 t->Edges[c] = NewLev->AllVerts;
187 while (t->Edges[c] && Token.IQuantity--)
188 t->Edges[c] = t->Edges[c]->Next;
189
190 if (Token.IQuantity != -1) {
191 Error = 1;
192 uszprintf(ErrorText, sizeof(ErrorText),
193 "Unknown vertex referenced at line %d", Lines);
194 return;
195 }
196 ExpectToken(TK_COMMA);
197
198 ExpectToken(TK_NUMBER);
199 t->EdgeFlags[c] = (Token.IQuantity * SCREEN_H) / 480;
200
201 GetToken();
202 switch (Token.Type) {
203 case TK_COMMA:
204 {
205 int Finished = FALSE;
206
207 while (!Finished) {
208 GetToken();
209 switch (Token.Type) {
210 case TK_CLOSEBRACE:
211 Finished = TRUE;
212 break;
213 case TK_STRING:
214 if (!strcmp(Token.Text, "edge"))
215 t->EdgeFlags[c] |= TRIFLAGS_EDGE;
216 if (!strcmp(Token.Text, "collidable"))
217 t->EdgeFlags[c] |= FLAGS_COLLIDABLE;
218 if (!strcmp(Token.Text, "foreground"))
219 t->EdgeFlags[c] |= FLAGS_FOREGROUND;
220 break;
221 default:
222 Error = 1;
223 return;
224 }
225 }
226 }
227 break;
228 case TK_CLOSEBRACE:
229 break;
230 default:
231 Error = 1;
232 return;
233 }
234 }
235
236 /*
237
238 GetNormal is a function that doesn't read anything from a file but calculates
239 an edge normal for 'e' based on end points 'v1' and 'v2'
240
241 */
242 int GetNormal(struct Edge *e, float *v1, float *v2)
243 {
244 /* get line normal */
245 float length;
246
247 e->a = v2[1] - v1[1];
248 e->b = -(v2[0] - v1[0]);
249
250 /* make line normal unit length */
251 length = sqrt(e->a * e->a + e->b * e->b);
252 if (length < 1.0f)
253 return TRUE;
254 e->a /= length;
255 e->b /= length;
256
257 /* calculate distance of line from origin */
258 e->c = -(v1[0] * e->a + v1[1] * e->b);
259 return FALSE;
260 }
261
262 /*
263
264 InitEdge intialises edges, which means calculating the 'expanded' edge equation
265 (i.e. one moved away from the real edge by 'radius' units) and making a note at
266 both end vertices of the edge they meet
267
268 */
269 void InitEdge(struct Edge *e, int radius)
270 {
271 /* get edge normal */
272 GetNormal(e, e->EndPoints[0]->Pos, e->EndPoints[1]->Pos);
273
274 /* calculate distance to line from origin */
275 e->c -= radius * (e->a * e->a + e->b * e->b);
276
277 /* link edge as necessary */
278 if (!e->EndPoints[0]->Edges[0])
279 e->EndPoints[0]->Edges[0] = e;
280 else
281 e->EndPoints[0]->Edges[1] = e;
282 if (!e->EndPoints[1]->Edges[0])
283 e->EndPoints[1]->Edges[0] = e;
284 else
285 e->EndPoints[1]->Edges[1] = e;
286 }
287
288 /*
289
290 LoadTriangles loads a triangle list, using GetVert as required. Grammar is:
291
292 vertex reference -> (see GetVert commentary)
293 material reference -> number
294 triangle -> { vertex reference, vertex reference, vertex reference,
295 material reference }
296 triangle list -> { triangle* }
297
298 */
299 void LoadTriangles(struct Level *NewLev, int radius)
300 {
301 struct Triangle *Tri;
302 struct Edge *NextEdge;
303 int c;
304
305 ExpectToken(TK_OPENBRACE);
306 while (1) {
307 GetToken();
308 switch (Token.Type) {
309 case TK_CLOSEBRACE:
310 return;
311 case TK_OPENBRACE:
312 {
313 Tri = NewTriangle();
314
315 /* read vertex pointers & edge flags */
316 GetVert(NewLev, Tri, 0);
317 ExpectToken(TK_COMMA);
318 GetVert(NewLev, Tri, 1);
319 ExpectToken(TK_COMMA);
320 GetVert(NewLev, Tri, 2);
321 ExpectToken(TK_COMMA);
322
323 /* read material reference and store correct pointer */
324 ExpectToken(TK_NUMBER);
325
326 Tri->Material = NewLev->AllMats;
327 while (Tri->Material && Token.IQuantity--)
328 Tri->Material = Tri->Material->Next;
329 if (Token.IQuantity != -1) {
330 Error = 1;
331 uszprintf(ErrorText, sizeof(ErrorText),
332 "Unknown material referenced at line %d", Lines);
333 return;
334 }
335
336 /* expect end of this triangle */
337 ExpectToken(TK_CLOSEBRACE);
338
339 /* insert new triangle into total level list */
340 Tri->Next = NewLev->AllTris;
341 NewLev->AllTris = Tri;
342
343 /* generate edges */
344 c = 3;
345 while (c--) {
346 if (Tri->EdgeFlags[c] & FLAGS_COLLIDABLE) {
347 NextEdge = NewLev->AllEdges;
348 NewLev->AllEdges = NewEdge();
349 NewLev->AllEdges->Material = Tri->Material;
350 NewLev->AllEdges->Next = NextEdge;
351
352 NewLev->AllEdges->EndPoints[0] = Tri->Edges[c];
353 NewLev->AllEdges->EndPoints[1] = Tri->Edges[(c + 1) % 3];
354
355 InitEdge(NewLev->AllEdges, radius);
356 }
357 }
358 }
359 break;
360 default:
361 Error = 1;
362 return;
363 }
364 }
365 }
366
367 /*
368
369 LoadObjectTypes loads a list of object types. Grammar is:
370
371 image name -> string
372 collection noise -> string
373 object type -> { image name, collection noise }
374 object type list -> { object type* }
375
376 */
377 void LoadObjectTypes(struct Level *NewLev, int radius)
378 {
379 struct ObjectType **NewObjectPtr = &NewLev->AllObjectTypes;
380
381 ExpectToken(TK_OPENBRACE);
382 while (1) {
383 GetToken();
384 switch (Token.Type) {
385 case TK_CLOSEBRACE:
386 return;
387 case TK_OPENBRACE:
388 {
389 *NewObjectPtr = NewObjectType();
390
391 ExpectToken(TK_STRING);
392 if (!((*NewObjectPtr)->Image = ObtainBitmap(Token.Text))) {
393 Error = 1;
394 uszprintf(ErrorText, sizeof(ErrorText),
395 "Could not load object image %s at line %d",
396 Token.Text, Lines);
397 return;
398 }
399 (*NewObjectPtr)->Radius =
400 ((*NewObjectPtr)->Image->w >
401 (*NewObjectPtr)->Image->h) ? (*NewObjectPtr)->
402 Image->w : (*NewObjectPtr)->Image->h;
403 (*NewObjectPtr)->Radius += radius;
404
405 ExpectToken(TK_COMMA);
406 ExpectToken(TK_STRING);
407
408 /* this doesn't generate an error as it is permissible to have objects that don't make a noise when collected */
409 (*NewObjectPtr)->CollectNoise = ObtainSample(Token.Text);
410
411 ExpectToken(TK_CLOSEBRACE);
412 NewObjectPtr = &(*NewObjectPtr)->Next;
413 }
414 break;
415 default:
416 Error = 1;
417 return;
418 }
419 }
420 }
421
422 /*
423
424 LoadObjects loads a list of objects. Grammar is:
425
426 object pos x -> number
427 object pos y -> number
428 object type -> number
429 flags -> "collidable" | "foreground"
430 object -> { object pos x, object pos y, object type [, flags] }
431 object list -> { object* }
432
433 */
434 void LoadObjects(struct Level *NewLev)
435 {
436 struct Object *Obj;
437 int Finished;
438
439 ExpectToken(TK_OPENBRACE);
440 while (1) {
441 GetToken();
442 switch (Token.Type) {
443 case TK_CLOSEBRACE:
444 return;
445 case TK_OPENBRACE:
446 {
447 Obj = NewObject();
448 NewLev->TotalObjects++;
449
450 /* read location */
451 ExpectToken(TK_NUMBER);
452 Obj->Pos[0] = Token.FQuantity;
453 ExpectToken(TK_COMMA);
454 ExpectToken(TK_NUMBER);
455 Obj->Pos[1] = Token.FQuantity;
456 ExpectToken(TK_COMMA);
457
458 /* read angle, convert into Allegro format */
459 ExpectToken(TK_NUMBER);
460 Obj->Angle = (Token.FQuantity * 128.0f) / AL_PI;
461 ExpectToken(TK_COMMA);
462
463 /* read object type, manipulate pointer */
464 ExpectToken(TK_NUMBER);
465 if (Token.IQuantity >= 0) {
466 Obj->ObjType = NewLev->AllObjectTypes;
467 while (Obj->ObjType && Token.IQuantity--)
468 Obj->ObjType = Obj->ObjType->Next;
469
470 if (Token.IQuantity != -1) {
471 Error = 1;
472 uszprintf(ErrorText, sizeof(ErrorText),
473 "Unknown object referenced at line %d", Lines);
474 return;
475 }
476 } else {
477 /* this is the door - a hard coded type */
478 Obj->ObjType = &NewLev->Door;
479 Obj->Flags |= OBJFLAGS_DOOR;
480 NewLev->TotalObjects--;
481 }
482
483 /* parse any flags that may exist */
484 GetToken();
485 switch (Token.Type) {
486 case TK_COMMA:
487 {
488 Finished = FALSE;
489 while (!Finished) {
490 GetToken();
491 switch (Token.Type) {
492 case TK_CLOSEBRACE:
493 Finished = TRUE;
494 break;
495 case TK_STRING:
496 if (!strcmp(Token.Text, "collidable"))
497 Obj->Flags |= FLAGS_COLLIDABLE;
498 if (!strcmp(Token.Text, "foreground"))
499 Obj->Flags |= FLAGS_FOREGROUND;
500 break;
501 default:
502 Error = 1;
503 return;
504 }
505 }
506 }
507 break;
508 case TK_CLOSEBRACE:
509 break;
510 default:
511 Error = 1;
512 return;
513 }
514
515 /* thread into list */
516 Obj->Next = NewLev->AllObjects;
517 NewLev->AllObjects = Obj;
518 }
519 break;
520 default:
521 Error = 1;
522 return;
523 }
524 }
525 }
526
527 /*
528
529 LoadStats loads some special variables. Grammar is:
530
531 player start x -> number
532 player start y -> number
533 required number of objects -> number
534 stats -> { player start x, player start y, required number of objects }
535
536 */
537 void LoadStats(struct Level *NewLev)
538 {
539 ExpectToken(TK_OPENBRACE);
540
541 ExpectToken(TK_NUMBER);
542 NewLev->PlayerStartPos[0] = Token.FQuantity;
543 ExpectToken(TK_COMMA);
544 ExpectToken(TK_NUMBER);
545 NewLev->PlayerStartPos[1] = Token.FQuantity;
546 ExpectToken(TK_COMMA);
547
548 ExpectToken(TK_NUMBER);
549 NewLev->ObjectsRequired = Token.IQuantity;
550 ExpectToken(TK_CLOSEBRACE);
551 }
552