improved configurability of tape date and time display positions
[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     int xpos1 = VX + pos->x;
300     int xpos2 = VX + pos->x + pos->xoffset;
301     int xpos3 = VX + pos->x + pos->xoffset2;
302     int ypos  = VY + pos->y;
303
304     DrawText(xpos1, ypos, int2str(tag, 2),   pos->font);
305     DrawText(xpos2, ypos, monatsname[monat], pos->font);
306     DrawText(xpos3, ypos, int2str(jahr, 2),  pos->font);
307   }
308
309   if (state & VIDEO_STATE_TIME_ON)
310   {
311     struct TextPosInfo *pos = &tape.text.time;
312     int min = value / 60;
313     int sec = value % 60;
314     int xpos1 = VX + pos->x;
315     int xpos2 = VX + pos->x + pos->xoffset;
316     int ypos  = VY + pos->y;
317
318     DrawText(xpos1, ypos, int2str(min, 2), pos->font);
319     DrawText(xpos2, ypos, int2str(sec, 2), pos->font);
320   }
321
322   redraw_mask |= REDRAW_DOOR_2;
323 }
324
325 void DrawCompleteVideoDisplay()
326 {
327   struct GraphicInfo *g_tape = &graphic_info[IMG_BACKGROUND_TAPE];
328
329   /* draw tape background */
330   BlitBitmap(g_tape->bitmap, drawto, g_tape->src_x, g_tape->src_y,
331              gfx.vxsize, gfx.vysize, gfx.vx, gfx.vy);
332
333   /* draw tape buttons (forced) */
334   UnmapTapeButtons();
335   MapTapeButtons();
336
337   DrawVideoDisplay(VIDEO_ALL_OFF, 0);
338
339   if (tape.recording)
340   {
341     DrawVideoDisplay(VIDEO_STATE_REC_ON, 0);
342     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
343     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
344
345     if (tape.pausing)
346       DrawVideoDisplay(VIDEO_STATE_PAUSE_ON, 0);
347   }
348   else if (tape.playing)
349   {
350     DrawVideoDisplay(VIDEO_STATE_PLAY_ON, 0);
351     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
352     DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
353
354     if (tape.pausing)
355       DrawVideoDisplay(VIDEO_STATE_PAUSE_ON, 0);
356   }
357   else if (tape.date && tape.length)
358   {
359     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
360     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
361   }
362
363   BlitBitmap(drawto, bitmap_db_door_2, gfx.vx, gfx.vy, gfx.vxsize, gfx.vysize,
364              0, 0);
365 }
366
367 void TapeDeactivateDisplayOn()
368 {
369   SetDrawDeactivationMask(REDRAW_FIELD);
370   audio.sound_deactivated = TRUE;
371 }
372
373 void TapeDeactivateDisplayOff(boolean redraw_display)
374 {
375   SetDrawDeactivationMask(REDRAW_NONE);
376   audio.sound_deactivated = FALSE;
377
378   if (redraw_display)
379   {
380     RedrawPlayfield(TRUE, 0,0,0,0);
381     DrawGameDoorValues();
382   }
383 }
384
385
386 /* ========================================================================= */
387 /* tape control functions                                                    */
388 /* ========================================================================= */
389
390 void TapeSetDateFromEpochSeconds(time_t epoch_seconds)
391 {
392   struct tm *lt = localtime(&epoch_seconds);
393
394   tape.date = 10000 * (lt->tm_year % 100) + 100 * lt->tm_mon + lt->tm_mday;
395 }
396
397 void TapeSetDateFromNow()
398 {
399   TapeSetDateFromEpochSeconds(time(NULL));
400 }
401
402 void TapeErase()
403 {
404   int i;
405
406   tape.counter = 0;
407   tape.length = 0;
408   tape.length_seconds = 0;
409
410   if (leveldir_current)
411     setString(&tape.level_identifier, leveldir_current->identifier);
412
413   tape.level_nr = level_nr;
414   tape.pos[tape.counter].delay = 0;
415   tape.changed = TRUE;
416
417   tape.random_seed = InitRND(level.random_seed);
418
419   tape.file_version = FILE_VERSION_ACTUAL;
420   tape.game_version = GAME_VERSION_ACTUAL;
421   tape.engine_version = level.game_version;
422
423   TapeSetDateFromNow();
424
425   for (i = 0; i < MAX_PLAYERS; i++)
426     tape.player_participates[i] = FALSE;
427
428   tape.centered_player_nr_next = -1;
429   tape.set_centered_player = FALSE;
430 }
431
432 static void TapeRewind()
433 {
434   tape.counter = 0;
435   tape.delay_played = 0;
436   tape.pause_before_death = FALSE;
437   tape.recording = FALSE;
438   tape.playing = FALSE;
439   tape.fast_forward = FALSE;
440   tape.warp_forward = FALSE;
441   tape.deactivate_display = FALSE;
442   tape.auto_play = (global.autoplay_leveldir != NULL);
443   tape.auto_play_level_solved = FALSE;
444   tape.quick_resume = FALSE;
445   tape.single_step = FALSE;
446
447   tape.centered_player_nr_next = -1;
448   tape.set_centered_player = FALSE;
449
450   InitRND(tape.random_seed);
451 }
452
453 static void TapeSetRandomSeed(int random_seed)
454 {
455   tape.random_seed = InitRND(random_seed);
456 }
457
458 void TapeStartRecording(int random_seed)
459 {
460   if (!TAPE_IS_STOPPED(tape))
461     TapeStop();
462
463   TapeErase();
464   TapeRewind();
465   TapeSetRandomSeed(random_seed);
466
467   tape.recording = TRUE;
468
469   DrawVideoDisplay(VIDEO_STATE_REC_ON, 0);
470   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
471   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
472
473   MapTapeWarpButton();
474
475   SetDrawDeactivationMask(REDRAW_NONE);
476   audio.sound_deactivated = FALSE;
477 }
478
479 static void TapeStartGameRecording()
480 {
481   TapeStartRecording(level.random_seed);
482
483 #if defined(NETWORK_AVALIABLE)
484   if (options.network)
485   {
486     SendToServer_StartPlaying();
487
488     return;
489   }
490 #endif
491
492   InitGame();
493 }
494
495 static void TapeAppendRecording()
496 {
497   if (!tape.playing || !tape.pausing)
498     return;
499
500   tape.pos[tape.counter].delay = tape.delay_played;
501   tape.playing = FALSE;
502   tape.recording = TRUE;
503   tape.changed = TRUE;
504
505   TapeSetDateFromNow();
506
507   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
508   DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON, 0);
509
510   UpdateAndDisplayGameControlValues();
511 }
512
513 void TapeHaltRecording()
514 {
515   if (!tape.recording)
516     return;
517
518   tape.counter++;
519   tape.pos[tape.counter].delay = 0;
520
521   tape.length = tape.counter;
522   tape.length_seconds = GetTapeLength();
523 }
524
525 void TapeStopRecording()
526 {
527   TapeHaltRecording();
528
529   tape.recording = FALSE;
530   tape.pausing = FALSE;
531
532   DrawVideoDisplay(VIDEO_STATE_REC_OFF, 0);
533   MapTapeEjectButton();
534 }
535
536 void TapeRecordAction(byte action_raw[MAX_PLAYERS])
537 {
538   byte action[MAX_PLAYERS];
539   int i;
540
541   if (!tape.recording)          /* (record action even when tape is paused) */
542     return;
543
544   if (tape.counter >= MAX_TAPE_LEN - 1)
545   {
546     TapeStopRecording();
547     return;
548   }
549
550   for (i = 0; i < MAX_PLAYERS; i++)
551     action[i] = action_raw[i];
552
553   if (tape.set_centered_player)
554   {
555     for (i = 0; i < MAX_PLAYERS; i++)
556       if (tape.centered_player_nr_next == i ||
557           tape.centered_player_nr_next == -1)
558         action[i] |= KEY_SET_FOCUS;
559
560     tape.set_centered_player = FALSE;
561   }
562
563   if (tape.pos[tape.counter].delay > 0)         /* already stored action */
564   {
565     boolean changed_events = FALSE;
566
567     for (i = 0; i < MAX_PLAYERS; i++)
568       if (tape.pos[tape.counter].action[i] != action[i])
569         changed_events = TRUE;
570
571     if (changed_events || tape.pos[tape.counter].delay >= 255)
572     {
573       tape.counter++;
574       tape.pos[tape.counter].delay = 0;
575     }
576     else
577       tape.pos[tape.counter].delay++;
578   }
579
580   if (tape.pos[tape.counter].delay == 0)        /* store new action */
581   {
582     for (i = 0; i < MAX_PLAYERS; i++)
583       tape.pos[tape.counter].action[i] = action[i];
584
585     tape.pos[tape.counter].delay++;
586   }
587 }
588
589 void TapeTogglePause(boolean toggle_manual)
590 {
591   int state = 0;
592
593   if (tape.pause_before_death)
594     state |= VIDEO_STATE_PBEND_OFF;
595   else if (tape.fast_forward)
596     state |= VIDEO_STATE_FFWD_OFF;
597
598   tape.pausing = !tape.pausing;
599   tape.fast_forward = FALSE;
600   tape.pause_before_death = FALSE;
601
602   if (tape.single_step && toggle_manual)
603     tape.single_step = FALSE;
604
605   state |= (tape.pausing ? VIDEO_STATE_PAUSE_ON : VIDEO_STATE_PAUSE_OFF);
606   if (tape.playing)
607     state |= VIDEO_STATE_PLAY_ON;
608   else
609     state |= (tape.single_step ? VIDEO_STATE_1STEP_ON : VIDEO_STATE_1STEP_OFF);
610
611   DrawVideoDisplay(state, 0);
612
613   if (tape.warp_forward)
614   {
615     TapeStopWarpForward();
616
617     if (tape.quick_resume)
618     {
619       tape.quick_resume = FALSE;
620
621       TapeAppendRecording();
622
623       if (!CheckEngineSnapshot())
624         SaveEngineSnapshot();
625     }
626   }
627 }
628
629 void TapeStartPlaying()
630 {
631   if (TAPE_IS_EMPTY(tape))
632     return;
633
634   if (!TAPE_IS_STOPPED(tape))
635     TapeStop();
636
637   TapeRewind();
638
639   tape.playing = TRUE;
640
641   DrawVideoDisplay(VIDEO_STATE_PLAY_ON, 0);
642   DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
643   DrawVideoDisplay(VIDEO_STATE_TIME_ON, 0);
644
645   MapTapeWarpButton();
646
647   SetDrawDeactivationMask(REDRAW_NONE);
648   audio.sound_deactivated = FALSE;
649 }
650
651 static void TapeStartGamePlaying()
652 {
653   TapeStartPlaying();
654
655   InitGame();
656 }
657
658 void TapeStopPlaying()
659 {
660   tape.playing = FALSE;
661   tape.pausing = FALSE;
662
663   if (tape.warp_forward)
664     TapeStopWarpForward();
665
666   DrawVideoDisplay(VIDEO_STATE_PLAY_OFF, 0);
667   MapTapeEjectButton();
668 }
669
670 byte *TapePlayAction()
671 {
672   static byte action[MAX_PLAYERS];
673   int i;
674
675   if (!tape.playing || tape.pausing)
676     return NULL;
677
678   if (tape.pause_before_death)  /* stop 10 seconds before player gets killed */
679   {
680     if (!(FrameCounter % 20))
681     {
682       if ((FrameCounter / 20) % 2)
683         DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
684       else
685         DrawVideoDisplay(VIDEO_STATE_PBEND_OFF, VIDEO_DISPLAY_LABEL_ONLY);
686     }
687
688     if (tape.warp_forward)
689     {
690       if (tape.deactivate_display)
691         DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
692       else
693         DrawVideoDisplay(VIDEO_STATE_WARP2_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
694     }
695
696     if (TapeTime > tape.length_seconds - TAPE_PAUSE_SECONDS_BEFORE_DEATH)
697     {
698       TapeTogglePause(TAPE_TOGGLE_MANUAL);
699
700       return NULL;
701     }
702   }
703   else if (tape.fast_forward)
704   {
705     if ((FrameCounter / 20) % 2)
706       DrawVideoDisplay(VIDEO_STATE_FFWD_ON, VIDEO_DISPLAY_LABEL_ONLY);
707     else
708       DrawVideoDisplay(VIDEO_STATE_FFWD_OFF, VIDEO_DISPLAY_LABEL_ONLY);
709
710     if (tape.warp_forward)
711     {
712       if (tape.deactivate_display)
713         DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
714       else
715         DrawVideoDisplay(VIDEO_STATE_WARP2_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
716     }
717   }
718
719 #if 0
720   /* !!! this makes things much slower !!! */
721   else if (tape.warp_forward)
722   {
723     if ((FrameCounter / 20) % 2)
724       DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_LABEL_ONLY);
725     else
726       DrawVideoDisplay(VIDEO_STATE_WARP_OFF, VIDEO_DISPLAY_LABEL_ONLY);
727
728     DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
729   }
730 #endif
731
732   if (tape.counter >= tape.length)      /* end of tape reached */
733   {
734     if (tape.warp_forward && !tape.auto_play)
735       TapeTogglePause(TAPE_TOGGLE_MANUAL);
736     else
737       TapeStop();
738
739     return NULL;
740   }
741
742   for (i = 0; i < MAX_PLAYERS; i++)
743     action[i] = tape.pos[tape.counter].action[i];
744
745 #if DEBUG_TAPE_WHEN_PLAYING
746   printf("%05d", FrameCounter);
747   for (i = 0; i < MAX_PLAYERS; i++)
748     printf("   %08x", action[i]);
749   printf("\n");
750 #endif
751
752   tape.set_centered_player = FALSE;
753   tape.centered_player_nr_next = -999;
754
755   for (i = 0; i < MAX_PLAYERS; i++)
756   {
757     if (action[i] & KEY_SET_FOCUS)
758     {
759       tape.set_centered_player = TRUE;
760       tape.centered_player_nr_next =
761         (tape.centered_player_nr_next == -999 ? i : -1);
762     }
763
764     action[i] &= ~KEY_SET_FOCUS;
765   }
766
767   tape.delay_played++;
768   if (tape.delay_played >= tape.pos[tape.counter].delay)
769   {
770     tape.counter++;
771     tape.delay_played = 0;
772   }
773
774   return action;
775 }
776
777 void TapeStop()
778 {
779   TapeStopRecording();
780   TapeStopPlaying();
781
782   DrawVideoDisplay(VIDEO_STATE_OFF, 0);
783
784   if (tape.date && tape.length)
785   {
786     DrawVideoDisplay(VIDEO_STATE_DATE_ON, tape.date);
787     DrawVideoDisplay(VIDEO_STATE_TIME_ON, tape.length_seconds);
788   }
789 }
790
791 unsigned int GetTapeLength()
792 {
793   unsigned int tape_length = 0;
794   int i;
795
796   if (TAPE_IS_EMPTY(tape))
797     return(0);
798
799   for (i = 0; i < tape.length; i++)
800     tape_length += tape.pos[i].delay;
801
802   return(tape_length * GAME_FRAME_DELAY / 1000);
803 }
804
805 static void TapeStartWarpForward()
806 {
807   tape.warp_forward = TRUE;
808
809   if (!tape.fast_forward && !tape.pause_before_death)
810   {
811     tape.pausing = FALSE;
812     tape.deactivate_display = TRUE;
813
814     TapeDeactivateDisplayOn();
815   }
816
817   if (tape.fast_forward || tape.pause_before_death)
818     DrawVideoDisplay(VIDEO_STATE_WARP_ON, VIDEO_DISPLAY_SYMBOL_ONLY);
819   else
820     DrawVideoDisplay(VIDEO_STATE_WARP_ON, 0);
821 }
822
823 static void TapeStopWarpForward()
824 {
825   int state = (tape.pausing ? VIDEO_STATE_PAUSE_ON : VIDEO_STATE_PAUSE_OFF);
826
827   tape.warp_forward = FALSE;
828   tape.deactivate_display = FALSE;
829
830   TapeDeactivateDisplayOff(game_status == GAME_MODE_PLAYING);
831
832   if (tape.pause_before_death)
833     state |= VIDEO_STATE_WARP_OFF | VIDEO_STATE_PBEND_ON;
834   else if (tape.fast_forward)
835     state |= VIDEO_STATE_WARP_OFF | VIDEO_STATE_FFWD_ON;
836   else
837     state |= VIDEO_STATE_WARP_OFF | VIDEO_STATE_PLAY_ON;
838
839   DrawVideoDisplay(state, 0);
840 }
841
842 static void TapeSingleStep()
843 {
844   if (options.network)
845     return;
846
847   if (!tape.pausing)
848     TapeTogglePause(TAPE_TOGGLE_MANUAL);
849
850   tape.single_step = !tape.single_step;
851
852   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
853                     VIDEO_STATE_1STEP_OFF), 0);
854 }
855
856 void TapeQuickSave()
857 {
858   if (game_status == GAME_MODE_MAIN)
859   {
860     Request("No game that can be saved!", REQ_CONFIRM);
861
862     return;
863   }
864
865   if (game_status != GAME_MODE_PLAYING)
866     return;
867
868   if (tape.recording)
869     TapeHaltRecording();        /* prepare tape for saving on-the-fly */
870
871   if (TAPE_IS_EMPTY(tape))
872   {
873     Request("No tape that can be saved!", REQ_CONFIRM);
874
875     return;
876   }
877
878   if (SaveTapeChecked(tape.level_nr))
879     SaveEngineSnapshot();
880 }
881
882 void TapeQuickLoad()
883 {
884   char *filename = getTapeFilename(level_nr);
885
886   if (!fileExists(filename))
887   {
888     Request("No tape for this level!", REQ_CONFIRM);
889
890     return;
891   }
892
893   if (tape.recording && !Request("Stop recording and load tape?",
894                                  REQ_ASK | REQ_STAY_CLOSED))
895   {
896     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
897
898     return;
899   }
900
901   if (game_status != GAME_MODE_PLAYING && game_status != GAME_MODE_MAIN)
902     return;
903
904   if (CheckEngineSnapshot())
905   {
906     TapeStartGamePlaying();
907
908     LoadEngineSnapshot();
909
910     DrawCompleteVideoDisplay();
911
912     tape.playing = TRUE;
913     tape.pausing = TRUE;
914
915     TapeStopWarpForward();
916     TapeAppendRecording();
917
918     if (FrameCounter > 0)
919       return;
920   }
921
922   TapeStop();
923   TapeErase();
924
925   LoadTape(level_nr);
926
927   if (!TAPE_IS_EMPTY(tape))
928   {
929     TapeStartGamePlaying();
930     TapeStartWarpForward();
931
932     tape.quick_resume = TRUE;
933   }
934   else  /* this should not happen (basically checked above) */
935   {
936     int reopen_door = (game_status == GAME_MODE_PLAYING ? REQ_REOPEN : 0);
937
938     Request("No tape for this level!", REQ_CONFIRM | reopen_door);
939   }
940 }
941
942 void InsertSolutionTape()
943 {
944   if (!TAPE_IS_EMPTY(tape))
945     return;
946
947   LoadSolutionTape(level_nr);
948
949   if (TAPE_IS_EMPTY(tape))
950     Request("No solution tape for this level!", REQ_CONFIRM);
951
952   DrawCompleteVideoDisplay();
953 }
954
955
956 /* ------------------------------------------------------------------------- *
957  * tape autoplay functions
958  * ------------------------------------------------------------------------- */
959
960 void AutoPlayTape()
961 {
962   static LevelDirTree *autoplay_leveldir = NULL;
963   static boolean autoplay_initialized = FALSE;
964   static int autoplay_level_nr = -1;
965   static int num_levels_played = 0;
966   static int num_levels_solved = 0;
967   static int num_tape_missing = 0;
968   static boolean level_failed[MAX_TAPES_PER_SET];
969   int i;
970
971   if (autoplay_initialized)
972   {
973     /* just finished auto-playing tape */
974     printf("%s.\n", tape.auto_play_level_solved ? "solved" : "NOT SOLVED");
975
976     num_levels_played++;
977
978     if (tape.auto_play_level_solved)
979       num_levels_solved++;
980     else if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
981       level_failed[level_nr] = TRUE;
982   }
983   else
984   {
985     DrawCompleteVideoDisplay();
986     audio.sound_enabled = FALSE;
987
988     autoplay_leveldir = getTreeInfoFromIdentifier(leveldir_first,
989                                                   global.autoplay_leveldir);
990
991     if (autoplay_leveldir == NULL)
992       Error(ERR_EXIT, "no such level identifier: '%s'",
993             global.autoplay_leveldir);
994
995     leveldir_current = autoplay_leveldir;
996
997     if (autoplay_leveldir->first_level < 0)
998       autoplay_leveldir->first_level = 0;
999     if (autoplay_leveldir->last_level >= MAX_TAPES_PER_SET)
1000       autoplay_leveldir->last_level = MAX_TAPES_PER_SET - 1;
1001
1002     autoplay_level_nr = autoplay_leveldir->first_level;
1003
1004     printf_line("=", 79);
1005     printf("Automatically playing level tapes\n");
1006     printf_line("-", 79);
1007     printf("Level series identifier: '%s'\n", autoplay_leveldir->identifier);
1008     printf("Level series name:       '%s'\n", autoplay_leveldir->name);
1009     printf("Level series author:     '%s'\n", autoplay_leveldir->author);
1010     printf("Number of levels:        %d\n",   autoplay_leveldir->levels);
1011     printf_line("=", 79);
1012     printf("\n");
1013
1014     for (i = 0; i < MAX_TAPES_PER_SET; i++)
1015       level_failed[i] = FALSE;
1016
1017     autoplay_initialized = TRUE;
1018   }
1019
1020   while (autoplay_level_nr <= autoplay_leveldir->last_level)
1021   {
1022     level_nr = autoplay_level_nr++;
1023
1024     if (!global.autoplay_all && !global.autoplay_level[level_nr])
1025       continue;
1026
1027     TapeErase();
1028
1029     printf("Level %03d: ", level_nr);
1030
1031     LoadLevel(level_nr);
1032     if (level.no_valid_file)
1033     {
1034       printf("(no level)\n");
1035       continue;
1036     }
1037
1038 #if 0
1039     /* ACTIVATE THIS FOR LOADING/TESTING OF LEVELS ONLY */
1040     printf("(only testing level)\n");
1041     continue;
1042 #endif
1043
1044     LoadSolutionTape(level_nr);
1045
1046     if (tape.no_valid_file)
1047     {
1048       num_tape_missing++;
1049
1050       printf("(no tape)\n");
1051
1052       continue;
1053     }
1054
1055     printf("playing tape ... ");
1056
1057     TapeStartGamePlaying();
1058     TapeStartWarpForward();
1059
1060     return;
1061   }
1062
1063   printf("\n");
1064   printf_line("=", 79);
1065   printf("Number of levels played: %d\n", num_levels_played);
1066   printf("Number of levels solved: %d (%d%%)\n", num_levels_solved,
1067          (num_levels_played ? num_levels_solved * 100 / num_levels_played :0));
1068   printf_line("-", 79);
1069   printf("Summary (for automatic parsing by scripts):\n");
1070   printf("LEVELDIR '%s', SOLVED %d/%d (%d%%)",
1071          autoplay_leveldir->identifier, num_levels_solved, num_levels_played,
1072          (num_levels_played ? num_levels_solved * 100 / num_levels_played :0));
1073
1074   if (num_levels_played != num_levels_solved)
1075   {
1076     printf(", FAILED:");
1077     for (i = 0; i < MAX_TAPES_PER_SET; i++)
1078       if (level_failed[i])
1079         printf(" %03d", i);
1080   }
1081
1082   printf("\n");
1083   printf_line("=", 79);
1084
1085   CloseAllAndExit(0);
1086 }
1087
1088
1089 /* ---------- new tape button stuff ---------------------------------------- */
1090
1091 static struct
1092 {
1093   int graphic;
1094   struct Rect *pos;
1095   int gadget_id;
1096   char *infotext;
1097 } tapebutton_info[NUM_TAPE_BUTTONS] =
1098 {
1099   {
1100     IMG_TAPE_BUTTON_GFX_EJECT,          &tape.button.eject,
1101     TAPE_CTRL_ID_EJECT,                 "eject tape"
1102   },
1103   {
1104     /* (same position as "eject" button) */
1105     IMG_TAPE_BUTTON_GFX_EXTRA,          &tape.button.eject,
1106     TAPE_CTRL_ID_EXTRA,                 "extra functions"
1107   },
1108   {
1109     IMG_TAPE_BUTTON_GFX_STOP,           &tape.button.stop,
1110     TAPE_CTRL_ID_STOP,                  "stop tape"
1111   },
1112   {
1113     IMG_TAPE_BUTTON_GFX_PAUSE,          &tape.button.pause,
1114     TAPE_CTRL_ID_PAUSE,                 "pause tape"
1115   },
1116   {
1117     IMG_TAPE_BUTTON_GFX_RECORD,         &tape.button.record,
1118     TAPE_CTRL_ID_RECORD,                "record tape"
1119   },
1120   {
1121     IMG_TAPE_BUTTON_GFX_PLAY,           &tape.button.play,
1122     TAPE_CTRL_ID_PLAY,                  "play tape"
1123   }
1124 };
1125
1126 void CreateTapeButtons()
1127 {
1128   int i;
1129
1130   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1131   {
1132     struct GraphicInfo *gfx = &graphic_info[tapebutton_info[i].graphic];
1133     struct Rect *pos = tapebutton_info[i].pos;
1134     struct GadgetInfo *gi;
1135     int gd_x = gfx->src_x;
1136     int gd_y = gfx->src_y;
1137     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
1138     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
1139     int id = i;
1140
1141     gi = CreateGadget(GDI_CUSTOM_ID, id,
1142                       GDI_INFO_TEXT, tapebutton_info[i].infotext,
1143                       GDI_X, VX + pos->x,
1144                       GDI_Y, VY + pos->y,
1145                       GDI_WIDTH, gfx->width,
1146                       GDI_HEIGHT, gfx->height,
1147                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
1148                       GDI_STATE, GD_BUTTON_UNPRESSED,
1149                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
1150                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
1151                       GDI_DIRECT_DRAW, FALSE,
1152                       GDI_EVENT_MASK, GD_EVENT_RELEASED,
1153                       GDI_CALLBACK_ACTION, HandleTapeButtons,
1154                       GDI_END);
1155
1156     if (gi == NULL)
1157       Error(ERR_EXIT, "cannot create gadget");
1158
1159     tape_gadget[id] = gi;
1160   }
1161 }
1162
1163 void FreeTapeButtons()
1164 {
1165   int i;
1166
1167   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1168     FreeGadget(tape_gadget[i]);
1169 }
1170
1171 void MapTapeEjectButton()
1172 {
1173   UnmapGadget(tape_gadget[TAPE_CTRL_ID_EXTRA]);
1174   MapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
1175 }
1176
1177 void MapTapeWarpButton()
1178 {
1179   UnmapGadget(tape_gadget[TAPE_CTRL_ID_EJECT]);
1180   MapGadget(tape_gadget[TAPE_CTRL_ID_EXTRA]);
1181 }
1182
1183 void MapTapeButtons()
1184 {
1185   int i;
1186
1187   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1188     if (i != TAPE_CTRL_ID_EXTRA)
1189       MapGadget(tape_gadget[i]);
1190
1191   if (tape.recording || tape.playing)
1192     MapTapeWarpButton();
1193
1194   if (tape.show_game_buttons)
1195     MapGameButtons();
1196 }
1197
1198 void UnmapTapeButtons()
1199 {
1200   int i;
1201
1202   for (i = 0; i < NUM_TAPE_BUTTONS; i++)
1203     UnmapGadget(tape_gadget[i]);
1204 }
1205
1206 static void HandleTapeButtonsExt(int id)
1207 {
1208   if (game_status != GAME_MODE_MAIN && game_status != GAME_MODE_PLAYING)
1209     return;
1210
1211   switch (id)
1212   {
1213     case TAPE_CTRL_ID_EJECT:
1214       TapeStop();
1215
1216       if (TAPE_IS_EMPTY(tape))
1217       {
1218         LoadTape(level_nr);
1219
1220         if (TAPE_IS_EMPTY(tape))
1221           Request("No tape for this level!", REQ_CONFIRM);
1222       }
1223       else
1224       {
1225         if (tape.changed)
1226           SaveTapeChecked(tape.level_nr);
1227
1228         TapeErase();
1229       }
1230
1231       DrawCompleteVideoDisplay();
1232       break;
1233
1234     case TAPE_CTRL_ID_EXTRA:
1235       if (tape.playing)
1236       {
1237         if (!tape.warp_forward)                 /* PLAY -> WARP FORWARD PLAY */
1238         {
1239           TapeStartWarpForward();
1240         }
1241         else                                    /* WARP FORWARD PLAY -> PLAY */
1242         {
1243           TapeStopWarpForward();
1244         }
1245       }
1246       else if (tape.recording)
1247         TapeSingleStep();
1248
1249       break;
1250
1251     case TAPE_CTRL_ID_STOP:
1252       TapeStop();
1253       break;
1254
1255     case TAPE_CTRL_ID_PAUSE:
1256       TapeTogglePause(TAPE_TOGGLE_MANUAL);
1257       break;
1258
1259     case TAPE_CTRL_ID_RECORD:
1260       if (TAPE_IS_STOPPED(tape))
1261         TapeStartGameRecording();
1262       else if (tape.pausing)
1263       {
1264         if (tape.playing)                       /* PLAY -> PAUSE -> RECORD */
1265           TapeAppendRecording();
1266         else
1267           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1268       }
1269       break;
1270
1271     case TAPE_CTRL_ID_PLAY:
1272       if (TAPE_IS_EMPTY(tape))
1273         break;
1274
1275       if (TAPE_IS_STOPPED(tape))
1276       {
1277         TapeStartGamePlaying();
1278       }
1279       else if (tape.playing)
1280       {
1281         if (tape.pausing)                       /* PAUSE -> PLAY */
1282         {
1283           TapeTogglePause(TAPE_TOGGLE_MANUAL);
1284         }
1285         else if (!tape.fast_forward)            /* PLAY -> FAST FORWARD PLAY */
1286         {
1287           tape.fast_forward = TRUE;
1288           DrawVideoDisplay(VIDEO_STATE_FFWD_ON, 0);
1289         }
1290         else if (!tape.pause_before_death)      /* FFWD PLAY -> AUTO PAUSE */
1291         {
1292           tape.pause_before_death = TRUE;
1293           DrawVideoDisplay(VIDEO_STATE_FFWD_OFF | VIDEO_STATE_PBEND_ON, 0);
1294         }
1295         else                                    /* AUTO PAUSE -> NORMAL PLAY */
1296         {
1297           if (tape.warp_forward)
1298             TapeStopWarpForward();
1299
1300           tape.fast_forward = FALSE;
1301           tape.pause_before_death = FALSE;
1302
1303           DrawVideoDisplay(VIDEO_STATE_PBEND_OFF | VIDEO_STATE_PLAY_ON, 0);
1304         }
1305       }
1306       break;
1307
1308     default:
1309       break;
1310   }
1311 }
1312
1313 static void HandleTapeButtons(struct GadgetInfo *gi)
1314 {
1315   HandleTapeButtonsExt(gi->custom_id);
1316 }
1317
1318 void HandleTapeButtonKeys(Key key)
1319 {
1320   boolean eject_button_is_active = TAPE_IS_STOPPED(tape);
1321   boolean extra_button_is_active = !eject_button_is_active;
1322
1323   if (key == setup.shortcut.tape_eject && eject_button_is_active)
1324     HandleTapeButtonsExt(TAPE_CTRL_ID_EJECT);
1325   else if (key == setup.shortcut.tape_extra && extra_button_is_active)
1326     HandleTapeButtonsExt(TAPE_CTRL_ID_EXTRA);
1327   else if (key == setup.shortcut.tape_stop)
1328     HandleTapeButtonsExt(TAPE_CTRL_ID_STOP);
1329   else if (key == setup.shortcut.tape_pause)
1330     HandleTapeButtonsExt(TAPE_CTRL_ID_PAUSE);
1331   else if (key == setup.shortcut.tape_record)
1332     HandleTapeButtonsExt(TAPE_CTRL_ID_RECORD);
1333   else if (key == setup.shortcut.tape_play)
1334     HandleTapeButtonsExt(TAPE_CTRL_ID_PLAY);
1335 }