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