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