Subversion Repositories Games.Rick Dangerous

Rev

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