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