Subversion Repositories Games.Rick Dangerous

Rev

Rev 11 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * src/game.c
3
 *
4
 * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
5
 *
6
 * The use and distribution terms for this software are contained in the file
7
 * named README, which can be found in the root of this distribution. By
8
 * using this software in any fashion, you are agreeing to be bound by the
9
 * terms of this license.
10
 *
11
 * You must not remove this notice, or any other, from this software.
12
 */
13
 
10 pmbaty 14
#include <stdio.h>
1 pmbaty 15
#include <stdlib.h>
16
 
17
#include "system.h"
18
#include "game.h"
19
 
20
#include "draw.h"
21
#include "maps.h"
22
#include "ents.h"
23
#include "e_rick.h"
24
#include "e_sbonus.h"
25
#include "e_them.h"
26
#include "screens.h"
27
#include "rects.h"
28
#include "scroller.h"
29
#include "control.h"
30
 
31
 
32
/*
33
 * local typedefs
34
 */
35
typedef enum
36
{
37
   INIT_GAME, INIT_BUFFER,
38
   INTRO_MAIN, INTRO_MAP,
39
   PAUSE_PRESSED1, PAUSE_PRESSED1B, PAUSED, PAUSE_PRESSED2,
40
   PLAY0, PLAY1, PLAY2, PLAY3,
41
   CHAIN_SUBMAP, CHAIN_MAP, CHAIN_END,
42
   SCROLL_UP, SCROLL_DOWN,
43
   RESTART, GAMEOVER, GETNAME, EXIT
44
} game_state_t;
45
 
46
 
47
/*
48
 * global vars
49
 */
50
U8 game_period = 0;
51
U8 game_waitevt = FALSE;
52
rect_t *game_rects = NULL;
53
 
54
U8 game_lives = 0;
55
U8 game_bombs = 0;
56
U8 game_bullets = 0;
57
U32 game_score = 0;
58
 
59
U16 game_map = 0;
60
U16 game_submap = 0;
61
 
62
U8 game_dir = 0;
63
U8 game_chsm = FALSE;
64
 
65
U8 want_infinitelives = FALSE;
11 pmbaty 66
U8 want_infiniteammo = FALSE;
67
U8 want_last_map = FALSE;
68
U8 want_last_submap = FALSE;
10 pmbaty 69
U8 enable_endkey = TRUE;
1 pmbaty 70
 
71
 
72
hscore_t game_hscores[8] =
73
{
74
   { 8000, "DANGERSTU@" },
75
   { 7000, "SIMES@@@@@" },
76
   { 6000, "KEN@T@ZEN@" },
77
   { 5000, "BOBBLE@@@@" },
78
   { 4000, "GREG@LAA@@" },
79
   { 3000, "TELLY@@@@@" },
80
   { 2000, "CHIGLET@@@" },
81
   { 1000, "ANDYSPLEEN" }
82
};
83
 
84
sound_t *WAV_GAMEOVER;
85
sound_t *WAV_SBONUS2;
86
sound_t *WAV_BULLET;
87
sound_t *WAV_BOMBSHHT;
88
sound_t *WAV_EXPLODE;
89
sound_t *WAV_STICK;
90
sound_t *WAV_WALK;
91
sound_t *WAV_CRAWL;
92
sound_t *WAV_JUMP;
93
sound_t *WAV_PAD;
94
sound_t *WAV_BOX;
95
sound_t *WAV_BONUS;
96
sound_t *WAV_SBONUS1;
97
sound_t *WAV_DIE;
98
sound_t *WAV_ENTITY[10];
99
 
100
 
101
/*
102
 * local vars
103
 */
104
static U8 isave_frow;
105
static game_state_t game_state;
106
static sound_t *music_snd;
107
 
108
 
109
/*
110
 * prototypes
111
 */
112
static void frame (void);
113
static void init (void);
114
static void play0 (void);
115
static void play3 (void);
116
static void restart (void);
117
static void isave (void);
118
static void irestore (void);
119
static void loaddata (void);
120
static void freedata (void);
121
 
122
 
123
/*
124
 * Music
125
 */
126
void game_setmusic (char *name, U8 loop)
127
{
128
   U8 channel;
129
 
130
   if (music_snd)
131
      game_stopmusic ();
132
 
133
   music_snd = syssnd_load (name);
134
 
135
   if (music_snd)
136
   {
137
      music_snd->dispose = TRUE; /* music is always "fire and forget" */
138
      channel = syssnd_play(music_snd, loop);
139
   }
140
}
141
 
142
 
143
void game_stopmusic (void)
144
{
145
   syssnd_stopsound (music_snd);
146
   music_snd = NULL;
147
}
148
 
149
 
150
/*
151
 * Main loop
152
 */
153
void game_run (void)
154
{
155
   U32 tm, tmx;
156
 
157
   loaddata (); /* load cached data */
158
 
159
   tm = sys_gettime ();
160
   game_state = INIT_GAME;
161
 
162
   /* main loop */
163
   while (game_state != EXIT)
164
   {
9 pmbaty 165
      // Pierre-Marie Baty -- intro runs faster
1 pmbaty 166
      if (game_state != INTRO_MAP)
9 pmbaty 167
         game_period = (sysarg_args_period ? sysarg_args_period : GAME_PERIOD);
1 pmbaty 168
      else
169
         game_period = (sysarg_args_period ? sysarg_args_period : GAME_PERIOD) / 2;
170
 
171
      /* timer */
172
      tmx = tm; tm = sys_gettime (); tmx = tm - tmx;
173
      if (tmx < game_period)
174
         sys_sleep (game_period - tmx);
175
 
176
      /* video */
177
      /*DEBUG*//*game_rects=&draw_SCREENRECT;*//*DEBUG*/
178
      sysvid_paint (game_rects);
179
      draw_STATUSRECT.next = NULL; /* FIXME freerects should handle this */
180
 
181
      /* sound */
182
      /*snd_mix ();*/
183
 
184
      /* events */
185
      if (game_waitevt)
186
         sysevt_wait (); /* wait for an event */
187
      else
188
         sysevt_poll (); /* process events (non-blocking) */
189
 
190
      /* frame */
191
      frame ();
192
   }
193
 
194
   freedata (); /* free cached data */
195
}
196
 
197
 
9 pmbaty 198
// Pierre-Marie Baty -- addition: sort high scores
199
int sort_hscores (const void *q1, const void *q2)
200
{
201
   return (((hscore_t *) q1)->score < ((hscore_t *) q2)->score);
202
}
203
 
204
 
1 pmbaty 205
/*
206
 * Prepare frame
207
 *
208
 * This function loops forever: use 'return' when a frame is ready.
209
 * When returning, game_rects must contain every parts of the buffer
210
 * that have been modified.
211
 */
212
static void frame (void)
213
{
214
   while (1)
215
   {
216
 
217
      switch (game_state)
218
      {
219
 
220
 
221
      case INIT_GAME:
222
         init ();
223
         game_state = INTRO_MAIN;
224
         break;
225
 
226
 
227
 
228
      case INTRO_MAIN:
229
         switch (screen_introMain ())
230
         {
231
         case SCREEN_RUNNING:
232
            return;
233
         case SCREEN_DONE:
234
            game_state = INTRO_MAP;
235
            break;
236
         case SCREEN_EXIT:
237
            game_state = EXIT;
238
            return;
239
         }
240
      break;
241
 
242
 
243
 
244
      case INTRO_MAP:
245
         switch (screen_introMap ())
246
         {
247
         case SCREEN_RUNNING:
248
            return;
249
         case SCREEN_DONE:
250
            game_waitevt = FALSE;
251
            game_state = INIT_BUFFER;
252
            break;
253
         case SCREEN_EXIT:
254
            game_state = EXIT;
255
            return;
256
         }
257
      break;
258
 
259
 
260
 
261
      case INIT_BUFFER:
262
         syssnd_pause (FALSE, TRUE); // reset sounds
263
         sysvid_clear (); /* clear buffer */
264
         draw_map (); /* draw the map onto the buffer */
265
         draw_drawStatus (); /* draw the status bar onto the buffer */
266
         game_rects = &draw_SCREENRECT; /* request full buffer refresh */
267
         game_state = PLAY0;
268
         return;
269
 
270
 
271
 
272
      case PAUSE_PRESSED1:
273
         screen_pause (TRUE);
274
         game_state = PAUSE_PRESSED1B;
275
         break;
276
 
277
 
278
 
279
      case PAUSE_PRESSED1B:
280
         if (control_status & CONTROL_PAUSE)
281
            return;
282
         game_state = PAUSED;
283
         break;
284
 
285
 
286
 
287
      case PAUSED:
288
         if (control_status & CONTROL_PAUSE)
289
            game_state = PAUSE_PRESSED2;
290
         if (control_status & CONTROL_EXIT)
291
            game_state = EXIT;
292
         return;
293
 
294
 
295
 
296
      case PAUSE_PRESSED2:
297
         if (!(control_status & CONTROL_PAUSE))
298
         {
299
            game_waitevt = FALSE;
300
            screen_pause (FALSE);
301
            syssnd_pause (FALSE, FALSE);
302
            game_state = PLAY2;
303
         }
304
      return;
305
 
306
 
307
 
308
      case PLAY0:
309
         play0 ();
310
         break;
311
 
312
 
313
 
314
      case PLAY1:
315
         if (control_status & CONTROL_PAUSE)
316
         {
317
            syssnd_pause (TRUE, FALSE);
318
            game_waitevt = TRUE;
319
            game_state = PAUSE_PRESSED1;
320
         }
321
         else
322
            game_state = PLAY2;
323
         break;
324
 
325
 
326
 
327
      case PLAY2:
328
         if (E_RICK_STTST (E_RICK_STDEAD))
329
         {
330
            /* rick is dead */
331
            if (--game_lives)
332
            {
333
               game_state = RESTART;
334
               if (want_infinitelives)
335
                  game_lives = 6;
336
            }
337
            else
338
               game_state = GAMEOVER;
339
         }
340
         else if (game_chsm)
341
            game_state = CHAIN_SUBMAP; // request to chain to next submap
342
         else
343
            game_state = PLAY3;
344
         break;
345
 
346
 
347
 
348
      case PLAY3:
349
         play3 ();
350
         return;
351
 
352
 
353
 
354
      case CHAIN_SUBMAP:
355
         if (map_chain ())
356
            game_state = CHAIN_END;
357
         else
358
         {
11 pmbaty 359
            game_bullets = 6;
360
            game_bombs = 6;
1 pmbaty 361
            game_map++;
11 pmbaty 362
            if (want_last_map)
363
               sysarg_args_map = game_map;
1 pmbaty 364
 
365
            if (game_map == 0x04)
366
            {
367
               /* reached end of game */
368
               /* FIXME @292?*/
369
            }
370
 
371
            game_state = CHAIN_MAP;
372
         }
373
         break;
374
 
375
 
376
 
377
      case CHAIN_MAP:/* CHAIN MAP */
378
         switch (screen_introMap ())
379
         {
380
         case SCREEN_RUNNING:
381
            return;
382
         case SCREEN_DONE:
383
            if (game_map >= 0x04)
384
            {
385
               /* reached end of game */
386
               sysarg_args_map = 0;
387
               sysarg_args_submap = 0;
9 pmbaty 388
               sysarg_args_score = 0;
1 pmbaty 389
               game_state = GAMEOVER;
390
            }
391
            else
392
            {
393
               /* initialize game */
394
               ent_ents[1].x = map_maps[game_map].x;
395
               ent_ents[1].y = map_maps[game_map].y;
396
               map_frow = (U8) map_maps[game_map].row;
397
               game_submap = map_maps[game_map].submap;
398
               game_state = CHAIN_END;
399
            }
400
            break;
401
               case SCREEN_EXIT:
402
            game_state = EXIT;
403
            return;
404
         }
405
         break;
406
 
407
 
408
 
409
      case CHAIN_END:
410
         syssnd_pause (FALSE, TRUE); // reset sounds
411
         map_init (); /* initialize the map */
412
         isave (); /* save data in case of a restart */
413
         ent_clprev (); /* cleanup entities */
414
         draw_map (); /* draw the map onto the buffer */
415
         draw_drawStatus (); /* draw the status bar onto the buffer */
7 pmbaty 416
         draw_STATUSRECT.next = NULL;
1 pmbaty 417
         game_state = PLAY3;
7 pmbaty 418
         frame (); // Pierre-Marie Baty -- patch by Jason Andersen: skip refreshing the screen for 2 pumps
419
         frame (); // to allow the sprites to settle into the appropriate frame
420
         game_rects = &draw_SCREENRECT;  /* request full screen refresh */
1 pmbaty 421
         return;
422
 
423
 
424
 
425
      case SCROLL_UP:
426
         switch (scroll_up ())
427
         {
428
         case SCROLL_RUNNING:
429
            return;
430
         case SCROLL_DONE:
431
            game_state = PLAY0;
432
            break;
433
         }
434
         break;
435
 
436
 
437
 
438
      case SCROLL_DOWN:
439
         switch (scroll_down ())
440
         {
441
         case SCROLL_RUNNING:
442
            return;
443
         case SCROLL_DONE:
444
            game_state = PLAY0;
445
            break;
446
         }
447
         break;
448
 
449
 
450
 
451
      case RESTART:
452
         restart ();
453
         game_state = PLAY0;
454
         return;
455
 
456
 
457
 
458
      case GAMEOVER:
459
         switch (screen_gameover ())
460
         {
461
         case SCREEN_RUNNING:
462
            return;
463
         case SCREEN_DONE:
464
            game_state = GETNAME;
465
            break;
466
         case SCREEN_EXIT:
467
            game_state = EXIT;
468
            break;
469
         }
470
         break;
471
 
472
 
473
 
474
      case GETNAME:
475
         switch (screen_getname ()) {
476
         case SCREEN_RUNNING:
477
            return;
478
         case SCREEN_DONE:
479
            game_state = INIT_GAME;
480
            return;
481
         case SCREEN_EXIT:
482
            game_state = EXIT;
483
            break;
484
         }
485
         break;
486
 
487
 
488
 
489
      case EXIT:
490
         return;
491
 
492
      }
493
   }
494
}
495
 
496
 
497
/*
498
 * Initialize the game
499
 */
500
static void init (void)
501
{
502
   U8 i;
503
 
504
   E_RICK_STRST (0xff);
505
 
506
   game_lives = 6;
507
   game_bombs = 6;
508
   game_bullets = 6;
509
   game_score = 0;
510
 
511
   game_map = sysarg_args_map;
512
 
513
   if (sysarg_args_submap == 0)
514
   {
515
      game_submap = map_maps[game_map].submap;
516
      map_frow = (U8)map_maps[game_map].row;
517
   }
518
   else
519
   {
520
      /* dirty hack to determine frow */
521
      game_submap = sysarg_args_submap;
522
      i = 0;
523
      while (i < MAP_NBR_CONNECT && (map_connect[i].submap != game_submap || map_connect[i].dir != RIGHT))
524
         i++;
525
      map_frow = map_connect[i].rowin - 0x10;
526
      ent_ents[1].y = 0x10 << 3;
527
   }
528
 
9 pmbaty 529
   // Pierre-Marie Baty -- addition: start score
530
   if (sysarg_args_score != 0)
531
      game_score = sysarg_args_score + 1;
532
 
1 pmbaty 533
   ent_ents[1].x = map_maps[game_map].x;
534
   ent_ents[1].y = map_maps[game_map].y;
535
   ent_ents[1].w = 0x18;
536
   ent_ents[1].h = 0x15;
537
   ent_ents[1].n = 0x01;
538
   ent_ents[1].sprite = 0x01;
539
   ent_ents[1].front = FALSE;
540
   ent_ents[ENT_ENTSNUM].n = 0xFF;
541
 
542
   map_resetMarks ();
543
   map_init ();
544
   isave ();
545
}
546
 
547
 
548
/*
549
 * play0
550
 *
551
 */
552
static void play0 (void)
553
{
554
   if (control_status & CONTROL_END)
555
   {
556
      /* request to end the game */
557
      game_state = GAMEOVER;
558
      return;
559
   }
560
 
561
   if (control_last == CONTROL_EXIT)
562
   {
563
      /* request to exit the game */
564
      game_state = EXIT;
565
      return;
566
   }
567
 
568
   ent_action (); /* run entities */
569
   e_them_rndseed++; /* (0270) */
570
 
571
   game_state = PLAY1;
572
}
573
 
574
 
575
/*
576
 * play3
577
 *
578
 */
579
static void play3 (void)
580
{
581
   static rect_t *r;
582
 
583
   ent_draw (); /* draw all entities onto the buffer */
584
 
585
   /* sound */
586
   draw_drawStatus (); /* draw the status bar onto the buffer*/
587
 
588
   r = &draw_STATUSRECT;
589
   r->next = ent_rects; /* refresh status bar too */
590
   game_rects = r; /* take care to cleanup draw_STATUSRECT->next later! */
591
 
592
   if (!E_RICK_STTST (E_RICK_STZOMBIE))
593
   {
594
      /* need to scroll ? */
9 pmbaty 595
      if (ent_ents[1].y >= 200 + 4)
1 pmbaty 596
      {
597
         game_state = SCROLL_UP;
598
         return;
599
      }
9 pmbaty 600
      if (ent_ents[1].y <= 100 - 4)
1 pmbaty 601
      {
602
         game_state = SCROLL_DOWN;
603
         return;
604
      }
605
   }
606
 
607
   game_state = PLAY0;
608
}
609
 
610
 
611
/*
612
 * restart
613
 *
614
 */
615
static void restart (void)
616
{
9 pmbaty 617
   E_RICK_STRST (/*E_RICK_STDEAD | E_RICK_STZOMBIE*/ 0xff); // Pierre-Marie Baty -- correct sprite position on restart
1 pmbaty 618
 
619
   game_bullets = 6;
620
   game_bombs = 6;
621
 
622
   ent_ents[1].n = 1;
623
 
624
   irestore ();
625
   map_init ();
626
   isave ();
627
   ent_clprev ();
628
   draw_map ();
629
   draw_drawStatus ();
630
   game_rects = &draw_SCREENRECT;
631
}
632
 
633
 
634
/*
635
 * isave (0bbb)
636
 *
637
 */
638
static void isave (void)
639
{
640
   e_rick_save ();
641
   isave_frow = map_frow;
642
}
643
 
644
 
645
/*
646
 * irestore (0bdc)
647
 *
648
 */
649
static void irestore (void)
650
{
651
   e_rick_restore ();
652
   map_frow = isave_frow;
653
}
654
 
655
 
656
/*
657
 *
658
 */
659
static void loaddata (void)
660
{
661
   /*
662
    * Cache sounds
663
    *
664
    * tune[0-5].wav not cached
665
    */
666
   WAV_GAMEOVER = syssnd_load ("sounds/gameover.wav");
667
   WAV_SBONUS2 = syssnd_load ("sounds/sbonus2.wav");
668
   WAV_BULLET = syssnd_load ("sounds/bullet.wav");
669
   WAV_BOMBSHHT = syssnd_load ("sounds/bombshht.wav");
670
   WAV_EXPLODE = syssnd_load ("sounds/explode.wav");
671
   WAV_STICK = syssnd_load ("sounds/stick.wav");
672
   WAV_WALK = syssnd_load ("sounds/walk.wav");
673
   WAV_CRAWL = syssnd_load ("sounds/crawl.wav");
674
   WAV_JUMP = syssnd_load ("sounds/jump.wav");
675
   WAV_PAD = syssnd_load ("sounds/pad.wav");
676
   WAV_BOX = syssnd_load ("sounds/box.wav");
677
   WAV_BONUS = syssnd_load ("sounds/bonus.wav");
678
   WAV_SBONUS1 = syssnd_load ("sounds/sbonus1.wav");
679
   WAV_DIE = syssnd_load ("sounds/die.wav");
680
   WAV_ENTITY[0] = syssnd_load ("sounds/ent0.wav");
681
   WAV_ENTITY[1] = syssnd_load ("sounds/ent1.wav");
682
   WAV_ENTITY[2] = syssnd_load ("sounds/ent2.wav");
683
   WAV_ENTITY[3] = syssnd_load ("sounds/ent3.wav");
684
   WAV_ENTITY[4] = syssnd_load ("sounds/ent4.wav");
685
   WAV_ENTITY[5] = syssnd_load ("sounds/ent5.wav");
686
   WAV_ENTITY[6] = syssnd_load ("sounds/ent6.wav");
687
   WAV_ENTITY[7] = syssnd_load ("sounds/ent7.wav");
688
   WAV_ENTITY[8] = syssnd_load ("sounds/ent8.wav");
9 pmbaty 689
 
690
   // Pierre-Marie Baty -- addition: load high scores
691
   {
692
      char hiscorefile_fullpathname[1024];
693
      char linebuf[256];
694
      hscore_t *hscore;
695
      U32 score;
696
      char *ptr;
697
      size_t i, j;
698
      FILE *fp;
13 pmbaty 699
      sprintf_s (hiscorefile_fullpathname, 1024, "%s/hiscores.txt", sys_getdatapath ());
9 pmbaty 700
      fopen_s (&fp, hiscorefile_fullpathname, "rb");
701
      if (fp != NULL)
702
      {
703
         for (i = 0; (fgets (linebuf, sizeof (linebuf), fp) != NULL) && (i < 8);)
704
         {
705
            score = strtol (linebuf, &ptr, 10);
706
            if (ptr <= linebuf)
707
               continue;
708
            while ((*ptr == '\t') || (*ptr == ' '))
709
               ptr++;
710
            hscore = &game_hscores[i++];
711
            hscore->score = score;
712
            for (j = 0; j < sizeof (hscore->name); j++)
713
               hscore->name[j] = '@';
714
            for (j = 0; j < sizeof (hscore->name); j++, ptr++)
715
               hscore->name[j] = (((*ptr >= 'A') && (*ptr <= 'Z')) || (*ptr == '.') ? *ptr : '@');
716
         }
717
         qsort (game_hscores, 8, sizeof (hscore_t), sort_hscores);
718
         fclose (fp);
719
      }
720
   }
1 pmbaty 721
}
722
 
723
 
724
/*
725
 *
726
 */
727
static void freedata (void)
728
{
729
   syssnd_stopall ();
730
   syssnd_free (WAV_GAMEOVER);
731
   syssnd_free (WAV_SBONUS2);
732
   syssnd_free (WAV_BULLET);
733
   syssnd_free (WAV_BOMBSHHT);
734
   syssnd_free (WAV_EXPLODE);
735
   syssnd_free (WAV_STICK);
736
   syssnd_free (WAV_WALK);
737
   syssnd_free (WAV_CRAWL);
738
   syssnd_free (WAV_JUMP);
739
   syssnd_free (WAV_PAD);
740
   syssnd_free (WAV_BOX);
741
   syssnd_free (WAV_BONUS);
742
   syssnd_free (WAV_SBONUS1);
743
   syssnd_free (WAV_DIE);
744
   syssnd_free (WAV_ENTITY[0]);
745
   syssnd_free (WAV_ENTITY[1]);
746
   syssnd_free (WAV_ENTITY[2]);
747
   syssnd_free (WAV_ENTITY[3]);
748
   syssnd_free (WAV_ENTITY[4]);
749
   syssnd_free (WAV_ENTITY[5]);
750
   syssnd_free (WAV_ENTITY[6]);
751
   syssnd_free (WAV_ENTITY[7]);
752
   syssnd_free (WAV_ENTITY[8]);
9 pmbaty 753
 
754
   // Pierre-Marie Baty -- addition: save high scores
755
   {
756
      char hiscorefile_fullpathname[1024];
757
      size_t i, j;
758
      FILE *fp;
13 pmbaty 759
      sprintf_s (hiscorefile_fullpathname, 1024, "%s/hiscores.txt", sys_getdatapath ());
9 pmbaty 760
      fopen_s (&fp, hiscorefile_fullpathname, "wb");
761
      if (fp != NULL)
762
      {
763
         for (i = 0; i < sizeof (game_hscores) / sizeof (game_hscores[0]); i++)
764
         {
765
            fprintf (fp, "%d\t", game_hscores[i].score);
766
            for (j = 0; j < sizeof (game_hscores[i].name); j++)
767
               fputc ((game_hscores[i].name[j] != '@' ? game_hscores[i].name[j] : ' '), fp);
768
            fputc ('\n', fp);
769
         }
770
         fclose (fp);
771
      }
772
   }
1 pmbaty 773
}