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