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