major cleanup of preprocessor hell
[rocksndiamonds.git] / src / tape.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // tape.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "tape.h"
15 #include "init.h"
16 #include "game.h"
17 #include "tools.h"
18 #include "files.h"
19 #include "network.h"
20 #include "cartoons.h"
21
22 /* tape button identifiers */
23 #define TAPE_CTRL_ID_EJECT                      0
24 #define TAPE_CTRL_ID_EXTRA                      1
25 #define TAPE_CTRL_ID_STOP                       2
26 #define TAPE_CTRL_ID_PAUSE                      3
27 #define TAPE_CTRL_ID_RECORD                     4
28 #define TAPE_CTRL_ID_PLAY                       5
29
30 #define NUM_TAPE_BUTTONS                        6
31
32 /* values for tape handling */
33 #define TAPE_PAUSE_SECONDS_BEFORE_DEATH         5
34
35 /* forward declaration for internal use */
36 static void HandleTapeButtons(struct GadgetInfo *);
37 static void TapeStopWarpForward();
38
39 static struct GadgetInfo *tape_gadget[NUM_TAPE_BUTTONS];
40
41
42 /* ========================================================================= */
43 /* video tape definitions                                                    */
44 /* ========================================================================= */
45
46 /* some positions in the video tape control window */
47 #define VIDEO_DATE_LABEL_XPOS           (VIDEO_DISPLAY1_XPOS)
48 #define VIDEO_DATE_LABEL_YPOS           (VIDEO_DISPLAY1_YPOS)
49 #define VIDEO_DATE_LABEL_XSIZE          (VIDEO_DISPLAY_XSIZE)
50 #define VIDEO_DATE_LABEL_YSIZE          (VIDEO_DISPLAY_YSIZE)
51 #define VIDEO_DATE_XPOS                 (VIDEO_DISPLAY1_XPOS + 2)
52 #define VIDEO_DATE_YPOS                 (VIDEO_DISPLAY1_YPOS + 14)
53 #define VIDEO_DATE_XSIZE                (VIDEO_DISPLAY_XSIZE)
54 #define VIDEO_DATE_YSIZE                16
55 #define VIDEO_REC_LABEL_XPOS            (VIDEO_DISPLAY2_XPOS)
56 #define VIDEO_REC_LABEL_YPOS            (VIDEO_DISPLAY2_YPOS)
57 #define VIDEO_REC_LABEL_XSIZE           20
58 #define VIDEO_REC_LABEL_YSIZE           12
59 #define VIDEO_REC_SYMBOL_XPOS           (VIDEO_DISPLAY2_XPOS + 20)
60 #define VIDEO_REC_SYMBOL_YPOS           (VIDEO_DISPLAY2_YPOS)
61 #define VIDEO_REC_SYMBOL_XSIZE          16
62 #define VIDEO_REC_SYMBOL_YSIZE          16
63 #define VIDEO_PLAY_LABEL_XPOS           (VIDEO_DISPLAY2_XPOS + 65)
64 #define VIDEO_PLAY_LABEL_YPOS           (VIDEO_DISPLAY2_YPOS)
65 #define VIDEO_PLAY_LABEL_XSIZE          22
66 #define VIDEO_PLAY_LABEL_YSIZE          12
67 #define VIDEO_PLAY_SYMBOL_XPOS          (VIDEO_DISPLAY2_XPOS + 52)
68 #define VIDEO_PLAY_SYMBOL_YPOS          (VIDEO_DISPLAY2_YPOS)
69 #define VIDEO_PLAY_SYMBOL_XSIZE         11
70 #define VIDEO_PLAY_SYMBOL_YSIZE         13
71 #define VIDEO_PAUSE_LABEL_XPOS          (VIDEO_DISPLAY2_XPOS)
72 #define VIDEO_PAUSE_LABEL_YPOS          (VIDEO_DISPLAY2_YPOS + 20)
73 #define VIDEO_PAUSE_LABEL_XSIZE         35
74 #define VIDEO_PAUSE_LABEL_YSIZE         8
75 #define VIDEO_PAUSE_SYMBOL_XPOS         (VIDEO_DISPLAY2_XPOS + 35)
76 #define VIDEO_PAUSE_SYMBOL_YPOS         (VIDEO_DISPLAY2_YPOS)
77 #define VIDEO_PAUSE_SYMBOL_XSIZE        17
78 #define VIDEO_PAUSE_SYMBOL_YSIZE        13
79 #define VIDEO_TIME_XPOS                 (VIDEO_DISPLAY2_XPOS + 39)
80 #define VIDEO_TIME_YPOS                 (VIDEO_DISPLAY2_YPOS + 14)
81 #define VIDEO_TIME_XSIZE                50
82 #define VIDEO_TIME_YSIZE                16
83
84 /* some default values for tape labels and symbols */
85 #define VIDEO_LABEL_XPOS                5
86 #define VIDEO_LABEL_YPOS                42
87 #define VIDEO_LABEL_XSIZE               40
88 #define VIDEO_LABEL_YSIZE               28
89 #define VIDEO_SYMBOL_XPOS               39
90 #define VIDEO_SYMBOL_YPOS               42
91 #define VIDEO_SYMBOL_XSIZE              56
92 #define VIDEO_SYMBOL_YSIZE              13
93
94 /* values for certain tape labels and symbols */
95 #define VIDEO_FFWD_LABEL_XPOS           VIDEO_LABEL_XPOS
96 #define VIDEO_FFWD_LABEL_YPOS           193
97 #define VIDEO_FFWD_LABEL_XSIZE          VIDEO_LABEL_XSIZE
98 #define VIDEO_FFWD_LABEL_YSIZE          VIDEO_LABEL_YSIZE
99 #define VIDEO_FFWD_SYMBOL_XPOS          VIDEO_SYMBOL_XPOS
100 #define VIDEO_FFWD_SYMBOL_YPOS          193
101 #define VIDEO_FFWD_SYMBOL_XSIZE         27
102 #define VIDEO_FFWD_SYMBOL_YSIZE         VIDEO_SYMBOL_YSIZE
103
104 #define VIDEO_PBEND_LABEL_XPOS          VIDEO_LABEL_XPOS
105 #define VIDEO_PBEND_LABEL_YPOS          221
106 #define VIDEO_PBEND_LABEL_XSIZE         VIDEO_LABEL_XSIZE
107 #define VIDEO_PBEND_LABEL_YSIZE         VIDEO_LABEL_YSIZE
108 #define VIDEO_PBEND_SYMBOL_XPOS         VIDEO_SYMBOL_XPOS
109 #define VIDEO_PBEND_SYMBOL_YPOS         221
110 #define VIDEO_PBEND_SYMBOL_XSIZE        27
111 #define VIDEO_PBEND_SYMBOL_YSIZE        VIDEO_SYMBOL_YSIZE
112
113 #define VIDEO_WARP_LABEL_XPOS           VIDEO_LABEL_XPOS
114 #define VIDEO_WARP_LABEL_YPOS           165
115 #define VIDEO_WARP_LABEL_XSIZE          VIDEO_LABEL_XSIZE
116 #define VIDEO_WARP_LABEL_YSIZE          VIDEO_LABEL_YSIZE
117 #define VIDEO_WARP_SYMBOL_XPOS          VIDEO_SYMBOL_XPOS
118 #define VIDEO_WARP_SYMBOL_YPOS          165
119 #define VIDEO_WARP_SYMBOL_XSIZE         27
120 #define VIDEO_WARP_SYMBOL_YSIZE         VIDEO_SYMBOL_YSIZE
121 #define VIDEO_WARP2_SYMBOL_XPOS         VIDEO_SYMBOL_XPOS
122 #define VIDEO_WARP2_SYMBOL_YPOS         152
123 #define VIDEO_WARP2_SYMBOL_XSIZE        27
124 #define VIDEO_WARP2_SYMBOL_YSIZE        VIDEO_SYMBOL_YSIZE
125
126 #define VIDEO_1STEP_SYMBOL_XPOS         (VIDEO_SYMBOL_XPOS + 18)
127 #define VIDEO_1STEP_SYMBOL_YPOS         139
128 #define VIDEO_1STEP_SYMBOL_XSIZE        (VIDEO_SYMBOL_XSIZE - 18)
129 #define VIDEO_1STEP_SYMBOL_YSIZE        VIDEO_SYMBOL_YSIZE
130
131 #define PG_X3(x)                        (DOOR_GFX_PAGEX3 + (x))
132 #define PG_X4(x)                        (DOOR_GFX_PAGEX4 + (x))
133 #define PG_X5(x)                        (DOOR_GFX_PAGEX5 + (x))
134 #define PG_X6(x)                        (DOOR_GFX_PAGEX6 + (x))
135
136 #define PG_Y1(y)                        (DOOR_GFX_PAGEY1 + (y))
137 #define PG_Y2(y)                        (DOOR_GFX_PAGEY2 + (y))
138
139 #define VIDEO_INFO_OFF                  (VIDEO_STATE_DATE_OFF   |       \
140                                          VIDEO_STATE_TIME_OFF)
141 #define VIDEO_STATE_OFF                 (VIDEO_STATE_PLAY_OFF   |       \
142                                          VIDEO_STATE_REC_OFF    |       \
143                                          VIDEO_STATE_PAUSE_OFF  |       \
144                                          VIDEO_STATE_FFWD_OFF   |       \
145                                          VIDEO_STATE_PBEND_OFF  |       \
146                                          VIDEO_STATE_1STEP_OFF)
147 #define VIDEO_PRESS_OFF                 (VIDEO_PRESS_PLAY_OFF   |       \
148                                          VIDEO_PRESS_REC_OFF    |       \
149                                          VIDEO_PRESS_PAUSE_OFF  |       \
150                                          VIDEO_PRESS_STOP_OFF   |       \
151                                          VIDEO_PRESS_EJECT_OFF)
152 #define VIDEO_ALL_OFF                   (VIDEO_INFO_OFF         |       \
153                                          VIDEO_STATE_OFF        |       \
154                                          VIDEO_PRESS_OFF)
155
156 #define VIDEO_INFO_ON                   (VIDEO_STATE_DATE_ON    |       \
157                                          VIDEO_STATE_TIME_ON)
158 #define VIDEO_STATE_ON                  (VIDEO_STATE_PLAY_ON    |       \
159                                          VIDEO_STATE_REC_ON     |       \
160                                          VIDEO_STATE_PAUSE_ON   |       \
161                                          VIDEO_STATE_FFWD_ON    |       \
162                                          VIDEO_STATE_PBEND_ON   |       \
163                                          VIDEO_STATE_1STEP_ON)
164 #define VIDEO_PRESS_ON                  (VIDEO_PRESS_PLAY_ON    |       \
165                                          VIDEO_PRESS_REC_ON     |       \
166                                          VIDEO_PRESS_PAUSE_ON   |       \
167                                          VIDEO_PRESS_STOP_ON    |       \
168                                          VIDEO_PRESS_EJECT_ON)
169 #define VIDEO_ALL_ON                    (VIDEO_INFO_ON          |       \
170                                          VIDEO_STATE_ON         |       \
171                                          VIDEO_PRESS_ON)
172
173 #define VIDEO_INFO                      (VIDEO_INFO_ON | VIDEO_INFO_OFF)
174 #define VIDEO_STATE                     (VIDEO_STATE_ON | VIDEO_STATE_OFF)
175 #define VIDEO_PRESS                     (VIDEO_PRESS_ON | VIDEO_PRESS_OFF)
176 #define VIDEO_ALL                       (VIDEO_ALL_ON | VIDEO_ALL_OFF)
177
178 #define NUM_TAPE_FUNCTIONS              10
179 #define NUM_TAPE_FUNCTION_PARTS         2
180 #define NUM_TAPE_FUNCTION_STATES        2
181
182
183 /* ========================================================================= */
184 /* video display functions                                                   */
185 /* ========================================================================= */
186
187 void DrawVideoDisplay(unsigned int state, unsigned int value)
188 {
189   int i, j, k;
190   static char *monatsname[12] =
191   {
192     "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
193     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
194   };
195
196   static struct
197   {
198     int graphic;
199     struct Rect *pos;
200   }
201   video_pos[NUM_TAPE_FUNCTIONS][NUM_TAPE_FUNCTION_PARTS] =
202   {
203     {
204       { IMG_TAPE_LABEL_GFX_PLAY,                &tape.label.play        },
205       { IMG_TAPE_SYMBOL_GFX_PLAY,               &tape.symbol.play       },
206     },
207     {
208       { IMG_TAPE_LABEL_GFX_RECORD,              &tape.label.record      },
209       { IMG_TAPE_SYMBOL_GFX_RECORD,             &tape.symbol.record     },
210     },
211     {
212       { IMG_TAPE_LABEL_GFX_PAUSE,               &tape.label.pause       },
213       { IMG_TAPE_SYMBOL_GFX_PAUSE,              &tape.symbol.pause      },
214     },
215     {
216       { IMG_TAPE_LABEL_GFX_DATE,                &tape.label.date        },
217       { -1,                                     NULL                    },
218     },
219     {
220       { IMG_TAPE_LABEL_GFX_TIME,                &tape.label.time        },
221       { -1,                                     NULL                    },
222     },
223     {
224       { IMG_TAPE_LABEL_GFX_FAST_FORWARD,        &tape.label.fast_forward  },
225       { IMG_TAPE_SYMBOL_GFX_FAST_FORWARD,       &tape.symbol.fast_forward },
226     },
227     {
228       { IMG_TAPE_LABEL_GFX_PAUSE_BEFORE_END,    &tape.label.pause_before_end  },
229       { IMG_TAPE_SYMBOL_GFX_PAUSE_BEFORE_END,   &tape.symbol.pause_before_end },
230     },
231     {
232       { IMG_TAPE_LABEL_GFX_WARP_FORWARD_BLIND,  &tape.label.warp_forward_blind},
233       { IMG_TAPE_SYMBOL_GFX_WARP_FORWARD_BLIND, &tape.symbol.warp_forward_blind},
234     },
235     {
236       { IMG_TAPE_LABEL_GFX_WARP_FORWARD,        &tape.label.warp_forward  },
237       { IMG_TAPE_SYMBOL_GFX_WARP_FORWARD,       &tape.symbol.warp_forward },
238     },
239     {
240       { IMG_TAPE_LABEL_GFX_SINGLE_STEP,         &tape.label.single_step  },
241       { IMG_TAPE_SYMBOL_GFX_SINGLE_STEP,        &tape.symbol.single_step },
242     },
243   };
244
245   for (k = 0; k < NUM_TAPE_FUNCTION_STATES; k++)        /* on or off states */
246   {
247     for (i = 0; i < NUM_TAPE_FUNCTIONS; i++)            /* record, play, ... */
248     {
249       for (j = 0; j < NUM_TAPE_FUNCTION_PARTS; j++)     /* label or symbol */
250       {
251         if (video_pos[i][j].graphic == -1 ||
252             video_pos[i][j].pos->x < 0 ||
253             video_pos[i][j].pos->y < 0)
254           continue;
255
256         if (state & (1 << (i * 2 + k)))
257         {
258           struct GraphicInfo *gfx_bg = &graphic_info[IMG_BACKGROUND_TAPE];
259           struct GraphicInfo *gfx = &graphic_info[video_pos[i][j].graphic];
260           struct Rect *pos = video_pos[i][j].pos;
261           Bitmap *gd_bitmap;
262           int gd_x, gd_y;
263           int skip_value =
264             (j == 0 ? VIDEO_DISPLAY_SYMBOL_ONLY : VIDEO_DISPLAY_LABEL_ONLY);
265
266           if (value == skip_value)
267             continue;
268
269           if (k == 1)           /* on */
270           {
271             gd_bitmap = gfx->bitmap;
272             gd_x = gfx->src_x;
273             gd_y = gfx->src_y;
274           }
275           else                  /* off */
276           {
277             gd_bitmap = gfx_bg->bitmap;
278             gd_x = gfx_bg->src_x + pos->x;
279             gd_y = gfx_bg->src_y + pos->y;
280           }
281
282           /* some tape graphics may be undefined -- only draw if defined */
283           if (gd_bitmap != NULL)
284             BlitBitmap(gd_bitmap, drawto, gd_x, gd_y, gfx->width, gfx->height,
285                        VX + pos->x, VY + pos->y);
286         }
287       }
288     }
289   }
290
291   if (state & VIDEO_STATE_DATE_ON)
292   {
293     struct TextPosInfo *pos = &tape.text.date;
294     int tag = value % 100;
295     int monat = (value/100) % 100;
296     int jahr = (value/10000);
297
298     DrawText(VX + pos->x,      VY + pos->y, int2str(tag, 2),   pos->font);
299     DrawText(VX + pos->x + 27, VY + pos->y, monatsname[monat], pos->font);
300     DrawText(VX + pos->x + 64, VY + pos->y, int2str(jahr, 2),  pos->font);
301   }
302
303   if (state & VIDEO_STATE_TIME_ON)
304   {
305     struct TextPosInfo *pos = &tape.text.time;
306     int min = value / 60;
307     int sec = value % 60;
308
309     DrawText(VX + pos->x,      VY + pos->y, int2str(min, 2), pos->font);
310     DrawText(VX + pos->x + 27, VY + pos->y, int2str(sec, 2), pos->font);
311   }
312
313   redraw_mask |= REDRAW_DOOR_2;
314 }
315
316 void DrawCompleteVideoDisplay()
317 {
318   struct GraphicInfo *g_tape = &graphic_info[IMG_BACKGROUND_TAPE];
319
320   /* draw tape background */
321   BlitBitmap(g_tape->bitmap, drawto, g_tape->src_x, g_tape->src_y,
322              gfx.vxsize, gfx.vysize, gfx.vx, gfx.vy);
323
324   /* draw tape buttons (forced) */
325   UnmapTapeButtons();
326   MapTapeButtons();
327
328   DrawVideoDisplay(VIDEO_ALL_OFF, 0);
329
330   if (tape.recording)
331   {
332     DrawVideoDisplay(VIDEO_STATE_REC_ON, 0);
333     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
334     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
335
336     if (tape.pausing)
337       DrawVideoDisplay(VIDEO_STATE_PAUSE_ON, 0);
338   }
339   else if (tape.playing)
340   {
341     DrawVideoDisplay(VIDEO_STATE_PLAY_ON, 0);
342     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
343     DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
344
345     if (tape.pausing)
346       DrawVideoDisplay(VIDEO_STATE_PAUSE_ON, 0);
347   }
348   else if (tape.date && tape.length)
349   {
350     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
351     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
352   }
353
354   BlitBitmap(drawto, bitmap_db_door_2, gfx.vx, gfx.vy, gfx.vxsize, gfx.vysize,
355              0, 0);
356 }
357
358 void TapeDeactivateDisplayOn()
359 {
360   SetDrawDeactivationMask(REDRAW_FIELD);
361   audio.sound_deactivated = TRUE;
362 }
363
364 void TapeDeactivateDisplayOff(boolean redraw_display)
365 {
366   SetDrawDeactivationMask(REDRAW_NONE);
367   audio.sound_deactivated = FALSE;
368
369   if (redraw_display)
370   {
371     RedrawPlayfield(TRUE, 0,0,0,0);
372     DrawGameDoorValues();
373   }
374 }
375
376
377 /* ========================================================================= */
378 /* tape control functions                                                    */
379 /* ========================================================================= */
380
381 void TapeSetDateFromEpochSeconds(time_t epoch_seconds)
382 {
383   struct tm *lt = localtime(&epoch_seconds);
384
385   tape.date = 10000 * (lt->tm_year % 100) + 100 * lt->tm_mon + lt->tm_mday;
386 }
387
388 void TapeSetDateFromNow()
389 {
390   TapeSetDateFromEpochSeconds(time(NULL));
391 }
392
393 void TapeErase()
394 {
395   int i;
396
397   tape.counter = 0;
398   tape.length = 0;
399   tape.length_seconds = 0;
400
401   if (leveldir_current)
402     setString(&tape.level_identifier, leveldir_current->identifier);
403
404   tape.level_nr = level_nr;
405   tape.pos[tape.counter].delay = 0;
406   tape.changed = TRUE;
407
408   tape.random_seed = InitRND(level.random_seed);
409
410   tape.file_version = FILE_VERSION_ACTUAL;
411   tape.game_version = GAME_VERSION_ACTUAL;
412   tape.engine_version = level.game_version;
413
414   TapeSetDateFromNow();
415
416   for (i = 0; i < MAX_PLAYERS; i++)
417     tape.player_participates[i] = FALSE;
418
419   tape.centered_player_nr_next = -1;
420   tape.set_centered_player = FALSE;
421 }
422
423 static void TapeRewind()
424 {
425   tape.counter = 0;
426   tape.delay_played = 0;
427   tape.pause_before_death = FALSE;
428   tape.recording = FALSE;
429   tape.playing = FALSE;
430   tape.fast_forward = FALSE;
431   tape.warp_forward = FALSE;
432   tape.deactivate_display = FALSE;
433   tape.auto_play = (global.autoplay_leveldir != NULL);
434   tape.auto_play_level_solved = FALSE;
435   tape.quick_resume = FALSE;
436   tape.single_step = FALSE;
437
438   tape.centered_player_nr_next = -1;
439   tape.set_centered_player = FALSE;
440
441   InitRND(tape.random_seed);
442 }
443
444 static void TapeSetRandomSeed(int random_seed)
445 {
446   tape.random_seed = InitRND(random_seed);
447 }
448
449 void TapeStartRecording(int random_seed)
450 {
451   if (!TAPE_IS_STOPPED(tape))
452     TapeStop();
453
454   TapeErase();
455   TapeRewind();
456   TapeSetRandomSeed(random_seed);
457
458   tape.recording = TRUE;
459
460   DrawVideoDisplay(VIDEO_STATE_REC_ON, 0);
461   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
462   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
463
464   MapTapeWarpButton();
465
466   SetDrawDeactivationMask(REDRAW_NONE);
467   audio.sound_deactivated = FALSE;
468 }
469
470 static void TapeStartGameRecording()
471 {
472   TapeStartRecording(level.random_seed);
473
474 #if defined(NETWORK_AVALIABLE)
475   if (options.network)
476   {
477     SendToServer_StartPlaying();
478
479     return;
480   }
481 #endif
482
483   InitGame();
484 }
485
486 static void TapeAppendRecording()
487 {
488   if (!tape.playing || !tape.pausing)
489     return;
490
491   tape.pos[tape.counter].delay = tape.delay_played;
492   tape.playing = FALSE;
493   tape.recording = TRUE;
494   tape.changed = TRUE;
495
496   TapeSetDateFromNow();
497
498   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
499   DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON, 0);
500
501   UpdateAndDisplayGameControlValues();
502 }
503
504 void TapeHaltRecording()
505 {
506   if (!tape.recording)
507     return;
508
509   tape.counter++;
510   tape.pos[tape.counter].delay = 0;
511
512   tape.length = tape.counter;
513   tape.length_seconds = GetTapeLength();
514 }
515
516 void TapeStopRecording()
517 {
518   TapeHaltRecording();
519
520   tape.recording = FALSE;
521   tape.pausing = FALSE;
522
523   DrawVideoDisplay(VIDEO_STATE_REC_OFF, 0);
524   MapTapeEjectButton();
525 }
526
527 void TapeRecordAction(byte action_raw[MAX_PLAYERS])
528 {
529   byte action[MAX_PLAYERS];
530   int i;
531
532   if (!tape.recording)          /* (record action even when tape is paused) */
533     return;
534
535   if (tape.counter >= MAX_TAPE_LEN - 1)
536   {
537     TapeStopRecording();
538     return;
539   }
540
541   for (i = 0; i < MAX_PLAYERS; i++)
542     action[i] = action_raw[i];
543
544   if (tape.set_centered_player)
545   {
546     for (i = 0; i < MAX_PLAYERS; i++)
547       if (tape.centered_player_nr_next == i ||
548           tape.centered_player_nr_next == -1)
549         action[i] |= KEY_SET_FOCUS;
550
551     tape.set_centered_player = FALSE;
552   }
553
554   if (tape.pos[tape.counter].delay > 0)         /* already stored action */
555   {
556     boolean changed_events = FALSE;
557
558     for (i = 0; i < MAX_PLAYERS; i++)
559       if (tape.pos[tape.counter].action[i] != action[i])
560         changed_events = TRUE;
561
562     if (changed_events || tape.pos[tape.counter].delay >= 255)
563     {
564       tape.counter++;
565       tape.pos[tape.counter].delay = 0;
566     }
567     else
568       tape.pos[tape.counter].delay++;
569   }
570
571   if (tape.pos[tape.counter].delay == 0)        /* store new action */
572   {
573     for (i = 0; i < MAX_PLAYERS; i++)
574       tape.pos[tape.counter].action[i] = action[i];
575
576     tape.pos[tape.counter].delay++;
577   }
578 }
579
580 void TapeTogglePause(boolean toggle_manual)
581 {
582   int state = 0;
583
584   if (tape.pause_before_death)
585     state |= VIDEO_STATE_PBEND_OFF;
586   else if (tape.fast_forward)
587     state |= VIDEO_STATE_FFWD_OFF;
588
589   tape.pausing = !tape.pausing;
590   tape.fast_forward = FALSE;
591   tape.pause_before_death = FALSE;
592
593   if (tape.single_step && toggle_manual)
594     tape.single_step = FALSE;
595
596   state |= (tape.pausing ? VIDEO_STATE_PAUSE_ON : VIDEO_STATE_PAUSE_OFF);
597   if (tape.playing)
598     state |= VIDEO_STATE_PLAY_ON;
599   else
600     state |= (tape.single_step ? VIDEO_STATE_1STEP_ON : VIDEO_STATE_1STEP_OFF);
601
602   DrawVideoDisplay(state, 0);
603
604   if (tape.warp_forward)
605   {
606     TapeStopWarpForward();
607
608     if (tape.quick_resume)
609     {
610       tape.quick_resume = FALSE;
611
612       TapeAppendRecording();
613
614       if (!CheckEngineSnapshot())
615         SaveEngineSnapshot();
616     }
617   }
618 }
619
620 void TapeStartPlaying()
621 {
622   if (TAPE_IS_EMPTY(tape))
623     return;
624
625   if (!TAPE_IS_STOPPED(tape))
626     TapeStop();
627
628   TapeRewind();
629
630   tape.playing = TRUE;
631
632   DrawVideoDisplay(VIDEO_STATE_PLAY_ON, 0);
633   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
634   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
635
636   MapTapeWarpButton();
637
638   SetDrawDeactivationMask(REDRAW_NONE);
639   audio.sound_deactivated = FALSE;
640 }
641
642 static void TapeStartGamePlaying()
643 {
644   TapeStartPlaying();
645
646   InitGame();
647 }
648
649 void TapeStopPlaying()
650 {
651   tape.playing = FALSE;
652   tape.pausing = FALSE;
653
654   if (tape.warp_forward)
655     TapeStopWarpForward();
656
657   DrawVideoDisplay(VIDEO_STATE_PLAY_OFF, 0);
658   MapTapeEjectButton();
659 }
660
661 byte *TapePlayAction()
662 {
663   static byte action[MAX_PLAYERS];
664   int i;
665
666   if (!tape.playing || tape.pausing)
667     return NULL;
668
669   if (tape.pause_before_death)  /* stop 10 seconds before player gets killed */
670   {
671     if (!(FrameCounter % 20))
672     {
673       if ((FrameCounter / 20) % 2)
674         DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
675       else
676         DrawVideoDisplay(VIDEO_STATE_PBEND_OFF, VIDEO_DISPLAY_LABEL_ONLY);
677     }
678
679     if (tape.warp_forward)
680     {
681       if (tape.deactivate_display)
682         DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
683       else
684         DrawVideoDisplay(VIDEO_STATE_WARP2_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
685     }
686
687     if (TapeTime > tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
688     {
689       TapeTogglePause(TAPE_TOGGLE_MANUAL);
690
691       return NULL;
692     }
693   }
694   else if (tape.fast_forward)
695   {
696     if ((FrameCounter / 20) % 2)
697       DrawVideoDisplay(VIDEO_STATE_FFWD_ON, VIDEO_DISPLAY_LABEL_ONLY);
698     else
699       DrawVideoDisplay(VIDEO_STATE_FFWD_OFF, VIDEO_DISPLAY_LABEL_ONLY);
700
701     if (tape.warp_forward)
702     {
703       if (tape.deactivate_display)
704         DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
705       else
706         DrawVideoDisplay(VIDEO_STATE_WARP2_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
707     }
708   }
709
710 #if 0
711   /* !!! this makes things much slower !!! */
712   else if (tape.warp_forward)
713   {
714     if ((FrameCounter / 20) % 2)
715       DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_LABEL_ONLY);
716     else
717       DrawVideoDisplay(VIDEO_STATE_WARP_OFF, VIDEO_DISPLAY_LABEL_ONLY);
718
719     DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
720   }
721 #endif
722
723   if (tape.counter >= tape.length)      /* end of tape reached */
724   {
725     if (tape.warp_forward && !tape.auto_play)
726       TapeTogglePause(TAPE_TOGGLE_MANUAL);
727     else
728       TapeStop();
729
730     return NULL;
731   }
732
733   for (i = 0; i < MAX_PLAYERS; i++)
734     action[i] = tape.pos[tape.counter].action[i];
735
736   tape.set_centered_player = FALSE;
737   tape.centered_player_nr_next = -999;
738
739   for (i = 0; i < MAX_PLAYERS; i++)
740   {
741     if (action[i] & KEY_SET_FOCUS)
742     {
743       tape.set_centered_player = TRUE;
744       tape.centered_player_nr_next =
745         (tape.centered_player_nr_next == -999 ? i : -1);
746     }
747
748     action[i] &= ~KEY_SET_FOCUS;
749   }
750
751   tape.delay_played++;
752   if (tape.delay_played >= tape.pos[tape.counter].delay)
753   {
754     tape.counter++;
755     tape.delay_played = 0;
756   }
757
758   return action;
759 }
760
761 void TapeStop()
762 {
763   TapeStopRecording();
764   TapeStopPlaying();
765
766   DrawVideoDisplay(VIDEO_STATE_OFF, 0);
767
768   if (tape.date && tape.length)
769   {
770     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
771     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
772   }
773 }
774
775 unsigned int GetTapeLength()
776 {
777   unsigned int tape_length = 0;
778   int i;
779
780   if (TAPE_IS_EMPTY(tape))
781     return(0);
782
783   for (i = 0; i < tape.length; i++)
784     tape_length += tape.pos[i].delay;
785
786   return(tape_length * GAME_FRAME_DELAY / 1000);
787 }
788
789 static void TapeStartWarpForward()
790 {
791   tape.warp_forward = TRUE;
792
793   if (!tape.fast_forward && !tape.pause_before_death)
794   {
795     tape.pausing = FALSE;
796     tape.deactivate_display = TRUE;
797
798     TapeDeactivateDisplayOn();
799   }
800
801   if (tape.fast_forward || tape.pause_before_death)
802     DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
803   else
804     DrawVideoDisplay(VIDEO_STATE_WARP_ON, 0);
805 }
806
807 static void TapeStopWarpForward()
808 {
809   int state = (tape.pausing ? VIDEO_STATE_PAUSE_ON : VIDEO_STATE_PAUSE_OFF);
810
811   tape.warp_forward = FALSE;
812   tape.deactivate_display = FALSE;
813
814   TapeDeactivateDisplayOff(game_status == GAME_MODE_PLAYING);
815
816   if (tape.pause_before_death)
817     state |= VIDEO_STATE_WARP_OFF | VIDEO_STATE_PBEND_ON;
818   else if (tape.fast_forward)
819     state |= VIDEO_STATE_WARP_OFF | VIDEO_STATE_FFWD_ON;
820   else
821     state |= VIDEO_STATE_WARP_OFF | VIDEO_STATE_PLAY_ON;
822
823   DrawVideoDisplay(state, 0);
824 }
825
826 static void TapeSingleStep()
827 {
828   if (options.network)
829     return;
830
831   if (!tape.pausing)
832     TapeTogglePause(TAPE_TOGGLE_MANUAL);
833
834   tape.single_step = !tape.single_step;
835
836   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
837                     VIDEO_STATE_1STEP_OFF), 0);
838 }
839
840 void TapeQuickSave()
841 {
842   if (game_status == GAME_MODE_MAIN)
843   {
844     Request("No game that can be saved!", REQ_CONFIRM);
845
846     return;
847   }
848
849   if (game_status != GAME_MODE_PLAYING)
850     return;
851
852   if (tape.recording)
853     TapeHaltRecording();        /* prepare tape for saving on-the-fly */
854
855   if (TAPE_IS_EMPTY(tape))
856   {
857     Request("No tape that can be saved!", REQ_CONFIRM);
858
859     return;
860   }
861
862   if (SaveTapeChecked(tape.level_nr))
863     SaveEngineSnapshot();
864 }
865
866 void TapeQuickLoad()
867 {
868   char *filename = getTapeFilename(level_nr);
869
870   if (!fileExists(filename))
871   {
872     Request("No tape for this level!", REQ_CONFIRM);
873
874     return;
875   }
876
877   if (tape.recording && !Request("Stop recording and load tape?",
878                                  REQ_ASK | REQ_STAY_CLOSED))
879   {
880     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
881
882     return;
883   }
884
885   if (game_status != GAME_MODE_PLAYING && game_status != GAME_MODE_MAIN)
886     return;
887
888   if (CheckEngineSnapshot())
889   {
890     TapeStartGamePlaying();
891
892     LoadEngineSnapshot();
893
894     DrawCompleteVideoDisplay();
895
896     tape.playing = TRUE;
897     tape.pausing = TRUE;
898
899     TapeStopWarpForward();
900     TapeAppendRecording();
901
902     if (FrameCounter > 0)
903       return;
904   }
905
906   TapeStop();
907   TapeErase();
908
909   LoadTape(level_nr);
910
911   if (!TAPE_IS_EMPTY(tape))
912   {
913     TapeStartGamePlaying();
914     TapeStartWarpForward();
915
916     tape.quick_resume = TRUE;
917   }
918   else  /* this should not happen (basically checked above) */
919   {
920     int reopen_door = (game_status == GAME_MODE_PLAYING ? REQ_REOPEN : 0);
921
922     Request("No tape for this level!", REQ_CONFIRM | reopen_door);
923   }
924 }
925
926 void InsertSolutionTape()
927 {
928   if (!TAPE_IS_EMPTY(tape))
929     return;
930
931   LoadSolutionTape(level_nr);
932
933   if (TAPE_IS_EMPTY(tape))
934     Request("No solution tape for this level!", REQ_CONFIRM);
935
936   DrawCompleteVideoDisplay();
937 }
938
939
940 /* ------------------------------------------------------------------------- *
941  * tape autoplay functions
942  * ------------------------------------------------------------------------- */
943
944 void AutoPlayTape()
945 {
946   static LevelDirTree *autoplay_leveldir = NULL;
947   static boolean autoplay_initialized = FALSE;
948   static int autoplay_level_nr = -1;
949   static int num_levels_played = 0;
950   static int num_levels_solved = 0;
951   static int num_tape_missing = 0;
952   static boolean level_failed[MAX_TAPES_PER_SET];
953   int i;
954
955   if (autoplay_initialized)
956   {
957     /* just finished auto-playing tape */
958     printf("%s.\n", tape.auto_play_level_solved ? "solved" : "NOT SOLVED");
959
960     num_levels_played++;
961
962     if (tape.auto_play_level_solved)
963       num_levels_solved++;
964     else if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
965       level_failed[level_nr] = TRUE;
966   }
967   else
968   {
969     DrawCompleteVideoDisplay();
970     audio.sound_enabled = FALSE;
971
972     autoplay_leveldir = getTreeInfoFromIdentifier(leveldir_first,
973                                                   global.autoplay_leveldir);
974
975     if (autoplay_leveldir == NULL)
976       Error(ERR_EXIT, "no such level identifier: '%s'",
977             global.autoplay_leveldir);
978
979     leveldir_current = autoplay_leveldir;
980
981     if (autoplay_leveldir->first_level < 0)
982       autoplay_leveldir->first_level = 0;
983     if (autoplay_leveldir->last_level >= MAX_TAPES_PER_SET)
984       autoplay_leveldir->last_level = MAX_TAPES_PER_SET - 1;
985
986     autoplay_level_nr = autoplay_leveldir->first_level;
987
988     printf_line("=", 79);
989     printf("Automatically playing level tapes\n");
990     printf_line("-", 79);
991     printf("Level series identifier: '%s'\n", autoplay_leveldir->identifier);
992     printf("Level series name:       '%s'\n", autoplay_leveldir->name);
993     printf("Level series author:     '%s'\n", autoplay_leveldir->author);
994     printf("Number of levels:        %d\n",   autoplay_leveldir->levels);
995     printf_line("=", 79);
996     printf("\n");
997
998     for (i = 0; i < MAX_TAPES_PER_SET; i++)
999       level_failed[i] = FALSE;
1000
1001     autoplay_initialized = TRUE;
1002   }
1003
1004   while (autoplay_level_nr <= autoplay_leveldir->last_level)
1005   {
1006     level_nr = autoplay_level_nr++;
1007
1008     if (!global.autoplay_all && !global.autoplay_level[level_nr])
1009       continue;
1010
1011     TapeErase();
1012
1013     printf("Level %03d: ", level_nr);
1014
1015     LoadLevel(level_nr);
1016     if (level.no_valid_file)
1017     {
1018       printf("(no level)\n");
1019       continue;
1020     }
1021
1022 #if 0
1023     /* ACTIVATE THIS FOR LOADING/TESTING OF LEVELS ONLY */
1024     printf("(only testing level)\n");
1025     continue;
1026 #endif
1027
1028     LoadSolutionTape(level_nr);
1029
1030     if (tape.no_valid_file)
1031     {
1032       num_tape_missing++;
1033
1034       printf("(no tape)\n");
1035
1036       continue;
1037     }
1038
1039     printf("playing tape ... ");
1040
1041     TapeStartGamePlaying();
1042     TapeStartWarpForward();
1043
1044     return;
1045   }
1046
1047   printf("\n");
1048   printf_line("=", 79);
1049   printf("Number of levels played: %d\n", num_levels_played);
1050   printf("Number of levels solved: %d (%d%%)\n", num_levels_solved,
1051          (num_levels_played ? num_levels_solved * 100 / num_levels_played :0));
1052   printf_line("-", 79);
1053   printf("Summary (for automatic parsing by scripts):\n");
1054   printf("LEVELDIR '%s', SOLVED %d/%d (%d%%)",
1055          autoplay_leveldir->identifier, num_levels_solved, num_levels_played,
1056          (num_levels_played ? num_levels_solved * 100 / num_levels_played :0));
1057
1058   if (num_levels_played != num_levels_solved)
1059   {
1060     printf(", FAILED:");
1061     for (i = 0; i < MAX_TAPES_PER_SET; i++)
1062       if (level_failed[i])
1063         printf(" %03d", i);
1064   }
1065
1066   printf("\n");
1067   printf_line("=", 79);
1068
1069   CloseAllAndExit(0);
1070 }
1071
1072
1073 /* ---------- new tape button stuff ---------------------------------------- */
1074
1075 static struct
1076 {
1077   int graphic;
1078   struct Rect *pos;
1079   int gadget_id;
1080   char *infotext;
1081 } tapebutton_info[NUM_TAPE_BUTTONS] =
1082 {
1083   {
1084     IMG_TAPE_BUTTON_GFX_EJECT,          &tape.button.eject,
1085     TAPE_CTRL_ID_EJECT,                 "eject tape"
1086   },
1087   {
1088     /* (same position as "eject" button) */
1089     IMG_TAPE_BUTTON_GFX_EXTRA,          &tape.button.eject,
1090     TAPE_CTRL_ID_EXTRA,                 "extra functions"
1091   },
1092   {
1093     IMG_TAPE_BUTTON_GFX_STOP,           &tape.button.stop,
1094     TAPE_CTRL_ID_STOP,                  "stop tape"
1095   },
1096   {
1097     IMG_TAPE_BUTTON_GFX_PAUSE,          &tape.button.pause,
1098     TAPE_CTRL_ID_PAUSE,                 "pause tape"
1099   },
1100   {
1101     IMG_TAPE_BUTTON_GFX_RECORD,         &tape.button.record,
1102     TAPE_CTRL_ID_RECORD,                "record tape"
1103   },
1104   {
1105     IMG_TAPE_BUTTON_GFX_PLAY,           &tape.button.play,
1106     TAPE_CTRL_ID_PLAY,                  "play tape"
1107   }
1108 };
1109
1110 void CreateTapeButtons()
1111 {
1112   int i;
1113
1114   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1115   {
1116     struct GraphicInfo *gfx = &graphic_info[tapebutton_info[i].graphic];
1117     struct Rect *pos = tapebutton_info[i].pos;
1118     struct GadgetInfo *gi;
1119     int gd_x = gfx->src_x;
1120     int gd_y = gfx->src_y;
1121     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
1122     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
1123     int id = i;
1124
1125     gi = CreateGadget(GDI_CUSTOM_ID, id,
1126                       GDI_INFO_TEXT, tapebutton_info[i].infotext,
1127                       GDI_X, VX + pos->x,
1128                       GDI_Y, VY + pos->y,
1129                       GDI_WIDTH, gfx->width,
1130                       GDI_HEIGHT, gfx->height,
1131                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
1132                       GDI_STATE, GD_BUTTON_UNPRESSED,
1133                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
1134                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
1135                       GDI_DIRECT_DRAW, FALSE,
1136                       GDI_EVENT_MASK, GD_EVENT_RELEASED,
1137                       GDI_CALLBACK_ACTION, HandleTapeButtons,
1138                       GDI_END);
1139
1140     if (gi == NULL)
1141       Error(ERR_EXIT, "cannot create gadget");
1142
1143     tape_gadget[id] = gi;
1144   }
1145 }
1146
1147 void FreeTapeButtons()
1148 {
1149   int i;
1150
1151   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1152     FreeGadget(tape_gadget[i]);
1153 }
1154
1155 void MapTapeEjectButton()
1156 {
1157   UnmapGadget(tape_gadget[TAPE_CTRL_ID_EXTRA]);
1158   MapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
1159 }
1160
1161 void MapTapeWarpButton()
1162 {
1163   UnmapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
1164   MapGadget(tape_gadget[TAPE_CTRL_ID_EXTRA]);
1165 }
1166
1167 void MapTapeButtons()
1168 {
1169   int i;
1170
1171   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1172     if (i != TAPE_CTRL_ID_EXTRA)
1173       MapGadget(tape_gadget[i]);
1174
1175   if (tape.recording || tape.playing)
1176     MapTapeWarpButton();
1177
1178   if (tape.show_game_buttons)
1179     MapGameButtons();
1180 }
1181
1182 void UnmapTapeButtons()
1183 {
1184   int i;
1185
1186   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1187     UnmapGadget(tape_gadget[i]);
1188 }
1189
1190 static void HandleTapeButtonsExt(int id)
1191 {
1192   if (game_status != GAME_MODE_MAIN && game_status != GAME_MODE_PLAYING)
1193     return;
1194
1195   switch (id)
1196   {
1197     case TAPE_CTRL_ID_EJECT:
1198       TapeStop();
1199
1200       if (TAPE_IS_EMPTY(tape))
1201       {
1202         LoadTape(level_nr);
1203
1204         if (TAPE_IS_EMPTY(tape))
1205           Request("No tape for this level!", REQ_CONFIRM);
1206       }
1207       else
1208       {
1209         if (tape.changed)
1210           SaveTapeChecked(tape.level_nr);
1211
1212         TapeErase();
1213       }
1214
1215       DrawCompleteVideoDisplay();
1216       break;
1217
1218     case TAPE_CTRL_ID_EXTRA:
1219       if (tape.playing)
1220       {
1221         if (!tape.warp_forward)                 /* PLAY -> WARP FORWARD PLAY */
1222         {
1223           TapeStartWarpForward();
1224         }
1225         else                                    /* WARP FORWARD PLAY -> PLAY */
1226         {
1227           TapeStopWarpForward();
1228         }
1229       }
1230       else if (tape.recording)
1231         TapeSingleStep();
1232
1233       break;
1234
1235     case TAPE_CTRL_ID_STOP:
1236       TapeStop();
1237       break;
1238
1239     case TAPE_CTRL_ID_PAUSE:
1240       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1241       break;
1242
1243     case TAPE_CTRL_ID_RECORD:
1244       if (TAPE_IS_STOPPED(tape))
1245         TapeStartGameRecording();
1246       else if (tape.pausing)
1247       {
1248         if (tape.playing)                       /* PLAY -> PAUSE -> RECORD */
1249           TapeAppendRecording();
1250         else
1251           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1252       }
1253       break;
1254
1255     case TAPE_CTRL_ID_PLAY:
1256       if (TAPE_IS_EMPTY(tape))
1257         break;
1258
1259       if (TAPE_IS_STOPPED(tape))
1260       {
1261         TapeStartGamePlaying();
1262       }
1263       else if (tape.playing)
1264       {
1265         if (tape.pausing)                       /* PAUSE -> PLAY */
1266         {
1267           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1268         }
1269         else if (!tape.fast_forward)            /* PLAY -> FAST FORWARD PLAY */
1270         {
1271           tape.fast_forward = TRUE;
1272           DrawVideoDisplay(VIDEO_STATE_FFWD_ON, 0);
1273         }
1274         else if (!tape.pause_before_death)      /* FFWD PLAY -> AUTO PAUSE */
1275         {
1276           tape.pause_before_death = TRUE;
1277           DrawVideoDisplay(VIDEO_STATE_FFWD_OFF | VIDEO_STATE_PBEND_ON, 0);
1278         }
1279         else                                    /* AUTO PAUSE -> NORMAL PLAY */
1280         {
1281           if (tape.warp_forward)
1282             TapeStopWarpForward();
1283
1284           tape.fast_forward = FALSE;
1285           tape.pause_before_death = FALSE;
1286
1287           DrawVideoDisplay(VIDEO_STATE_PBEND_OFF | VIDEO_STATE_PLAY_ON, 0);
1288         }
1289       }
1290       break;
1291
1292     default:
1293       break;
1294   }
1295 }
1296
1297 static void HandleTapeButtons(struct GadgetInfo *gi)
1298 {
1299   HandleTapeButtonsExt(gi->custom_id);
1300 }
1301
1302 void HandleTapeButtonKeys(Key key)
1303 {
1304   boolean eject_button_is_active = TAPE_IS_STOPPED(tape);
1305   boolean extra_button_is_active = !eject_button_is_active;
1306
1307   if (key == setup.shortcut.tape_eject && eject_button_is_active)
1308     HandleTapeButtonsExt(TAPE_CTRL_ID_EJECT);
1309   else if (key == setup.shortcut.tape_extra && extra_button_is_active)
1310     HandleTapeButtonsExt(TAPE_CTRL_ID_EXTRA);
1311   else if (key == setup.shortcut.tape_stop)
1312     HandleTapeButtonsExt(TAPE_CTRL_ID_STOP);
1313   else if (key == setup.shortcut.tape_pause)
1314     HandleTapeButtonsExt(TAPE_CTRL_ID_PAUSE);
1315   else if (key == setup.shortcut.tape_record)
1316     HandleTapeButtonsExt(TAPE_CTRL_ID_RECORD);
1317   else if (key == setup.shortcut.tape_play)
1318     HandleTapeButtonsExt(TAPE_CTRL_ID_PLAY);
1319 }