Subversion Repositories Games.Rick Dangerous

Rev

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