rnd-20040222-1-src
[rocksndiamonds.git] / src / tools.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * tools.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "game.h"
18 #include "events.h"
19 #include "cartoons.h"
20 #include "network.h"
21 #include "tape.h"
22
23 /* tool button identifiers */
24 #define TOOL_CTRL_ID_YES        0
25 #define TOOL_CTRL_ID_NO         1
26 #define TOOL_CTRL_ID_CONFIRM    2
27 #define TOOL_CTRL_ID_PLAYER_1   3
28 #define TOOL_CTRL_ID_PLAYER_2   4
29 #define TOOL_CTRL_ID_PLAYER_3   5
30 #define TOOL_CTRL_ID_PLAYER_4   6
31
32 #define NUM_TOOL_BUTTONS        7
33
34 /* forward declaration for internal use */
35 static void UnmapToolButtons();
36 static void HandleToolButtons(struct GadgetInfo *);
37 static int el_act_dir2crm(int, int, int);
38 static int el_act2crm(int, int);
39
40 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
41 static int request_gadget_id = -1;
42
43 void DumpTile(int x, int y)
44 {
45   int sx = SCREENX(x);
46   int sy = SCREENX(y);
47
48   printf_line("-", 79);
49   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
50   printf_line("-", 79);
51
52   if (!IN_LEV_FIELD(x, y))
53   {
54     printf("(not in level field)\n");
55     printf("\n");
56
57     return;
58   }
59
60   printf("  Feld:        %d ['%s']\n", Feld[x][y],
61          element_info[Feld[x][y]].token_name);
62   printf("  Back:        %d\n", Back[x][y]);
63   printf("  Store:       %d\n", Store[x][y]);
64   printf("  Store2:      %d\n", Store2[x][y]);
65   printf("  StorePlayer: %d\n", StorePlayer[x][y]);
66   printf("  MovPos:      %d\n", MovPos[x][y]);
67   printf("  MovDir:      %d\n", MovDir[x][y]);
68   printf("  MovDelay:    %d\n", MovDelay[x][y]);
69   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
70   printf("  GfxElement:  %d\n", GfxElement[x][y]);
71   printf("  GfxAction:   %d\n", GfxAction[x][y]);
72   printf("  GfxFrame:    %d\n", GfxFrame[x][y]);
73   printf("\n");
74 }
75
76 void SetDrawtoField(int mode)
77 {
78   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
79   {
80     FX = TILEX;
81     FY = TILEY;
82     BX1 = -1;
83     BY1 = -1;
84     BX2 = SCR_FIELDX;
85     BY2 = SCR_FIELDY;
86     redraw_x1 = 1;
87     redraw_y1 = 1;
88
89     drawto_field = fieldbuffer;
90   }
91   else  /* DRAW_DIRECT, DRAW_BACKBUFFER */
92   {
93     FX = SX;
94     FY = SY;
95     BX1 = 0;
96     BY1 = 0;
97     BX2 = SCR_FIELDX - 1;
98     BY2 = SCR_FIELDY - 1;
99     redraw_x1 = 0;
100     redraw_y1 = 0;
101
102     drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
103   }
104 }
105
106 void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
107 {
108   if (game_status == GAME_MODE_PLAYING && !game.envelope_active)
109   {
110     if (force_redraw)
111     {
112       x = gfx.sx - TILEX;
113       y = gfx.sy - TILEY;
114       width = gfx.sxsize + 2 * TILEX;
115       height = gfx.sysize + 2 * TILEY;
116     }
117
118     if (force_redraw || setup.direct_draw)
119     {
120       int xx, yy;
121       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
122       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
123
124       if (setup.direct_draw)
125         SetDrawtoField(DRAW_BACKBUFFER);
126
127       for (xx = BX1; xx <= BX2; xx++)
128         for (yy = BY1; yy <= BY2; yy++)
129           if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
130             DrawScreenField(xx, yy);
131       DrawAllPlayers();
132
133       if (setup.direct_draw)
134         SetDrawtoField(DRAW_DIRECT);
135     }
136
137     if (setup.soft_scrolling)
138     {
139       int fx = FX, fy = FY;
140
141       fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
142       fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
143
144       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
145     }
146   }
147
148   BlitBitmap(drawto, window, x, y, width, height, x, y);
149 }
150
151 void BackToFront()
152 {
153   int x,y;
154   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
155
156   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
157     redraw_mask &= ~REDRAW_MAIN;
158
159   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
160     redraw_mask |= REDRAW_FIELD;
161
162   if (redraw_mask & REDRAW_FIELD)
163     redraw_mask &= ~REDRAW_TILES;
164
165   if (redraw_mask == REDRAW_NONE)
166     return;
167
168   if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
169   {
170     static boolean last_frame_skipped = FALSE;
171     boolean skip_even_when_not_scrolling = TRUE;
172     boolean just_scrolling = (ScreenMovDir != 0);
173     boolean verbose = FALSE;
174
175     if (global.fps_slowdown_factor > 1 &&
176         (FrameCounter % global.fps_slowdown_factor) &&
177         (just_scrolling || skip_even_when_not_scrolling))
178     {
179       redraw_mask &= ~REDRAW_MAIN;
180
181       last_frame_skipped = TRUE;
182
183       if (verbose)
184         printf("FRAME SKIPPED\n");
185     }
186     else
187     {
188       if (last_frame_skipped)
189         redraw_mask |= REDRAW_FIELD;
190
191       last_frame_skipped = FALSE;
192
193       if (verbose)
194         printf("frame not skipped\n");
195     }
196   }
197
198   /* synchronize X11 graphics at this point; if we would synchronize the
199      display immediately after the buffer switching (after the XFlush),
200      this could mean that we have to wait for the graphics to complete,
201      although we could go on doing calculations for the next frame */
202
203   SyncDisplay();
204
205   if (redraw_mask & REDRAW_ALL)
206   {
207     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
208     redraw_mask = 0;
209   }
210
211   if (redraw_mask & REDRAW_FIELD)
212   {
213     if (game_status != GAME_MODE_PLAYING ||
214         redraw_mask & REDRAW_FROM_BACKBUFFER)
215     {
216       BlitBitmap(backbuffer, window,
217                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
218     }
219     else
220     {
221       int fx = FX, fy = FY;
222
223       if (setup.soft_scrolling)
224       {
225         fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
226         fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
227       }
228
229       if (setup.soft_scrolling ||
230           ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
231           ABS(ScreenMovPos) == ScrollStepSize ||
232           redraw_tiles > REDRAWTILES_THRESHOLD)
233       {
234         BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
235
236 #ifdef DEBUG
237 #if 0
238         printf("redrawing all (ScreenGfxPos == %d) because %s\n",
239                ScreenGfxPos,
240                (setup.soft_scrolling ?
241                 "setup.soft_scrolling" :
242                 ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
243                 "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
244                 ABS(ScreenGfxPos) == ScrollStepSize ?
245                 "ABS(ScreenGfxPos) == ScrollStepSize" :
246                 "redraw_tiles > REDRAWTILES_THRESHOLD"));
247 #endif
248 #endif
249       }
250     }
251
252     redraw_mask &= ~REDRAW_MAIN;
253   }
254
255   if (redraw_mask & REDRAW_DOORS)
256   {
257     if (redraw_mask & REDRAW_DOOR_1)
258       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
259     if (redraw_mask & REDRAW_DOOR_2)
260     {
261       if ((redraw_mask & REDRAW_DOOR_2) == REDRAW_DOOR_2)
262         BlitBitmap(backbuffer, window, VX,VY, VXSIZE,VYSIZE, VX,VY);
263       else
264       {
265         if (redraw_mask & REDRAW_VIDEO_1)
266           BlitBitmap(backbuffer, window,
267                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS,
268                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
269                      VX+VIDEO_DISPLAY1_XPOS,VY+VIDEO_DISPLAY1_YPOS);
270         if (redraw_mask & REDRAW_VIDEO_2)
271           BlitBitmap(backbuffer, window,
272                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS,
273                      VIDEO_DISPLAY_XSIZE,VIDEO_DISPLAY_YSIZE,
274                      VX+VIDEO_DISPLAY2_XPOS,VY+VIDEO_DISPLAY2_YPOS);
275         if (redraw_mask & REDRAW_VIDEO_3)
276           BlitBitmap(backbuffer, window,
277                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS,
278                      VIDEO_CONTROL_XSIZE,VIDEO_CONTROL_YSIZE,
279                      VX+VIDEO_CONTROL_XPOS,VY+VIDEO_CONTROL_YPOS);
280       }
281     }
282
283     if (redraw_mask & REDRAW_DOOR_3)
284       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
285
286     redraw_mask &= ~REDRAW_DOORS;
287   }
288
289   if (redraw_mask & REDRAW_MICROLEVEL)
290   {
291     BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
292                SX, SY + 10 * TILEY);
293
294     redraw_mask &= ~REDRAW_MICROLEVEL;
295   }
296
297   if (redraw_mask & REDRAW_TILES)
298   {
299     for (x = 0; x < SCR_FIELDX; x++)
300       for (y =0 ; y < SCR_FIELDY; y++)
301         if (redraw[redraw_x1 + x][redraw_y1 + y])
302           BlitBitmap(buffer, window,
303                      FX + x * TILEX, FX + y * TILEY, TILEX, TILEY,
304                      SX + x * TILEX, SY + y * TILEY);
305   }
306
307   if (redraw_mask & REDRAW_FPS)         /* display frames per second */
308   {
309     char text[100];
310     char info1[100];
311
312     sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
313     if (!global.fps_slowdown)
314       info1[0] = '\0';
315
316     sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
317     DrawTextExt(window, SX, SY, text, FONT_TEXT_2, BLIT_OPAQUE);
318   }
319
320   FlushDisplay();
321
322   for (x = 0; x < MAX_BUF_XSIZE; x++)
323     for (y = 0; y < MAX_BUF_YSIZE; y++)
324       redraw[x][y] = 0;
325   redraw_tiles = 0;
326   redraw_mask = REDRAW_NONE;
327 }
328
329 void FadeToFront()
330 {
331 #if 0
332   long fading_delay = 300;
333
334   if (setup.fading && (redraw_mask & REDRAW_FIELD))
335   {
336 #endif
337
338 #if 0
339     int x,y;
340
341     ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
342     FlushDisplay();
343
344     for (i = 0; i < 2 * FULL_SYSIZE; i++)
345     {
346       for (y = 0; y < FULL_SYSIZE; y++)
347       {
348         BlitBitmap(backbuffer, window,
349                    REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
350       }
351       FlushDisplay();
352       Delay(10);
353     }
354 #endif
355
356 #if 0
357     for (i = 1; i < FULL_SYSIZE; i+=2)
358       BlitBitmap(backbuffer, window,
359                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
360     FlushDisplay();
361     Delay(fading_delay);
362 #endif
363
364 #if 0
365     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
366     BlitBitmapMasked(backbuffer, window,
367                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
368                      REAL_SX,REAL_SY);
369     FlushDisplay();
370     Delay(fading_delay);
371
372     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
373     BlitBitmapMasked(backbuffer, window,
374                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
375                      REAL_SX,REAL_SY);
376     FlushDisplay();
377     Delay(fading_delay);
378
379     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
380     BlitBitmapMasked(backbuffer, window,
381                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
382                      REAL_SX,REAL_SY);
383     FlushDisplay();
384     Delay(fading_delay);
385
386     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
387     BlitBitmapMasked(backbuffer, window,
388                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
389                      REAL_SX,REAL_SY);
390     FlushDisplay();
391     Delay(fading_delay);
392
393     redraw_mask &= ~REDRAW_MAIN;
394   }
395 #endif
396
397   BackToFront();
398 }
399
400 void SetMainBackgroundImage(int graphic)
401 {
402   SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
403                           graphic_info[graphic].bitmap ?
404                           graphic_info[graphic].bitmap :
405                           graphic_info[IMG_BACKGROUND].bitmap);
406 }
407
408 void SetDoorBackgroundImage(int graphic)
409 {
410   SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
411                           graphic_info[graphic].bitmap ?
412                           graphic_info[graphic].bitmap :
413                           graphic_info[IMG_BACKGROUND].bitmap);
414 }
415
416 void DrawBackground(int dest_x, int dest_y, int width, int height)
417 {
418   ClearRectangleOnBackground(backbuffer, dest_x, dest_y, width, height);
419
420   redraw_mask |= REDRAW_FIELD;
421 }
422
423 void ClearWindow()
424 {
425   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
426
427   if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
428   {
429     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
430     SetDrawtoField(DRAW_BUFFERED);
431   }
432   else
433     SetDrawtoField(DRAW_BACKBUFFER);
434
435   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
436   {
437     ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
438     SetDrawtoField(DRAW_DIRECT);
439   }
440 }
441
442 void MarkTileDirty(int x, int y)
443 {
444   int xx = redraw_x1 + x;
445   int yy = redraw_y1 + y;
446
447   if (!redraw[xx][yy])
448     redraw_tiles++;
449
450   redraw[xx][yy] = TRUE;
451   redraw_mask |= REDRAW_TILES;
452 }
453
454 void SetBorderElement()
455 {
456   int x, y;
457
458   BorderElement = EL_EMPTY;
459
460   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
461   {
462     for (x = 0; x < lev_fieldx; x++)
463     {
464       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
465         BorderElement = EL_STEELWALL;
466
467       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
468         x = lev_fieldx - 2;
469     }
470   }
471 }
472
473 void SetRandomAnimationValue(int x, int y)
474 {
475   gfx.anim_random_frame = GfxRandom[x][y];
476 }
477
478 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
479 {
480   /* animation synchronized with global frame counter, not move position */
481   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
482     sync_frame = FrameCounter;
483
484   return getAnimationFrame(graphic_info[graphic].anim_frames,
485                            graphic_info[graphic].anim_delay,
486                            graphic_info[graphic].anim_mode,
487                            graphic_info[graphic].anim_start_frame,
488                            sync_frame);
489 }
490
491 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
492                                     int graphic, int sync_frame, int mask_mode)
493 {
494   int frame = getGraphicAnimationFrame(graphic, sync_frame);
495
496   if (mask_mode == USE_MASKING)
497     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
498   else
499     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
500 }
501
502 inline void DrawGraphicAnimation(int x, int y, int graphic)
503 {
504   int lx = LEVELX(x), ly = LEVELY(y);
505
506   if (!IN_SCR_FIELD(x, y))
507     return;
508
509   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
510                           graphic, GfxFrame[lx][ly], NO_MASKING);
511   MarkTileDirty(x, y);
512 }
513
514 void DrawLevelGraphicAnimation(int x, int y, int graphic)
515 {
516   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
517 }
518
519 void DrawLevelElementAnimation(int x, int y, int element)
520 {
521 #if 1
522   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
523
524   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
525 #else
526   DrawGraphicAnimation(SCREENX(x), SCREENY(y), el2img(element));
527 #endif
528 }
529
530 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
531 {
532   int sx = SCREENX(x), sy = SCREENY(y);
533
534   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
535     return;
536
537   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
538     return;
539
540   DrawGraphicAnimation(sx, sy, graphic);
541
542   if (GFX_CRUMBLED(Feld[x][y]))
543     DrawLevelFieldCrumbledSand(x, y);
544 }
545
546 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
547 {
548   int sx = SCREENX(x), sy = SCREENY(y);
549   int graphic;
550
551   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
552     return;
553
554   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
555
556   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
557     return;
558
559   DrawGraphicAnimation(sx, sy, graphic);
560
561   if (GFX_CRUMBLED(element))
562     DrawLevelFieldCrumbledSand(x, y);
563 }
564
565 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
566 {
567   if (player->use_murphy_graphic)
568   {
569     /* this works only because currently only one player can be "murphy" ... */
570     static int last_horizontal_dir = MV_LEFT;
571     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
572
573     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
574       last_horizontal_dir = move_dir;
575
576     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
577     {
578       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
579
580       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
581     }
582
583     return graphic;
584   }
585   else
586     return el_act_dir2img(player->element_nr, player->GfxAction, move_dir);
587 }
588
589 static boolean equalGraphics(int graphic1, int graphic2)
590 {
591   struct GraphicInfo *g1 = &graphic_info[graphic1];
592   struct GraphicInfo *g2 = &graphic_info[graphic2];
593
594   return (g1->bitmap      == g2->bitmap &&
595           g1->src_x       == g2->src_x &&
596           g1->src_y       == g2->src_y &&
597           g1->anim_frames == g2->anim_frames &&
598           g1->anim_delay  == g2->anim_delay &&
599           g1->anim_mode   == g2->anim_mode);
600 }
601
602 void DrawAllPlayers()
603 {
604   int i;
605
606   for (i = 0; i < MAX_PLAYERS; i++)
607     if (stored_player[i].active)
608       DrawPlayer(&stored_player[i]);
609 }
610
611 void DrawPlayerField(int x, int y)
612 {
613   if (!IS_PLAYER(x, y))
614     return;
615
616   DrawPlayer(PLAYERINFO(x, y));
617 }
618
619 void DrawPlayer(struct PlayerInfo *player)
620 {
621   int jx = player->jx;
622   int jy = player->jy;
623   int move_dir = player->MovDir;
624 #if 0
625   int last_jx = player->last_jx;
626   int last_jy = player->last_jy;
627   int next_jx = jx + (jx - last_jx);
628   int next_jy = jy + (jy - last_jy);
629   boolean player_is_moving = (last_jx != jx || last_jy != jy ? TRUE : FALSE);
630 #else
631   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
632   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
633   int last_jx = (player->is_moving ? jx - dx : jx);
634   int last_jy = (player->is_moving ? jy - dy : jy);
635   int next_jx = jx + dx;
636   int next_jy = jy + dy;
637   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
638 #endif
639   int sx = SCREENX(jx), sy = SCREENY(jy);
640   int sxx = 0, syy = 0;
641   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
642   int graphic;
643   int action = ACTION_DEFAULT;
644   int last_player_graphic = getPlayerGraphic(player, move_dir);
645   int last_player_frame = player->Frame;
646   int frame = 0;
647
648   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
649     return;
650
651 #if DEBUG
652   if (!IN_LEV_FIELD(jx, jy))
653   {
654     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
655     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
656     printf("DrawPlayerField(): This should never happen!\n");
657     return;
658   }
659 #endif
660
661   if (element == EL_EXPLOSION)
662     return;
663
664   action = (player->is_pushing    ? ACTION_PUSHING         :
665             player->is_digging    ? ACTION_DIGGING         :
666             player->is_collecting ? ACTION_COLLECTING      :
667             player->is_moving     ? ACTION_MOVING          :
668             player->is_snapping   ? ACTION_SNAPPING        :
669             player->is_dropping   ? ACTION_DROPPING        :
670             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
671
672   InitPlayerGfxAnimation(player, action, move_dir);
673
674   /* ----------------------------------------------------------------------- */
675   /* draw things in the field the player is leaving, if needed               */
676   /* ----------------------------------------------------------------------- */
677
678 #if 1
679   if (player->is_moving)
680 #else
681   if (player_is_moving)
682 #endif
683   {
684     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
685     {
686       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
687
688       if (last_element == EL_DYNAMITE_ACTIVE ||
689           last_element == EL_SP_DISK_RED_ACTIVE)
690         DrawDynamite(last_jx, last_jy);
691       else
692         DrawLevelFieldThruMask(last_jx, last_jy);
693     }
694     else if (last_element == EL_DYNAMITE_ACTIVE ||
695              last_element == EL_SP_DISK_RED_ACTIVE)
696       DrawDynamite(last_jx, last_jy);
697     else
698       DrawLevelField(last_jx, last_jy);
699
700     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
701       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
702   }
703
704   if (!IN_SCR_FIELD(sx, sy))
705     return;
706
707   if (setup.direct_draw)
708     SetDrawtoField(DRAW_BUFFERED);
709
710   /* ----------------------------------------------------------------------- */
711   /* draw things behind the player, if needed                                */
712   /* ----------------------------------------------------------------------- */
713
714   if (Back[jx][jy])
715     DrawLevelElement(jx, jy, Back[jx][jy]);
716   else if (IS_ACTIVE_BOMB(element))
717     DrawLevelElement(jx, jy, EL_EMPTY);
718   else
719   {
720     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
721     {
722       if (GFX_CRUMBLED(GfxElement[jx][jy]))
723         DrawLevelFieldCrumbledSandDigging(jx, jy, move_dir, player->StepFrame);
724       else
725       {
726         int old_element = GfxElement[jx][jy];
727         int old_graphic = el_act_dir2img(old_element, action, move_dir);
728         int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
729
730         DrawGraphic(sx, sy, old_graphic, frame);
731       }
732     }
733     else
734     {
735       GfxElement[jx][jy] = EL_UNDEFINED;
736
737       DrawLevelField(jx, jy);
738     }
739   }
740
741   /* ----------------------------------------------------------------------- */
742   /* draw player himself                                                     */
743   /* ----------------------------------------------------------------------- */
744
745 #if 1
746
747   graphic = getPlayerGraphic(player, move_dir);
748
749   /* in the case of changed player action or direction, prevent the current
750      animation frame from being restarted for identical animations */
751   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
752     player->Frame = last_player_frame;
753
754 #else
755
756   if (player->use_murphy_graphic)
757   {
758     static int last_horizontal_dir = MV_LEFT;
759
760     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
761       last_horizontal_dir = move_dir;
762
763     graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
764
765     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
766     {
767       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
768
769       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
770     }
771   }
772   else
773     graphic = el_act_dir2img(player->element_nr, player->GfxAction, move_dir);
774
775 #endif
776
777   frame = getGraphicAnimationFrame(graphic, player->Frame);
778
779   if (player->GfxPos)
780   {
781     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
782       sxx = player->GfxPos;
783     else
784       syy = player->GfxPos;
785   }
786
787   if (!setup.soft_scrolling && ScreenMovPos)
788     sxx = syy = 0;
789
790   DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
791
792   if (SHIELD_ON(player))
793   {
794     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
795                    IMG_SHIELD_NORMAL_ACTIVE);
796     int frame = getGraphicAnimationFrame(graphic, -1);
797
798     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
799   }
800
801   /* ----------------------------------------------------------------------- */
802   /* draw things the player is pushing, if needed                            */
803   /* ----------------------------------------------------------------------- */
804
805 #if 0
806   printf("::: %d, %d [%d, %d] [%d]\n",
807          player->is_pushing, player_is_moving, player->GfxAction,
808          player->is_moving, player_is_moving);
809 #endif
810
811 #if 1
812   if (player->is_pushing && player->is_moving)
813 #else
814   if (player->is_pushing && player_is_moving)
815 #endif
816   {
817     int px = SCREENX(next_jx), py = SCREENY(next_jy);
818
819     if (Back[next_jx][next_jy])
820       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
821
822     if ((sxx || syy) && element == EL_SOKOBAN_OBJECT)
823       DrawGraphicShiftedThruMask(px, py, sxx, syy, IMG_SOKOBAN_OBJECT, 0,
824                                  NO_CUTTING);
825     else
826     {
827       int element = MovingOrBlocked2Element(next_jx, next_jy);
828       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
829 #if 1
830       int frame = getGraphicAnimationFrame(graphic, player->StepFrame);
831 #else
832       int frame = getGraphicAnimationFrame(graphic, player->Frame);
833 #endif
834
835       DrawGraphicShifted(px, py, sxx, syy, graphic, frame,
836                          NO_CUTTING, NO_MASKING);
837     }
838   }
839
840   /* ----------------------------------------------------------------------- */
841   /* draw things in front of player (active dynamite or dynabombs)           */
842   /* ----------------------------------------------------------------------- */
843
844   if (IS_ACTIVE_BOMB(element))
845   {
846     graphic = el2img(element);
847     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
848
849     if (game.emulation == EMU_SUPAPLEX)
850       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
851     else
852       DrawGraphicThruMask(sx, sy, graphic, frame);
853   }
854
855   if (player_is_moving && last_element == EL_EXPLOSION)
856   {
857     int graphic = el_act2img(GfxElement[last_jx][last_jy], ACTION_EXPLODING);
858     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
859     int phase = ExplodePhase[last_jx][last_jy] - 1;
860     int frame = getGraphicAnimationFrame(graphic, phase - delay);
861
862     if (phase >= delay)
863       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
864   }
865
866   /* ----------------------------------------------------------------------- */
867   /* draw elements the player is just walking/passing through/under          */
868   /* ----------------------------------------------------------------------- */
869
870   /* handle the field the player is leaving ... */
871   if (player_is_moving && IS_ACCESSIBLE_INSIDE(last_element))
872     DrawLevelField(last_jx, last_jy);
873   else if (player_is_moving && IS_ACCESSIBLE_UNDER(last_element))
874     DrawLevelFieldThruMask(last_jx, last_jy);
875
876   /* ... and the field the player is entering */
877   if (IS_ACCESSIBLE_INSIDE(element))
878     DrawLevelField(jx, jy);
879   else if (IS_ACCESSIBLE_UNDER(element))
880     DrawLevelFieldThruMask(jx, jy);
881
882   if (setup.direct_draw)
883   {
884     int dest_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
885     int dest_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
886     int x_size = TILEX * (1 + ABS(jx - last_jx));
887     int y_size = TILEY * (1 + ABS(jy - last_jy));
888
889     BlitBitmap(drawto_field, window,
890                dest_x, dest_y, x_size, y_size, dest_x, dest_y);
891     SetDrawtoField(DRAW_DIRECT);
892   }
893
894   MarkTileDirty(sx, sy);
895 }
896
897 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
898 {
899   struct GraphicInfo *g = &graphic_info[graphic];
900
901   *bitmap = g->bitmap;
902
903   if (g->offset_y == 0)         /* frames are ordered horizontally */
904   {
905     int max_width = g->anim_frames_per_line * g->width;
906
907     *x = (g->src_x + frame * g->offset_x) % max_width;
908     *y = g->src_y + (g->src_x + frame * g->offset_x) / max_width * g->height;
909   }
910   else if (g->offset_x == 0)    /* frames are ordered vertically */
911   {
912     int max_height = g->anim_frames_per_line * g->height;
913
914     *x = g->src_x + (g->src_y + frame * g->offset_y) / max_height * g->width;
915     *y = (g->src_y + frame * g->offset_y) % max_height;
916   }
917   else                          /* frames are ordered diagonally */
918   {
919     *x = g->src_x + frame * g->offset_x;
920     *y = g->src_y + frame * g->offset_y;
921   }
922 }
923
924 void DrawGraphic(int x, int y, int graphic, int frame)
925 {
926 #if DEBUG
927   if (!IN_SCR_FIELD(x, y))
928   {
929     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
930     printf("DrawGraphic(): This should never happen!\n");
931     return;
932   }
933 #endif
934
935   DrawGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic, frame);
936   MarkTileDirty(x, y);
937 }
938
939 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
940                     int frame)
941 {
942   Bitmap *src_bitmap;
943   int src_x, src_y;
944
945   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
946   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
947 }
948
949 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
950 {
951 #if DEBUG
952   if (!IN_SCR_FIELD(x, y))
953   {
954     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
955     printf("DrawGraphicThruMask(): This should never happen!\n");
956     return;
957   }
958 #endif
959
960   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y *TILEY, graphic,
961                          frame);
962   MarkTileDirty(x, y);
963 }
964
965 void DrawGraphicThruMaskExt(DrawBuffer *d, int dest_x, int dest_y, int graphic,
966                             int frame)
967 {
968 #if 1
969   Bitmap *src_bitmap;
970   int src_x, src_y;
971   GC drawing_gc;
972
973   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
974   drawing_gc = src_bitmap->stored_clip_gc;
975 #else
976   GC drawing_gc = src_bitmap->stored_clip_gc;
977   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
978   int src_x = graphic_info[graphic].src_x;
979   int src_y = graphic_info[graphic].src_y;
980   int offset_x = graphic_info[graphic].offset_x;
981   int offset_y = graphic_info[graphic].offset_y;
982
983   src_x += frame * offset_x;
984   src_y += frame * offset_y;
985
986 #endif
987
988   SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
989   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
990 }
991
992 void DrawMiniGraphic(int x, int y, int graphic)
993 {
994   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
995   MarkTileDirty(x / 2, y / 2);
996 }
997
998 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
999 {
1000   struct GraphicInfo *g = &graphic_info[graphic];
1001   int mini_startx = 0;
1002   int mini_starty = g->bitmap->height * 2 / 3;
1003
1004   *bitmap = g->bitmap;
1005   *x = mini_startx + g->src_x / 2;
1006   *y = mini_starty + g->src_y / 2;
1007 }
1008
1009 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1010 {
1011   Bitmap *src_bitmap;
1012   int src_x, src_y;
1013
1014   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1015   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1016 }
1017
1018 void DrawGraphicShifted(int x, int y, int dx, int dy, int graphic, int frame,
1019                         int cut_mode, int mask_mode)
1020 {
1021   Bitmap *src_bitmap;
1022   GC drawing_gc;
1023   int src_x, src_y;
1024   int width = TILEX, height = TILEY;
1025   int cx = 0, cy = 0;
1026   int dest_x, dest_y;
1027
1028   if (graphic < 0)
1029   {
1030     DrawGraphic(x, y, graphic, frame);
1031     return;
1032   }
1033
1034   if (dx || dy)                 /* shifted graphic */
1035   {
1036     if (x < BX1)                /* object enters playfield from the left */
1037     {
1038       x = BX1;
1039       width = dx;
1040       cx = TILEX - dx;
1041       dx = 0;
1042     }
1043     else if (x > BX2)           /* object enters playfield from the right */
1044     {
1045       x = BX2;
1046       width = -dx;
1047       dx = TILEX + dx;
1048     }
1049     else if (x==BX1 && dx < 0)  /* object leaves playfield to the left */
1050     {
1051       width += dx;
1052       cx = -dx;
1053       dx = 0;
1054     }
1055     else if (x==BX2 && dx > 0)  /* object leaves playfield to the right */
1056       width -= dx;
1057     else if (dx)                /* general horizontal movement */
1058       MarkTileDirty(x + SIGN(dx), y);
1059
1060     if (y < BY1)                /* object enters playfield from the top */
1061     {
1062       if (cut_mode==CUT_BELOW)  /* object completely above top border */
1063         return;
1064
1065       y = BY1;
1066       height = dy;
1067       cy = TILEY - dy;
1068       dy = 0;
1069     }
1070     else if (y > BY2)           /* object enters playfield from the bottom */
1071     {
1072       y = BY2;
1073       height = -dy;
1074       dy = TILEY + dy;
1075     }
1076     else if (y==BY1 && dy < 0)  /* object leaves playfield to the top */
1077     {
1078       height += dy;
1079       cy = -dy;
1080       dy = 0;
1081     }
1082     else if (dy > 0 && cut_mode == CUT_ABOVE)
1083     {
1084       if (y == BY2)             /* object completely above bottom border */
1085         return;
1086
1087       height = dy;
1088       cy = TILEY - dy;
1089       dy = TILEY;
1090       MarkTileDirty(x, y + 1);
1091     }                           /* object leaves playfield to the bottom */
1092     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1093       height -= dy;
1094     else if (dy)                /* general vertical movement */
1095       MarkTileDirty(x, y + SIGN(dy));
1096   }
1097
1098 #if 1
1099   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1100 #else
1101   src_bitmap = graphic_info[graphic].bitmap;
1102   src_x = graphic_info[graphic].src_x;
1103   src_y = graphic_info[graphic].src_y;
1104   offset_x = graphic_info[graphic].offset_x;
1105   offset_y = graphic_info[graphic].offset_y;
1106
1107   src_x += frame * offset_x;
1108   src_y += frame * offset_y;
1109 #endif
1110
1111   drawing_gc = src_bitmap->stored_clip_gc;
1112
1113   src_x += cx;
1114   src_y += cy;
1115
1116   dest_x = FX + x * TILEX + dx;
1117   dest_y = FY + y * TILEY + dy;
1118
1119 #if DEBUG
1120   if (!IN_SCR_FIELD(x,y))
1121   {
1122     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1123     printf("DrawGraphicShifted(): This should never happen!\n");
1124     return;
1125   }
1126 #endif
1127
1128   if (mask_mode == USE_MASKING)
1129   {
1130     SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
1131     BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1132                      dest_x, dest_y);
1133   }
1134   else
1135     BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1136                dest_x, dest_y);
1137
1138   MarkTileDirty(x, y);
1139 }
1140
1141 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1142                                 int frame, int cut_mode)
1143 {
1144   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1145 }
1146
1147 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1148                           int cut_mode, int mask_mode)
1149 {
1150   int lx = LEVELX(x), ly = LEVELY(y);
1151   int graphic;
1152   int frame;
1153
1154   if (IN_LEV_FIELD(lx, ly))
1155   {
1156     SetRandomAnimationValue(lx, ly);
1157
1158     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1159     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1160   }
1161   else  /* border element */
1162   {
1163     graphic = el2img(element);
1164     frame = getGraphicAnimationFrame(graphic, -1);
1165   }
1166
1167   if (element == EL_EXPANDABLE_WALL)
1168   {
1169     boolean left_stopped = FALSE, right_stopped = FALSE;
1170
1171     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1172       left_stopped = TRUE;
1173     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1174       right_stopped = TRUE;
1175
1176     if (left_stopped && right_stopped)
1177       graphic = IMG_WALL;
1178     else if (left_stopped)
1179     {
1180       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1181       frame = graphic_info[graphic].anim_frames - 1;
1182     }
1183     else if (right_stopped)
1184     {
1185       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1186       frame = graphic_info[graphic].anim_frames - 1;
1187     }
1188   }
1189
1190   if (dx || dy)
1191     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1192   else if (mask_mode == USE_MASKING)
1193     DrawGraphicThruMask(x, y, graphic, frame);
1194   else
1195     DrawGraphic(x, y, graphic, frame);
1196 }
1197
1198 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1199                          int cut_mode, int mask_mode)
1200 {
1201   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1202     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1203                          cut_mode, mask_mode);
1204 }
1205
1206 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1207                               int cut_mode)
1208 {
1209   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1210 }
1211
1212 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1213                              int cut_mode)
1214 {
1215   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1216 }
1217
1218 void DrawLevelElementThruMask(int x, int y, int element)
1219 {
1220   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1221 }
1222
1223 void DrawLevelFieldThruMask(int x, int y)
1224 {
1225   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1226 }
1227
1228 #define TILE_GFX_ELEMENT(x, y)                                              \
1229         (GfxElement[x][y] != EL_UNDEFINED && Feld[x][y] != EL_EXPLOSION ?   \
1230          GfxElement[x][y] : Feld[x][y])
1231
1232 static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
1233 {
1234   Bitmap *src_bitmap;
1235   int src_x, src_y;
1236   int sx = SCREENX(x), sy = SCREENY(y);
1237   int element;
1238   int width, height, cx, cy, i;
1239 #if 1
1240   int crumbled_border_size = graphic_info[graphic].border_size;
1241 #else
1242   int snip = TILEX / 8; /* number of border pixels from "crumbled graphic" */
1243 #endif
1244   static int xy[4][2] =
1245   {
1246     { 0, -1 },
1247     { -1, 0 },
1248     { +1, 0 },
1249     { 0, +1 }
1250   };
1251
1252 #if 0
1253   if (x == 0 && y == 7)
1254     printf("::: %d, %d [%d]\n", GfxElement[x][y], Feld[x][y],
1255            crumbled_border_size);
1256 #endif
1257
1258   if (!IN_LEV_FIELD(x, y))
1259     return;
1260
1261   element = TILE_GFX_ELEMENT(x, y);
1262
1263   /* crumble field itself */
1264   if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
1265   {
1266     if (!IN_SCR_FIELD(sx, sy))
1267       return;
1268
1269     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1270
1271     for (i = 0; i < 4; i++)
1272     {
1273       int xx = x + xy[i][0];
1274       int yy = y + xy[i][1];
1275
1276 #if 1
1277       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1278                  BorderElement);
1279 #else
1280       element = (IN_LEV_FIELD(xx, yy) ? Feld[xx][yy] : BorderElement);
1281 #endif
1282
1283       /* check if neighbour field is of same type */
1284       if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
1285         continue;
1286
1287 #if 0
1288       if (Feld[x][y] == EL_CUSTOM_START + 123)
1289         printf("::: crumble [%d] THE CHAOS ENGINE (%d, %d): %d, %d\n",
1290                i, Feld[x][y], element,
1291                GFX_CRUMBLED(element), IS_MOVING(x, y));
1292 #endif
1293
1294       if (i == 1 || i == 2)
1295       {
1296         width = crumbled_border_size;
1297         height = TILEY;
1298         cx = (i == 2 ? TILEX - crumbled_border_size : 0);
1299         cy = 0;
1300       }
1301       else
1302       {
1303         width = TILEX;
1304         height = crumbled_border_size;
1305         cx = 0;
1306         cy = (i == 3 ? TILEY - crumbled_border_size : 0);
1307       }
1308
1309       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1310                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
1311     }
1312
1313     MarkTileDirty(sx, sy);
1314   }
1315   else          /* crumble neighbour fields */
1316   {
1317 #if 0
1318     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1319 #endif
1320
1321     for (i = 0; i < 4; i++)
1322     {
1323       int xx = x + xy[i][0];
1324       int yy = y + xy[i][1];
1325       int sxx = sx + xy[i][0];
1326       int syy = sy + xy[i][1];
1327
1328 #if 1
1329       if (!IN_LEV_FIELD(xx, yy) ||
1330           !IN_SCR_FIELD(sxx, syy) ||
1331           IS_MOVING(xx, yy))
1332         continue;
1333
1334       element = TILE_GFX_ELEMENT(xx, yy);
1335
1336       if (!GFX_CRUMBLED(element))
1337         continue;
1338 #else
1339       if (!IN_LEV_FIELD(xx, yy) ||
1340           !IN_SCR_FIELD(sxx, syy) ||
1341           !GFX_CRUMBLED(Feld[xx][yy]) ||
1342           IS_MOVING(xx, yy))
1343         continue;
1344 #endif
1345
1346 #if 1
1347       graphic = el_act2crm(Feld[xx][yy], ACTION_DEFAULT);
1348       crumbled_border_size = graphic_info[graphic].border_size;
1349
1350       getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1351 #endif
1352
1353       if (i == 1 || i == 2)
1354       {
1355         width = crumbled_border_size;
1356         height = TILEY;
1357         cx = (i == 1 ? TILEX - crumbled_border_size : 0);
1358         cy = 0;
1359       }
1360       else
1361       {
1362         width = TILEX;
1363         height = crumbled_border_size;
1364         cx = 0;
1365         cy = (i == 0 ? TILEY - crumbled_border_size : 0);
1366       }
1367
1368       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1369                  width, height, FX + sxx * TILEX + cx, FY + syy * TILEY + cy);
1370
1371       MarkTileDirty(sxx, syy);
1372     }
1373   }
1374 }
1375
1376 void DrawLevelFieldCrumbledSand(int x, int y)
1377 {
1378 #if 1
1379   int graphic;
1380
1381   if (!IN_LEV_FIELD(x, y))
1382     return;
1383
1384   graphic = el_act2crm(Feld[x][y], ACTION_DEFAULT);
1385
1386   DrawLevelFieldCrumbledSandExt(x, y, graphic, 0);
1387 #else
1388   DrawLevelFieldCrumbledSandExt(x, y, IMG_SAND_CRUMBLED, 0);
1389 #endif
1390 }
1391
1392 void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
1393                                        int step_frame)
1394 {
1395 #if 1
1396   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1397   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1398 #else
1399   int graphic1 = el_act_dir2img(EL_SAND,          ACTION_DIGGING, direction);
1400   int graphic2 = el_act_dir2img(EL_SAND_CRUMBLED, ACTION_DIGGING, direction);
1401 #endif
1402   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1403   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1404   int sx = SCREENX(x), sy = SCREENY(y);
1405
1406   DrawGraphic(sx, sy, graphic1, frame1);
1407   DrawLevelFieldCrumbledSandExt(x, y, graphic2, frame2);
1408 }
1409
1410 void DrawLevelFieldCrumbledSandNeighbours(int x, int y)
1411 {
1412   int sx = SCREENX(x), sy = SCREENY(y);
1413   static int xy[4][2] =
1414   {
1415     { 0, -1 },
1416     { -1, 0 },
1417     { +1, 0 },
1418     { 0, +1 }
1419   };
1420   int i;
1421
1422   for (i = 0; i < 4; i++)
1423   {
1424     int xx = x + xy[i][0];
1425     int yy = y + xy[i][1];
1426     int sxx = sx + xy[i][0];
1427     int syy = sy + xy[i][1];
1428
1429     if (!IN_LEV_FIELD(xx, yy) ||
1430         !IN_SCR_FIELD(sxx, syy) ||
1431         !GFX_CRUMBLED(Feld[xx][yy]) ||
1432         IS_MOVING(xx, yy))
1433       continue;
1434
1435     DrawLevelField(xx, yy);
1436   }
1437 }
1438
1439 static int getBorderElement(int x, int y)
1440 {
1441   int border[7][2] =
1442   {
1443     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
1444     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
1445     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1446     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1447     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
1448     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
1449     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
1450   };
1451   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1452   int steel_position = (x == -1         && y == -1              ? 0 :
1453                         x == lev_fieldx && y == -1              ? 1 :
1454                         x == -1         && y == lev_fieldy      ? 2 :
1455                         x == lev_fieldx && y == lev_fieldy      ? 3 :
1456                         x == -1         || x == lev_fieldx      ? 4 :
1457                         y == -1         || y == lev_fieldy      ? 5 : 6);
1458
1459   return border[steel_position][steel_type];
1460 }
1461
1462 void DrawScreenElement(int x, int y, int element)
1463 {
1464   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1465   DrawLevelFieldCrumbledSand(LEVELX(x), LEVELY(y));
1466 }
1467
1468 void DrawLevelElement(int x, int y, int element)
1469 {
1470   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1471     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1472 }
1473
1474 void DrawScreenField(int x, int y)
1475 {
1476   int lx = LEVELX(x), ly = LEVELY(y);
1477   int element, content;
1478
1479   if (!IN_LEV_FIELD(lx, ly))
1480   {
1481     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1482       element = EL_EMPTY;
1483     else
1484       element = getBorderElement(lx, ly);
1485
1486     DrawScreenElement(x, y, element);
1487     return;
1488   }
1489
1490   element = Feld[lx][ly];
1491   content = Store[lx][ly];
1492
1493   if (IS_MOVING(lx, ly))
1494   {
1495     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1496     boolean cut_mode = NO_CUTTING;
1497
1498     if (element == EL_QUICKSAND_EMPTYING ||
1499         element == EL_MAGIC_WALL_EMPTYING ||
1500         element == EL_BD_MAGIC_WALL_EMPTYING ||
1501         element == EL_AMOEBA_DROPPING)
1502       cut_mode = CUT_ABOVE;
1503     else if (element == EL_QUICKSAND_FILLING ||
1504              element == EL_MAGIC_WALL_FILLING ||
1505              element == EL_BD_MAGIC_WALL_FILLING)
1506       cut_mode = CUT_BELOW;
1507
1508     if (cut_mode == CUT_ABOVE)
1509       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
1510     else
1511       DrawScreenElement(x, y, EL_EMPTY);
1512
1513     if (horiz_move)
1514       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1515     else if (cut_mode == NO_CUTTING)
1516       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1517     else
1518       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1519
1520     if (content == EL_ACID)
1521     {
1522       int dir = MovDir[lx][ly];
1523       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1524       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
1525
1526       DrawLevelElementThruMask(newlx, newly, EL_ACID);
1527     }
1528   }
1529   else if (IS_BLOCKED(lx, ly))
1530   {
1531     int oldx, oldy;
1532     int sx, sy;
1533     int horiz_move;
1534     boolean cut_mode = NO_CUTTING;
1535     int element_old, content_old;
1536
1537     Blocked2Moving(lx, ly, &oldx, &oldy);
1538     sx = SCREENX(oldx);
1539     sy = SCREENY(oldy);
1540     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1541                   MovDir[oldx][oldy] == MV_RIGHT);
1542
1543     element_old = Feld[oldx][oldy];
1544     content_old = Store[oldx][oldy];
1545
1546     if (element_old == EL_QUICKSAND_EMPTYING ||
1547         element_old == EL_MAGIC_WALL_EMPTYING ||
1548         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1549         element_old == EL_AMOEBA_DROPPING)
1550       cut_mode = CUT_ABOVE;
1551
1552     DrawScreenElement(x, y, EL_EMPTY);
1553
1554     if (horiz_move)
1555       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1556                                NO_CUTTING);
1557     else if (cut_mode == NO_CUTTING)
1558       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1559                                cut_mode);
1560     else
1561       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1562                                cut_mode);
1563   }
1564   else if (IS_DRAWABLE(element))
1565     DrawScreenElement(x, y, element);
1566   else
1567     DrawScreenElement(x, y, EL_EMPTY);
1568 }
1569
1570 void DrawLevelField(int x, int y)
1571 {
1572   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1573     DrawScreenField(SCREENX(x), SCREENY(y));
1574   else if (IS_MOVING(x, y))
1575   {
1576     int newx,newy;
1577
1578     Moving2Blocked(x, y, &newx, &newy);
1579     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1580       DrawScreenField(SCREENX(newx), SCREENY(newy));
1581   }
1582   else if (IS_BLOCKED(x, y))
1583   {
1584     int oldx, oldy;
1585
1586     Blocked2Moving(x, y, &oldx, &oldy);
1587     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1588       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1589   }
1590 }
1591
1592 void DrawMiniElement(int x, int y, int element)
1593 {
1594   int graphic;
1595
1596   graphic = el2edimg(element);
1597   DrawMiniGraphic(x, y, graphic);
1598 }
1599
1600 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1601 {
1602   int x = sx + scroll_x, y = sy + scroll_y;
1603
1604   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1605     DrawMiniElement(sx, sy, EL_EMPTY);
1606   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1607     DrawMiniElement(sx, sy, Feld[x][y]);
1608   else
1609     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
1610 }
1611
1612 void DrawEnvelopeBackground(int envelope_nr, int startx, int starty,
1613                             int x, int y, int xsize, int ysize, int font_nr)
1614 {
1615   int font_width  = getFontWidth(font_nr);
1616   int font_height = getFontHeight(font_nr);
1617   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1618   Bitmap *src_bitmap;
1619   int src_x, src_y;
1620   int dst_x = SX + startx + x * font_width;
1621   int dst_y = SY + starty + y * font_height;
1622   int width  = graphic_info[graphic].width;
1623   int height = graphic_info[graphic].height;
1624   int inner_width  = MAX(width  - 2 * font_width,  font_width);
1625   int inner_height = MAX(height - 2 * font_height, font_height);
1626   int inner_sx = (width >= 3 * font_width ? font_width : 0);
1627   int inner_sy = (height >= 3 * font_height ? font_height : 0);
1628   boolean draw_masked = graphic_info[graphic].draw_masked;
1629
1630   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1631
1632   if (src_bitmap == NULL || width < font_width || height < font_height)
1633   {
1634     ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
1635     return;
1636   }
1637
1638   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - font_width  :
1639             inner_sx + (x - 1) * font_width  % inner_width);
1640   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - font_height :
1641             inner_sy + (y - 1) * font_height % inner_height);
1642
1643   if (draw_masked)
1644   {
1645     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1646                   dst_x - src_x, dst_y - src_y);
1647     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1648                      dst_x, dst_y);
1649   }
1650   else
1651     BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1652                dst_x, dst_y);
1653 }
1654
1655 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
1656 {
1657   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1658   boolean draw_masked = graphic_info[graphic].draw_masked;
1659   int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1660   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1661   boolean no_delay = (tape.index_search);
1662   unsigned long anim_delay = 0;
1663   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
1664   int anim_delay_value = (no_delay ? 0 : frame_delay_value);
1665   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
1666   int font_width = getFontWidth(font_nr);
1667   int font_height = getFontHeight(font_nr);
1668   int max_xsize = level.envelope_xsize[envelope_nr];
1669   int max_ysize = level.envelope_ysize[envelope_nr];
1670   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
1671   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
1672   int xend = max_xsize;
1673   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
1674   int xstep = (xstart < xend ? 1 : 0);
1675   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
1676   int x, y;
1677
1678   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
1679   {
1680     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
1681     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
1682     int sx = (SXSIZE - xsize * font_width)  / 2;
1683     int sy = (SYSIZE - ysize * font_height) / 2;
1684     int xx, yy;
1685
1686     SetDrawtoField(DRAW_BUFFERED);
1687
1688     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1689
1690     SetDrawtoField(DRAW_BACKBUFFER);
1691
1692     for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++)
1693       DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
1694
1695     DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
1696                        level.envelope_text[envelope_nr], font_nr, max_xsize,
1697                        xsize - 2, ysize - 2, mask_mode);
1698
1699     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1700     BackToFront();
1701
1702     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1703   }
1704 }
1705
1706 void ShowEnvelope(int envelope_nr)
1707 {
1708   int element = EL_ENVELOPE_1 + envelope_nr;
1709   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1710   int sound_opening = element_info[element].sound[ACTION_OPENING];
1711   int sound_closing = element_info[element].sound[ACTION_CLOSING];
1712   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1713   boolean no_delay = (tape.index_search);
1714   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
1715   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
1716   int anim_mode = graphic_info[graphic].anim_mode;
1717   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
1718                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
1719
1720   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
1721
1722   PlaySoundStereo(sound_opening, SOUND_MIDDLE);
1723
1724   if (anim_mode == ANIM_DEFAULT)
1725     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
1726
1727   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
1728
1729   if (tape.playing)
1730     Delay(wait_delay_value);
1731   else
1732     WaitForEventToContinue();
1733
1734   PlaySoundStereo(sound_closing, SOUND_MIDDLE);
1735
1736   if (anim_mode != ANIM_NONE)
1737     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
1738
1739   if (anim_mode == ANIM_DEFAULT)
1740     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
1741
1742   game.envelope_active = FALSE;
1743
1744   SetDrawtoField(DRAW_BUFFERED);
1745
1746   redraw_mask |= REDRAW_FIELD;
1747   BackToFront();
1748 }
1749
1750 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1751 {
1752   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1753   int mini_startx = src_bitmap->width * 3 / 4;
1754   int mini_starty = src_bitmap->height * 2 / 3;
1755   int src_x = mini_startx + graphic_info[graphic].src_x / 8;
1756   int src_y = mini_starty + graphic_info[graphic].src_y / 8;
1757
1758   *bitmap = src_bitmap;
1759   *x = src_x;
1760   *y = src_y;
1761 }
1762
1763 void DrawMicroElement(int xpos, int ypos, int element)
1764 {
1765   Bitmap *src_bitmap;
1766   int src_x, src_y;
1767   int graphic = el2preimg(element);
1768
1769   getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1770   BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
1771              xpos, ypos);
1772 }
1773
1774 void DrawLevel()
1775 {
1776   int x,y;
1777
1778   SetDrawBackgroundMask(REDRAW_NONE);
1779   ClearWindow();
1780
1781   for (x = BX1; x <= BX2; x++)
1782     for (y = BY1; y <= BY2; y++)
1783       DrawScreenField(x, y);
1784
1785   redraw_mask |= REDRAW_FIELD;
1786 }
1787
1788 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1789 {
1790   int x,y;
1791
1792   for (x = 0; x < size_x; x++)
1793     for (y = 0; y < size_y; y++)
1794       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1795
1796   redraw_mask |= REDRAW_FIELD;
1797 }
1798
1799 static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
1800 {
1801   int x, y;
1802
1803   DrawBackground(xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
1804
1805   if (lev_fieldx < STD_LEV_FIELDX)
1806     xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
1807   if (lev_fieldy < STD_LEV_FIELDY)
1808     ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
1809
1810   xpos += MICRO_TILEX;
1811   ypos += MICRO_TILEY;
1812
1813   for (x = -1; x <= STD_LEV_FIELDX; x++)
1814   {
1815     for (y = -1; y <= STD_LEV_FIELDY; y++)
1816     {
1817       int lx = from_x + x, ly = from_y + y;
1818
1819       if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
1820         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1821                          level.field[lx][ly]);
1822       else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
1823                && BorderElement != EL_EMPTY)
1824         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1825                          getBorderElement(lx, ly));
1826     }
1827   }
1828
1829   redraw_mask |= REDRAW_MICROLEVEL;
1830 }
1831
1832 #define MICROLABEL_EMPTY                0
1833 #define MICROLABEL_LEVEL_NAME           1
1834 #define MICROLABEL_CREATED_BY           2
1835 #define MICROLABEL_LEVEL_AUTHOR         3
1836 #define MICROLABEL_IMPORTED_FROM        4
1837 #define MICROLABEL_LEVEL_IMPORT_INFO    5
1838
1839 static void DrawMicroLevelLabelExt(int mode)
1840 {
1841   char label_text[MAX_OUTPUT_LINESIZE + 1];
1842   int max_len_label_text;
1843   int font_nr = FONT_TEXT_2;
1844
1845   if (mode == MICROLABEL_CREATED_BY || mode == MICROLABEL_IMPORTED_FROM)
1846     font_nr = FONT_TEXT_3;
1847
1848   max_len_label_text = SXSIZE / getFontWidth(font_nr);
1849
1850   DrawBackground(SX, MICROLABEL_YPOS, SXSIZE, getFontHeight(font_nr));
1851
1852   strncpy(label_text, (mode == MICROLABEL_LEVEL_NAME ? level.name :
1853                        mode == MICROLABEL_CREATED_BY ? "created by" :
1854                        mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
1855                        mode == MICROLABEL_IMPORTED_FROM ? "imported from" :
1856                        mode == MICROLABEL_LEVEL_IMPORT_INFO ?
1857                        leveldir_current->imported_from : ""),
1858           max_len_label_text);
1859   label_text[max_len_label_text] = '\0';
1860
1861   if (strlen(label_text) > 0)
1862   {
1863     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1864     int lypos = MICROLABEL_YPOS;
1865
1866     DrawText(lxpos, lypos, label_text, font_nr);
1867   }
1868
1869   redraw_mask |= REDRAW_MICROLEVEL;
1870 }
1871
1872 void DrawMicroLevel(int xpos, int ypos, boolean restart)
1873 {
1874   static unsigned long scroll_delay = 0;
1875   static unsigned long label_delay = 0;
1876   static int from_x, from_y, scroll_direction;
1877   static int label_state, label_counter;
1878   int last_game_status = game_status;   /* save current game status */
1879
1880   /* force PREVIEW font on preview level */
1881   game_status = GAME_MODE_PSEUDO_PREVIEW;
1882
1883   if (restart)
1884   {
1885     from_x = from_y = 0;
1886     scroll_direction = MV_RIGHT;
1887     label_state = 1;
1888     label_counter = 0;
1889
1890     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1891     DrawMicroLevelLabelExt(label_state);
1892
1893     /* initialize delay counters */
1894     DelayReached(&scroll_delay, 0);
1895     DelayReached(&label_delay, 0);
1896
1897     if (leveldir_current->name)
1898     {
1899       int text_width = getTextWidth(leveldir_current->name, FONT_TEXT_1);
1900       int lxpos = SX + (SXSIZE - text_width) / 2;
1901       int lypos = SY + 352;
1902
1903       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
1904     }
1905
1906     game_status = last_game_status;     /* restore current game status */
1907
1908     return;
1909   }
1910
1911   /* scroll micro level, if needed */
1912   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1913       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1914   {
1915     switch (scroll_direction)
1916     {
1917       case MV_LEFT:
1918         if (from_x > 0)
1919           from_x--;
1920         else
1921           scroll_direction = MV_UP;
1922         break;
1923
1924       case MV_RIGHT:
1925         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1926           from_x++;
1927         else
1928           scroll_direction = MV_DOWN;
1929         break;
1930
1931       case MV_UP:
1932         if (from_y > 0)
1933           from_y--;
1934         else
1935           scroll_direction = MV_RIGHT;
1936         break;
1937
1938       case MV_DOWN:
1939         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1940           from_y++;
1941         else
1942           scroll_direction = MV_LEFT;
1943         break;
1944
1945       default:
1946         break;
1947     }
1948
1949     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1950   }
1951
1952   /* redraw micro level label, if needed */
1953   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
1954       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
1955       strcmp(level.author, leveldir_current->name) != 0 &&
1956       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1957   {
1958     int max_label_counter = 23;
1959
1960     if (leveldir_current->imported_from != NULL)
1961       max_label_counter += 14;
1962
1963     label_counter = (label_counter + 1) % max_label_counter;
1964     label_state = (label_counter >= 0 && label_counter <= 7 ?
1965                    MICROLABEL_LEVEL_NAME :
1966                    label_counter >= 9 && label_counter <= 12 ?
1967                    MICROLABEL_CREATED_BY :
1968                    label_counter >= 14 && label_counter <= 21 ?
1969                    MICROLABEL_LEVEL_AUTHOR :
1970                    label_counter >= 23 && label_counter <= 26 ?
1971                    MICROLABEL_IMPORTED_FROM :
1972                    label_counter >= 28 && label_counter <= 35 ?
1973                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
1974     DrawMicroLevelLabelExt(label_state);
1975   }
1976
1977   game_status = last_game_status;       /* restore current game status */
1978 }
1979
1980 void WaitForEventToContinue()
1981 {
1982   boolean still_wait = TRUE;
1983
1984   /* simulate releasing mouse button over last gadget, if still pressed */
1985   if (button_status)
1986     HandleGadgets(-1, -1, 0);
1987
1988   button_status = MB_RELEASED;
1989
1990   while (still_wait)
1991   {
1992     if (PendingEvent())
1993     {
1994       Event event;
1995
1996       NextEvent(&event);
1997
1998       switch (event.type)
1999       {
2000         case EVENT_BUTTONPRESS:
2001         case EVENT_KEYPRESS:
2002           still_wait = FALSE;
2003           break;
2004
2005         case EVENT_KEYRELEASE:
2006           ClearPlayerAction();
2007           break;
2008
2009         default:
2010           HandleOtherEvents(&event);
2011           break;
2012       }
2013     }
2014     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2015     {
2016       still_wait = FALSE;
2017     }
2018
2019     DoAnimation();
2020
2021     /* don't eat all CPU time */
2022     Delay(10);
2023   }
2024 }
2025
2026 #define MAX_REQUEST_LINES               13
2027 #define MAX_REQUEST_LINE_FONT1_LEN      7
2028 #define MAX_REQUEST_LINE_FONT2_LEN      10
2029
2030 boolean Request(char *text, unsigned int req_state)
2031 {
2032   int mx, my, ty, result = -1;
2033   unsigned int old_door_state;
2034   int last_game_status = game_status;   /* save current game status */
2035   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
2036   int font_nr = FONT_TEXT_2;
2037   int max_word_len = 0;
2038   char *text_ptr;
2039
2040   for (text_ptr = text; *text_ptr; text_ptr++)
2041   {
2042     max_word_len = (*text_ptr != ' ' ? max_word_len + 1 : 0);
2043
2044     if (max_word_len > MAX_REQUEST_LINE_FONT1_LEN)
2045     {
2046       max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
2047       font_nr = FONT_LEVEL_NUMBER;
2048
2049       break;
2050     }
2051   }
2052
2053 #if 1
2054   SetMouseCursor(CURSOR_DEFAULT);
2055 #endif
2056
2057 #if defined(PLATFORM_UNIX)
2058   /* pause network game while waiting for request to answer */
2059   if (options.network &&
2060       game_status == GAME_MODE_PLAYING &&
2061       req_state & REQUEST_WAIT_FOR)
2062     SendToServer_PausePlaying();
2063 #endif
2064
2065   old_door_state = GetDoorState();
2066
2067   /* simulate releasing mouse button over last gadget, if still pressed */
2068   if (button_status)
2069     HandleGadgets(-1, -1, 0);
2070
2071   UnmapAllGadgets();
2072
2073   CloseDoor(DOOR_CLOSE_1);
2074
2075   /* save old door content */
2076   BlitBitmap(bitmap_db_door, bitmap_db_door,
2077              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2078              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
2079
2080   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2081
2082   /* clear door drawing field */
2083   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2084
2085   /* force DOOR font on preview level */
2086   game_status = GAME_MODE_PSEUDO_DOOR;
2087
2088   /* write text for request */
2089   for (ty = 0; ty < MAX_REQUEST_LINES; ty++)
2090   {
2091     char text_line[max_request_line_len + 1];
2092     int tx, tl, tc = 0;
2093
2094     if (!*text)
2095       break;
2096
2097     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
2098     {
2099       tc = *(text + tx);
2100       if (!tc || tc == ' ')
2101         break;
2102     }
2103
2104     if (!tl)
2105     { 
2106       text++; 
2107       ty--; 
2108       continue; 
2109     }
2110
2111     strncpy(text_line, text, tl);
2112     text_line[tl] = 0;
2113
2114     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
2115              DY + 8 + ty * (getFontHeight(font_nr) + 2),
2116              text_line, font_nr);
2117
2118     text += tl + (tc == ' ' ? 1 : 0);
2119   }
2120
2121   game_status = last_game_status;       /* restore current game status */
2122
2123   if (req_state & REQ_ASK)
2124   {
2125     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2126     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2127   }
2128   else if (req_state & REQ_CONFIRM)
2129   {
2130     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2131   }
2132   else if (req_state & REQ_PLAYER)
2133   {
2134     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2135     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2136     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2137     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2138   }
2139
2140   /* copy request gadgets to door backbuffer */
2141   BlitBitmap(drawto, bitmap_db_door,
2142              DX, DY, DXSIZE, DYSIZE,
2143              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2144
2145   OpenDoor(DOOR_OPEN_1);
2146
2147 #if 0
2148   ClearEventQueue();
2149 #endif
2150
2151   if (!(req_state & REQUEST_WAIT_FOR))
2152   {
2153     SetDrawBackgroundMask(REDRAW_FIELD);
2154
2155     return FALSE;
2156   }
2157
2158   if (game_status != GAME_MODE_MAIN)
2159     InitAnimation();
2160
2161   button_status = MB_RELEASED;
2162
2163   request_gadget_id = -1;
2164
2165   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2166
2167 #if 0
2168   SetMouseCursor(CURSOR_DEFAULT);
2169 #endif
2170
2171   while (result < 0)
2172   {
2173     if (PendingEvent())
2174     {
2175       Event event;
2176
2177       NextEvent(&event);
2178
2179       switch(event.type)
2180       {
2181         case EVENT_BUTTONPRESS:
2182         case EVENT_BUTTONRELEASE:
2183         case EVENT_MOTIONNOTIFY:
2184         {
2185           if (event.type == EVENT_MOTIONNOTIFY)
2186           {
2187             if (!PointerInWindow(window))
2188               continue; /* window and pointer are on different screens */
2189
2190             if (!button_status)
2191               continue;
2192
2193             motion_status = TRUE;
2194             mx = ((MotionEvent *) &event)->x;
2195             my = ((MotionEvent *) &event)->y;
2196           }
2197           else
2198           {
2199             motion_status = FALSE;
2200             mx = ((ButtonEvent *) &event)->x;
2201             my = ((ButtonEvent *) &event)->y;
2202             if (event.type == EVENT_BUTTONPRESS)
2203               button_status = ((ButtonEvent *) &event)->button;
2204             else
2205               button_status = MB_RELEASED;
2206           }
2207
2208           /* this sets 'request_gadget_id' */
2209           HandleGadgets(mx, my, button_status);
2210
2211           switch(request_gadget_id)
2212           {
2213             case TOOL_CTRL_ID_YES:
2214               result = TRUE;
2215               break;
2216             case TOOL_CTRL_ID_NO:
2217               result = FALSE;
2218               break;
2219             case TOOL_CTRL_ID_CONFIRM:
2220               result = TRUE | FALSE;
2221               break;
2222
2223             case TOOL_CTRL_ID_PLAYER_1:
2224               result = 1;
2225               break;
2226             case TOOL_CTRL_ID_PLAYER_2:
2227               result = 2;
2228               break;
2229             case TOOL_CTRL_ID_PLAYER_3:
2230               result = 3;
2231               break;
2232             case TOOL_CTRL_ID_PLAYER_4:
2233               result = 4;
2234               break;
2235
2236             default:
2237               break;
2238           }
2239
2240           break;
2241         }
2242
2243         case EVENT_KEYPRESS:
2244           switch(GetEventKey((KeyEvent *)&event, TRUE))
2245           {
2246             case KSYM_Return:
2247               result = 1;
2248               break;
2249
2250             case KSYM_Escape:
2251               result = 0;
2252               break;
2253
2254             default:
2255               break;
2256           }
2257           if (req_state & REQ_PLAYER)
2258             result = 0;
2259           break;
2260
2261         case EVENT_KEYRELEASE:
2262           ClearPlayerAction();
2263           break;
2264
2265         default:
2266           HandleOtherEvents(&event);
2267           break;
2268       }
2269     }
2270     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2271     {
2272       int joy = AnyJoystick();
2273
2274       if (joy & JOY_BUTTON_1)
2275         result = 1;
2276       else if (joy & JOY_BUTTON_2)
2277         result = 0;
2278     }
2279
2280     DoAnimation();
2281
2282     /* don't eat all CPU time */
2283     Delay(10);
2284   }
2285
2286   if (game_status != GAME_MODE_MAIN)
2287     StopAnimation();
2288
2289   UnmapToolButtons();
2290
2291   if (!(req_state & REQ_STAY_OPEN))
2292   {
2293     CloseDoor(DOOR_CLOSE_1);
2294
2295     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
2296     {
2297       BlitBitmap(bitmap_db_door, bitmap_db_door,
2298                  DOOR_GFX_PAGEX2,DOOR_GFX_PAGEY1, DXSIZE,DYSIZE,
2299                  DOOR_GFX_PAGEX1,DOOR_GFX_PAGEY1);
2300       OpenDoor(DOOR_OPEN_1);
2301     }
2302   }
2303
2304   RemapAllGadgets();
2305
2306   SetDrawBackgroundMask(REDRAW_FIELD);
2307
2308 #if defined(PLATFORM_UNIX)
2309   /* continue network game after request */
2310   if (options.network &&
2311       game_status == GAME_MODE_PLAYING &&
2312       req_state & REQUEST_WAIT_FOR)
2313     SendToServer_ContinuePlaying();
2314 #endif
2315
2316   return result;
2317 }
2318
2319 unsigned int OpenDoor(unsigned int door_state)
2320 {
2321   unsigned int new_door_state;
2322
2323   if (door_state & DOOR_COPY_BACK)
2324   {
2325     BlitBitmap(bitmap_db_door, bitmap_db_door,
2326                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
2327                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2328     door_state &= ~DOOR_COPY_BACK;
2329   }
2330
2331   new_door_state = MoveDoor(door_state);
2332
2333   return(new_door_state);
2334 }
2335
2336 unsigned int CloseDoor(unsigned int door_state)
2337 {
2338   unsigned int new_door_state;
2339
2340   BlitBitmap(backbuffer, bitmap_db_door,
2341              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2342   BlitBitmap(backbuffer, bitmap_db_door,
2343              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2344
2345   new_door_state = MoveDoor(door_state);
2346
2347   return(new_door_state);
2348 }
2349
2350 unsigned int GetDoorState()
2351 {
2352   return MoveDoor(DOOR_GET_STATE);
2353 }
2354
2355 unsigned int SetDoorState(unsigned int door_state)
2356 {
2357   return MoveDoor(door_state | DOOR_SET_STATE);
2358 }
2359
2360 unsigned int MoveDoor(unsigned int door_state)
2361 {
2362   static int door1 = DOOR_OPEN_1;
2363   static int door2 = DOOR_CLOSE_2;
2364   unsigned long door_delay = 0;
2365   unsigned long door_delay_value;
2366   int stepsize = 1;
2367
2368   if (door_state == DOOR_GET_STATE)
2369     return(door1 | door2);
2370
2371   if (door_state & DOOR_SET_STATE)
2372   {
2373     if (door_state & DOOR_ACTION_1)
2374       door1 = door_state & DOOR_ACTION_1;
2375     if (door_state & DOOR_ACTION_2)
2376       door2 = door_state & DOOR_ACTION_2;
2377
2378     return(door1 | door2);
2379   }
2380
2381   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2382     door_state &= ~DOOR_OPEN_1;
2383   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2384     door_state &= ~DOOR_CLOSE_1;
2385   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2386     door_state &= ~DOOR_OPEN_2;
2387   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2388     door_state &= ~DOOR_CLOSE_2;
2389
2390   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
2391                       door_2.step_delay);
2392
2393   if (setup.quick_doors)
2394   {
2395     stepsize = 20;              /* must be choosen to always draw last frame */
2396     door_delay_value = 0;
2397
2398 #if 0
2399     StopSound(SND_DOOR_OPENING);
2400     StopSound(SND_DOOR_CLOSING);
2401 #endif
2402   }
2403
2404   if (global.autoplay_leveldir)
2405   {
2406     door_state |= DOOR_NO_DELAY;
2407     door_state &= ~DOOR_CLOSE_ALL;
2408   }
2409
2410   if (door_state & DOOR_ACTION)
2411   {
2412     boolean door_1_done = !(door_state & DOOR_ACTION_1);
2413     boolean door_2_done = !(door_state & DOOR_ACTION_2);
2414     int start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2415     int end = (door_state & DOOR_ACTION_1 &&
2416                door_1.anim_mode == ANIM_VERTICAL ? DYSIZE : DXSIZE);
2417     int x;
2418
2419     if (!(door_state & DOOR_NO_DELAY) && !setup.quick_doors)
2420     {
2421       /* opening door sound has priority over simultaneously closing door */
2422       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2423         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2424       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2425         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2426     }
2427
2428     for (x = start; x <= end && !(door_1_done && door_2_done); x += stepsize)
2429     {
2430       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2431       GC gc = bitmap->stored_clip_gc;
2432
2433       if (door_state & DOOR_ACTION_1)
2434       {
2435         int a = MIN(x * door_1.step_offset, end);
2436         int i = (door_state & DOOR_OPEN_1 ? end - a : a);
2437
2438         if (x <= a)
2439         {
2440           BlitBitmap(bitmap_db_door, drawto,
2441                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i / 2,
2442                      DXSIZE,DYSIZE - i / 2, DX, DY);
2443
2444           ClearRectangle(drawto, DX, DY + DYSIZE - i / 2, DXSIZE, i / 2);
2445         }
2446
2447         if (door_1.anim_mode == ANIM_HORIZONTAL && x <= DXSIZE)
2448         {
2449           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2450           int dst1_x = DX + DXSIZE - i, dst1_y = DY;
2451           int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
2452           int dst2_x = DX,              dst2_y = DY;
2453           int width = i, height = DYSIZE;
2454
2455           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2456           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2457                            dst1_x, dst1_y);
2458
2459           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2460           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2461                            dst2_x, dst2_y);
2462         }
2463         else if (door_1.anim_mode == ANIM_VERTICAL && x <= DYSIZE)
2464         {
2465           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2466           int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
2467           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
2468           int dst2_x = DX,              dst2_y = DY;
2469           int width = DXSIZE, height = i;
2470
2471           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2472           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2473                            dst1_x, dst1_y);
2474
2475           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2476           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2477                            dst2_x, dst2_y);
2478         }
2479         else if (x <= DXSIZE)   /* ANIM_DEFAULT */
2480         {
2481           int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
2482
2483           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2484           BlitBitmapMasked(bitmap, drawto,
2485                            DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2486                            DX + DXSIZE - i, DY + j);
2487           BlitBitmapMasked(bitmap, drawto,
2488                            DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2489                            DX + DXSIZE - i, DY + 140 + j);
2490           SetClipOrigin(bitmap, gc, DX - DXSIZE + i,
2491                         DY - (DOOR_GFX_PAGEY1 + j));
2492           BlitBitmapMasked(bitmap, drawto,
2493                            DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2494                            DX, DY);
2495           BlitBitmapMasked(bitmap, drawto,
2496                            DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2497                            DX, DY + 140 - j);
2498
2499           BlitBitmapMasked(bitmap, drawto,
2500                            DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2501                            DX, DY + 77 - j);
2502           BlitBitmapMasked(bitmap, drawto,
2503                            DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2504                            DX, DY + 203 - j);
2505           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2506           BlitBitmapMasked(bitmap, drawto,
2507                            DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2508                            DX + DXSIZE - i, DY + 77 + j);
2509           BlitBitmapMasked(bitmap, drawto,
2510                            DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2511                            DX + DXSIZE - i, DY + 203 + j);
2512         }
2513
2514         redraw_mask |= REDRAW_DOOR_1;
2515         door_1_done = (a == end);
2516       }
2517
2518       if (door_state & DOOR_ACTION_2)
2519       {
2520         int a = MIN(x * door_2.step_offset, VXSIZE);
2521         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - a : a);
2522
2523         if (x <= VYSIZE)
2524         {
2525           BlitBitmap(bitmap_db_door, drawto,
2526                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i / 2,
2527                      VXSIZE, VYSIZE - i / 2, VX, VY);
2528
2529           ClearRectangle(drawto, VX, VY + VYSIZE - i / 2, VXSIZE, i / 2);
2530         }
2531
2532         if (door_2.anim_mode == ANIM_HORIZONTAL && x <= VXSIZE)
2533         {
2534           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2535           int dst1_x = VX + VXSIZE - i, dst1_y = VY;
2536           int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
2537           int dst2_x = VX,              dst2_y = VY;
2538           int width = i, height = VYSIZE;
2539
2540           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2541           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2542                            dst1_x, dst1_y);
2543
2544           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2545           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2546                            dst2_x, dst2_y);
2547         }
2548         else if (door_2.anim_mode == ANIM_VERTICAL && x <= VYSIZE)
2549         {
2550           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2551           int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
2552           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
2553           int dst2_x = VX,              dst2_y = VY;
2554           int width = VXSIZE, height = i;
2555
2556           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2557           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2558                            dst1_x, dst1_y);
2559
2560           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2561           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2562                            dst2_x, dst2_y);
2563         }
2564         else if (x <= VXSIZE)   /* ANIM_DEFAULT */
2565         {
2566           int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
2567
2568           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2569           BlitBitmapMasked(bitmap, drawto,
2570                            VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2571                            VX + VXSIZE - i, VY + j);
2572           SetClipOrigin(bitmap, gc,
2573                         VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2574           BlitBitmapMasked(bitmap, drawto,
2575                            VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2576                            VX, VY);
2577
2578           BlitBitmapMasked(bitmap, drawto,
2579                            VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2580                            i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2581           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2582           BlitBitmapMasked(bitmap, drawto,
2583                            VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2584                            i, VYSIZE / 2 - j,
2585                            VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2586         }
2587
2588         redraw_mask |= REDRAW_DOOR_2;
2589         door_2_done = (a == VXSIZE);
2590       }
2591
2592       BackToFront();
2593
2594       if (game_status == GAME_MODE_MAIN)
2595         DoAnimation();
2596
2597       if (!(door_state & DOOR_NO_DELAY))
2598         WaitUntilDelayReached(&door_delay, door_delay_value);
2599     }
2600   }
2601
2602 #if 0
2603   if (setup.quick_doors)
2604   {
2605     StopSound(SND_DOOR_OPENING);
2606     StopSound(SND_DOOR_CLOSING);
2607   }
2608 #endif
2609
2610   if (door_state & DOOR_ACTION_1)
2611     door1 = door_state & DOOR_ACTION_1;
2612   if (door_state & DOOR_ACTION_2)
2613     door2 = door_state & DOOR_ACTION_2;
2614
2615   return (door1 | door2);
2616 }
2617
2618 void DrawSpecialEditorDoor()
2619 {
2620   /* draw bigger toolbox window */
2621   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2622              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2623              EX - 4, EY - 12);
2624   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2625              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2626              EX - 4, EY - 4);
2627
2628   redraw_mask |= REDRAW_ALL;
2629 }
2630
2631 void UndrawSpecialEditorDoor()
2632 {
2633   /* draw normal tape recorder window */
2634   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2635              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2636              EX - 4, EY - 12);
2637
2638   redraw_mask |= REDRAW_ALL;
2639 }
2640
2641
2642 /* ---------- new tool button stuff ---------------------------------------- */
2643
2644 /* graphic position values for tool buttons */
2645 #define TOOL_BUTTON_YES_XPOS            2
2646 #define TOOL_BUTTON_YES_YPOS            250
2647 #define TOOL_BUTTON_YES_GFX_YPOS        0
2648 #define TOOL_BUTTON_YES_XSIZE           46
2649 #define TOOL_BUTTON_YES_YSIZE           28
2650 #define TOOL_BUTTON_NO_XPOS             52
2651 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2652 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2653 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2654 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2655 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2656 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2657 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2658 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2659 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2660 #define TOOL_BUTTON_PLAYER_XSIZE        30
2661 #define TOOL_BUTTON_PLAYER_YSIZE        30
2662 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2663 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2664 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2665 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2666 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2667                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2668 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2669                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2670 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2671                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2672 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2673                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2674 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2675                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2676 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2677                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2678 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2679                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2680 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2681                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2682
2683 static struct
2684 {
2685   int xpos, ypos;
2686   int x, y;
2687   int width, height;
2688   int gadget_id;
2689   char *infotext;
2690 } toolbutton_info[NUM_TOOL_BUTTONS] =
2691 {
2692   {
2693     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2694     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2695     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2696     TOOL_CTRL_ID_YES,
2697     "yes"
2698   },
2699   {
2700     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2701     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2702     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2703     TOOL_CTRL_ID_NO,
2704     "no"
2705   },
2706   {
2707     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2708     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2709     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2710     TOOL_CTRL_ID_CONFIRM,
2711     "confirm"
2712   },
2713   {
2714     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2715     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2716     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2717     TOOL_CTRL_ID_PLAYER_1,
2718     "player 1"
2719   },
2720   {
2721     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2722     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2723     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2724     TOOL_CTRL_ID_PLAYER_2,
2725     "player 2"
2726   },
2727   {
2728     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2729     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2730     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2731     TOOL_CTRL_ID_PLAYER_3,
2732     "player 3"
2733   },
2734   {
2735     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2736     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2737     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2738     TOOL_CTRL_ID_PLAYER_4,
2739     "player 4"
2740   }
2741 };
2742
2743 void CreateToolButtons()
2744 {
2745   int i;
2746
2747   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2748   {
2749     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2750     Bitmap *deco_bitmap = None;
2751     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2752     struct GadgetInfo *gi;
2753     unsigned long event_mask;
2754     int gd_xoffset, gd_yoffset;
2755     int gd_x1, gd_x2, gd_y;
2756     int id = i;
2757
2758     event_mask = GD_EVENT_RELEASED;
2759
2760     gd_xoffset = toolbutton_info[i].xpos;
2761     gd_yoffset = toolbutton_info[i].ypos;
2762     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2763     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2764     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2765
2766     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2767     {
2768       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2769
2770       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2771                            &deco_bitmap, &deco_x, &deco_y);
2772       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2773       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2774     }
2775
2776     gi = CreateGadget(GDI_CUSTOM_ID, id,
2777                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2778                       GDI_X, DX + toolbutton_info[i].x,
2779                       GDI_Y, DY + toolbutton_info[i].y,
2780                       GDI_WIDTH, toolbutton_info[i].width,
2781                       GDI_HEIGHT, toolbutton_info[i].height,
2782                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2783                       GDI_STATE, GD_BUTTON_UNPRESSED,
2784                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2785                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2786                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2787                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2788                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2789                       GDI_DECORATION_SHIFTING, 1, 1,
2790                       GDI_EVENT_MASK, event_mask,
2791                       GDI_CALLBACK_ACTION, HandleToolButtons,
2792                       GDI_END);
2793
2794     if (gi == NULL)
2795       Error(ERR_EXIT, "cannot create gadget");
2796
2797     tool_gadget[id] = gi;
2798   }
2799 }
2800
2801 void FreeToolButtons()
2802 {
2803   int i;
2804
2805   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2806     FreeGadget(tool_gadget[i]);
2807 }
2808
2809 static void UnmapToolButtons()
2810 {
2811   int i;
2812
2813   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2814     UnmapGadget(tool_gadget[i]);
2815 }
2816
2817 static void HandleToolButtons(struct GadgetInfo *gi)
2818 {
2819   request_gadget_id = gi->custom_id;
2820 }
2821
2822 int get_next_element(int element)
2823 {
2824   switch(element)
2825   {
2826     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2827     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2828     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2829     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2830     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2831     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2832     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2833
2834     default:                            return element;
2835   }
2836 }
2837
2838 int el_act_dir2img(int element, int action, int direction)
2839 {
2840   element = GFX_ELEMENT(element);
2841   direction = MV_DIR_BIT(direction);
2842
2843   return element_info[element].direction_graphic[action][direction];
2844 }
2845
2846 static int el_act_dir2crm(int element, int action, int direction)
2847 {
2848   element = GFX_ELEMENT(element);
2849   direction = MV_DIR_BIT(direction);
2850
2851   return element_info[element].direction_crumbled[action][direction];
2852 }
2853
2854 int el_act2img(int element, int action)
2855 {
2856   element = GFX_ELEMENT(element);
2857
2858   return element_info[element].graphic[action];
2859 }
2860
2861 int el_act2crm(int element, int action)
2862 {
2863   element = GFX_ELEMENT(element);
2864
2865   return element_info[element].crumbled[action];
2866 }
2867
2868 int el_dir2img(int element, int direction)
2869 {
2870   element = GFX_ELEMENT(element);
2871
2872   return el_act_dir2img(element, ACTION_DEFAULT, direction);
2873 }
2874
2875 int el2img(int element)
2876 {
2877   element = GFX_ELEMENT(element);
2878
2879   return element_info[element].graphic[ACTION_DEFAULT];
2880 }
2881
2882 int el2edimg(int element)
2883 {
2884   element = GFX_ELEMENT(element);
2885
2886   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
2887 }
2888
2889 int el2preimg(int element)
2890 {
2891   element = GFX_ELEMENT(element);
2892
2893   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
2894 }