1 #include <allegro5/allegro.h>
2 #include <allegro5/allegro_primitives.h>
3 #include <math.h>
4 #include "defines.h"
5 #include "game.h"
6 #include "global.h"
7 #include "menus.h"
8 #include "music.h"
9 #include "physics.h"
10 #include "vcontroller.h"
11 
12 /*
13 
14 	All variables relevant to player position and level / level status. Only
15 	thing of note is that PlayerPos is a 3 element array - it contains
16 	player position (in elements 0 and 1) and rotation (in element 2)
17 
18 */
19 int KeyFlags;
20 int RequiredObjectsLeft, TotalObjectsLeft;
21 double PlayerPos[3], PlayerVec[2];
22 double ScrollPos[2];
23 struct Animation *PlayerAnim = NULL;
24 struct Level *Lvl = NULL;
25 struct LevelState *LvlState = NULL;
26 double LeftWindow = -120, RightWindow = 120;
27 double Pusher = 0;
28 static ALLEGRO_COLOR cloud_color;
29 
30 #define PLAYER_STRENGTH 0.14f
31 
32 /*
33 
34 	ID things, for GAMESTATE purposes. Note that two menu entries head this way -
35 	"new game" and "continue game", so we have to IDs
36 */
37 static int _newid = DEMO_STATE_NEW_GAME;
newid(void)38 static int newid(void)
39 {
40    return _newid;
41 }
42 
43 static int _continueid = DEMO_STATE_CONTINUE_GAME;
continueid(void)44 static int continueid(void)
45 {
46    return _continueid;
47 }
48 
49 int CurrentID;
50 
51 /* background stuff */
52 ALLEGRO_BITMAP *water;
53 ALLEGRO_SAMPLE_INSTANCE *WaterVoice, *WaterVoice2;
54 ALLEGRO_SAMPLE *WaveNoise = NULL;
55 
56 ALLEGRO_BITMAP *cloud;
57 al_fixed *TanTable = NULL;
58 int CalibRes = 0;
59 
60 double TexX;
61 double WaveY;
62 
63 struct {
64    double x, y;
65 } Clouds[8];
66 double CloudX;
67 
load_game_resources(const char * data_path)68 char *load_game_resources(const char *data_path)
69 {
70    int c;
71    ALLEGRO_PATH *path;
72 
73    printf("load_game_resources\n");
74    path = al_create_path_for_directory(data_path);
75    al_set_path_filename(path, "level.txt");
76    Lvl = LoadLevel(al_path_cstr(path, '/'), 15);
77    al_destroy_path(path);
78 
79    if (!Lvl)
80       return GetLevelError();
81 
82    ReturnState(Lvl, LvlState);
83 
84    PlayerAnim = SeedPlayerAnimation();
85    cloud = ObtainBitmap("cloud");
86    water = ObtainBitmap("water");
87 
88    c = 480 << 2;
89    TanTable = (al_fixed *) malloc(sizeof(al_fixed) * c);
90    while (c--)
91       TanTable[c] =
92          al_ftofix(tan
93                 ((3.141592654f / 2.0f -
94                   atan2(0.75f *
95                         (((float)(c * 2) / (float)(480 << 2)) - 1), 1)
96                  )));
97 
98    WaveNoise = ObtainSample("wave");
99 
100    cloud_color = al_get_pixel(cloud, 0, 0);
101 
102    return NULL;
103 }
104 
unload_game_resources(void)105 void unload_game_resources(void)
106 {
107    printf("unload_game_resources\n");
108    FreePlayerAnimation(PlayerAnim);
109    PlayerAnim = NULL;
110 
111    FreeState(LvlState);
112    LvlState = BorrowState(Lvl);
113    FreeLevel(Lvl);
114    Lvl = NULL;
115 
116    if (TanTable) {
117       free(TanTable);
118       TanTable = NULL;
119    }
120 }
121 
DeInit(void)122 static void DeInit(void)
123 {
124    if (WaterVoice) {
125       al_stop_sample_instance(WaterVoice);
126       al_destroy_sample_instance(WaterVoice);
127       WaterVoice = NULL;
128    }
129    if (WaterVoice2) {
130       al_stop_sample_instance(WaterVoice2);
131       al_destroy_sample_instance(WaterVoice2);
132       WaterVoice2 = NULL;
133    }
134    PauseAnimation(PlayerAnim);
135 }
136 
GenericInit(void)137 static void GenericInit(void)
138 {
139    int c;
140 
141    CloudX = 0;
142    Clouds[0].x = Clouds[0].y = 0;
143    for (c = 1; c < 8; c++) {
144       Clouds[c].x =
145          Clouds[c - 1].x + al_get_bitmap_width(cloud) + 8 +
146             rand() * (screen_width * 480 / screen_height / 8) / RAND_MAX;
147       Clouds[c].y = rand() * 0.5 * 480 / RAND_MAX;
148    }
149 
150    WaterVoice = al_create_sample_instance(WaveNoise);
151    al_set_sample_instance_playmode(WaterVoice, ALLEGRO_PLAYMODE_BIDIR);
152    al_set_sample_instance_gain(WaterVoice, 0.5);
153    al_attach_sample_instance_to_mixer(WaterVoice, al_get_default_mixer());
154    al_play_sample_instance(WaterVoice);
155 
156    WaterVoice2 = al_create_sample_instance(WaveNoise);
157    al_set_sample_instance_playmode(WaterVoice2,
158                       ALLEGRO_PLAYMODE_BIDIR);
159    al_set_sample_instance_gain(WaterVoice2, 0.25);
160    al_attach_sample_instance_to_mixer(WaterVoice2, al_get_default_mixer());
161    al_play_sample_instance(WaterVoice2);
162 
163    play_music(DEMO_MIDI_INGAME, true);
164    UnpauseAnimation(PlayerAnim);
165 }
166 
ContinueInit(void)167 static void ContinueInit(void)
168 {
169    GenericInit();
170    CurrentID = continueid();
171 }
172 
GameInit(void)173 static void GameInit(void)
174 {
175    KeyFlags = 0;
176    enable_continue_game();
177 
178    SetInitialState(Lvl);
179    ScrollPos[0] = PlayerPos[0] = Lvl->PlayerStartPos[0];
180    ScrollPos[1] = PlayerPos[1] = Lvl->PlayerStartPos[1];
181    PlayerVec[0] = PlayerVec[1] = 0;
182    RequiredObjectsLeft = Lvl->ObjectsRequired;
183    TotalObjectsLeft = Lvl->TotalObjects;
184 
185    GenericInit();
186    CurrentID = newid();
187 }
188 
DrawClouds(void)189 static void DrawClouds(void)
190 {
191    int c = 8;
192    double x1, y1, x2, y2, x;
193 
194    while (c--) {
195       x = Clouds[c].x + CloudX;
196 
197       x1 = (x - sin(x) * 1.4);
198       x2 = (x + al_get_bitmap_width(cloud) + sin(x) * 1.4);
199       y1 = (Clouds[c].y - cos(x) * 1.4);
200       y2 = (Clouds[c].y + al_get_bitmap_height(cloud) + cos(x) * 1.4);
201 
202       if (x2 < 0) {
203          Clouds[c].x =
204             Clouds[(c - 1) & 7].x + al_get_bitmap_width(cloud) + 8 +
205             (rand() * 32.0 / RAND_MAX);
206          Clouds[c].y = rand() * 0.5 * screen_height / RAND_MAX;
207       }
208       else {
209          al_draw_scaled_bitmap(cloud, 0, 0, al_get_bitmap_width(cloud),
210             al_get_bitmap_height(cloud),
211             x1, y1, x2 - x1, y2 - y1, 0);
212       }
213    }
214 }
215 
set_v(ALLEGRO_VERTEX * vt,double x,double y,double u,double v)216 static void set_v(ALLEGRO_VERTEX *vt, double x, double y, double u, double v)
217 {
218    vt->x = x;
219    vt->y = y;
220    vt->z = 0;
221    vt->u = u;
222    vt->v = v;
223    vt->color = al_map_rgb_f(1, 1, 1);
224 }
225 
GameDraw(void)226 static void GameDraw(void)
227 {
228    ALLEGRO_BITMAP *ch;
229    double depth;
230    ALLEGRO_VERTEX Points[4];
231    ALLEGRO_TRANSFORM transform;
232    int chw, chh;
233    float w;
234 
235    al_identity_transform(&transform);
236    al_scale_transform(&transform, screen_height / 480.0, screen_height / 480.0);
237    al_use_transform(&transform);
238 
239    w = screen_width * 480.0 / screen_height;
240 
241    /* draw background */
242    {
243       int c = 480 * 4 - 1;
244       double y2, lowy = 480, y1, u;
245       double index;
246 
247       while (c > 480 * 2 + 15) {
248 
249          depth = al_fixtof(al_fixmul(TanTable[c], (int)((ScrollPos[1] - 240) * 4096.0f)));
250 
251          if (depth > -261 && depth < -5.0f) {
252             int d = ((int)(depth * 65536)) & (256 * 65536 - 1);
253             y1 = lowy;
254             index = (d / 65536.0 - 1.0) * ALLEGRO_PI * 2 * 8 / 128.0f;
255 
256             y2 = c / 4.0 - 125.0f * sin(index + WaveY) / depth;
257 
258             if (y2 < lowy) {
259                lowy = ceil(y2);
260 
261                u = 64 + ScrollPos[0] / 8 + TexX;
262                set_v(Points + 0, 0, y2, u + depth * 4, depth + 5);
263                set_v(Points + 1, w, y2, u - depth * 4, depth + 5);
264                set_v(Points + 2, w, y1, u - depth * 4, depth + 5);
265                set_v(Points + 3, 0, y1, u + depth * 4, depth + 5);
266 
267                al_draw_prim(Points, NULL, water, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
268             }
269          }
270          c--;
271       }
272 
273       al_set_clipping_rectangle(0, 0, screen_width, lowy * screen_height / 480 + 1);
274       al_clear_to_color(cloud_color);
275       DrawClouds();
276       al_set_clipping_rectangle(0, 0, screen_width, screen_height);
277    }
278 
279    /* draw interactable parts of level */
280    DrawLevelBackground(Lvl, ScrollPos);
281 
282 #ifdef DEBUG_EDGES
283    solid_mode();
284    struct Edge *E = Lvl->AllEdges;
285 
286    while (E) {
287       line(buffer,
288            E->EndPoints[0]->Normal[0] - x + (screen_width >> 1),
289            E->EndPoints[0]->Normal[1] - y + (screen_height >> 1),
290            E->EndPoints[1]->Normal[0] - x + (screen_width >> 1),
291            E->EndPoints[1]->Normal[1] - y + (screen_height >> 1), al_map_rgb(0, 0,
292                                                                      0));
293       E = E->Next;
294    }
295 #endif
296 
297 #ifdef DEBUG_OBJECTS
298    struct Object *O = Lvl->AllObjects;
299 
300    while (O) {
301       rect(buffer,
302            O->Bounder.TL.Pos[0] - x + (screen_width >> 1),
303            O->Bounder.TL.Pos[1] - y + (screen_height >> 1),
304            O->Bounder.BR.Pos[0] - x + (screen_width >> 1),
305            O->Bounder.BR.Pos[1] - y + (screen_height >> 1), al_map_rgb(0, 0, 0));
306       circle(buffer, O->Pos[0] - x + (screen_width >> 1),
307              O->Pos[1] - y + (screen_height >> 1), O->ObjType->Radius, al_map_rgb(0,
308                                                                           0,
309                                                                           0));
310       O = O->Next;
311    }
312 #endif
313 
314    /* add player sprite */
315    ch = GetCurrentBitmap(PlayerAnim);
316    chw = al_get_bitmap_width(ch);
317    chh = al_get_bitmap_height(ch);
318 
319    al_draw_scaled_rotated_bitmap(ch, chw / 2.0, chh / 2.0,
320       (PlayerPos[0] - ScrollPos[0]) + w / 2,
321       (PlayerPos[1] - ScrollPos[1]) + 480.0 / 2,
322       1, 1, PlayerPos[2],
323       KeyFlags & KEYFLAG_FLIP ? ALLEGRO_FLIP_HORIZONTAL : 0);
324 
325    DrawLevelForeground(Lvl);
326 
327    al_identity_transform(&transform);
328    al_use_transform(&transform);
329 
330    /* add status */
331    demo_textprintf(demo_font,
332                  screen_width - al_get_text_width(demo_font,
333                                         "Items Required: 1000"), 8,
334                  al_map_rgb(255, 255, 255), "Items Required: %d",
335                  RequiredObjectsLeft);
336    demo_textprintf(demo_font,
337                  screen_width - al_get_text_width(demo_font,
338                                         "Items Remaining: 1000"),
339                  8 + al_get_font_line_height(demo_font), al_map_rgb(255, 255, 255),
340                  "Items Remaining: %d", TotalObjectsLeft);
341 }
342 
GameUpdate(void)343 static int GameUpdate(void)
344 {
345    struct QuadTreeNode *CollTree;
346    struct Container *EPtr;
347 
348    if (RequiredObjectsLeft < 0)
349       return DEMO_STATE_SUCCESS;
350 
351    TexX += 0.3f;
352    WaveY -= 0.02f;
353    CloudX -= 0.125f;
354 
355    /* scrolling */
356    if ((PlayerPos[0] - ScrollPos[0]) < LeftWindow)
357       ScrollPos[0] = PlayerPos[0] - LeftWindow;
358    if ((PlayerPos[0] - ScrollPos[0]) > RightWindow)
359       ScrollPos[0] = PlayerPos[0] - RightWindow;
360    if ((PlayerPos[1] - ScrollPos[1]) < -80)
361       ScrollPos[1] = PlayerPos[1] + 80;
362    if ((PlayerPos[1] - ScrollPos[1]) > 80)
363       ScrollPos[1] = PlayerPos[1] - 80;
364 
365    if ((KeyFlags & KEYFLAG_FLIP) ^ (((PlayerPos[2] < ALLEGRO_PI * 0.5f)
366                                      && (PlayerPos[2] >
367                                          -ALLEGRO_PI * 0.5f)) ? 0 : KEYFLAG_FLIP)
368       ) {
369       if (LeftWindow < 0)
370          LeftWindow++;
371       if (RightWindow < 120)
372          RightWindow++;
373    } else {
374       if (LeftWindow > -120)
375          LeftWindow--;
376       if (RightWindow > 0)
377          RightWindow--;
378    }
379 
380    /* update controls */
381    controller[controller_id]->poll(controller[controller_id]);
382    if (controller[controller_id]->button[0]) {
383       if (KeyFlags & KEYFLAG_LEFT) {
384          Pusher += 0.005f;
385          if (Pusher >= PLAYER_STRENGTH)
386             Pusher = PLAYER_STRENGTH;
387       } else {
388          KeyFlags |= KEYFLAG_LEFT;
389          Pusher = 0;
390       }
391    } else
392       KeyFlags &= ~KEYFLAG_LEFT;
393 
394    if (controller[controller_id]->button[1]) {
395       if (KeyFlags & KEYFLAG_RIGHT) {
396          Pusher += 0.005f;
397          if (Pusher >= PLAYER_STRENGTH)
398             Pusher = PLAYER_STRENGTH;
399       } else {
400          KeyFlags |= KEYFLAG_RIGHT;
401          Pusher = 0;
402       }
403    } else
404       KeyFlags &= ~KEYFLAG_RIGHT;
405 
406    if (controller[controller_id]->button[2])
407       KeyFlags |= KEYFLAG_JUMP;
408    else {
409       if ((KeyFlags & KEYFLAG_JUMPING) && PlayerVec[1] < -2.0f)
410          PlayerVec[1] = -2.0f;
411       KeyFlags &= ~(KEYFLAG_JUMP | KEYFLAG_JUMP_ISSUED);
412    }
413 
414    /* run physics */
415    CollTree = RunPhysics(Lvl, PlayerPos, PlayerVec, 0.6f, PlayerAnim);
416 
417    /* check whether any objects are collected */
418    EPtr = CollTree->Contents;
419    while (EPtr && EPtr->Type == OBJECT) {
420       if (EPtr->Content.O->Flags & OBJFLAGS_VISIBLE) {
421          double SqDistance, XDiff, YDiff;
422 
423          XDiff = PlayerPos[0] - EPtr->Content.O->Pos[0];
424          YDiff = PlayerPos[1] - EPtr->Content.O->Pos[1];
425          SqDistance = XDiff * XDiff + YDiff * YDiff;
426 
427          if (SqDistance <=
428              EPtr->Content.O->ObjType->Radius *
429              EPtr->Content.O->ObjType->Radius) {
430             /* collision! */
431             if (EPtr->Content.O->Flags & OBJFLAGS_DOOR) {
432                if (!RequiredObjectsLeft) {
433                   if (EPtr->Content.O->ObjType->CollectNoise)
434                      play_sound(EPtr->Content.O->ObjType->
435                                  CollectNoise, 255, 128, 1000, 0);
436                   RequiredObjectsLeft = -1;
437                }
438             } else {
439                EPtr->Content.O->Flags &= ~OBJFLAGS_VISIBLE;
440                if (EPtr->Content.O->ObjType->CollectNoise)
441                   play_sound(EPtr->Content.O->ObjType->CollectNoise,
442                               255, 128, 1000, 0);
443 
444                if (RequiredObjectsLeft > 0) {
445                   RequiredObjectsLeft--;
446                   if (!RequiredObjectsLeft)
447                      SetDoorOpen(Lvl);
448                }
449                TotalObjectsLeft--;
450             }
451          }
452       }
453 
454       EPtr = EPtr->Next;
455    }
456 
457    return key_pressed(ALLEGRO_KEY_ESCAPE) ? DEMO_STATE_MAIN_MENU : CurrentID;
458 }
459 
destroy_game()460 void destroy_game()
461 {
462    FreeState(LvlState);
463    LvlState = NULL;
464 }
465 
466 /*
467 
468 	GAMESTATE generation things - see comments in gamestate.h for more
469 	information
470 
471 */
create_new_game(GAMESTATE * game)472 void create_new_game(GAMESTATE * game)
473 {
474    game->id = newid;
475    game->init = GameInit;
476    game->update = GameUpdate;
477    game->draw = GameDraw;
478    game->deinit = DeInit;
479 }
480 
create_continue_game(GAMESTATE * game)481 void create_continue_game(GAMESTATE * game)
482 {
483    game->id = continueid;
484    game->init = ContinueInit;
485    game->update = GameUpdate;
486    game->draw = GameDraw;
487    game->deinit = DeInit;
488 }
489