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