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