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