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