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