rnd-20001205-1-src
[rocksndiamonds.git] / src / tape.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  tape.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tape.h"
17 #include "game.h"
18 #include "tools.h"
19 #include "files.h"
20 #include "network.h"
21
22 /* tape button identifiers */
23 #define TAPE_CTRL_ID_EJECT              0
24 #define TAPE_CTRL_ID_STOP               1
25 #define TAPE_CTRL_ID_PAUSE              2
26 #define TAPE_CTRL_ID_RECORD             3
27 #define TAPE_CTRL_ID_PLAY               4
28
29 #define NUM_TAPE_BUTTONS                5
30
31 /* forward declaration for internal use */
32 static void HandleTapeButtons(struct GadgetInfo *);
33
34 static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
35
36
37 /* ========================================================================= */
38 /* video tape definitions                                                    */
39 /* ========================================================================= */
40
41 /* some positions in the video tape control window */
42 #define VIDEO_DATE_LABEL_XPOS   (VIDEO_DISPLAY1_XPOS)
43 #define VIDEO_DATE_LABEL_YPOS   (VIDEO_DISPLAY1_YPOS)
44 #define VIDEO_DATE_LABEL_XSIZE  (VIDEO_DISPLAY_XSIZE)
45 #define VIDEO_DATE_LABEL_YSIZE  (VIDEO_DISPLAY_YSIZE)
46 #define VIDEO_DATE_XPOS         (VIDEO_DISPLAY1_XPOS + 1)
47 #define VIDEO_DATE_YPOS         (VIDEO_DISPLAY1_YPOS + 14)
48 #define VIDEO_DATE_XSIZE        (VIDEO_DISPLAY_XSIZE)
49 #define VIDEO_DATE_YSIZE        16
50 #define VIDEO_REC_LABEL_XPOS    (VIDEO_DISPLAY2_XPOS)
51 #define VIDEO_REC_LABEL_YPOS    (VIDEO_DISPLAY2_YPOS)
52 #define VIDEO_REC_LABEL_XSIZE   20
53 #define VIDEO_REC_LABEL_YSIZE   12
54 #define VIDEO_REC_SYMBOL_XPOS   (VIDEO_DISPLAY2_XPOS + 20)
55 #define VIDEO_REC_SYMBOL_YPOS   (VIDEO_DISPLAY2_YPOS)
56 #define VIDEO_REC_SYMBOL_XSIZE  16
57 #define VIDEO_REC_SYMBOL_YSIZE  16
58 #define VIDEO_PLAY_LABEL_XPOS   (VIDEO_DISPLAY2_XPOS + 65)
59 #define VIDEO_PLAY_LABEL_YPOS   (VIDEO_DISPLAY2_YPOS)
60 #define VIDEO_PLAY_LABEL_XSIZE  22
61 #define VIDEO_PLAY_LABEL_YSIZE  12
62 #define VIDEO_PLAY_SYMBOL_XPOS  (VIDEO_DISPLAY2_XPOS + 52)
63 #define VIDEO_PLAY_SYMBOL_YPOS  (VIDEO_DISPLAY2_YPOS)
64 #define VIDEO_PLAY_SYMBOL_XSIZE 11
65 #define VIDEO_PLAY_SYMBOL_YSIZE 13
66 #define VIDEO_PAUSE_LABEL_XPOS  (VIDEO_DISPLAY2_XPOS)
67 #define VIDEO_PAUSE_LABEL_YPOS  (VIDEO_DISPLAY2_YPOS + 20)
68 #define VIDEO_PAUSE_LABEL_XSIZE 35
69 #define VIDEO_PAUSE_LABEL_YSIZE 8
70 #define VIDEO_PAUSE_SYMBOL_XPOS (VIDEO_DISPLAY2_XPOS + 35)
71 #define VIDEO_PAUSE_SYMBOL_YPOS (VIDEO_DISPLAY2_YPOS)
72 #define VIDEO_PAUSE_SYMBOL_XSIZE 17
73 #define VIDEO_PAUSE_SYMBOL_YSIZE 13
74 #define VIDEO_TIME_XPOS         (VIDEO_DISPLAY2_XPOS + 38)
75 #define VIDEO_TIME_YPOS         (VIDEO_DISPLAY2_YPOS + 14)
76 #define VIDEO_TIME_XSIZE        50
77 #define VIDEO_TIME_YSIZE        16
78
79 /* special */
80 #define VIDEO_PBEND_LABEL_XPOS  6
81 #define VIDEO_PBEND_LABEL_YPOS  220
82 #define VIDEO_PBEND_LABEL_XSIZE 35
83 #define VIDEO_PBEND_LABEL_YSIZE 30
84
85 #define VIDEO_STATE_OFF         (VIDEO_STATE_PLAY_OFF   |       \
86                                  VIDEO_STATE_REC_OFF    |       \
87                                  VIDEO_STATE_PAUSE_OFF  |       \
88                                  VIDEO_STATE_FFWD_OFF   |       \
89                                  VIDEO_STATE_PBEND_OFF  |       \
90                                  VIDEO_STATE_DATE_OFF   |       \
91                                  VIDEO_STATE_TIME_OFF)
92 #define VIDEO_PRESS_OFF         (VIDEO_PRESS_PLAY_OFF   |       \
93                                  VIDEO_PRESS_REC_OFF    |       \
94                                  VIDEO_PRESS_PAUSE_OFF  |       \
95                                  VIDEO_PRESS_STOP_OFF   |       \
96                                  VIDEO_PRESS_EJECT_OFF)
97 #define VIDEO_ALL_OFF           (VIDEO_STATE_OFF | VIDEO_PRESS_OFF)
98
99 #define VIDEO_STATE_ON          (VIDEO_STATE_PLAY_ON    |       \
100                                  VIDEO_STATE_REC_ON     |       \
101                                  VIDEO_STATE_PAUSE_ON   |       \
102                                  VIDEO_STATE_FFWD_ON    |       \
103                                  VIDEO_STATE_PBEND_ON   |       \
104                                  VIDEO_STATE_DATE_ON    |       \
105                                  VIDEO_STATE_TIME_ON)
106 #define VIDEO_PRESS_ON          (VIDEO_PRESS_PLAY_ON    |       \
107                                  VIDEO_PRESS_REC_ON     |       \
108                                  VIDEO_PRESS_PAUSE_ON   |       \
109                                  VIDEO_PRESS_STOP_ON    |       \
110                                  VIDEO_PRESS_EJECT_ON)
111 #define VIDEO_ALL_ON            (VIDEO_STATE_ON | VIDEO_PRESS_ON)
112
113 #define VIDEO_STATE             (VIDEO_STATE_ON | VIDEO_STATE_OFF)
114 #define VIDEO_PRESS             (VIDEO_PRESS_ON | VIDEO_PRESS_OFF)
115 #define VIDEO_ALL               (VIDEO_ALL_ON | VIDEO_ALL_OFF)
116
117
118 /* ========================================================================= */
119 /* video display functions                                                   */
120 /* ========================================================================= */
121
122 void DrawVideoDisplay(unsigned long state, unsigned long value)
123 {
124   int i;
125   int part_label = 0, part_symbol = 1;
126   int xpos = 0, ypos = 1, xsize = 2, ysize = 3;
127   static char *monatsname[12] =
128   {
129     "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
130     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
131   };
132   static int video_pos[5][2][4] =
133   {
134     {{ VIDEO_PLAY_LABEL_XPOS, VIDEO_PLAY_LABEL_YPOS,
135        VIDEO_PLAY_LABEL_XSIZE,VIDEO_PLAY_LABEL_YSIZE },
136      { VIDEO_PLAY_SYMBOL_XPOS, VIDEO_PLAY_SYMBOL_YPOS,
137        VIDEO_PLAY_SYMBOL_XSIZE,VIDEO_PLAY_SYMBOL_YSIZE }},
138
139     {{ VIDEO_REC_LABEL_XPOS, VIDEO_REC_LABEL_YPOS,
140        VIDEO_REC_LABEL_XSIZE,VIDEO_REC_LABEL_YSIZE },
141      { VIDEO_REC_SYMBOL_XPOS, VIDEO_REC_SYMBOL_YPOS,
142        VIDEO_REC_SYMBOL_XSIZE,VIDEO_REC_SYMBOL_YSIZE }},
143
144     {{ VIDEO_PAUSE_LABEL_XPOS, VIDEO_PAUSE_LABEL_YPOS,
145        VIDEO_PAUSE_LABEL_XSIZE,VIDEO_PAUSE_LABEL_YSIZE },
146      { VIDEO_PAUSE_SYMBOL_XPOS, VIDEO_PAUSE_SYMBOL_YPOS,
147        VIDEO_PAUSE_SYMBOL_XSIZE,VIDEO_PAUSE_SYMBOL_YSIZE }},
148
149     {{ VIDEO_DATE_LABEL_XPOS, VIDEO_DATE_LABEL_YPOS,
150        VIDEO_DATE_LABEL_XSIZE,VIDEO_DATE_LABEL_YSIZE },
151      { VIDEO_DATE_XPOS, VIDEO_DATE_YPOS,
152        VIDEO_DATE_XSIZE,VIDEO_DATE_YSIZE }},
153
154     {{ 0,0,
155        0,0 },
156      { VIDEO_TIME_XPOS, VIDEO_TIME_YPOS,
157        VIDEO_TIME_XSIZE,VIDEO_TIME_YSIZE }}
158   };
159
160   if (state & VIDEO_STATE_PBEND_OFF)
161   {
162     int cx = DOOR_GFX_PAGEX3, cy = DOOR_GFX_PAGEY2;
163
164     BlitBitmap(pix[PIX_DOOR], drawto,
165                cx + VIDEO_REC_LABEL_XPOS,
166                cy + VIDEO_REC_LABEL_YPOS,
167                VIDEO_PBEND_LABEL_XSIZE,
168                VIDEO_PBEND_LABEL_YSIZE,
169                VX + VIDEO_REC_LABEL_XPOS,
170                VY + VIDEO_REC_LABEL_YPOS);
171   }
172
173   for(i=0;i<10;i++)
174   {
175     if (state & (1<<i))
176     {
177       int pos = i/2, cx, cy = DOOR_GFX_PAGEY2;
178
179       if (i%2)                  /* i ungerade => STATE_ON / PRESS_OFF */
180         cx = DOOR_GFX_PAGEX4;
181       else
182         cx = DOOR_GFX_PAGEX3;   /* i gerade => STATE_OFF / PRESS_ON */
183
184       if (video_pos[pos][part_label][0] && value != VIDEO_DISPLAY_SYMBOL_ONLY)
185         BlitBitmap(pix[PIX_DOOR], drawto,
186                    cx + video_pos[pos][part_label][xpos],
187                    cy + video_pos[pos][part_label][ypos],
188                    video_pos[pos][part_label][xsize],
189                    video_pos[pos][part_label][ysize],
190                    VX + video_pos[pos][part_label][xpos],
191                    VY + video_pos[pos][part_label][ypos]);
192       if (video_pos[pos][part_symbol][0] && value != VIDEO_DISPLAY_LABEL_ONLY)
193         BlitBitmap(pix[PIX_DOOR], drawto,
194                    cx + video_pos[pos][part_symbol][xpos],
195                    cy + video_pos[pos][part_symbol][ypos],
196                    video_pos[pos][part_symbol][xsize],
197                    video_pos[pos][part_symbol][ysize],
198                    VX + video_pos[pos][part_symbol][xpos],
199                    VY + video_pos[pos][part_symbol][ypos]);
200     }
201   }
202
203   if (state & VIDEO_STATE_FFWD_ON)
204   {
205     int cx = DOOR_GFX_PAGEX4, cy = DOOR_GFX_PAGEY2;
206
207     BlitBitmap(pix[PIX_DOOR], drawto,
208                cx + VIDEO_PLAY_SYMBOL_XPOS,
209                cy + VIDEO_PLAY_SYMBOL_YPOS,
210                VIDEO_PLAY_SYMBOL_XSIZE - 2,
211                VIDEO_PLAY_SYMBOL_YSIZE,
212                VX + VIDEO_PLAY_SYMBOL_XPOS - 9,
213                VY + VIDEO_PLAY_SYMBOL_YPOS);
214   }
215
216   if (state & VIDEO_STATE_PBEND_ON)
217   {
218     int cx = DOOR_GFX_PAGEX6, cy = DOOR_GFX_PAGEY1;
219
220     BlitBitmap(pix[PIX_DOOR], drawto,
221                cx + VIDEO_PBEND_LABEL_XPOS,
222                cy + VIDEO_PBEND_LABEL_YPOS,
223                VIDEO_PBEND_LABEL_XSIZE,
224                VIDEO_PBEND_LABEL_YSIZE,
225                VX + VIDEO_REC_LABEL_XPOS,
226                VY + VIDEO_REC_LABEL_YPOS);
227   }
228
229   if (state & VIDEO_STATE_DATE_ON)
230   {
231     int tag = value % 100;
232     int monat = (value/100) % 100;
233     int jahr = (value/10000);
234
235     DrawText(VX+VIDEO_DATE_XPOS,VY+VIDEO_DATE_YPOS,
236              int2str(tag,2),FS_SMALL,FC_SPECIAL1);
237     DrawText(VX+VIDEO_DATE_XPOS+27,VY+VIDEO_DATE_YPOS,
238              monatsname[monat],FS_SMALL,FC_SPECIAL1);
239     DrawText(VX+VIDEO_DATE_XPOS+64,VY+VIDEO_DATE_YPOS,
240              int2str(jahr,2),FS_SMALL,FC_SPECIAL1);
241   }
242
243   if (state & VIDEO_STATE_TIME_ON)
244   {
245     int min = value / 60;
246     int sec = value % 60;
247
248     DrawText(VX+VIDEO_TIME_XPOS,VY+VIDEO_TIME_YPOS,
249              int2str(min,2),FS_SMALL,FC_SPECIAL1);
250     DrawText(VX+VIDEO_TIME_XPOS+27,VY+VIDEO_TIME_YPOS,
251              int2str(sec,2),FS_SMALL,FC_SPECIAL1);
252   }
253
254   if (state & VIDEO_STATE_DATE)
255     redraw_mask |= REDRAW_VIDEO_1;
256   if ((state & ~VIDEO_STATE_DATE) & VIDEO_STATE)
257     redraw_mask |= REDRAW_VIDEO_2;
258   if (state & VIDEO_PRESS)
259     redraw_mask |= REDRAW_VIDEO_3;
260 }
261
262 void DrawCompleteVideoDisplay()
263 {
264   BlitBitmap(pix[PIX_DOOR], drawto, DOOR_GFX_PAGEX3, DOOR_GFX_PAGEY2,
265              gfx.vxsize, gfx.vysize, gfx.vx, gfx.vy);
266   BlitBitmap(pix[PIX_DOOR], drawto,
267              DOOR_GFX_PAGEX4 + VIDEO_CONTROL_XPOS,
268              DOOR_GFX_PAGEY2 + VIDEO_CONTROL_YPOS,
269              VIDEO_CONTROL_XSIZE, VIDEO_CONTROL_YSIZE,
270              gfx.vx + VIDEO_CONTROL_XPOS, gfx.vy + VIDEO_CONTROL_YPOS);
271
272   DrawVideoDisplay(VIDEO_ALL_OFF, 0);
273   if (tape.date && tape.length)
274   {
275     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
276     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
277   }
278
279   BlitBitmap(drawto, pix[PIX_DB_DOOR], gfx.vx, gfx.vy, gfx.vxsize, gfx.vysize,
280              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
281 }
282
283
284 /* ========================================================================= */
285 /* tape control functions                                                    */
286 /* ========================================================================= */
287
288 void TapeStartRecording()
289 {
290   time_t zeit1 = time(NULL);
291   struct tm *zeit2 = localtime(&zeit1);
292   int i;
293
294   if (!TAPE_IS_STOPPED(tape))
295     TapeStop();
296
297   tape.level_nr = level_nr;
298   tape.length = 0;
299   tape.counter = 0;
300   tape.pos[tape.counter].delay = 0;
301   tape.recording = TRUE;
302   tape.playing = FALSE;
303   tape.pausing = FALSE;
304   tape.changed = TRUE;
305   tape.date = 10000*(zeit2->tm_year%100) + 100*zeit2->tm_mon + zeit2->tm_mday;
306   tape.random_seed = InitRND(NEW_RANDOMIZE);
307
308   for(i=0; i<MAX_PLAYERS; i++)
309     tape.player_participates[i] = FALSE;
310
311   DrawVideoDisplay(VIDEO_STATE_REC_ON, 0);
312   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
313   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
314 }
315
316 void TapeStopRecording()
317 {
318   int i;
319
320   if (!tape.recording)
321     return;
322
323   for(i=0; i<MAX_PLAYERS; i++)
324     tape.pos[tape.counter].action[i] = 0;
325
326   tape.counter++;
327   tape.length = tape.counter;
328   tape.length_seconds = GetTapeLength();
329   tape.recording = FALSE;
330   tape.pausing = FALSE;
331   DrawVideoDisplay(VIDEO_STATE_REC_OFF, 0);
332 }
333
334 void TapeRecordAction(byte joy[MAX_PLAYERS])
335 {
336   int i;
337
338   if (!tape.recording || tape.pausing)
339     return;
340
341   if (tape.counter >= MAX_TAPELEN-1)
342   {
343     TapeStopRecording();
344     return;
345   }
346
347   for(i=0; i<MAX_PLAYERS; i++)
348     tape.pos[tape.counter].action[i] = joy[i];
349
350   tape.counter++;
351   tape.pos[tape.counter].delay = 0;
352 }
353
354 void TapeRecordDelay()
355 {
356   int i;
357
358   if (!tape.recording || tape.pausing)
359     return;
360
361   if (tape.counter >= MAX_TAPELEN)
362   {
363     TapeStopRecording();
364     return;
365   }
366
367   tape.pos[tape.counter].delay++;
368
369   if (tape.pos[tape.counter].delay >= 255)
370   {
371     for(i=0; i<MAX_PLAYERS; i++)
372       tape.pos[tape.counter].action[i] = 0;
373
374     tape.counter++;
375     tape.pos[tape.counter].delay = 0;
376   }
377 }
378
379 void TapeTogglePause()
380 {
381   unsigned long state;
382
383   if (!tape.recording && !tape.playing)
384     return;
385
386   tape.pausing = !tape.pausing;
387   tape.fast_forward = FALSE;
388   tape.pause_before_death = FALSE;
389
390   state = (tape.pausing ? VIDEO_STATE_PAUSE_ON : VIDEO_STATE_PAUSE_OFF);
391   if (tape.playing)
392     state |= VIDEO_STATE_PBEND_OFF;
393
394   DrawVideoDisplay(state, 0);
395 }
396
397 void TapeStartPlaying()
398 {
399   if (TAPE_IS_EMPTY(tape))
400     return;
401
402   if (!TAPE_IS_STOPPED(tape))
403     TapeStop();
404
405   tape.counter = 0;
406   tape.delay_played = 0;
407   tape.pause_before_death = FALSE;
408   tape.recording = FALSE;
409   tape.playing = TRUE;
410   tape.pausing = FALSE;
411   tape.fast_forward = FALSE;
412   InitRND(tape.random_seed);
413
414   DrawVideoDisplay(VIDEO_STATE_PLAY_ON, 0);
415   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
416   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
417 }
418
419 void TapeStopPlaying()
420 {
421   if (!tape.playing)
422     return;
423
424   tape.playing = FALSE;
425   tape.pausing = FALSE;
426   DrawVideoDisplay(VIDEO_STATE_PLAY_OFF, 0);
427 }
428
429 byte *TapePlayAction()
430 {
431   static byte joy[MAX_PLAYERS];
432   int i;
433
434   if (!tape.playing || tape.pausing)
435     return(NULL);
436
437   if (tape.counter >= tape.length)
438   {
439     TapeStop();
440     return(NULL);
441   }
442
443   if (tape.delay_played == tape.pos[tape.counter].delay)
444   {
445     tape.delay_played = 0;
446     tape.counter++;
447
448     for(i=0; i<MAX_PLAYERS; i++)
449       joy[i] = tape.pos[tape.counter-1].action[i];
450   }
451   else
452   {
453     for(i=0; i<MAX_PLAYERS; i++)
454       joy[i] = 0;
455   }
456
457   return(joy);
458 }
459
460 boolean TapePlayDelay()
461 {
462   if (!tape.playing || tape.pausing)
463     return(FALSE);
464
465   if (tape.pause_before_death)  /* STOP 10s BEFORE PLAYER GETS KILLED... */
466   {
467     if (!(FrameCounter % 20))
468     {
469       if ((FrameCounter / 20) % 2)
470         DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
471       else
472         DrawVideoDisplay(VIDEO_STATE_PBEND_OFF, VIDEO_DISPLAY_LABEL_ONLY);
473     }
474
475     if (TimePlayed > tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
476     {
477       TapeTogglePause();
478       return(FALSE);
479     }
480   }
481
482   if (tape.counter >= tape.length)
483   {
484     TapeStop();
485     return(TRUE);
486   }
487
488   if (tape.delay_played < tape.pos[tape.counter].delay)
489   {
490     tape.delay_played++;
491     return(TRUE);
492   }
493   else
494     return(FALSE);
495 }
496
497 void TapeStop()
498 {
499   TapeStopRecording();
500   TapeStopPlaying();
501
502   DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
503   if (tape.date && tape.length)
504   {
505     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
506     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
507   }
508 }
509
510 void TapeErase()
511 {
512   tape.length = 0;
513 }
514
515 unsigned int GetTapeLength()
516 {
517   unsigned int tape_length = 0;
518   int i;
519
520   if (TAPE_IS_EMPTY(tape))
521     return(0);
522
523   for(i=0;i<tape.length;i++)
524     tape_length += tape.pos[i].delay;
525
526   return(tape_length * GAME_FRAME_DELAY / 1000);
527 }
528
529 /* ---------- new tape button stuff ---------------------------------------- */
530
531 /* graphic position values for tape buttons */
532 #define TAPE_BUTTON_XSIZE       18
533 #define TAPE_BUTTON_YSIZE       18
534 #define TAPE_BUTTON_XPOS        5
535 #define TAPE_BUTTON_YPOS        77
536
537 #define TAPE_BUTTON_EJECT_XPOS  (TAPE_BUTTON_XPOS + 0 * TAPE_BUTTON_XSIZE)
538 #define TAPE_BUTTON_STOP_XPOS   (TAPE_BUTTON_XPOS + 1 * TAPE_BUTTON_XSIZE)
539 #define TAPE_BUTTON_PAUSE_XPOS  (TAPE_BUTTON_XPOS + 2 * TAPE_BUTTON_XSIZE)
540 #define TAPE_BUTTON_RECORD_XPOS (TAPE_BUTTON_XPOS + 3 * TAPE_BUTTON_XSIZE)
541 #define TAPE_BUTTON_PLAY_XPOS   (TAPE_BUTTON_XPOS + 4 * TAPE_BUTTON_XSIZE)
542
543 static struct
544 {
545   int x, y;
546   int gadget_id;
547   char *infotext;
548 } tapebutton_info[NUM_TAPE_BUTTONS] =
549 {
550   {
551     TAPE_BUTTON_EJECT_XPOS,     TAPE_BUTTON_YPOS,
552     TAPE_CTRL_ID_EJECT,
553     "eject tape"
554   },
555   {
556     TAPE_BUTTON_STOP_XPOS,      TAPE_BUTTON_YPOS,
557     TAPE_CTRL_ID_STOP,
558     "stop tape"
559   },
560   {
561     TAPE_BUTTON_PAUSE_XPOS,     TAPE_BUTTON_YPOS,
562     TAPE_CTRL_ID_PAUSE,
563     "pause tape"
564   },
565   {
566     TAPE_BUTTON_RECORD_XPOS,    TAPE_BUTTON_YPOS,
567     TAPE_CTRL_ID_RECORD,
568     "record tape"
569   },
570   {
571     TAPE_BUTTON_PLAY_XPOS,      TAPE_BUTTON_YPOS,
572     TAPE_CTRL_ID_PLAY,
573     "play tape"
574   }
575 };
576
577 void CreateTapeButtons()
578 {
579   int i;
580
581   for (i=0; i<NUM_TAPE_BUTTONS; i++)
582   {
583     Bitmap *gd_bitmap = pix[PIX_DOOR];
584     struct GadgetInfo *gi;
585     int gd_xoffset, gd_yoffset;
586     int gd_x1, gd_x2, gd_y;
587     int id = i;
588
589     gd_xoffset = tapebutton_info[i].x;
590     gd_yoffset = tapebutton_info[i].y;
591     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
592     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
593     gd_y  = DOOR_GFX_PAGEY2 + gd_yoffset;
594
595     gi = CreateGadget(GDI_CUSTOM_ID, id,
596                       GDI_INFO_TEXT, tapebutton_info[i].infotext,
597                       GDI_X, VX + gd_xoffset,
598                       GDI_Y, VY + gd_yoffset,
599                       GDI_WIDTH, TAPE_BUTTON_XSIZE,
600                       GDI_HEIGHT, TAPE_BUTTON_YSIZE,
601                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
602                       GDI_STATE, GD_BUTTON_UNPRESSED,
603                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
604                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
605                       GDI_EVENT_MASK, GD_EVENT_RELEASED,
606                       GDI_CALLBACK_ACTION, HandleTapeButtons,
607                       GDI_END);
608
609     if (gi == NULL)
610       Error(ERR_EXIT, "cannot create gadget");
611
612     tape_gadget[id] = gi;
613   }
614 }
615
616 void MapTapeButtons()
617 {
618   int i;
619
620   for (i=0; i<NUM_TAPE_BUTTONS; i++)
621     MapGadget(tape_gadget[i]);
622 }
623
624 void UnmapTapeButtons()
625 {
626   int i;
627
628   for (i=0; i<NUM_TAPE_BUTTONS; i++)
629     UnmapGadget(tape_gadget[i]);
630 }
631
632 static void HandleTapeButtons(struct GadgetInfo *gi)
633 {
634   int id = gi->custom_id;
635
636   if (game_status != MAINMENU && game_status != PLAYING)
637     return;
638
639   switch (id)
640   {
641     case TAPE_CTRL_ID_EJECT:
642       TapeStop();
643       if (TAPE_IS_EMPTY(tape))
644       {
645         LoadTape(level_nr);
646         if (TAPE_IS_EMPTY(tape))
647           Request("No tape for this level !", REQ_CONFIRM);
648       }
649       else
650       {
651         if (tape.changed)
652           SaveTape(tape.level_nr);
653         TapeErase();
654       }
655       DrawCompleteVideoDisplay();
656       break;
657
658     case TAPE_CTRL_ID_STOP:
659       TapeStop();
660       break;
661
662     case TAPE_CTRL_ID_PAUSE:
663       TapeTogglePause();
664       break;
665
666     case TAPE_CTRL_ID_RECORD:
667       if (TAPE_IS_STOPPED(tape))
668       {
669         TapeStartRecording();
670
671 #if defined(PLATFORM_UNIX)
672         if (options.network)
673           SendToServer_StartPlaying();
674         else
675 #endif
676         {
677           game_status = PLAYING;
678           InitGame();
679         }
680       }
681       else if (tape.pausing)
682       {
683         if (tape.playing)       /* PLAYING -> PAUSING -> RECORDING */
684         {
685           tape.pos[tape.counter].delay = tape.delay_played;
686           tape.playing = FALSE;
687           tape.recording = TRUE;
688           tape.changed = TRUE;
689
690           DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON,0);
691         }
692         else
693           TapeTogglePause();
694       }
695       break;
696
697     case TAPE_CTRL_ID_PLAY:
698       if (TAPE_IS_EMPTY(tape))
699         break;
700
701       if (TAPE_IS_STOPPED(tape))
702       {
703         TapeStartPlaying();
704
705         game_status = PLAYING;
706         InitGame();
707       }
708       else if (tape.playing)
709       {
710         if (tape.pausing)                       /* PAUSE -> PLAY */
711           TapeTogglePause();
712         else if (!tape.fast_forward)            /* PLAY -> FAST FORWARD PLAY */
713         {
714           tape.fast_forward = TRUE;
715           DrawVideoDisplay(VIDEO_STATE_FFWD_ON, 0);
716         }
717         else if (!tape.pause_before_death)      /* FFWD PLAY -> + AUTO PAUSE */
718         {
719           tape.pause_before_death = TRUE;
720           DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
721         }
722         else                                    /* -> NORMAL PLAY */
723         {
724           tape.fast_forward = FALSE;
725           tape.pause_before_death = FALSE;
726           DrawVideoDisplay(VIDEO_STATE_FFWD_OFF | VIDEO_STATE_PBEND_OFF, 0);
727         }
728       }
729       break;
730
731     default:
732       break;
733   }
734 }