fixed asking all network clients to "play again" after unsolved game end
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 static void InitGameControlValues(void)
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 static void UpdatePlayfieldElementCount(void)
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 static void UpdateGameControlValues(void)
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   /* used instead of "level_nr" (for network games) */
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       /* only one player in Supaplex game engine */
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   /* update game panel control frames */
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 static void DisplayGameControlValues(void)
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   /* copy default game door content to main double buffer */
2533
2534   /* !!! CHECK AGAIN !!! */
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   /* redraw game control buttons */
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           /* use dynamic number of digits */
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          /* (used for normal state) */
2728         int font2 = pos->font_alt;      /* (used for active state) */
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           /* don't truncate output if "chars" is zero or less */
2740           size = strlen(s);
2741
2742           /* dynamically correct text alignment */
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues(void)
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 #if 0
2771 static void UpdateGameDoorValues(void)
2772 {
2773   UpdateGameControlValues();
2774 }
2775 #endif
2776
2777 void DrawGameDoorValues(void)
2778 {
2779   DisplayGameControlValues();
2780 }
2781
2782
2783 /*
2784   =============================================================================
2785   InitGameEngine()
2786   -----------------------------------------------------------------------------
2787   initialize game engine due to level / tape version number
2788   =============================================================================
2789 */
2790
2791 static void InitGameEngine(void)
2792 {
2793   int i, j, k, l, x, y;
2794
2795   /* set game engine from tape file when re-playing, else from level file */
2796   game.engine_version = (tape.playing ? tape.engine_version :
2797                          level.game_version);
2798
2799   /* set single or multi-player game mode (needed for re-playing tapes) */
2800   game.team_mode = setup.team_mode;
2801
2802   if (tape.playing)
2803   {
2804     int num_players = 0;
2805
2806     for (i = 0; i < MAX_PLAYERS; i++)
2807       if (tape.player_participates[i])
2808         num_players++;
2809
2810     /* multi-player tapes contain input data for more than one player */
2811     game.team_mode = (num_players > 1);
2812   }
2813
2814   /* ---------------------------------------------------------------------- */
2815   /* set flags for bugs and changes according to active game engine version */
2816   /* ---------------------------------------------------------------------- */
2817
2818   /*
2819     Summary of bugfix/change:
2820     Fixed handling for custom elements that change when pushed by the player.
2821
2822     Fixed/changed in version:
2823     3.1.0
2824
2825     Description:
2826     Before 3.1.0, custom elements that "change when pushing" changed directly
2827     after the player started pushing them (until then handled in "DigField()").
2828     Since 3.1.0, these custom elements are not changed until the "pushing"
2829     move of the element is finished (now handled in "ContinueMoving()").
2830
2831     Affected levels/tapes:
2832     The first condition is generally needed for all levels/tapes before version
2833     3.1.0, which might use the old behaviour before it was changed; known tapes
2834     that are affected are some tapes from the level set "Walpurgis Gardens" by
2835     Jamie Cullen.
2836     The second condition is an exception from the above case and is needed for
2837     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2838     above (including some development versions of 3.1.0), but before it was
2839     known that this change would break tapes like the above and was fixed in
2840     3.1.1, so that the changed behaviour was active although the engine version
2841     while recording maybe was before 3.1.0. There is at least one tape that is
2842     affected by this exception, which is the tape for the one-level set "Bug
2843     Machine" by Juergen Bonhagen.
2844   */
2845
2846   game.use_change_when_pushing_bug =
2847     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2848      !(tape.playing &&
2849        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2850        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2851
2852   /*
2853     Summary of bugfix/change:
2854     Fixed handling for blocking the field the player leaves when moving.
2855
2856     Fixed/changed in version:
2857     3.1.1
2858
2859     Description:
2860     Before 3.1.1, when "block last field when moving" was enabled, the field
2861     the player is leaving when moving was blocked for the time of the move,
2862     and was directly unblocked afterwards. This resulted in the last field
2863     being blocked for exactly one less than the number of frames of one player
2864     move. Additionally, even when blocking was disabled, the last field was
2865     blocked for exactly one frame.
2866     Since 3.1.1, due to changes in player movement handling, the last field
2867     is not blocked at all when blocking is disabled. When blocking is enabled,
2868     the last field is blocked for exactly the number of frames of one player
2869     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2870     last field is blocked for exactly one more than the number of frames of
2871     one player move.
2872
2873     Affected levels/tapes:
2874     (!!! yet to be determined -- probably many !!!)
2875   */
2876
2877   game.use_block_last_field_bug =
2878     (game.engine_version < VERSION_IDENT(3,1,1,0));
2879
2880   game_em.use_single_button =
2881     (game.engine_version > VERSION_IDENT(4,0,0,2));
2882
2883   game_em.use_snap_key_bug =
2884     (game.engine_version < VERSION_IDENT(4,0,1,0));
2885
2886   /* ---------------------------------------------------------------------- */
2887
2888   /* set maximal allowed number of custom element changes per game frame */
2889   game.max_num_changes_per_frame = 1;
2890
2891   /* default scan direction: scan playfield from top/left to bottom/right */
2892   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2893
2894   /* dynamically adjust element properties according to game engine version */
2895   InitElementPropertiesEngine(game.engine_version);
2896
2897 #if 0
2898   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2899   printf("          tape version == %06d [%s] [file: %06d]\n",
2900          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2901          tape.file_version);
2902   printf("       => game.engine_version == %06d\n", game.engine_version);
2903 #endif
2904
2905   /* ---------- initialize player's initial move delay --------------------- */
2906
2907   /* dynamically adjust player properties according to level information */
2908   for (i = 0; i < MAX_PLAYERS; i++)
2909     game.initial_move_delay_value[i] =
2910       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2911
2912   /* dynamically adjust player properties according to game engine version */
2913   for (i = 0; i < MAX_PLAYERS; i++)
2914     game.initial_move_delay[i] =
2915       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2916        game.initial_move_delay_value[i] : 0);
2917
2918   /* ---------- initialize player's initial push delay --------------------- */
2919
2920   /* dynamically adjust player properties according to game engine version */
2921   game.initial_push_delay_value =
2922     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2923
2924   /* ---------- initialize changing elements ------------------------------- */
2925
2926   /* initialize changing elements information */
2927   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2928   {
2929     struct ElementInfo *ei = &element_info[i];
2930
2931     /* this pointer might have been changed in the level editor */
2932     ei->change = &ei->change_page[0];
2933
2934     if (!IS_CUSTOM_ELEMENT(i))
2935     {
2936       ei->change->target_element = EL_EMPTY_SPACE;
2937       ei->change->delay_fixed = 0;
2938       ei->change->delay_random = 0;
2939       ei->change->delay_frames = 1;
2940     }
2941
2942     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2943     {
2944       ei->has_change_event[j] = FALSE;
2945
2946       ei->event_page_nr[j] = 0;
2947       ei->event_page[j] = &ei->change_page[0];
2948     }
2949   }
2950
2951   /* add changing elements from pre-defined list */
2952   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2953   {
2954     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2955     struct ElementInfo *ei = &element_info[ch_delay->element];
2956
2957     ei->change->target_element       = ch_delay->target_element;
2958     ei->change->delay_fixed          = ch_delay->change_delay;
2959
2960     ei->change->pre_change_function  = ch_delay->pre_change_function;
2961     ei->change->change_function      = ch_delay->change_function;
2962     ei->change->post_change_function = ch_delay->post_change_function;
2963
2964     ei->change->can_change = TRUE;
2965     ei->change->can_change_or_has_action = TRUE;
2966
2967     ei->has_change_event[CE_DELAY] = TRUE;
2968
2969     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2970     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2971   }
2972
2973   /* ---------- initialize internal run-time variables --------------------- */
2974
2975   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2976   {
2977     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2978
2979     for (j = 0; j < ei->num_change_pages; j++)
2980     {
2981       ei->change_page[j].can_change_or_has_action =
2982         (ei->change_page[j].can_change |
2983          ei->change_page[j].has_action);
2984     }
2985   }
2986
2987   /* add change events from custom element configuration */
2988   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2989   {
2990     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2991
2992     for (j = 0; j < ei->num_change_pages; j++)
2993     {
2994       if (!ei->change_page[j].can_change_or_has_action)
2995         continue;
2996
2997       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2998       {
2999         /* only add event page for the first page found with this event */
3000         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3001         {
3002           ei->has_change_event[k] = TRUE;
3003
3004           ei->event_page_nr[k] = j;
3005           ei->event_page[k] = &ei->change_page[j];
3006         }
3007       }
3008     }
3009   }
3010
3011   /* ---------- initialize reference elements in change conditions --------- */
3012
3013   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3014   {
3015     int element = EL_CUSTOM_START + i;
3016     struct ElementInfo *ei = &element_info[element];
3017
3018     for (j = 0; j < ei->num_change_pages; j++)
3019     {
3020       int trigger_element = ei->change_page[j].initial_trigger_element;
3021
3022       if (trigger_element >= EL_PREV_CE_8 &&
3023           trigger_element <= EL_NEXT_CE_8)
3024         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3025
3026       ei->change_page[j].trigger_element = trigger_element;
3027     }
3028   }
3029
3030   /* ---------- initialize run-time trigger player and element ------------- */
3031
3032   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3033   {
3034     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3035
3036     for (j = 0; j < ei->num_change_pages; j++)
3037     {
3038       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3039       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3040       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3041       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3042       ei->change_page[j].actual_trigger_ce_value = 0;
3043       ei->change_page[j].actual_trigger_ce_score = 0;
3044     }
3045   }
3046
3047   /* ---------- initialize trigger events ---------------------------------- */
3048
3049   /* initialize trigger events information */
3050   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3051     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3052       trigger_events[i][j] = FALSE;
3053
3054   /* add trigger events from element change event properties */
3055   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3056   {
3057     struct ElementInfo *ei = &element_info[i];
3058
3059     for (j = 0; j < ei->num_change_pages; j++)
3060     {
3061       if (!ei->change_page[j].can_change_or_has_action)
3062         continue;
3063
3064       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3065       {
3066         int trigger_element = ei->change_page[j].trigger_element;
3067
3068         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3069         {
3070           if (ei->change_page[j].has_event[k])
3071           {
3072             if (IS_GROUP_ELEMENT(trigger_element))
3073             {
3074               struct ElementGroupInfo *group =
3075                 element_info[trigger_element].group;
3076
3077               for (l = 0; l < group->num_elements_resolved; l++)
3078                 trigger_events[group->element_resolved[l]][k] = TRUE;
3079             }
3080             else if (trigger_element == EL_ANY_ELEMENT)
3081               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3082                 trigger_events[l][k] = TRUE;
3083             else
3084               trigger_events[trigger_element][k] = TRUE;
3085           }
3086         }
3087       }
3088     }
3089   }
3090
3091   /* ---------- initialize push delay -------------------------------------- */
3092
3093   /* initialize push delay values to default */
3094   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3095   {
3096     if (!IS_CUSTOM_ELEMENT(i))
3097     {
3098       /* set default push delay values (corrected since version 3.0.7-1) */
3099       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3100       {
3101         element_info[i].push_delay_fixed = 2;
3102         element_info[i].push_delay_random = 8;
3103       }
3104       else
3105       {
3106         element_info[i].push_delay_fixed = 8;
3107         element_info[i].push_delay_random = 8;
3108       }
3109     }
3110   }
3111
3112   /* set push delay value for certain elements from pre-defined list */
3113   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3114   {
3115     int e = push_delay_list[i].element;
3116
3117     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3118     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3119   }
3120
3121   /* set push delay value for Supaplex elements for newer engine versions */
3122   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3123   {
3124     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3125     {
3126       if (IS_SP_ELEMENT(i))
3127       {
3128         /* set SP push delay to just enough to push under a falling zonk */
3129         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3130
3131         element_info[i].push_delay_fixed  = delay;
3132         element_info[i].push_delay_random = 0;
3133       }
3134     }
3135   }
3136
3137   /* ---------- initialize move stepsize ----------------------------------- */
3138
3139   /* initialize move stepsize values to default */
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141     if (!IS_CUSTOM_ELEMENT(i))
3142       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3143
3144   /* set move stepsize value for certain elements from pre-defined list */
3145   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3146   {
3147     int e = move_stepsize_list[i].element;
3148
3149     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3150   }
3151
3152   /* ---------- initialize collect score ----------------------------------- */
3153
3154   /* initialize collect score values for custom elements from initial value */
3155   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3156     if (IS_CUSTOM_ELEMENT(i))
3157       element_info[i].collect_score = element_info[i].collect_score_initial;
3158
3159   /* ---------- initialize collect count ----------------------------------- */
3160
3161   /* initialize collect count values for non-custom elements */
3162   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3163     if (!IS_CUSTOM_ELEMENT(i))
3164       element_info[i].collect_count_initial = 0;
3165
3166   /* add collect count values for all elements from pre-defined list */
3167   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3168     element_info[collect_count_list[i].element].collect_count_initial =
3169       collect_count_list[i].count;
3170
3171   /* ---------- initialize access direction -------------------------------- */
3172
3173   /* initialize access direction values to default (access from every side) */
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     if (!IS_CUSTOM_ELEMENT(i))
3176       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3177
3178   /* set access direction value for certain elements from pre-defined list */
3179   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3180     element_info[access_direction_list[i].element].access_direction =
3181       access_direction_list[i].direction;
3182
3183   /* ---------- initialize explosion content ------------------------------- */
3184   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3185   {
3186     if (IS_CUSTOM_ELEMENT(i))
3187       continue;
3188
3189     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3190     {
3191       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3192
3193       element_info[i].content.e[x][y] =
3194         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3195          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3196          i == EL_PLAYER_3 ? EL_EMERALD :
3197          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3198          i == EL_MOLE ? EL_EMERALD_RED :
3199          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3200          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3201          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3202          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3203          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3204          i == EL_WALL_EMERALD ? EL_EMERALD :
3205          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3206          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3207          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3208          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3209          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3210          i == EL_WALL_PEARL ? EL_PEARL :
3211          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212          EL_EMPTY);
3213     }
3214   }
3215
3216   /* ---------- initialize recursion detection ------------------------------ */
3217   recursion_loop_depth = 0;
3218   recursion_loop_detected = FALSE;
3219   recursion_loop_element = EL_UNDEFINED;
3220
3221   /* ---------- initialize graphics engine ---------------------------------- */
3222   game.scroll_delay_value =
3223     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3224      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3225   game.scroll_delay_value =
3226     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3227
3228   /* ---------- initialize game engine snapshots ---------------------------- */
3229   for (i = 0; i < MAX_PLAYERS; i++)
3230     game.snapshot.last_action[i] = 0;
3231   game.snapshot.changed_action = FALSE;
3232   game.snapshot.collected_item = FALSE;
3233   game.snapshot.mode =
3234     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3235      SNAPSHOT_MODE_EVERY_STEP :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3237      SNAPSHOT_MODE_EVERY_MOVE :
3238      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3239      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3240   game.snapshot.save_snapshot = FALSE;
3241
3242   /* ---------- initialize level time for Supaplex engine ------------------- */
3243   /* Supaplex levels with time limit currently unsupported -- should be added */
3244   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3245     level.time = 0;
3246 }
3247
3248 static int get_num_special_action(int element, int action_first,
3249                                   int action_last)
3250 {
3251   int num_special_action = 0;
3252   int i, j;
3253
3254   for (i = action_first; i <= action_last; i++)
3255   {
3256     boolean found = FALSE;
3257
3258     for (j = 0; j < NUM_DIRECTIONS; j++)
3259       if (el_act_dir2img(element, i, j) !=
3260           el_act_dir2img(element, ACTION_DEFAULT, j))
3261         found = TRUE;
3262
3263     if (found)
3264       num_special_action++;
3265     else
3266       break;
3267   }
3268
3269   return num_special_action;
3270 }
3271
3272
3273 /*
3274   =============================================================================
3275   InitGame()
3276   -----------------------------------------------------------------------------
3277   initialize and start new game
3278   =============================================================================
3279 */
3280
3281 #if DEBUG_INIT_PLAYER
3282 static void DebugPrintPlayerStatus(char *message)
3283 {
3284   int i;
3285
3286   if (!options.debug)
3287     return;
3288
3289   printf("%s:\n", message);
3290
3291   for (i = 0; i < MAX_PLAYERS; i++)
3292   {
3293     struct PlayerInfo *player = &stored_player[i];
3294
3295     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3296            i + 1,
3297            player->present,
3298            player->connected,
3299            player->connected_locally,
3300            player->connected_network,
3301            player->active);
3302
3303     if (local_player == player)
3304       printf(" (local player)");
3305
3306     printf("\n");
3307   }
3308 }
3309 #endif
3310
3311 void InitGame(void)
3312 {
3313   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3314   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3315   int fade_mask = REDRAW_FIELD;
3316
3317   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3318   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3319   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3320   int initial_move_dir = MV_DOWN;
3321   int i, j, x, y;
3322
3323   // required here to update video display before fading (FIX THIS)
3324   DrawMaskedBorder(REDRAW_DOOR_2);
3325
3326   if (!game.restart_level)
3327     CloseDoor(DOOR_CLOSE_1);
3328
3329   SetGameStatus(GAME_MODE_PLAYING);
3330
3331   if (level_editor_test_game)
3332     FadeSkipNextFadeIn();
3333   else
3334     FadeSetEnterScreen();
3335
3336   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3337     fade_mask = REDRAW_ALL;
3338
3339   FadeLevelSoundsAndMusic();
3340
3341   ExpireSoundLoops(TRUE);
3342
3343   FadeOut(fade_mask);
3344
3345   /* needed if different viewport properties defined for playing */
3346   ChangeViewportPropertiesIfNeeded();
3347
3348   ClearField();
3349
3350   DrawCompleteVideoDisplay();
3351
3352   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3353
3354   InitGameEngine();
3355   InitGameControlValues();
3356
3357   /* don't play tapes over network */
3358   network_playing = (network.enabled && !tape.playing);
3359
3360   for (i = 0; i < MAX_PLAYERS; i++)
3361   {
3362     struct PlayerInfo *player = &stored_player[i];
3363
3364     player->index_nr = i;
3365     player->index_bit = (1 << i);
3366     player->element_nr = EL_PLAYER_1 + i;
3367
3368     player->present = FALSE;
3369     player->active = FALSE;
3370     player->mapped = FALSE;
3371
3372     player->killed = FALSE;
3373     player->reanimated = FALSE;
3374
3375     player->action = 0;
3376     player->effective_action = 0;
3377     player->programmed_action = 0;
3378
3379     player->mouse_action.lx = 0;
3380     player->mouse_action.ly = 0;
3381     player->mouse_action.button = 0;
3382     player->mouse_action.button_hint = 0;
3383
3384     player->effective_mouse_action.lx = 0;
3385     player->effective_mouse_action.ly = 0;
3386     player->effective_mouse_action.button = 0;
3387     player->effective_mouse_action.button_hint = 0;
3388
3389     player->score = 0;
3390     player->score_final = 0;
3391
3392     player->health = MAX_HEALTH;
3393     player->health_final = MAX_HEALTH;
3394
3395     player->gems_still_needed = level.gems_needed;
3396     player->sokobanfields_still_needed = 0;
3397     player->lights_still_needed = 0;
3398     player->players_still_needed = 0;
3399     player->friends_still_needed = 0;
3400
3401     for (j = 0; j < MAX_NUM_KEYS; j++)
3402       player->key[j] = FALSE;
3403
3404     player->num_white_keys = 0;
3405
3406     player->dynabomb_count = 0;
3407     player->dynabomb_size = 1;
3408     player->dynabombs_left = 0;
3409     player->dynabomb_xl = FALSE;
3410
3411     player->MovDir = initial_move_dir;
3412     player->MovPos = 0;
3413     player->GfxPos = 0;
3414     player->GfxDir = initial_move_dir;
3415     player->GfxAction = ACTION_DEFAULT;
3416     player->Frame = 0;
3417     player->StepFrame = 0;
3418
3419     player->initial_element = player->element_nr;
3420     player->artwork_element =
3421       (level.use_artwork_element[i] ? level.artwork_element[i] :
3422        player->element_nr);
3423     player->use_murphy = FALSE;
3424
3425     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3426     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3427
3428     player->gravity = level.initial_player_gravity[i];
3429
3430     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3431
3432     player->actual_frame_counter = 0;
3433
3434     player->step_counter = 0;
3435
3436     player->last_move_dir = initial_move_dir;
3437
3438     player->is_active = FALSE;
3439
3440     player->is_waiting = FALSE;
3441     player->is_moving = FALSE;
3442     player->is_auto_moving = FALSE;
3443     player->is_digging = FALSE;
3444     player->is_snapping = FALSE;
3445     player->is_collecting = FALSE;
3446     player->is_pushing = FALSE;
3447     player->is_switching = FALSE;
3448     player->is_dropping = FALSE;
3449     player->is_dropping_pressed = FALSE;
3450
3451     player->is_bored = FALSE;
3452     player->is_sleeping = FALSE;
3453
3454     player->was_waiting = TRUE;
3455     player->was_moving = FALSE;
3456     player->was_snapping = FALSE;
3457     player->was_dropping = FALSE;
3458
3459     player->force_dropping = FALSE;
3460
3461     player->frame_counter_bored = -1;
3462     player->frame_counter_sleeping = -1;
3463
3464     player->anim_delay_counter = 0;
3465     player->post_delay_counter = 0;
3466
3467     player->dir_waiting = initial_move_dir;
3468     player->action_waiting = ACTION_DEFAULT;
3469     player->last_action_waiting = ACTION_DEFAULT;
3470     player->special_action_bored = ACTION_DEFAULT;
3471     player->special_action_sleeping = ACTION_DEFAULT;
3472
3473     player->switch_x = -1;
3474     player->switch_y = -1;
3475
3476     player->drop_x = -1;
3477     player->drop_y = -1;
3478
3479     player->show_envelope = 0;
3480
3481     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3482
3483     player->push_delay       = -1;      /* initialized when pushing starts */
3484     player->push_delay_value = game.initial_push_delay_value;
3485
3486     player->drop_delay = 0;
3487     player->drop_pressed_delay = 0;
3488
3489     player->last_jx = -1;
3490     player->last_jy = -1;
3491     player->jx = -1;
3492     player->jy = -1;
3493
3494     player->shield_normal_time_left = 0;
3495     player->shield_deadly_time_left = 0;
3496
3497     player->inventory_infinite_element = EL_UNDEFINED;
3498     player->inventory_size = 0;
3499
3500     if (level.use_initial_inventory[i])
3501     {
3502       for (j = 0; j < level.initial_inventory_size[i]; j++)
3503       {
3504         int element = level.initial_inventory_content[i][j];
3505         int collect_count = element_info[element].collect_count_initial;
3506         int k;
3507
3508         if (!IS_CUSTOM_ELEMENT(element))
3509           collect_count = 1;
3510
3511         if (collect_count == 0)
3512           player->inventory_infinite_element = element;
3513         else
3514           for (k = 0; k < collect_count; k++)
3515             if (player->inventory_size < MAX_INVENTORY_SIZE)
3516               player->inventory_element[player->inventory_size++] = element;
3517       }
3518     }
3519
3520     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3521     SnapField(player, 0, 0);
3522
3523     player->LevelSolved = FALSE;
3524     player->GameOver = FALSE;
3525
3526     player->LevelSolved_GameWon = FALSE;
3527     player->LevelSolved_GameEnd = FALSE;
3528     player->LevelSolved_SaveTape = FALSE;
3529     player->LevelSolved_SaveScore = FALSE;
3530
3531     player->LevelSolved_CountingTime = 0;
3532     player->LevelSolved_CountingScore = 0;
3533     player->LevelSolved_CountingHealth = 0;
3534
3535     map_player_action[i] = i;
3536   }
3537
3538   network_player_action_received = FALSE;
3539
3540   /* initial null action */
3541   if (network_playing)
3542     SendToServer_MovePlayer(MV_NONE);
3543
3544   ZX = ZY = -1;
3545   ExitX = ExitY = -1;
3546
3547   FrameCounter = 0;
3548   TimeFrames = 0;
3549   TimePlayed = 0;
3550   TimeLeft = level.time;
3551   TapeTime = 0;
3552
3553   ScreenMovDir = MV_NONE;
3554   ScreenMovPos = 0;
3555   ScreenGfxPos = 0;
3556
3557   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3558
3559   AllPlayersGone = FALSE;
3560
3561   game.panel.active = TRUE;
3562
3563   game.no_time_limit = (level.time == 0);
3564
3565   game.yamyam_content_nr = 0;
3566   game.robot_wheel_active = FALSE;
3567   game.magic_wall_active = FALSE;
3568   game.magic_wall_time_left = 0;
3569   game.light_time_left = 0;
3570   game.timegate_time_left = 0;
3571   game.switchgate_pos = 0;
3572   game.wind_direction = level.wind_direction_initial;
3573
3574   game.lenses_time_left = 0;
3575   game.magnify_time_left = 0;
3576
3577   game.ball_state = level.ball_state_initial;
3578   game.ball_content_nr = 0;
3579
3580   game.explosions_delayed = TRUE;
3581
3582   game.envelope_active = FALSE;
3583
3584   for (i = 0; i < NUM_BELTS; i++)
3585   {
3586     game.belt_dir[i] = MV_NONE;
3587     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3588   }
3589
3590   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3591     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3592
3593 #if DEBUG_INIT_PLAYER
3594   DebugPrintPlayerStatus("Player status at level initialization");
3595 #endif
3596
3597   SCAN_PLAYFIELD(x, y)
3598   {
3599     Feld[x][y] = Last[x][y] = level.field[x][y];
3600     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3601     ChangeDelay[x][y] = 0;
3602     ChangePage[x][y] = -1;
3603     CustomValue[x][y] = 0;              /* initialized in InitField() */
3604     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3605     AmoebaNr[x][y] = 0;
3606     WasJustMoving[x][y] = 0;
3607     WasJustFalling[x][y] = 0;
3608     CheckCollision[x][y] = 0;
3609     CheckImpact[x][y] = 0;
3610     Stop[x][y] = FALSE;
3611     Pushed[x][y] = FALSE;
3612
3613     ChangeCount[x][y] = 0;
3614     ChangeEvent[x][y] = -1;
3615
3616     ExplodePhase[x][y] = 0;
3617     ExplodeDelay[x][y] = 0;
3618     ExplodeField[x][y] = EX_TYPE_NONE;
3619
3620     RunnerVisit[x][y] = 0;
3621     PlayerVisit[x][y] = 0;
3622
3623     GfxFrame[x][y] = 0;
3624     GfxRandom[x][y] = INIT_GFX_RANDOM();
3625     GfxElement[x][y] = EL_UNDEFINED;
3626     GfxAction[x][y] = ACTION_DEFAULT;
3627     GfxDir[x][y] = MV_NONE;
3628     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3629   }
3630
3631   SCAN_PLAYFIELD(x, y)
3632   {
3633     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3634       emulate_bd = FALSE;
3635     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3636       emulate_sb = FALSE;
3637     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3638       emulate_sp = FALSE;
3639
3640     InitField(x, y, TRUE);
3641
3642     ResetGfxAnimation(x, y);
3643   }
3644
3645   InitBeltMovement();
3646
3647   for (i = 0; i < MAX_PLAYERS; i++)
3648   {
3649     struct PlayerInfo *player = &stored_player[i];
3650
3651     /* set number of special actions for bored and sleeping animation */
3652     player->num_special_action_bored =
3653       get_num_special_action(player->artwork_element,
3654                              ACTION_BORING_1, ACTION_BORING_LAST);
3655     player->num_special_action_sleeping =
3656       get_num_special_action(player->artwork_element,
3657                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3658   }
3659
3660   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3661                     emulate_sb ? EMU_SOKOBAN :
3662                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3663
3664   /* initialize type of slippery elements */
3665   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3666   {
3667     if (!IS_CUSTOM_ELEMENT(i))
3668     {
3669       /* default: elements slip down either to the left or right randomly */
3670       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3671
3672       /* SP style elements prefer to slip down on the left side */
3673       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3674         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3675
3676       /* BD style elements prefer to slip down on the left side */
3677       if (game.emulation == EMU_BOULDERDASH)
3678         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3679     }
3680   }
3681
3682   /* initialize explosion and ignition delay */
3683   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3684   {
3685     if (!IS_CUSTOM_ELEMENT(i))
3686     {
3687       int num_phase = 8;
3688       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3689                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3690                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3691       int last_phase = (num_phase + 1) * delay;
3692       int half_phase = (num_phase / 2) * delay;
3693
3694       element_info[i].explosion_delay = last_phase - 1;
3695       element_info[i].ignition_delay = half_phase;
3696
3697       if (i == EL_BLACK_ORB)
3698         element_info[i].ignition_delay = 1;
3699     }
3700   }
3701
3702   /* correct non-moving belts to start moving left */
3703   for (i = 0; i < NUM_BELTS; i++)
3704     if (game.belt_dir[i] == MV_NONE)
3705       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3706
3707 #if USE_NEW_PLAYER_ASSIGNMENTS
3708   for (i = 0; i < MAX_PLAYERS; i++)
3709   {
3710     stored_player[i].connected = FALSE;
3711
3712     /* in network game mode, the local player might not be the first player */
3713     if (stored_player[i].connected_locally)
3714       local_player = &stored_player[i];
3715   }
3716
3717   if (!network.enabled)
3718     local_player->connected = TRUE;
3719
3720   if (tape.playing)
3721   {
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       stored_player[i].connected = tape.player_participates[i];
3724   }
3725   else if (network.enabled)
3726   {
3727     /* add team mode players connected over the network (needed for correct
3728        assignment of player figures from level to locally playing players) */
3729
3730     for (i = 0; i < MAX_PLAYERS; i++)
3731       if (stored_player[i].connected_network)
3732         stored_player[i].connected = TRUE;
3733   }
3734   else if (game.team_mode)
3735   {
3736     /* try to guess locally connected team mode players (needed for correct
3737        assignment of player figures from level to locally playing players) */
3738
3739     for (i = 0; i < MAX_PLAYERS; i++)
3740       if (setup.input[i].use_joystick ||
3741           setup.input[i].key.left != KSYM_UNDEFINED)
3742         stored_player[i].connected = TRUE;
3743   }
3744
3745 #if DEBUG_INIT_PLAYER
3746   DebugPrintPlayerStatus("Player status after level initialization");
3747 #endif
3748
3749 #if DEBUG_INIT_PLAYER
3750   if (options.debug)
3751     printf("Reassigning players ...\n");
3752 #endif
3753
3754   /* check if any connected player was not found in playfield */
3755   for (i = 0; i < MAX_PLAYERS; i++)
3756   {
3757     struct PlayerInfo *player = &stored_player[i];
3758
3759     if (player->connected && !player->present)
3760     {
3761       struct PlayerInfo *field_player = NULL;
3762
3763 #if DEBUG_INIT_PLAYER
3764       if (options.debug)
3765         printf("- looking for field player for player %d ...\n", i + 1);
3766 #endif
3767
3768       /* assign first free player found that is present in the playfield */
3769
3770       /* first try: look for unmapped playfield player that is not connected */
3771       for (j = 0; j < MAX_PLAYERS; j++)
3772         if (field_player == NULL &&
3773             stored_player[j].present &&
3774             !stored_player[j].mapped &&
3775             !stored_player[j].connected)
3776           field_player = &stored_player[j];
3777
3778       /* second try: look for *any* unmapped playfield player */
3779       for (j = 0; j < MAX_PLAYERS; j++)
3780         if (field_player == NULL &&
3781             stored_player[j].present &&
3782             !stored_player[j].mapped)
3783           field_player = &stored_player[j];
3784
3785       if (field_player != NULL)
3786       {
3787         int jx = field_player->jx, jy = field_player->jy;
3788
3789 #if DEBUG_INIT_PLAYER
3790         if (options.debug)
3791           printf("- found player %d\n", field_player->index_nr + 1);
3792 #endif
3793
3794         player->present = FALSE;
3795         player->active = FALSE;
3796
3797         field_player->present = TRUE;
3798         field_player->active = TRUE;
3799
3800         /*
3801         player->initial_element = field_player->initial_element;
3802         player->artwork_element = field_player->artwork_element;
3803
3804         player->block_last_field       = field_player->block_last_field;
3805         player->block_delay_adjustment = field_player->block_delay_adjustment;
3806         */
3807
3808         StorePlayer[jx][jy] = field_player->element_nr;
3809
3810         field_player->jx = field_player->last_jx = jx;
3811         field_player->jy = field_player->last_jy = jy;
3812
3813         if (local_player == player)
3814           local_player = field_player;
3815
3816         map_player_action[field_player->index_nr] = i;
3817
3818         field_player->mapped = TRUE;
3819
3820 #if DEBUG_INIT_PLAYER
3821         if (options.debug)
3822           printf("- map_player_action[%d] == %d\n",
3823                  field_player->index_nr + 1, i + 1);
3824 #endif
3825       }
3826     }
3827
3828     if (player->connected && player->present)
3829       player->mapped = TRUE;
3830   }
3831
3832 #if DEBUG_INIT_PLAYER
3833   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3834 #endif
3835
3836 #else
3837
3838   /* check if any connected player was not found in playfield */
3839   for (i = 0; i < MAX_PLAYERS; i++)
3840   {
3841     struct PlayerInfo *player = &stored_player[i];
3842
3843     if (player->connected && !player->present)
3844     {
3845       for (j = 0; j < MAX_PLAYERS; j++)
3846       {
3847         struct PlayerInfo *field_player = &stored_player[j];
3848         int jx = field_player->jx, jy = field_player->jy;
3849
3850         /* assign first free player found that is present in the playfield */
3851         if (field_player->present && !field_player->connected)
3852         {
3853           player->present = TRUE;
3854           player->active = TRUE;
3855
3856           field_player->present = FALSE;
3857           field_player->active = FALSE;
3858
3859           player->initial_element = field_player->initial_element;
3860           player->artwork_element = field_player->artwork_element;
3861
3862           player->block_last_field       = field_player->block_last_field;
3863           player->block_delay_adjustment = field_player->block_delay_adjustment;
3864
3865           StorePlayer[jx][jy] = player->element_nr;
3866
3867           player->jx = player->last_jx = jx;
3868           player->jy = player->last_jy = jy;
3869
3870           break;
3871         }
3872       }
3873     }
3874   }
3875 #endif
3876
3877 #if 0
3878   printf("::: local_player->present == %d\n", local_player->present);
3879 #endif
3880
3881   /* set focus to local player for network games, else to all players */
3882   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3883   game.centered_player_nr_next = game.centered_player_nr;
3884   game.set_centered_player = FALSE;
3885
3886   if (network_playing && tape.recording)
3887   {
3888     /* store client dependent player focus when recording network games */
3889     tape.centered_player_nr_next = game.centered_player_nr_next;
3890     tape.set_centered_player = TRUE;
3891   }
3892
3893   if (tape.playing)
3894   {
3895     /* when playing a tape, eliminate all players who do not participate */
3896
3897 #if USE_NEW_PLAYER_ASSIGNMENTS
3898
3899     if (!game.team_mode)
3900     {
3901       for (i = 0; i < MAX_PLAYERS; i++)
3902       {
3903         if (stored_player[i].active &&
3904             !tape.player_participates[map_player_action[i]])
3905         {
3906           struct PlayerInfo *player = &stored_player[i];
3907           int jx = player->jx, jy = player->jy;
3908
3909 #if DEBUG_INIT_PLAYER
3910           if (options.debug)
3911             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3912 #endif
3913
3914           player->active = FALSE;
3915           StorePlayer[jx][jy] = 0;
3916           Feld[jx][jy] = EL_EMPTY;
3917         }
3918       }
3919     }
3920
3921 #else
3922
3923     for (i = 0; i < MAX_PLAYERS; i++)
3924     {
3925       if (stored_player[i].active &&
3926           !tape.player_participates[i])
3927       {
3928         struct PlayerInfo *player = &stored_player[i];
3929         int jx = player->jx, jy = player->jy;
3930
3931         player->active = FALSE;
3932         StorePlayer[jx][jy] = 0;
3933         Feld[jx][jy] = EL_EMPTY;
3934       }
3935     }
3936 #endif
3937   }
3938   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3939   {
3940     /* when in single player mode, eliminate all but the local player */
3941
3942     for (i = 0; i < MAX_PLAYERS; i++)
3943     {
3944       struct PlayerInfo *player = &stored_player[i];
3945
3946       if (player->active && player != local_player)
3947       {
3948         int jx = player->jx, jy = player->jy;
3949
3950         player->active = FALSE;
3951         player->present = FALSE;
3952
3953         StorePlayer[jx][jy] = 0;
3954         Feld[jx][jy] = EL_EMPTY;
3955       }
3956     }
3957   }
3958
3959   for (i = 0; i < MAX_PLAYERS; i++)
3960     if (stored_player[i].active)
3961       local_player->players_still_needed++;
3962
3963   if (level.solved_by_one_player)
3964     local_player->players_still_needed = 1;
3965
3966   /* when recording the game, store which players take part in the game */
3967   if (tape.recording)
3968   {
3969 #if USE_NEW_PLAYER_ASSIGNMENTS
3970     for (i = 0; i < MAX_PLAYERS; i++)
3971       if (stored_player[i].connected)
3972         tape.player_participates[i] = TRUE;
3973 #else
3974     for (i = 0; i < MAX_PLAYERS; i++)
3975       if (stored_player[i].active)
3976         tape.player_participates[i] = TRUE;
3977 #endif
3978   }
3979
3980 #if DEBUG_INIT_PLAYER
3981   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3982 #endif
3983
3984   if (BorderElement == EL_EMPTY)
3985   {
3986     SBX_Left = 0;
3987     SBX_Right = lev_fieldx - SCR_FIELDX;
3988     SBY_Upper = 0;
3989     SBY_Lower = lev_fieldy - SCR_FIELDY;
3990   }
3991   else
3992   {
3993     SBX_Left = -1;
3994     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3995     SBY_Upper = -1;
3996     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3997   }
3998
3999   if (full_lev_fieldx <= SCR_FIELDX)
4000     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4001   if (full_lev_fieldy <= SCR_FIELDY)
4002     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4003
4004   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4005     SBX_Left--;
4006   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4007     SBY_Upper--;
4008
4009   /* if local player not found, look for custom element that might create
4010      the player (make some assumptions about the right custom element) */
4011   if (!local_player->present)
4012   {
4013     int start_x = 0, start_y = 0;
4014     int found_rating = 0;
4015     int found_element = EL_UNDEFINED;
4016     int player_nr = local_player->index_nr;
4017
4018     SCAN_PLAYFIELD(x, y)
4019     {
4020       int element = Feld[x][y];
4021       int content;
4022       int xx, yy;
4023       boolean is_player;
4024
4025       if (level.use_start_element[player_nr] &&
4026           level.start_element[player_nr] == element &&
4027           found_rating < 4)
4028       {
4029         start_x = x;
4030         start_y = y;
4031
4032         found_rating = 4;
4033         found_element = element;
4034       }
4035
4036       if (!IS_CUSTOM_ELEMENT(element))
4037         continue;
4038
4039       if (CAN_CHANGE(element))
4040       {
4041         for (i = 0; i < element_info[element].num_change_pages; i++)
4042         {
4043           /* check for player created from custom element as single target */
4044           content = element_info[element].change_page[i].target_element;
4045           is_player = ELEM_IS_PLAYER(content);
4046
4047           if (is_player && (found_rating < 3 ||
4048                             (found_rating == 3 && element < found_element)))
4049           {
4050             start_x = x;
4051             start_y = y;
4052
4053             found_rating = 3;
4054             found_element = element;
4055           }
4056         }
4057       }
4058
4059       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4060       {
4061         /* check for player created from custom element as explosion content */
4062         content = element_info[element].content.e[xx][yy];
4063         is_player = ELEM_IS_PLAYER(content);
4064
4065         if (is_player && (found_rating < 2 ||
4066                           (found_rating == 2 && element < found_element)))
4067         {
4068           start_x = x + xx - 1;
4069           start_y = y + yy - 1;
4070
4071           found_rating = 2;
4072           found_element = element;
4073         }
4074
4075         if (!CAN_CHANGE(element))
4076           continue;
4077
4078         for (i = 0; i < element_info[element].num_change_pages; i++)
4079         {
4080           /* check for player created from custom element as extended target */
4081           content =
4082             element_info[element].change_page[i].target_content.e[xx][yy];
4083
4084           is_player = ELEM_IS_PLAYER(content);
4085
4086           if (is_player && (found_rating < 1 ||
4087                             (found_rating == 1 && element < found_element)))
4088           {
4089             start_x = x + xx - 1;
4090             start_y = y + yy - 1;
4091
4092             found_rating = 1;
4093             found_element = element;
4094           }
4095         }
4096       }
4097     }
4098
4099     scroll_x = SCROLL_POSITION_X(start_x);
4100     scroll_y = SCROLL_POSITION_Y(start_y);
4101   }
4102   else
4103   {
4104     scroll_x = SCROLL_POSITION_X(local_player->jx);
4105     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4106   }
4107
4108   /* !!! FIX THIS (START) !!! */
4109   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4110   {
4111     InitGameEngine_EM();
4112   }
4113   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4114   {
4115     InitGameEngine_SP();
4116   }
4117   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4118   {
4119     InitGameEngine_MM();
4120   }
4121   else
4122   {
4123     DrawLevel(REDRAW_FIELD);
4124     DrawAllPlayers();
4125
4126     /* after drawing the level, correct some elements */
4127     if (game.timegate_time_left == 0)
4128       CloseAllOpenTimegates();
4129   }
4130
4131   /* blit playfield from scroll buffer to normal back buffer for fading in */
4132   BlitScreenToBitmap(backbuffer);
4133   /* !!! FIX THIS (END) !!! */
4134
4135   DrawMaskedBorder(fade_mask);
4136
4137   FadeIn(fade_mask);
4138
4139 #if 1
4140   // full screen redraw is required at this point in the following cases:
4141   // - special editor door undrawn when game was started from level editor
4142   // - drawing area (playfield) was changed and has to be removed completely
4143   redraw_mask = REDRAW_ALL;
4144   BackToFront();
4145 #endif
4146
4147   if (!game.restart_level)
4148   {
4149     /* copy default game door content to main double buffer */
4150
4151     /* !!! CHECK AGAIN !!! */
4152     SetPanelBackground();
4153     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4154     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4155   }
4156
4157   SetPanelBackground();
4158   SetDrawBackgroundMask(REDRAW_DOOR_1);
4159
4160   UpdateAndDisplayGameControlValues();
4161
4162   if (!game.restart_level)
4163   {
4164     UnmapGameButtons();
4165     UnmapTapeButtons();
4166
4167     FreeGameButtons();
4168     CreateGameButtons();
4169
4170     MapGameButtons();
4171     MapTapeButtons();
4172
4173     /* copy actual game door content to door double buffer for OpenDoor() */
4174     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4175
4176     OpenDoor(DOOR_OPEN_ALL);
4177
4178     KeyboardAutoRepeatOffUnlessAutoplay();
4179
4180 #if DEBUG_INIT_PLAYER
4181     DebugPrintPlayerStatus("Player status (final)");
4182 #endif
4183   }
4184
4185   UnmapAllGadgets();
4186
4187   MapGameButtons();
4188   MapTapeButtons();
4189
4190   if (!game.restart_level && !tape.playing)
4191   {
4192     LevelStats_incPlayed(level_nr);
4193
4194     SaveLevelSetup_SeriesInfo();
4195   }
4196
4197   game.restart_level = FALSE;
4198   game.restart_game_message = NULL;
4199   game.request_active = FALSE;
4200
4201   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4202     InitGameActions_MM();
4203
4204   SaveEngineSnapshotToListInitial();
4205
4206   if (!game.restart_level)
4207   {
4208     PlaySound(SND_GAME_STARTING);
4209
4210     if (setup.sound_music)
4211       PlayLevelMusic();
4212   }
4213 }
4214
4215 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4216                         int actual_player_x, int actual_player_y)
4217 {
4218   /* this is used for non-R'n'D game engines to update certain engine values */
4219
4220   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4221   {
4222     actual_player_x = correctLevelPosX_EM(actual_player_x);
4223     actual_player_y = correctLevelPosY_EM(actual_player_y);
4224   }
4225
4226   /* needed to determine if sounds are played within the visible screen area */
4227   scroll_x = actual_scroll_x;
4228   scroll_y = actual_scroll_y;
4229
4230   /* needed to get player position for "follow finger" playing input method */
4231   local_player->jx = actual_player_x;
4232   local_player->jy = actual_player_y;
4233 }
4234
4235 void InitMovDir(int x, int y)
4236 {
4237   int i, element = Feld[x][y];
4238   static int xy[4][2] =
4239   {
4240     {  0, +1 },
4241     { +1,  0 },
4242     {  0, -1 },
4243     { -1,  0 }
4244   };
4245   static int direction[3][4] =
4246   {
4247     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4248     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4249     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4250   };
4251
4252   switch (element)
4253   {
4254     case EL_BUG_RIGHT:
4255     case EL_BUG_UP:
4256     case EL_BUG_LEFT:
4257     case EL_BUG_DOWN:
4258       Feld[x][y] = EL_BUG;
4259       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4260       break;
4261
4262     case EL_SPACESHIP_RIGHT:
4263     case EL_SPACESHIP_UP:
4264     case EL_SPACESHIP_LEFT:
4265     case EL_SPACESHIP_DOWN:
4266       Feld[x][y] = EL_SPACESHIP;
4267       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4268       break;
4269
4270     case EL_BD_BUTTERFLY_RIGHT:
4271     case EL_BD_BUTTERFLY_UP:
4272     case EL_BD_BUTTERFLY_LEFT:
4273     case EL_BD_BUTTERFLY_DOWN:
4274       Feld[x][y] = EL_BD_BUTTERFLY;
4275       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4276       break;
4277
4278     case EL_BD_FIREFLY_RIGHT:
4279     case EL_BD_FIREFLY_UP:
4280     case EL_BD_FIREFLY_LEFT:
4281     case EL_BD_FIREFLY_DOWN:
4282       Feld[x][y] = EL_BD_FIREFLY;
4283       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4284       break;
4285
4286     case EL_PACMAN_RIGHT:
4287     case EL_PACMAN_UP:
4288     case EL_PACMAN_LEFT:
4289     case EL_PACMAN_DOWN:
4290       Feld[x][y] = EL_PACMAN;
4291       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4292       break;
4293
4294     case EL_YAMYAM_LEFT:
4295     case EL_YAMYAM_RIGHT:
4296     case EL_YAMYAM_UP:
4297     case EL_YAMYAM_DOWN:
4298       Feld[x][y] = EL_YAMYAM;
4299       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4300       break;
4301
4302     case EL_SP_SNIKSNAK:
4303       MovDir[x][y] = MV_UP;
4304       break;
4305
4306     case EL_SP_ELECTRON:
4307       MovDir[x][y] = MV_LEFT;
4308       break;
4309
4310     case EL_MOLE_LEFT:
4311     case EL_MOLE_RIGHT:
4312     case EL_MOLE_UP:
4313     case EL_MOLE_DOWN:
4314       Feld[x][y] = EL_MOLE;
4315       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4316       break;
4317
4318     default:
4319       if (IS_CUSTOM_ELEMENT(element))
4320       {
4321         struct ElementInfo *ei = &element_info[element];
4322         int move_direction_initial = ei->move_direction_initial;
4323         int move_pattern = ei->move_pattern;
4324
4325         if (move_direction_initial == MV_START_PREVIOUS)
4326         {
4327           if (MovDir[x][y] != MV_NONE)
4328             return;
4329
4330           move_direction_initial = MV_START_AUTOMATIC;
4331         }
4332
4333         if (move_direction_initial == MV_START_RANDOM)
4334           MovDir[x][y] = 1 << RND(4);
4335         else if (move_direction_initial & MV_ANY_DIRECTION)
4336           MovDir[x][y] = move_direction_initial;
4337         else if (move_pattern == MV_ALL_DIRECTIONS ||
4338                  move_pattern == MV_TURNING_LEFT ||
4339                  move_pattern == MV_TURNING_RIGHT ||
4340                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4341                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4342                  move_pattern == MV_TURNING_RANDOM)
4343           MovDir[x][y] = 1 << RND(4);
4344         else if (move_pattern == MV_HORIZONTAL)
4345           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4346         else if (move_pattern == MV_VERTICAL)
4347           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4348         else if (move_pattern & MV_ANY_DIRECTION)
4349           MovDir[x][y] = element_info[element].move_pattern;
4350         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4351                  move_pattern == MV_ALONG_RIGHT_SIDE)
4352         {
4353           /* use random direction as default start direction */
4354           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4355             MovDir[x][y] = 1 << RND(4);
4356
4357           for (i = 0; i < NUM_DIRECTIONS; i++)
4358           {
4359             int x1 = x + xy[i][0];
4360             int y1 = y + xy[i][1];
4361
4362             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4363             {
4364               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4365                 MovDir[x][y] = direction[0][i];
4366               else
4367                 MovDir[x][y] = direction[1][i];
4368
4369               break;
4370             }
4371           }
4372         }                
4373       }
4374       else
4375       {
4376         MovDir[x][y] = 1 << RND(4);
4377
4378         if (element != EL_BUG &&
4379             element != EL_SPACESHIP &&
4380             element != EL_BD_BUTTERFLY &&
4381             element != EL_BD_FIREFLY)
4382           break;
4383
4384         for (i = 0; i < NUM_DIRECTIONS; i++)
4385         {
4386           int x1 = x + xy[i][0];
4387           int y1 = y + xy[i][1];
4388
4389           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4390           {
4391             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4392             {
4393               MovDir[x][y] = direction[0][i];
4394               break;
4395             }
4396             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4397                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4398             {
4399               MovDir[x][y] = direction[1][i];
4400               break;
4401             }
4402           }
4403         }
4404       }
4405       break;
4406   }
4407
4408   GfxDir[x][y] = MovDir[x][y];
4409 }
4410
4411 void InitAmoebaNr(int x, int y)
4412 {
4413   int i;
4414   int group_nr = AmoebeNachbarNr(x, y);
4415
4416   if (group_nr == 0)
4417   {
4418     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4419     {
4420       if (AmoebaCnt[i] == 0)
4421       {
4422         group_nr = i;
4423         break;
4424       }
4425     }
4426   }
4427
4428   AmoebaNr[x][y] = group_nr;
4429   AmoebaCnt[group_nr]++;
4430   AmoebaCnt2[group_nr]++;
4431 }
4432
4433 static void PlayerWins(struct PlayerInfo *player)
4434 {
4435   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4436       local_player->players_still_needed > 0)
4437     return;
4438
4439   player->LevelSolved = TRUE;
4440   player->GameOver = TRUE;
4441
4442   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4443                          level.native_em_level->lev->score :
4444                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4445                          game_mm.score :
4446                          player->score);
4447   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4448                           MM_HEALTH(game_mm.laser_overload_value) :
4449                           player->health);
4450
4451   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4452                                       TimeLeft);
4453   player->LevelSolved_CountingScore = player->score_final;
4454   player->LevelSolved_CountingHealth = player->health_final;
4455 }
4456
4457 void GameWon(void)
4458 {
4459   static int time_count_steps;
4460   static int time, time_final;
4461   static int score, score_final;
4462   static int health, health_final;
4463   static int game_over_delay_1 = 0;
4464   static int game_over_delay_2 = 0;
4465   static int game_over_delay_3 = 0;
4466   int game_over_delay_value_1 = 50;
4467   int game_over_delay_value_2 = 25;
4468   int game_over_delay_value_3 = 50;
4469
4470   if (!local_player->LevelSolved_GameWon)
4471   {
4472     int i;
4473
4474     /* do not start end game actions before the player stops moving (to exit) */
4475     if (local_player->MovPos)
4476       return;
4477
4478     local_player->LevelSolved_GameWon = TRUE;
4479     local_player->LevelSolved_SaveTape = tape.recording;
4480     local_player->LevelSolved_SaveScore = !tape.playing;
4481
4482     if (!tape.playing)
4483     {
4484       LevelStats_incSolved(level_nr);
4485
4486       SaveLevelSetup_SeriesInfo();
4487     }
4488
4489     if (tape.auto_play)         /* tape might already be stopped here */
4490       tape.auto_play_level_solved = TRUE;
4491
4492     TapeStop();
4493
4494     game_over_delay_1 = 0;
4495     game_over_delay_2 = 0;
4496     game_over_delay_3 = game_over_delay_value_3;
4497
4498     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4499     score = score_final = local_player->score_final;
4500     health = health_final = local_player->health_final;
4501
4502     if (level.score[SC_TIME_BONUS] > 0)
4503     {
4504       if (TimeLeft > 0)
4505       {
4506         time_final = 0;
4507         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4508       }
4509       else if (game.no_time_limit && TimePlayed < 999)
4510       {
4511         time_final = 999;
4512         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4513       }
4514
4515       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4516
4517       game_over_delay_1 = game_over_delay_value_1;
4518
4519       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4520       {
4521         health_final = 0;
4522         score_final += health * level.score[SC_TIME_BONUS];
4523
4524         game_over_delay_2 = game_over_delay_value_2;
4525       }
4526
4527       local_player->score_final = score_final;
4528       local_player->health_final = health_final;
4529     }
4530
4531     if (level_editor_test_game)
4532     {
4533       time = time_final;
4534       score = score_final;
4535
4536       local_player->LevelSolved_CountingTime = time;
4537       local_player->LevelSolved_CountingScore = score;
4538
4539       game_panel_controls[GAME_PANEL_TIME].value = time;
4540       game_panel_controls[GAME_PANEL_SCORE].value = score;
4541
4542       DisplayGameControlValues();
4543     }
4544
4545     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4546     {
4547       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4548       {
4549         /* close exit door after last player */
4550         if ((AllPlayersGone &&
4551              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4552               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4553               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4554             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4555             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4556         {
4557           int element = Feld[ExitX][ExitY];
4558
4559           Feld[ExitX][ExitY] =
4560             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4561              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4562              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4563              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4564              EL_EM_STEEL_EXIT_CLOSING);
4565
4566           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4567         }
4568
4569         /* player disappears */
4570         DrawLevelField(ExitX, ExitY);
4571       }
4572
4573       for (i = 0; i < MAX_PLAYERS; i++)
4574       {
4575         struct PlayerInfo *player = &stored_player[i];
4576
4577         if (player->present)
4578         {
4579           RemovePlayer(player);
4580
4581           /* player disappears */
4582           DrawLevelField(player->jx, player->jy);
4583         }
4584       }
4585     }
4586
4587     PlaySound(SND_GAME_WINNING);
4588   }
4589
4590   if (game_over_delay_1 > 0)
4591   {
4592     game_over_delay_1--;
4593
4594     return;
4595   }
4596
4597   if (time != time_final)
4598   {
4599     int time_to_go = ABS(time_final - time);
4600     int time_count_dir = (time < time_final ? +1 : -1);
4601
4602     if (time_to_go < time_count_steps)
4603       time_count_steps = 1;
4604
4605     time  += time_count_steps * time_count_dir;
4606     score += time_count_steps * level.score[SC_TIME_BONUS];
4607
4608     local_player->LevelSolved_CountingTime = time;
4609     local_player->LevelSolved_CountingScore = score;
4610
4611     game_panel_controls[GAME_PANEL_TIME].value = time;
4612     game_panel_controls[GAME_PANEL_SCORE].value = score;
4613
4614     DisplayGameControlValues();
4615
4616     if (time == time_final)
4617       StopSound(SND_GAME_LEVELTIME_BONUS);
4618     else if (setup.sound_loops)
4619       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4620     else
4621       PlaySound(SND_GAME_LEVELTIME_BONUS);
4622
4623     return;
4624   }
4625
4626   if (game_over_delay_2 > 0)
4627   {
4628     game_over_delay_2--;
4629
4630     return;
4631   }
4632
4633   if (health != health_final)
4634   {
4635     int health_count_dir = (health < health_final ? +1 : -1);
4636
4637     health += health_count_dir;
4638     score  += level.score[SC_TIME_BONUS];
4639
4640     local_player->LevelSolved_CountingHealth = health;
4641     local_player->LevelSolved_CountingScore = score;
4642
4643     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4644     game_panel_controls[GAME_PANEL_SCORE].value = score;
4645
4646     DisplayGameControlValues();
4647
4648     if (health == health_final)
4649       StopSound(SND_GAME_LEVELTIME_BONUS);
4650     else if (setup.sound_loops)
4651       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4652     else
4653       PlaySound(SND_GAME_LEVELTIME_BONUS);
4654
4655     return;
4656   }
4657
4658   game.panel.active = FALSE;
4659
4660   if (game_over_delay_3 > 0)
4661   {
4662     game_over_delay_3--;
4663
4664     return;
4665   }
4666
4667   GameEnd();
4668 }
4669
4670 void GameEnd(void)
4671 {
4672   /* used instead of "level_nr" (needed for network games) */
4673   int last_level_nr = levelset.level_nr;
4674   int hi_pos;
4675
4676   local_player->LevelSolved_GameEnd = TRUE;
4677
4678   if (local_player->LevelSolved_SaveTape)
4679   {
4680     /* make sure that request dialog to save tape does not open door again */
4681     if (!global.use_envelope_request)
4682       CloseDoor(DOOR_CLOSE_1);
4683
4684     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4685   }
4686
4687   /* if no tape is to be saved, close both doors simultaneously */
4688   CloseDoor(DOOR_CLOSE_ALL);
4689
4690   if (level_editor_test_game)
4691   {
4692     SetGameStatus(GAME_MODE_MAIN);
4693
4694     DrawMainMenu();
4695
4696     return;
4697   }
4698
4699   if (!local_player->LevelSolved_SaveScore)
4700   {
4701     SetGameStatus(GAME_MODE_MAIN);
4702
4703     DrawMainMenu();
4704
4705     return;
4706   }
4707
4708   if (level_nr == leveldir_current->handicap_level)
4709   {
4710     leveldir_current->handicap_level++;
4711
4712     SaveLevelSetup_SeriesInfo();
4713   }
4714
4715   if (setup.increment_levels &&
4716       level_nr < leveldir_current->last_level &&
4717       !network_playing)
4718   {
4719     level_nr++;         /* advance to next level */
4720     TapeErase();        /* start with empty tape */
4721
4722     if (setup.auto_play_next_level)
4723     {
4724       LoadLevel(level_nr);
4725
4726       SaveLevelSetup_SeriesInfo();
4727     }
4728   }
4729
4730   hi_pos = NewHiScore(last_level_nr);
4731
4732   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4733   {
4734     SetGameStatus(GAME_MODE_SCORES);
4735
4736     DrawHallOfFame(last_level_nr, hi_pos);
4737   }
4738   else if (setup.auto_play_next_level && setup.increment_levels &&
4739            last_level_nr < leveldir_current->last_level &&
4740            !network_playing)
4741   {
4742     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4743   }
4744   else
4745   {
4746     SetGameStatus(GAME_MODE_MAIN);
4747
4748     DrawMainMenu();
4749   }
4750 }
4751
4752 int NewHiScore(int level_nr)
4753 {
4754   int k, l;
4755   int position = -1;
4756   boolean one_score_entry_per_name = !program.many_scores_per_name;
4757
4758   LoadScore(level_nr);
4759
4760   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4761       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4762     return -1;
4763
4764   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4765   {
4766     if (local_player->score_final > highscore[k].Score)
4767     {
4768       /* player has made it to the hall of fame */
4769
4770       if (k < MAX_SCORE_ENTRIES - 1)
4771       {
4772         int m = MAX_SCORE_ENTRIES - 1;
4773
4774         if (one_score_entry_per_name)
4775         {
4776           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4777             if (strEqual(setup.player_name, highscore[l].Name))
4778               m = l;
4779
4780           if (m == k)   /* player's new highscore overwrites his old one */
4781             goto put_into_list;
4782         }
4783
4784         for (l = m; l > k; l--)
4785         {
4786           strcpy(highscore[l].Name, highscore[l - 1].Name);
4787           highscore[l].Score = highscore[l - 1].Score;
4788         }
4789       }
4790
4791       put_into_list:
4792
4793       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4794       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4795       highscore[k].Score = local_player->score_final; 
4796       position = k;
4797
4798       break;
4799     }
4800     else if (one_score_entry_per_name &&
4801              !strncmp(setup.player_name, highscore[k].Name,
4802                       MAX_PLAYER_NAME_LEN))
4803       break;    /* player already there with a higher score */
4804   }
4805
4806   if (position >= 0) 
4807     SaveScore(level_nr);
4808
4809   return position;
4810 }
4811
4812 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4813 {
4814   int element = Feld[x][y];
4815   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4816   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4817   int horiz_move = (dx != 0);
4818   int sign = (horiz_move ? dx : dy);
4819   int step = sign * element_info[element].move_stepsize;
4820
4821   /* special values for move stepsize for spring and things on conveyor belt */
4822   if (horiz_move)
4823   {
4824     if (CAN_FALL(element) &&
4825         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4826       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4827     else if (element == EL_SPRING)
4828       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4829   }
4830
4831   return step;
4832 }
4833
4834 inline static int getElementMoveStepsize(int x, int y)
4835 {
4836   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4837 }
4838
4839 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4840 {
4841   if (player->GfxAction != action || player->GfxDir != dir)
4842   {
4843     player->GfxAction = action;
4844     player->GfxDir = dir;
4845     player->Frame = 0;
4846     player->StepFrame = 0;
4847   }
4848 }
4849
4850 static void ResetGfxFrame(int x, int y)
4851 {
4852   // profiling showed that "autotest" spends 10~20% of its time in this function
4853   if (DrawingDeactivatedField())
4854     return;
4855
4856   int element = Feld[x][y];
4857   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4858
4859   if (graphic_info[graphic].anim_global_sync)
4860     GfxFrame[x][y] = FrameCounter;
4861   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4862     GfxFrame[x][y] = CustomValue[x][y];
4863   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4864     GfxFrame[x][y] = element_info[element].collect_score;
4865   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4866     GfxFrame[x][y] = ChangeDelay[x][y];
4867 }
4868
4869 static void ResetGfxAnimation(int x, int y)
4870 {
4871   GfxAction[x][y] = ACTION_DEFAULT;
4872   GfxDir[x][y] = MovDir[x][y];
4873   GfxFrame[x][y] = 0;
4874
4875   ResetGfxFrame(x, y);
4876 }
4877
4878 static void ResetRandomAnimationValue(int x, int y)
4879 {
4880   GfxRandom[x][y] = INIT_GFX_RANDOM();
4881 }
4882
4883 static void InitMovingField(int x, int y, int direction)
4884 {
4885   int element = Feld[x][y];
4886   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4887   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4888   int newx = x + dx;
4889   int newy = y + dy;
4890   boolean is_moving_before, is_moving_after;
4891
4892   /* check if element was/is moving or being moved before/after mode change */
4893   is_moving_before = (WasJustMoving[x][y] != 0);
4894   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4895
4896   /* reset animation only for moving elements which change direction of moving
4897      or which just started or stopped moving
4898      (else CEs with property "can move" / "not moving" are reset each frame) */
4899   if (is_moving_before != is_moving_after ||
4900       direction != MovDir[x][y])
4901     ResetGfxAnimation(x, y);
4902
4903   MovDir[x][y] = direction;
4904   GfxDir[x][y] = direction;
4905
4906   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4907                      direction == MV_DOWN && CAN_FALL(element) ?
4908                      ACTION_FALLING : ACTION_MOVING);
4909
4910   /* this is needed for CEs with property "can move" / "not moving" */
4911
4912   if (is_moving_after)
4913   {
4914     if (Feld[newx][newy] == EL_EMPTY)
4915       Feld[newx][newy] = EL_BLOCKED;
4916
4917     MovDir[newx][newy] = MovDir[x][y];
4918
4919     CustomValue[newx][newy] = CustomValue[x][y];
4920
4921     GfxFrame[newx][newy] = GfxFrame[x][y];
4922     GfxRandom[newx][newy] = GfxRandom[x][y];
4923     GfxAction[newx][newy] = GfxAction[x][y];
4924     GfxDir[newx][newy] = GfxDir[x][y];
4925   }
4926 }
4927
4928 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4929 {
4930   int direction = MovDir[x][y];
4931   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4932   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4933
4934   *goes_to_x = newx;
4935   *goes_to_y = newy;
4936 }
4937
4938 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4939 {
4940   int oldx = x, oldy = y;
4941   int direction = MovDir[x][y];
4942
4943   if (direction == MV_LEFT)
4944     oldx++;
4945   else if (direction == MV_RIGHT)
4946     oldx--;
4947   else if (direction == MV_UP)
4948     oldy++;
4949   else if (direction == MV_DOWN)
4950     oldy--;
4951
4952   *comes_from_x = oldx;
4953   *comes_from_y = oldy;
4954 }
4955
4956 static int MovingOrBlocked2Element(int x, int y)
4957 {
4958   int element = Feld[x][y];
4959
4960   if (element == EL_BLOCKED)
4961   {
4962     int oldx, oldy;
4963
4964     Blocked2Moving(x, y, &oldx, &oldy);
4965     return Feld[oldx][oldy];
4966   }
4967   else
4968     return element;
4969 }
4970
4971 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4972 {
4973   /* like MovingOrBlocked2Element(), but if element is moving
4974      and (x,y) is the field the moving element is just leaving,
4975      return EL_BLOCKED instead of the element value */
4976   int element = Feld[x][y];
4977
4978   if (IS_MOVING(x, y))
4979   {
4980     if (element == EL_BLOCKED)
4981     {
4982       int oldx, oldy;
4983
4984       Blocked2Moving(x, y, &oldx, &oldy);
4985       return Feld[oldx][oldy];
4986     }
4987     else
4988       return EL_BLOCKED;
4989   }
4990   else
4991     return element;
4992 }
4993
4994 static void RemoveField(int x, int y)
4995 {
4996   Feld[x][y] = EL_EMPTY;
4997
4998   MovPos[x][y] = 0;
4999   MovDir[x][y] = 0;
5000   MovDelay[x][y] = 0;
5001
5002   CustomValue[x][y] = 0;
5003
5004   AmoebaNr[x][y] = 0;
5005   ChangeDelay[x][y] = 0;
5006   ChangePage[x][y] = -1;
5007   Pushed[x][y] = FALSE;
5008
5009   GfxElement[x][y] = EL_UNDEFINED;
5010   GfxAction[x][y] = ACTION_DEFAULT;
5011   GfxDir[x][y] = MV_NONE;
5012 }
5013
5014 static void RemoveMovingField(int x, int y)
5015 {
5016   int oldx = x, oldy = y, newx = x, newy = y;
5017   int element = Feld[x][y];
5018   int next_element = EL_UNDEFINED;
5019
5020   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5021     return;
5022
5023   if (IS_MOVING(x, y))
5024   {
5025     Moving2Blocked(x, y, &newx, &newy);
5026
5027     if (Feld[newx][newy] != EL_BLOCKED)
5028     {
5029       /* element is moving, but target field is not free (blocked), but
5030          already occupied by something different (example: acid pool);
5031          in this case, only remove the moving field, but not the target */
5032
5033       RemoveField(oldx, oldy);
5034
5035       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5036
5037       TEST_DrawLevelField(oldx, oldy);
5038
5039       return;
5040     }
5041   }
5042   else if (element == EL_BLOCKED)
5043   {
5044     Blocked2Moving(x, y, &oldx, &oldy);
5045     if (!IS_MOVING(oldx, oldy))
5046       return;
5047   }
5048
5049   if (element == EL_BLOCKED &&
5050       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5051        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5052        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5053        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5054        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5055        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5056     next_element = get_next_element(Feld[oldx][oldy]);
5057
5058   RemoveField(oldx, oldy);
5059   RemoveField(newx, newy);
5060
5061   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5062
5063   if (next_element != EL_UNDEFINED)
5064     Feld[oldx][oldy] = next_element;
5065
5066   TEST_DrawLevelField(oldx, oldy);
5067   TEST_DrawLevelField(newx, newy);
5068 }
5069
5070 void DrawDynamite(int x, int y)
5071 {
5072   int sx = SCREENX(x), sy = SCREENY(y);
5073   int graphic = el2img(Feld[x][y]);
5074   int frame;
5075
5076   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5077     return;
5078
5079   if (IS_WALKABLE_INSIDE(Back[x][y]))
5080     return;
5081
5082   if (Back[x][y])
5083     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5084   else if (Store[x][y])
5085     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5086
5087   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5088
5089   if (Back[x][y] || Store[x][y])
5090     DrawGraphicThruMask(sx, sy, graphic, frame);
5091   else
5092     DrawGraphic(sx, sy, graphic, frame);
5093 }
5094
5095 static void CheckDynamite(int x, int y)
5096 {
5097   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5098   {
5099     MovDelay[x][y]--;
5100
5101     if (MovDelay[x][y] != 0)
5102     {
5103       DrawDynamite(x, y);
5104       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5105
5106       return;
5107     }
5108   }
5109
5110   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5111
5112   Bang(x, y);
5113 }
5114
5115 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5116 {
5117   boolean num_checked_players = 0;
5118   int i;
5119
5120   for (i = 0; i < MAX_PLAYERS; i++)
5121   {
5122     if (stored_player[i].active)
5123     {
5124       int sx = stored_player[i].jx;
5125       int sy = stored_player[i].jy;
5126
5127       if (num_checked_players == 0)
5128       {
5129         *sx1 = *sx2 = sx;
5130         *sy1 = *sy2 = sy;
5131       }
5132       else
5133       {
5134         *sx1 = MIN(*sx1, sx);
5135         *sy1 = MIN(*sy1, sy);
5136         *sx2 = MAX(*sx2, sx);
5137         *sy2 = MAX(*sy2, sy);
5138       }
5139
5140       num_checked_players++;
5141     }
5142   }
5143 }
5144
5145 static boolean checkIfAllPlayersFitToScreen_RND(void)
5146 {
5147   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5148
5149   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5150
5151   return (sx2 - sx1 < SCR_FIELDX &&
5152           sy2 - sy1 < SCR_FIELDY);
5153 }
5154
5155 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5156 {
5157   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5158
5159   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5160
5161   *sx = (sx1 + sx2) / 2;
5162   *sy = (sy1 + sy2) / 2;
5163 }
5164
5165 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5166                                boolean center_screen, boolean quick_relocation)
5167 {
5168   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5169   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5170   boolean no_delay = (tape.warp_forward);
5171   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5172   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5173   int new_scroll_x, new_scroll_y;
5174
5175   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5176   {
5177     /* case 1: quick relocation inside visible screen (without scrolling) */
5178
5179     RedrawPlayfield();
5180
5181     return;
5182   }
5183
5184   if (!level.shifted_relocation || center_screen)
5185   {
5186     /* relocation _with_ centering of screen */
5187
5188     new_scroll_x = SCROLL_POSITION_X(x);
5189     new_scroll_y = SCROLL_POSITION_Y(y);
5190   }
5191   else
5192   {
5193     /* relocation _without_ centering of screen */
5194
5195     int center_scroll_x = SCROLL_POSITION_X(old_x);
5196     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5197     int offset_x = x + (scroll_x - center_scroll_x);
5198     int offset_y = y + (scroll_y - center_scroll_y);
5199
5200     /* for new screen position, apply previous offset to center position */
5201     new_scroll_x = SCROLL_POSITION_X(offset_x);
5202     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5203   }
5204
5205   if (quick_relocation)
5206   {
5207     /* case 2: quick relocation (redraw without visible scrolling) */
5208
5209     scroll_x = new_scroll_x;
5210     scroll_y = new_scroll_y;
5211
5212     RedrawPlayfield();
5213
5214     return;
5215   }
5216
5217   /* case 3: visible relocation (with scrolling to new position) */
5218
5219   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5220
5221   SetVideoFrameDelay(wait_delay_value);
5222
5223   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5224   {
5225     int dx = 0, dy = 0;
5226     int fx = FX, fy = FY;
5227
5228     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5229     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5230
5231     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5232       break;
5233
5234     scroll_x -= dx;
5235     scroll_y -= dy;
5236
5237     fx += dx * TILEX / 2;
5238     fy += dy * TILEY / 2;
5239
5240     ScrollLevel(dx, dy);
5241     DrawAllPlayers();
5242
5243     /* scroll in two steps of half tile size to make things smoother */
5244     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5245
5246     /* scroll second step to align at full tile size */
5247     BlitScreenToBitmap(window);
5248   }
5249
5250   DrawAllPlayers();
5251   BackToFront();
5252
5253   SetVideoFrameDelay(frame_delay_value_old);
5254 }
5255
5256 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5257 {
5258   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5259   int player_nr = GET_PLAYER_NR(el_player);
5260   struct PlayerInfo *player = &stored_player[player_nr];
5261   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5262   boolean no_delay = (tape.warp_forward);
5263   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5264   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5265   int old_jx = player->jx;
5266   int old_jy = player->jy;
5267   int old_element = Feld[old_jx][old_jy];
5268   int element = Feld[jx][jy];
5269   boolean player_relocated = (old_jx != jx || old_jy != jy);
5270
5271   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5272   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5273   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5274   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5275   int leave_side_horiz = move_dir_horiz;
5276   int leave_side_vert  = move_dir_vert;
5277   int enter_side = enter_side_horiz | enter_side_vert;
5278   int leave_side = leave_side_horiz | leave_side_vert;
5279
5280   if (player->GameOver)         /* do not reanimate dead player */
5281     return;
5282
5283   if (!player_relocated)        /* no need to relocate the player */
5284     return;
5285
5286   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5287   {
5288     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5289     DrawLevelField(jx, jy);
5290   }
5291
5292   if (player->present)
5293   {
5294     while (player->MovPos)
5295     {
5296       ScrollPlayer(player, SCROLL_GO_ON);
5297       ScrollScreen(NULL, SCROLL_GO_ON);
5298
5299       AdvanceFrameAndPlayerCounters(player->index_nr);
5300
5301       DrawPlayer(player);
5302
5303       BackToFront_WithFrameDelay(wait_delay_value);
5304     }
5305
5306     DrawPlayer(player);         /* needed here only to cleanup last field */
5307     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5308
5309     player->is_moving = FALSE;
5310   }
5311
5312   if (IS_CUSTOM_ELEMENT(old_element))
5313     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5314                                CE_LEFT_BY_PLAYER,
5315                                player->index_bit, leave_side);
5316
5317   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5318                                       CE_PLAYER_LEAVES_X,
5319                                       player->index_bit, leave_side);
5320
5321   Feld[jx][jy] = el_player;
5322   InitPlayerField(jx, jy, el_player, TRUE);
5323
5324   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5325      possible that the relocation target field did not contain a player element,
5326      but a walkable element, to which the new player was relocated -- in this
5327      case, restore that (already initialized!) element on the player field */
5328   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5329   {
5330     Feld[jx][jy] = element;     /* restore previously existing element */
5331   }
5332
5333   /* only visually relocate centered player */
5334   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5335                      FALSE, level.instant_relocation);
5336
5337   TestIfPlayerTouchesBadThing(jx, jy);
5338   TestIfPlayerTouchesCustomElement(jx, jy);
5339
5340   if (IS_CUSTOM_ELEMENT(element))
5341     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5342                                player->index_bit, enter_side);
5343
5344   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5345                                       player->index_bit, enter_side);
5346
5347   if (player->is_switching)
5348   {
5349     /* ensure that relocation while still switching an element does not cause
5350        a new element to be treated as also switched directly after relocation
5351        (this is important for teleporter switches that teleport the player to
5352        a place where another teleporter switch is in the same direction, which
5353        would then incorrectly be treated as immediately switched before the
5354        direction key that caused the switch was released) */
5355
5356     player->switch_x += jx - old_jx;
5357     player->switch_y += jy - old_jy;
5358   }
5359 }
5360
5361 static void Explode(int ex, int ey, int phase, int mode)
5362 {
5363   int x, y;
5364   int last_phase;
5365   int border_element;
5366
5367   /* !!! eliminate this variable !!! */
5368   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5369
5370   if (game.explosions_delayed)
5371   {
5372     ExplodeField[ex][ey] = mode;
5373     return;
5374   }
5375
5376   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5377   {
5378     int center_element = Feld[ex][ey];
5379     int artwork_element, explosion_element;     /* set these values later */
5380
5381     /* remove things displayed in background while burning dynamite */
5382     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5383       Back[ex][ey] = 0;
5384
5385     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5386     {
5387       /* put moving element to center field (and let it explode there) */
5388       center_element = MovingOrBlocked2Element(ex, ey);
5389       RemoveMovingField(ex, ey);
5390       Feld[ex][ey] = center_element;
5391     }
5392
5393     /* now "center_element" is finally determined -- set related values now */
5394     artwork_element = center_element;           /* for custom player artwork */
5395     explosion_element = center_element;         /* for custom player artwork */
5396
5397     if (IS_PLAYER(ex, ey))
5398     {
5399       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5400
5401       artwork_element = stored_player[player_nr].artwork_element;
5402
5403       if (level.use_explosion_element[player_nr])
5404       {
5405         explosion_element = level.explosion_element[player_nr];
5406         artwork_element = explosion_element;
5407       }
5408     }
5409
5410     if (mode == EX_TYPE_NORMAL ||
5411         mode == EX_TYPE_CENTER ||
5412         mode == EX_TYPE_CROSS)
5413       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5414
5415     last_phase = element_info[explosion_element].explosion_delay + 1;
5416
5417     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5418     {
5419       int xx = x - ex + 1;
5420       int yy = y - ey + 1;
5421       int element;
5422
5423       if (!IN_LEV_FIELD(x, y) ||
5424           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5425           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5426         continue;
5427
5428       element = Feld[x][y];
5429
5430       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5431       {
5432         element = MovingOrBlocked2Element(x, y);
5433
5434         if (!IS_EXPLOSION_PROOF(element))
5435           RemoveMovingField(x, y);
5436       }
5437
5438       /* indestructible elements can only explode in center (but not flames) */
5439       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5440                                            mode == EX_TYPE_BORDER)) ||
5441           element == EL_FLAMES)
5442         continue;
5443
5444       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5445          behaviour, for example when touching a yamyam that explodes to rocks
5446          with active deadly shield, a rock is created under the player !!! */
5447       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5448 #if 0
5449       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5450           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5451            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5452 #else
5453       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5454 #endif
5455       {
5456         if (IS_ACTIVE_BOMB(element))
5457         {
5458           /* re-activate things under the bomb like gate or penguin */
5459           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5460           Back[x][y] = 0;
5461         }
5462
5463         continue;
5464       }
5465
5466       /* save walkable background elements while explosion on same tile */
5467       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5468           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5469         Back[x][y] = element;
5470
5471       /* ignite explodable elements reached by other explosion */
5472       if (element == EL_EXPLOSION)
5473         element = Store2[x][y];
5474
5475       if (AmoebaNr[x][y] &&
5476           (element == EL_AMOEBA_FULL ||
5477            element == EL_BD_AMOEBA ||
5478            element == EL_AMOEBA_GROWING))
5479       {
5480         AmoebaCnt[AmoebaNr[x][y]]--;
5481         AmoebaCnt2[AmoebaNr[x][y]]--;
5482       }
5483
5484       RemoveField(x, y);
5485
5486       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5487       {
5488         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5489
5490         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5491
5492         if (PLAYERINFO(ex, ey)->use_murphy)
5493           Store[x][y] = EL_EMPTY;
5494       }
5495
5496       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5497          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5498       else if (ELEM_IS_PLAYER(center_element))
5499         Store[x][y] = EL_EMPTY;
5500       else if (center_element == EL_YAMYAM)
5501         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5502       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5503         Store[x][y] = element_info[center_element].content.e[xx][yy];
5504 #if 1
5505       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5506          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5507          otherwise) -- FIX THIS !!! */
5508       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5509         Store[x][y] = element_info[element].content.e[1][1];
5510 #else
5511       else if (!CAN_EXPLODE(element))
5512         Store[x][y] = element_info[element].content.e[1][1];
5513 #endif
5514       else
5515         Store[x][y] = EL_EMPTY;
5516
5517       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5518           center_element == EL_AMOEBA_TO_DIAMOND)
5519         Store2[x][y] = element;
5520
5521       Feld[x][y] = EL_EXPLOSION;
5522       GfxElement[x][y] = artwork_element;
5523
5524       ExplodePhase[x][y] = 1;
5525       ExplodeDelay[x][y] = last_phase;
5526
5527       Stop[x][y] = TRUE;
5528     }
5529
5530     if (center_element == EL_YAMYAM)
5531       game.yamyam_content_nr =
5532         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5533
5534     return;
5535   }
5536
5537   if (Stop[ex][ey])
5538     return;
5539
5540   x = ex;
5541   y = ey;
5542
5543   if (phase == 1)
5544     GfxFrame[x][y] = 0;         /* restart explosion animation */
5545
5546   last_phase = ExplodeDelay[x][y];
5547
5548   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5549
5550   /* this can happen if the player leaves an explosion just in time */
5551   if (GfxElement[x][y] == EL_UNDEFINED)
5552     GfxElement[x][y] = EL_EMPTY;
5553
5554   border_element = Store2[x][y];
5555   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5556     border_element = StorePlayer[x][y];
5557
5558   if (phase == element_info[border_element].ignition_delay ||
5559       phase == last_phase)
5560   {
5561     boolean border_explosion = FALSE;
5562
5563     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5564         !PLAYER_EXPLOSION_PROTECTED(x, y))
5565     {
5566       KillPlayerUnlessExplosionProtected(x, y);
5567       border_explosion = TRUE;
5568     }
5569     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5570     {
5571       Feld[x][y] = Store2[x][y];
5572       Store2[x][y] = 0;
5573       Bang(x, y);
5574       border_explosion = TRUE;
5575     }
5576     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5577     {
5578       AmoebeUmwandeln(x, y);
5579       Store2[x][y] = 0;
5580       border_explosion = TRUE;
5581     }
5582
5583     /* if an element just explodes due to another explosion (chain-reaction),
5584        do not immediately end the new explosion when it was the last frame of
5585        the explosion (as it would be done in the following "if"-statement!) */
5586     if (border_explosion && phase == last_phase)
5587       return;
5588   }
5589
5590   if (phase == last_phase)
5591   {
5592     int element;
5593
5594     element = Feld[x][y] = Store[x][y];
5595     Store[x][y] = Store2[x][y] = 0;
5596     GfxElement[x][y] = EL_UNDEFINED;
5597
5598     /* player can escape from explosions and might therefore be still alive */
5599     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5600         element <= EL_PLAYER_IS_EXPLODING_4)
5601     {
5602       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5603       int explosion_element = EL_PLAYER_1 + player_nr;
5604       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5605       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5606
5607       if (level.use_explosion_element[player_nr])
5608         explosion_element = level.explosion_element[player_nr];
5609
5610       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5611                     element_info[explosion_element].content.e[xx][yy]);
5612     }
5613
5614     /* restore probably existing indestructible background element */
5615     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5616       element = Feld[x][y] = Back[x][y];
5617     Back[x][y] = 0;
5618
5619     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5620     GfxDir[x][y] = MV_NONE;
5621     ChangeDelay[x][y] = 0;
5622     ChangePage[x][y] = -1;
5623
5624     CustomValue[x][y] = 0;
5625
5626     InitField_WithBug2(x, y, FALSE);
5627
5628     TEST_DrawLevelField(x, y);
5629
5630     TestIfElementTouchesCustomElement(x, y);
5631
5632     if (GFX_CRUMBLED(element))
5633       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5634
5635     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5636       StorePlayer[x][y] = 0;
5637
5638     if (ELEM_IS_PLAYER(element))
5639       RelocatePlayer(x, y, element);
5640   }
5641   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5642   {
5643     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5644     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5645
5646     if (phase == delay)
5647       TEST_DrawLevelFieldCrumbled(x, y);
5648
5649     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5650     {
5651       DrawLevelElement(x, y, Back[x][y]);
5652       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5653     }
5654     else if (IS_WALKABLE_UNDER(Back[x][y]))
5655     {
5656       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5657       DrawLevelElementThruMask(x, y, Back[x][y]);
5658     }
5659     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5660       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5661   }
5662 }
5663
5664 static void DynaExplode(int ex, int ey)
5665 {
5666   int i, j;
5667   int dynabomb_element = Feld[ex][ey];
5668   int dynabomb_size = 1;
5669   boolean dynabomb_xl = FALSE;
5670   struct PlayerInfo *player;
5671   static int xy[4][2] =
5672   {
5673     { 0, -1 },
5674     { -1, 0 },
5675     { +1, 0 },
5676     { 0, +1 }
5677   };
5678
5679   if (IS_ACTIVE_BOMB(dynabomb_element))
5680   {
5681     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5682     dynabomb_size = player->dynabomb_size;
5683     dynabomb_xl = player->dynabomb_xl;
5684     player->dynabombs_left++;
5685   }
5686
5687   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5688
5689   for (i = 0; i < NUM_DIRECTIONS; i++)
5690   {
5691     for (j = 1; j <= dynabomb_size; j++)
5692     {
5693       int x = ex + j * xy[i][0];
5694       int y = ey + j * xy[i][1];
5695       int element;
5696
5697       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5698         break;
5699
5700       element = Feld[x][y];
5701
5702       /* do not restart explosions of fields with active bombs */
5703       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5704         continue;
5705
5706       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5707
5708       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5709           !IS_DIGGABLE(element) && !dynabomb_xl)
5710         break;
5711     }
5712   }
5713 }
5714
5715 void Bang(int x, int y)
5716 {
5717   int element = MovingOrBlocked2Element(x, y);
5718   int explosion_type = EX_TYPE_NORMAL;
5719
5720   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5721   {
5722     struct PlayerInfo *player = PLAYERINFO(x, y);
5723
5724     element = Feld[x][y] = player->initial_element;
5725
5726     if (level.use_explosion_element[player->index_nr])
5727     {
5728       int explosion_element = level.explosion_element[player->index_nr];
5729
5730       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5731         explosion_type = EX_TYPE_CROSS;
5732       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5733         explosion_type = EX_TYPE_CENTER;
5734     }
5735   }
5736
5737   switch (element)
5738   {
5739     case EL_BUG:
5740     case EL_SPACESHIP:
5741     case EL_BD_BUTTERFLY:
5742     case EL_BD_FIREFLY:
5743     case EL_YAMYAM:
5744     case EL_DARK_YAMYAM:
5745     case EL_ROBOT:
5746     case EL_PACMAN:
5747     case EL_MOLE:
5748       RaiseScoreElement(element);
5749       break;
5750
5751     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5752     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5753     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5754     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5755     case EL_DYNABOMB_INCREASE_NUMBER:
5756     case EL_DYNABOMB_INCREASE_SIZE:
5757     case EL_DYNABOMB_INCREASE_POWER:
5758       explosion_type = EX_TYPE_DYNA;
5759       break;
5760
5761     case EL_DC_LANDMINE:
5762       explosion_type = EX_TYPE_CENTER;
5763       break;
5764
5765     case EL_PENGUIN:
5766     case EL_LAMP:
5767     case EL_LAMP_ACTIVE:
5768     case EL_AMOEBA_TO_DIAMOND:
5769       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5770         explosion_type = EX_TYPE_CENTER;
5771       break;
5772
5773     default:
5774       if (element_info[element].explosion_type == EXPLODES_CROSS)
5775         explosion_type = EX_TYPE_CROSS;
5776       else if (element_info[element].explosion_type == EXPLODES_1X1)
5777         explosion_type = EX_TYPE_CENTER;
5778       break;
5779   }
5780
5781   if (explosion_type == EX_TYPE_DYNA)
5782     DynaExplode(x, y);
5783   else
5784     Explode(x, y, EX_PHASE_START, explosion_type);
5785
5786   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5787 }
5788
5789 static void SplashAcid(int x, int y)
5790 {
5791   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5792       (!IN_LEV_FIELD(x - 1, y - 2) ||
5793        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5794     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5795
5796   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5797       (!IN_LEV_FIELD(x + 1, y - 2) ||
5798        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5799     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5800
5801   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5802 }
5803
5804 static void InitBeltMovement(void)
5805 {
5806   static int belt_base_element[4] =
5807   {
5808     EL_CONVEYOR_BELT_1_LEFT,
5809     EL_CONVEYOR_BELT_2_LEFT,
5810     EL_CONVEYOR_BELT_3_LEFT,
5811     EL_CONVEYOR_BELT_4_LEFT
5812   };
5813   static int belt_base_active_element[4] =
5814   {
5815     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5816     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5817     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5818     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5819   };
5820
5821   int x, y, i, j;
5822
5823   /* set frame order for belt animation graphic according to belt direction */
5824   for (i = 0; i < NUM_BELTS; i++)
5825   {
5826     int belt_nr = i;
5827
5828     for (j = 0; j < NUM_BELT_PARTS; j++)
5829     {
5830       int element = belt_base_active_element[belt_nr] + j;
5831       int graphic_1 = el2img(element);
5832       int graphic_2 = el2panelimg(element);
5833
5834       if (game.belt_dir[i] == MV_LEFT)
5835       {
5836         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5837         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5838       }
5839       else
5840       {
5841         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5842         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5843       }
5844     }
5845   }
5846
5847   SCAN_PLAYFIELD(x, y)
5848   {
5849     int element = Feld[x][y];
5850
5851     for (i = 0; i < NUM_BELTS; i++)
5852     {
5853       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5854       {
5855         int e_belt_nr = getBeltNrFromBeltElement(element);
5856         int belt_nr = i;
5857
5858         if (e_belt_nr == belt_nr)
5859         {
5860           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5861
5862           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5863         }
5864       }
5865     }
5866   }
5867 }
5868
5869 static void ToggleBeltSwitch(int x, int y)
5870 {
5871   static int belt_base_element[4] =
5872   {
5873     EL_CONVEYOR_BELT_1_LEFT,
5874     EL_CONVEYOR_BELT_2_LEFT,
5875     EL_CONVEYOR_BELT_3_LEFT,
5876     EL_CONVEYOR_BELT_4_LEFT
5877   };
5878   static int belt_base_active_element[4] =
5879   {
5880     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5881     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5882     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5883     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5884   };
5885   static int belt_base_switch_element[4] =
5886   {
5887     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5888     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5889     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5890     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5891   };
5892   static int belt_move_dir[4] =
5893   {
5894     MV_LEFT,
5895     MV_NONE,
5896     MV_RIGHT,
5897     MV_NONE,
5898   };
5899
5900   int element = Feld[x][y];
5901   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5902   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5903   int belt_dir = belt_move_dir[belt_dir_nr];
5904   int xx, yy, i;
5905
5906   if (!IS_BELT_SWITCH(element))
5907     return;
5908
5909   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5910   game.belt_dir[belt_nr] = belt_dir;
5911
5912   if (belt_dir_nr == 3)
5913     belt_dir_nr = 1;
5914
5915   /* set frame order for belt animation graphic according to belt direction */
5916   for (i = 0; i < NUM_BELT_PARTS; i++)
5917   {
5918     int element = belt_base_active_element[belt_nr] + i;
5919     int graphic_1 = el2img(element);
5920     int graphic_2 = el2panelimg(element);
5921
5922     if (belt_dir == MV_LEFT)
5923     {
5924       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5925       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5926     }
5927     else
5928     {
5929       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5930       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5931     }
5932   }
5933
5934   SCAN_PLAYFIELD(xx, yy)
5935   {
5936     int element = Feld[xx][yy];
5937
5938     if (IS_BELT_SWITCH(element))
5939     {
5940       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5941
5942       if (e_belt_nr == belt_nr)
5943       {
5944         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5945         TEST_DrawLevelField(xx, yy);
5946       }
5947     }
5948     else if (IS_BELT(element) && belt_dir != MV_NONE)
5949     {
5950       int e_belt_nr = getBeltNrFromBeltElement(element);
5951
5952       if (e_belt_nr == belt_nr)
5953       {
5954         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5955
5956         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5957         TEST_DrawLevelField(xx, yy);
5958       }
5959     }
5960     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5961     {
5962       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5963
5964       if (e_belt_nr == belt_nr)
5965       {
5966         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5967
5968         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5969         TEST_DrawLevelField(xx, yy);
5970       }
5971     }
5972   }
5973 }
5974
5975 static void ToggleSwitchgateSwitch(int x, int y)
5976 {
5977   int xx, yy;
5978
5979   game.switchgate_pos = !game.switchgate_pos;
5980
5981   SCAN_PLAYFIELD(xx, yy)
5982   {
5983     int element = Feld[xx][yy];
5984
5985     if (element == EL_SWITCHGATE_SWITCH_UP)
5986     {
5987       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5988       TEST_DrawLevelField(xx, yy);
5989     }
5990     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5991     {
5992       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5993       TEST_DrawLevelField(xx, yy);
5994     }
5995     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5996     {
5997       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5998       TEST_DrawLevelField(xx, yy);
5999     }
6000     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6001     {
6002       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6003       TEST_DrawLevelField(xx, yy);
6004     }
6005     else if (element == EL_SWITCHGATE_OPEN ||
6006              element == EL_SWITCHGATE_OPENING)
6007     {
6008       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6009
6010       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6011     }
6012     else if (element == EL_SWITCHGATE_CLOSED ||
6013              element == EL_SWITCHGATE_CLOSING)
6014     {
6015       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6016
6017       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6018     }
6019   }
6020 }
6021
6022 static int getInvisibleActiveFromInvisibleElement(int element)
6023 {
6024   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6025           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6026           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6027           element);
6028 }
6029
6030 static int getInvisibleFromInvisibleActiveElement(int element)
6031 {
6032   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6033           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6034           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6035           element);
6036 }
6037
6038 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6039 {
6040   int x, y;
6041
6042   SCAN_PLAYFIELD(x, y)
6043   {
6044     int element = Feld[x][y];
6045
6046     if (element == EL_LIGHT_SWITCH &&
6047         game.light_time_left > 0)
6048     {
6049       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6050       TEST_DrawLevelField(x, y);
6051     }
6052     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6053              game.light_time_left == 0)
6054     {
6055       Feld[x][y] = EL_LIGHT_SWITCH;
6056       TEST_DrawLevelField(x, y);
6057     }
6058     else if (element == EL_EMC_DRIPPER &&
6059              game.light_time_left > 0)
6060     {
6061       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6062       TEST_DrawLevelField(x, y);
6063     }
6064     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6065              game.light_time_left == 0)
6066     {
6067       Feld[x][y] = EL_EMC_DRIPPER;
6068       TEST_DrawLevelField(x, y);
6069     }
6070     else if (element == EL_INVISIBLE_STEELWALL ||
6071              element == EL_INVISIBLE_WALL ||
6072              element == EL_INVISIBLE_SAND)
6073     {
6074       if (game.light_time_left > 0)
6075         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6076
6077       TEST_DrawLevelField(x, y);
6078
6079       /* uncrumble neighbour fields, if needed */
6080       if (element == EL_INVISIBLE_SAND)
6081         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6082     }
6083     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6084              element == EL_INVISIBLE_WALL_ACTIVE ||
6085              element == EL_INVISIBLE_SAND_ACTIVE)
6086     {
6087       if (game.light_time_left == 0)
6088         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6089
6090       TEST_DrawLevelField(x, y);
6091
6092       /* re-crumble neighbour fields, if needed */
6093       if (element == EL_INVISIBLE_SAND)
6094         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6095     }
6096   }
6097 }
6098
6099 static void RedrawAllInvisibleElementsForLenses(void)
6100 {
6101   int x, y;
6102
6103   SCAN_PLAYFIELD(x, y)
6104   {
6105     int element = Feld[x][y];
6106
6107     if (element == EL_EMC_DRIPPER &&
6108         game.lenses_time_left > 0)
6109     {
6110       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6111       TEST_DrawLevelField(x, y);
6112     }
6113     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6114              game.lenses_time_left == 0)
6115     {
6116       Feld[x][y] = EL_EMC_DRIPPER;
6117       TEST_DrawLevelField(x, y);
6118     }
6119     else if (element == EL_INVISIBLE_STEELWALL ||
6120              element == EL_INVISIBLE_WALL ||
6121              element == EL_INVISIBLE_SAND)
6122     {
6123       if (game.lenses_time_left > 0)
6124         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6125
6126       TEST_DrawLevelField(x, y);
6127
6128       /* uncrumble neighbour fields, if needed */
6129       if (element == EL_INVISIBLE_SAND)
6130         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6131     }
6132     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6133              element == EL_INVISIBLE_WALL_ACTIVE ||
6134              element == EL_INVISIBLE_SAND_ACTIVE)
6135     {
6136       if (game.lenses_time_left == 0)
6137         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6138
6139       TEST_DrawLevelField(x, y);
6140
6141       /* re-crumble neighbour fields, if needed */
6142       if (element == EL_INVISIBLE_SAND)
6143         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6144     }
6145   }
6146 }
6147
6148 static void RedrawAllInvisibleElementsForMagnifier(void)
6149 {
6150   int x, y;
6151
6152   SCAN_PLAYFIELD(x, y)
6153   {
6154     int element = Feld[x][y];
6155
6156     if (element == EL_EMC_FAKE_GRASS &&
6157         game.magnify_time_left > 0)
6158     {
6159       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6160       TEST_DrawLevelField(x, y);
6161     }
6162     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6163              game.magnify_time_left == 0)
6164     {
6165       Feld[x][y] = EL_EMC_FAKE_GRASS;
6166       TEST_DrawLevelField(x, y);
6167     }
6168     else if (IS_GATE_GRAY(element) &&
6169              game.magnify_time_left > 0)
6170     {
6171       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6172                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6173                     IS_EM_GATE_GRAY(element) ?
6174                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6175                     IS_EMC_GATE_GRAY(element) ?
6176                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6177                     IS_DC_GATE_GRAY(element) ?
6178                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6179                     element);
6180       TEST_DrawLevelField(x, y);
6181     }
6182     else if (IS_GATE_GRAY_ACTIVE(element) &&
6183              game.magnify_time_left == 0)
6184     {
6185       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6186                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6187                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6188                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6189                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6190                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6191                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6192                     EL_DC_GATE_WHITE_GRAY :
6193                     element);
6194       TEST_DrawLevelField(x, y);
6195     }
6196   }
6197 }
6198
6199 static void ToggleLightSwitch(int x, int y)
6200 {
6201   int element = Feld[x][y];
6202
6203   game.light_time_left =
6204     (element == EL_LIGHT_SWITCH ?
6205      level.time_light * FRAMES_PER_SECOND : 0);
6206
6207   RedrawAllLightSwitchesAndInvisibleElements();
6208 }
6209
6210 static void ActivateTimegateSwitch(int x, int y)
6211 {
6212   int xx, yy;
6213
6214   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6215
6216   SCAN_PLAYFIELD(xx, yy)
6217   {
6218     int element = Feld[xx][yy];
6219
6220     if (element == EL_TIMEGATE_CLOSED ||
6221         element == EL_TIMEGATE_CLOSING)
6222     {
6223       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6224       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6225     }
6226
6227     /*
6228     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6229     {
6230       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6231       TEST_DrawLevelField(xx, yy);
6232     }
6233     */
6234
6235   }
6236
6237   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6238                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6239 }
6240
6241 static void Impact(int x, int y)
6242 {
6243   boolean last_line = (y == lev_fieldy - 1);
6244   boolean object_hit = FALSE;
6245   boolean impact = (last_line || object_hit);
6246   int element = Feld[x][y];
6247   int smashed = EL_STEELWALL;
6248
6249   if (!last_line)       /* check if element below was hit */
6250   {
6251     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6252       return;
6253
6254     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6255                                          MovDir[x][y + 1] != MV_DOWN ||
6256                                          MovPos[x][y + 1] <= TILEY / 2));
6257
6258     /* do not smash moving elements that left the smashed field in time */
6259     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6260         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6261       object_hit = FALSE;
6262
6263 #if USE_QUICKSAND_IMPACT_BUGFIX
6264     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6265     {
6266       RemoveMovingField(x, y + 1);
6267       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6268       Feld[x][y + 2] = EL_ROCK;
6269       TEST_DrawLevelField(x, y + 2);
6270
6271       object_hit = TRUE;
6272     }
6273
6274     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6275     {
6276       RemoveMovingField(x, y + 1);
6277       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6278       Feld[x][y + 2] = EL_ROCK;
6279       TEST_DrawLevelField(x, y + 2);
6280
6281       object_hit = TRUE;
6282     }
6283 #endif
6284
6285     if (object_hit)
6286       smashed = MovingOrBlocked2Element(x, y + 1);
6287
6288     impact = (last_line || object_hit);
6289   }
6290
6291   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6292   {
6293     SplashAcid(x, y + 1);
6294     return;
6295   }
6296
6297   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6298   /* only reset graphic animation if graphic really changes after impact */
6299   if (impact &&
6300       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6301   {
6302     ResetGfxAnimation(x, y);
6303     TEST_DrawLevelField(x, y);
6304   }
6305
6306   if (impact && CAN_EXPLODE_IMPACT(element))
6307   {
6308     Bang(x, y);
6309     return;
6310   }
6311   else if (impact && element == EL_PEARL &&
6312            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6313   {
6314     ResetGfxAnimation(x, y);
6315
6316     Feld[x][y] = EL_PEARL_BREAKING;
6317     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6318     return;
6319   }
6320   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6321   {
6322     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6323
6324     return;
6325   }
6326
6327   if (impact && element == EL_AMOEBA_DROP)
6328   {
6329     if (object_hit && IS_PLAYER(x, y + 1))
6330       KillPlayerUnlessEnemyProtected(x, y + 1);
6331     else if (object_hit && smashed == EL_PENGUIN)
6332       Bang(x, y + 1);
6333     else
6334     {
6335       Feld[x][y] = EL_AMOEBA_GROWING;
6336       Store[x][y] = EL_AMOEBA_WET;
6337
6338       ResetRandomAnimationValue(x, y);
6339     }
6340     return;
6341   }
6342
6343   if (object_hit)               /* check which object was hit */
6344   {
6345     if ((CAN_PASS_MAGIC_WALL(element) && 
6346          (smashed == EL_MAGIC_WALL ||
6347           smashed == EL_BD_MAGIC_WALL)) ||
6348         (CAN_PASS_DC_MAGIC_WALL(element) &&
6349          smashed == EL_DC_MAGIC_WALL))
6350     {
6351       int xx, yy;
6352       int activated_magic_wall =
6353         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6354          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6355          EL_DC_MAGIC_WALL_ACTIVE);
6356
6357       /* activate magic wall / mill */
6358       SCAN_PLAYFIELD(xx, yy)
6359       {
6360         if (Feld[xx][yy] == smashed)
6361           Feld[xx][yy] = activated_magic_wall;
6362       }
6363
6364       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6365       game.magic_wall_active = TRUE;
6366
6367       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6368                             SND_MAGIC_WALL_ACTIVATING :
6369                             smashed == EL_BD_MAGIC_WALL ?
6370                             SND_BD_MAGIC_WALL_ACTIVATING :
6371                             SND_DC_MAGIC_WALL_ACTIVATING));
6372     }
6373
6374     if (IS_PLAYER(x, y + 1))
6375     {
6376       if (CAN_SMASH_PLAYER(element))
6377       {
6378         KillPlayerUnlessEnemyProtected(x, y + 1);
6379         return;
6380       }
6381     }
6382     else if (smashed == EL_PENGUIN)
6383     {
6384       if (CAN_SMASH_PLAYER(element))
6385       {
6386         Bang(x, y + 1);
6387         return;
6388       }
6389     }
6390     else if (element == EL_BD_DIAMOND)
6391     {
6392       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6393       {
6394         Bang(x, y + 1);
6395         return;
6396       }
6397     }
6398     else if (((element == EL_SP_INFOTRON ||
6399                element == EL_SP_ZONK) &&
6400               (smashed == EL_SP_SNIKSNAK ||
6401                smashed == EL_SP_ELECTRON ||
6402                smashed == EL_SP_DISK_ORANGE)) ||
6403              (element == EL_SP_INFOTRON &&
6404               smashed == EL_SP_DISK_YELLOW))
6405     {
6406       Bang(x, y + 1);
6407       return;
6408     }
6409     else if (CAN_SMASH_EVERYTHING(element))
6410     {
6411       if (IS_CLASSIC_ENEMY(smashed) ||
6412           CAN_EXPLODE_SMASHED(smashed))
6413       {
6414         Bang(x, y + 1);
6415         return;
6416       }
6417       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6418       {
6419         if (smashed == EL_LAMP ||
6420             smashed == EL_LAMP_ACTIVE)
6421         {
6422           Bang(x, y + 1);
6423           return;
6424         }
6425         else if (smashed == EL_NUT)
6426         {
6427           Feld[x][y + 1] = EL_NUT_BREAKING;
6428           PlayLevelSound(x, y, SND_NUT_BREAKING);
6429           RaiseScoreElement(EL_NUT);
6430           return;
6431         }
6432         else if (smashed == EL_PEARL)
6433         {
6434           ResetGfxAnimation(x, y);
6435
6436           Feld[x][y + 1] = EL_PEARL_BREAKING;
6437           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6438           return;
6439         }
6440         else if (smashed == EL_DIAMOND)
6441         {
6442           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6443           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6444           return;
6445         }
6446         else if (IS_BELT_SWITCH(smashed))
6447         {
6448           ToggleBeltSwitch(x, y + 1);
6449         }
6450         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6451                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6452                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6453                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6454         {
6455           ToggleSwitchgateSwitch(x, y + 1);
6456         }
6457         else if (smashed == EL_LIGHT_SWITCH ||
6458                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6459         {
6460           ToggleLightSwitch(x, y + 1);
6461         }
6462         else
6463         {
6464           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6465
6466           CheckElementChangeBySide(x, y + 1, smashed, element,
6467                                    CE_SWITCHED, CH_SIDE_TOP);
6468           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6469                                             CH_SIDE_TOP);
6470         }
6471       }
6472       else
6473       {
6474         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6475       }
6476     }
6477   }
6478
6479   /* play sound of magic wall / mill */
6480   if (!last_line &&
6481       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6482        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6483        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6484   {
6485     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6486       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6487     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6488       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6489     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6490       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6491
6492     return;
6493   }
6494
6495   /* play sound of object that hits the ground */
6496   if (last_line || object_hit)
6497     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6498 }
6499
6500 inline static void TurnRoundExt(int x, int y)
6501 {
6502   static struct
6503   {
6504     int dx, dy;
6505   } move_xy[] =
6506   {
6507     {  0,  0 },
6508     { -1,  0 },
6509     { +1,  0 },
6510     {  0,  0 },
6511     {  0, -1 },
6512     {  0,  0 }, { 0, 0 }, { 0, 0 },
6513     {  0, +1 }
6514   };
6515   static struct
6516   {
6517     int left, right, back;
6518   } turn[] =
6519   {
6520     { 0,        0,              0        },
6521     { MV_DOWN,  MV_UP,          MV_RIGHT },
6522     { MV_UP,    MV_DOWN,        MV_LEFT  },
6523     { 0,        0,              0        },
6524     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6525     { 0,        0,              0        },
6526     { 0,        0,              0        },
6527     { 0,        0,              0        },
6528     { MV_RIGHT, MV_LEFT,        MV_UP    }
6529   };
6530
6531   int element = Feld[x][y];
6532   int move_pattern = element_info[element].move_pattern;
6533
6534   int old_move_dir = MovDir[x][y];
6535   int left_dir  = turn[old_move_dir].left;
6536   int right_dir = turn[old_move_dir].right;
6537   int back_dir  = turn[old_move_dir].back;
6538
6539   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6540   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6541   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6542   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6543
6544   int left_x  = x + left_dx,  left_y  = y + left_dy;
6545   int right_x = x + right_dx, right_y = y + right_dy;
6546   int move_x  = x + move_dx,  move_y  = y + move_dy;
6547
6548   int xx, yy;
6549
6550   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6551   {
6552     TestIfBadThingTouchesOtherBadThing(x, y);
6553
6554     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6555       MovDir[x][y] = right_dir;
6556     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6557       MovDir[x][y] = left_dir;
6558
6559     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6560       MovDelay[x][y] = 9;
6561     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6562       MovDelay[x][y] = 1;
6563   }
6564   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6565   {
6566     TestIfBadThingTouchesOtherBadThing(x, y);
6567
6568     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6569       MovDir[x][y] = left_dir;
6570     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6571       MovDir[x][y] = right_dir;
6572
6573     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6574       MovDelay[x][y] = 9;
6575     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6576       MovDelay[x][y] = 1;
6577   }
6578   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6579   {
6580     TestIfBadThingTouchesOtherBadThing(x, y);
6581
6582     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6583       MovDir[x][y] = left_dir;
6584     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6585       MovDir[x][y] = right_dir;
6586
6587     if (MovDir[x][y] != old_move_dir)
6588       MovDelay[x][y] = 9;
6589   }
6590   else if (element == EL_YAMYAM)
6591   {
6592     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6593     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6594
6595     if (can_turn_left && can_turn_right)
6596       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6597     else if (can_turn_left)
6598       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6599     else if (can_turn_right)
6600       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6601     else
6602       MovDir[x][y] = back_dir;
6603
6604     MovDelay[x][y] = 16 + 16 * RND(3);
6605   }
6606   else if (element == EL_DARK_YAMYAM)
6607   {
6608     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6609                                                          left_x, left_y);
6610     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6611                                                          right_x, right_y);
6612
6613     if (can_turn_left && can_turn_right)
6614       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6615     else if (can_turn_left)
6616       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6617     else if (can_turn_right)
6618       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6619     else
6620       MovDir[x][y] = back_dir;
6621
6622     MovDelay[x][y] = 16 + 16 * RND(3);
6623   }
6624   else if (element == EL_PACMAN)
6625   {
6626     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6627     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6628
6629     if (can_turn_left && can_turn_right)
6630       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6631     else if (can_turn_left)
6632       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6633     else if (can_turn_right)
6634       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6635     else
6636       MovDir[x][y] = back_dir;
6637
6638     MovDelay[x][y] = 6 + RND(40);
6639   }
6640   else if (element == EL_PIG)
6641   {
6642     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6643     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6644     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6645     boolean should_turn_left, should_turn_right, should_move_on;
6646     int rnd_value = 24;
6647     int rnd = RND(rnd_value);
6648
6649     should_turn_left = (can_turn_left &&
6650                         (!can_move_on ||
6651                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6652                                                    y + back_dy + left_dy)));
6653     should_turn_right = (can_turn_right &&
6654                          (!can_move_on ||
6655                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6656                                                     y + back_dy + right_dy)));
6657     should_move_on = (can_move_on &&
6658                       (!can_turn_left ||
6659                        !can_turn_right ||
6660                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6661                                                  y + move_dy + left_dy) ||
6662                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6663                                                  y + move_dy + right_dy)));
6664
6665     if (should_turn_left || should_turn_right || should_move_on)
6666     {
6667       if (should_turn_left && should_turn_right && should_move_on)
6668         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6669                         rnd < 2 * rnd_value / 3 ? right_dir :
6670                         old_move_dir);
6671       else if (should_turn_left && should_turn_right)
6672         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6673       else if (should_turn_left && should_move_on)
6674         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6675       else if (should_turn_right && should_move_on)
6676         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6677       else if (should_turn_left)
6678         MovDir[x][y] = left_dir;
6679       else if (should_turn_right)
6680         MovDir[x][y] = right_dir;
6681       else if (should_move_on)
6682         MovDir[x][y] = old_move_dir;
6683     }
6684     else if (can_move_on && rnd > rnd_value / 8)
6685       MovDir[x][y] = old_move_dir;
6686     else if (can_turn_left && can_turn_right)
6687       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6688     else if (can_turn_left && rnd > rnd_value / 8)
6689       MovDir[x][y] = left_dir;
6690     else if (can_turn_right && rnd > rnd_value/8)
6691       MovDir[x][y] = right_dir;
6692     else
6693       MovDir[x][y] = back_dir;
6694
6695     xx = x + move_xy[MovDir[x][y]].dx;
6696     yy = y + move_xy[MovDir[x][y]].dy;
6697
6698     if (!IN_LEV_FIELD(xx, yy) ||
6699         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6700       MovDir[x][y] = old_move_dir;
6701
6702     MovDelay[x][y] = 0;
6703   }
6704   else if (element == EL_DRAGON)
6705   {
6706     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6707     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6708     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6709     int rnd_value = 24;
6710     int rnd = RND(rnd_value);
6711
6712     if (can_move_on && rnd > rnd_value / 8)
6713       MovDir[x][y] = old_move_dir;
6714     else if (can_turn_left && can_turn_right)
6715       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6716     else if (can_turn_left && rnd > rnd_value / 8)
6717       MovDir[x][y] = left_dir;
6718     else if (can_turn_right && rnd > rnd_value / 8)
6719       MovDir[x][y] = right_dir;
6720     else
6721       MovDir[x][y] = back_dir;
6722
6723     xx = x + move_xy[MovDir[x][y]].dx;
6724     yy = y + move_xy[MovDir[x][y]].dy;
6725
6726     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6727       MovDir[x][y] = old_move_dir;
6728
6729     MovDelay[x][y] = 0;
6730   }
6731   else if (element == EL_MOLE)
6732   {
6733     boolean can_move_on =
6734       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6735                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6736                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6737     if (!can_move_on)
6738     {
6739       boolean can_turn_left =
6740         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6741                               IS_AMOEBOID(Feld[left_x][left_y])));
6742
6743       boolean can_turn_right =
6744         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6745                               IS_AMOEBOID(Feld[right_x][right_y])));
6746
6747       if (can_turn_left && can_turn_right)
6748         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6749       else if (can_turn_left)
6750         MovDir[x][y] = left_dir;
6751       else
6752         MovDir[x][y] = right_dir;
6753     }
6754
6755     if (MovDir[x][y] != old_move_dir)
6756       MovDelay[x][y] = 9;
6757   }
6758   else if (element == EL_BALLOON)
6759   {
6760     MovDir[x][y] = game.wind_direction;
6761     MovDelay[x][y] = 0;
6762   }
6763   else if (element == EL_SPRING)
6764   {
6765     if (MovDir[x][y] & MV_HORIZONTAL)
6766     {
6767       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6768           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6769       {
6770         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6771         ResetGfxAnimation(move_x, move_y);
6772         TEST_DrawLevelField(move_x, move_y);
6773
6774         MovDir[x][y] = back_dir;
6775       }
6776       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6777                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6778         MovDir[x][y] = MV_NONE;
6779     }
6780
6781     MovDelay[x][y] = 0;
6782   }
6783   else if (element == EL_ROBOT ||
6784            element == EL_SATELLITE ||
6785            element == EL_PENGUIN ||
6786            element == EL_EMC_ANDROID)
6787   {
6788     int attr_x = -1, attr_y = -1;
6789
6790     if (AllPlayersGone)
6791     {
6792       attr_x = ExitX;
6793       attr_y = ExitY;
6794     }
6795     else
6796     {
6797       int i;
6798
6799       for (i = 0; i < MAX_PLAYERS; i++)
6800       {
6801         struct PlayerInfo *player = &stored_player[i];
6802         int jx = player->jx, jy = player->jy;
6803
6804         if (!player->active)
6805           continue;
6806
6807         if (attr_x == -1 ||
6808             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6809         {
6810           attr_x = jx;
6811           attr_y = jy;
6812         }
6813       }
6814     }
6815
6816     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6817         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6818          game.engine_version < VERSION_IDENT(3,1,0,0)))
6819     {
6820       attr_x = ZX;
6821       attr_y = ZY;
6822     }
6823
6824     if (element == EL_PENGUIN)
6825     {
6826       int i;
6827       static int xy[4][2] =
6828       {
6829         { 0, -1 },
6830         { -1, 0 },
6831         { +1, 0 },
6832         { 0, +1 }
6833       };
6834
6835       for (i = 0; i < NUM_DIRECTIONS; i++)
6836       {
6837         int ex = x + xy[i][0];
6838         int ey = y + xy[i][1];
6839
6840         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6841                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6842                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6843                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6844         {
6845           attr_x = ex;
6846           attr_y = ey;
6847           break;
6848         }
6849       }
6850     }
6851
6852     MovDir[x][y] = MV_NONE;
6853     if (attr_x < x)
6854       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6855     else if (attr_x > x)
6856       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6857     if (attr_y < y)
6858       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6859     else if (attr_y > y)
6860       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6861
6862     if (element == EL_ROBOT)
6863     {
6864       int newx, newy;
6865
6866       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6867         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6868       Moving2Blocked(x, y, &newx, &newy);
6869
6870       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6871         MovDelay[x][y] = 8 + 8 * !RND(3);
6872       else
6873         MovDelay[x][y] = 16;
6874     }
6875     else if (element == EL_PENGUIN)
6876     {
6877       int newx, newy;
6878
6879       MovDelay[x][y] = 1;
6880
6881       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6882       {
6883         boolean first_horiz = RND(2);
6884         int new_move_dir = MovDir[x][y];
6885
6886         MovDir[x][y] =
6887           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6888         Moving2Blocked(x, y, &newx, &newy);
6889
6890         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6891           return;
6892
6893         MovDir[x][y] =
6894           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6895         Moving2Blocked(x, y, &newx, &newy);
6896
6897         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6898           return;
6899
6900         MovDir[x][y] = old_move_dir;
6901         return;
6902       }
6903     }
6904     else if (element == EL_SATELLITE)
6905     {
6906       int newx, newy;
6907
6908       MovDelay[x][y] = 1;
6909
6910       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6911       {
6912         boolean first_horiz = RND(2);
6913         int new_move_dir = MovDir[x][y];
6914
6915         MovDir[x][y] =
6916           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6917         Moving2Blocked(x, y, &newx, &newy);
6918
6919         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6920           return;
6921
6922         MovDir[x][y] =
6923           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6924         Moving2Blocked(x, y, &newx, &newy);
6925
6926         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6927           return;
6928
6929         MovDir[x][y] = old_move_dir;
6930         return;
6931       }
6932     }
6933     else if (element == EL_EMC_ANDROID)
6934     {
6935       static int check_pos[16] =
6936       {
6937         -1,             /*  0 => (invalid)          */
6938         7,              /*  1 => MV_LEFT            */
6939         3,              /*  2 => MV_RIGHT           */
6940         -1,             /*  3 => (invalid)          */
6941         1,              /*  4 =>            MV_UP   */
6942         0,              /*  5 => MV_LEFT  | MV_UP   */
6943         2,              /*  6 => MV_RIGHT | MV_UP   */
6944         -1,             /*  7 => (invalid)          */
6945         5,              /*  8 =>            MV_DOWN */
6946         6,              /*  9 => MV_LEFT  | MV_DOWN */
6947         4,              /* 10 => MV_RIGHT | MV_DOWN */
6948         -1,             /* 11 => (invalid)          */
6949         -1,             /* 12 => (invalid)          */
6950         -1,             /* 13 => (invalid)          */
6951         -1,             /* 14 => (invalid)          */
6952         -1,             /* 15 => (invalid)          */
6953       };
6954       static struct
6955       {
6956         int dx, dy;
6957         int dir;
6958       } check_xy[8] =
6959       {
6960         { -1, -1,       MV_LEFT  | MV_UP   },
6961         {  0, -1,                  MV_UP   },
6962         { +1, -1,       MV_RIGHT | MV_UP   },
6963         { +1,  0,       MV_RIGHT           },
6964         { +1, +1,       MV_RIGHT | MV_DOWN },
6965         {  0, +1,                  MV_DOWN },
6966         { -1, +1,       MV_LEFT  | MV_DOWN },
6967         { -1,  0,       MV_LEFT            },
6968       };
6969       int start_pos, check_order;
6970       boolean can_clone = FALSE;
6971       int i;
6972
6973       /* check if there is any free field around current position */
6974       for (i = 0; i < 8; i++)
6975       {
6976         int newx = x + check_xy[i].dx;
6977         int newy = y + check_xy[i].dy;
6978
6979         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6980         {
6981           can_clone = TRUE;
6982
6983           break;
6984         }
6985       }
6986
6987       if (can_clone)            /* randomly find an element to clone */
6988       {
6989         can_clone = FALSE;
6990
6991         start_pos = check_pos[RND(8)];
6992         check_order = (RND(2) ? -1 : +1);
6993
6994         for (i = 0; i < 8; i++)
6995         {
6996           int pos_raw = start_pos + i * check_order;
6997           int pos = (pos_raw + 8) % 8;
6998           int newx = x + check_xy[pos].dx;
6999           int newy = y + check_xy[pos].dy;
7000
7001           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7002           {
7003             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7004             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7005
7006             Store[x][y] = Feld[newx][newy];
7007
7008             can_clone = TRUE;
7009
7010             break;
7011           }
7012         }
7013       }
7014
7015       if (can_clone)            /* randomly find a direction to move */
7016       {
7017         can_clone = FALSE;
7018
7019         start_pos = check_pos[RND(8)];
7020         check_order = (RND(2) ? -1 : +1);
7021
7022         for (i = 0; i < 8; i++)
7023         {
7024           int pos_raw = start_pos + i * check_order;
7025           int pos = (pos_raw + 8) % 8;
7026           int newx = x + check_xy[pos].dx;
7027           int newy = y + check_xy[pos].dy;
7028           int new_move_dir = check_xy[pos].dir;
7029
7030           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7031           {
7032             MovDir[x][y] = new_move_dir;
7033             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7034
7035             can_clone = TRUE;
7036
7037             break;
7038           }
7039         }
7040       }
7041
7042       if (can_clone)            /* cloning and moving successful */
7043         return;
7044
7045       /* cannot clone -- try to move towards player */
7046
7047       start_pos = check_pos[MovDir[x][y] & 0x0f];
7048       check_order = (RND(2) ? -1 : +1);
7049
7050       for (i = 0; i < 3; i++)
7051       {
7052         /* first check start_pos, then previous/next or (next/previous) pos */
7053         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7054         int pos = (pos_raw + 8) % 8;
7055         int newx = x + check_xy[pos].dx;
7056         int newy = y + check_xy[pos].dy;
7057         int new_move_dir = check_xy[pos].dir;
7058
7059         if (IS_PLAYER(newx, newy))
7060           break;
7061
7062         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7063         {
7064           MovDir[x][y] = new_move_dir;
7065           MovDelay[x][y] = level.android_move_time * 8 + 1;
7066
7067           break;
7068         }
7069       }
7070     }
7071   }
7072   else if (move_pattern == MV_TURNING_LEFT ||
7073            move_pattern == MV_TURNING_RIGHT ||
7074            move_pattern == MV_TURNING_LEFT_RIGHT ||
7075            move_pattern == MV_TURNING_RIGHT_LEFT ||
7076            move_pattern == MV_TURNING_RANDOM ||
7077            move_pattern == MV_ALL_DIRECTIONS)
7078   {
7079     boolean can_turn_left =
7080       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7081     boolean can_turn_right =
7082       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7083
7084     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7085       return;
7086
7087     if (move_pattern == MV_TURNING_LEFT)
7088       MovDir[x][y] = left_dir;
7089     else if (move_pattern == MV_TURNING_RIGHT)
7090       MovDir[x][y] = right_dir;
7091     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7092       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7093     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7094       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7095     else if (move_pattern == MV_TURNING_RANDOM)
7096       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7097                       can_turn_right && !can_turn_left ? right_dir :
7098                       RND(2) ? left_dir : right_dir);
7099     else if (can_turn_left && can_turn_right)
7100       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7101     else if (can_turn_left)
7102       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7103     else if (can_turn_right)
7104       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7105     else
7106       MovDir[x][y] = back_dir;
7107
7108     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7109   }
7110   else if (move_pattern == MV_HORIZONTAL ||
7111            move_pattern == MV_VERTICAL)
7112   {
7113     if (move_pattern & old_move_dir)
7114       MovDir[x][y] = back_dir;
7115     else if (move_pattern == MV_HORIZONTAL)
7116       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7117     else if (move_pattern == MV_VERTICAL)
7118       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7119
7120     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7121   }
7122   else if (move_pattern & MV_ANY_DIRECTION)
7123   {
7124     MovDir[x][y] = move_pattern;
7125     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7126   }
7127   else if (move_pattern & MV_WIND_DIRECTION)
7128   {
7129     MovDir[x][y] = game.wind_direction;
7130     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7131   }
7132   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7133   {
7134     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7135       MovDir[x][y] = left_dir;
7136     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7137       MovDir[x][y] = right_dir;
7138
7139     if (MovDir[x][y] != old_move_dir)
7140       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7141   }
7142   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7143   {
7144     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7145       MovDir[x][y] = right_dir;
7146     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7147       MovDir[x][y] = left_dir;
7148
7149     if (MovDir[x][y] != old_move_dir)
7150       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7151   }
7152   else if (move_pattern == MV_TOWARDS_PLAYER ||
7153            move_pattern == MV_AWAY_FROM_PLAYER)
7154   {
7155     int attr_x = -1, attr_y = -1;
7156     int newx, newy;
7157     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7158
7159     if (AllPlayersGone)
7160     {
7161       attr_x = ExitX;
7162       attr_y = ExitY;
7163     }
7164     else
7165     {
7166       int i;
7167
7168       for (i = 0; i < MAX_PLAYERS; i++)
7169       {
7170         struct PlayerInfo *player = &stored_player[i];
7171         int jx = player->jx, jy = player->jy;
7172
7173         if (!player->active)
7174           continue;
7175
7176         if (attr_x == -1 ||
7177             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7178         {
7179           attr_x = jx;
7180           attr_y = jy;
7181         }
7182       }
7183     }
7184
7185     MovDir[x][y] = MV_NONE;
7186     if (attr_x < x)
7187       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7188     else if (attr_x > x)
7189       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7190     if (attr_y < y)
7191       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7192     else if (attr_y > y)
7193       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7194
7195     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7196
7197     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7198     {
7199       boolean first_horiz = RND(2);
7200       int new_move_dir = MovDir[x][y];
7201
7202       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7203       {
7204         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7205         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7206
7207         return;
7208       }
7209
7210       MovDir[x][y] =
7211         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7212       Moving2Blocked(x, y, &newx, &newy);
7213
7214       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7215         return;
7216
7217       MovDir[x][y] =
7218         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7219       Moving2Blocked(x, y, &newx, &newy);
7220
7221       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7222         return;
7223
7224       MovDir[x][y] = old_move_dir;
7225     }
7226   }
7227   else if (move_pattern == MV_WHEN_PUSHED ||
7228            move_pattern == MV_WHEN_DROPPED)
7229   {
7230     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7231       MovDir[x][y] = MV_NONE;
7232
7233     MovDelay[x][y] = 0;
7234   }
7235   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7236   {
7237     static int test_xy[7][2] =
7238     {
7239       { 0, -1 },
7240       { -1, 0 },
7241       { +1, 0 },
7242       { 0, +1 },
7243       { 0, -1 },
7244       { -1, 0 },
7245       { +1, 0 },
7246     };
7247     static int test_dir[7] =
7248     {
7249       MV_UP,
7250       MV_LEFT,
7251       MV_RIGHT,
7252       MV_DOWN,
7253       MV_UP,
7254       MV_LEFT,
7255       MV_RIGHT,
7256     };
7257     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7258     int move_preference = -1000000;     /* start with very low preference */
7259     int new_move_dir = MV_NONE;
7260     int start_test = RND(4);
7261     int i;
7262
7263     for (i = 0; i < NUM_DIRECTIONS; i++)
7264     {
7265       int move_dir = test_dir[start_test + i];
7266       int move_dir_preference;
7267
7268       xx = x + test_xy[start_test + i][0];
7269       yy = y + test_xy[start_test + i][1];
7270
7271       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7272           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7273       {
7274         new_move_dir = move_dir;
7275
7276         break;
7277       }
7278
7279       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7280         continue;
7281
7282       move_dir_preference = -1 * RunnerVisit[xx][yy];
7283       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7284         move_dir_preference = PlayerVisit[xx][yy];
7285
7286       if (move_dir_preference > move_preference)
7287       {
7288         /* prefer field that has not been visited for the longest time */
7289         move_preference = move_dir_preference;
7290         new_move_dir = move_dir;
7291       }
7292       else if (move_dir_preference == move_preference &&
7293                move_dir == old_move_dir)
7294       {
7295         /* prefer last direction when all directions are preferred equally */
7296         move_preference = move_dir_preference;
7297         new_move_dir = move_dir;
7298       }
7299     }
7300
7301     MovDir[x][y] = new_move_dir;
7302     if (old_move_dir != new_move_dir)
7303       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7304   }
7305 }
7306
7307 static void TurnRound(int x, int y)
7308 {
7309   int direction = MovDir[x][y];
7310
7311   TurnRoundExt(x, y);
7312
7313   GfxDir[x][y] = MovDir[x][y];
7314
7315   if (direction != MovDir[x][y])
7316     GfxFrame[x][y] = 0;
7317
7318   if (MovDelay[x][y])
7319     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7320
7321   ResetGfxFrame(x, y);
7322 }
7323
7324 static boolean JustBeingPushed(int x, int y)
7325 {
7326   int i;
7327
7328   for (i = 0; i < MAX_PLAYERS; i++)
7329   {
7330     struct PlayerInfo *player = &stored_player[i];
7331
7332     if (player->active && player->is_pushing && player->MovPos)
7333     {
7334       int next_jx = player->jx + (player->jx - player->last_jx);
7335       int next_jy = player->jy + (player->jy - player->last_jy);
7336
7337       if (x == next_jx && y == next_jy)
7338         return TRUE;
7339     }
7340   }
7341
7342   return FALSE;
7343 }
7344
7345 static void StartMoving(int x, int y)
7346 {
7347   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7348   int element = Feld[x][y];
7349
7350   if (Stop[x][y])
7351     return;
7352
7353   if (MovDelay[x][y] == 0)
7354     GfxAction[x][y] = ACTION_DEFAULT;
7355
7356   if (CAN_FALL(element) && y < lev_fieldy - 1)
7357   {
7358     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7359         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7360       if (JustBeingPushed(x, y))
7361         return;
7362
7363     if (element == EL_QUICKSAND_FULL)
7364     {
7365       if (IS_FREE(x, y + 1))
7366       {
7367         InitMovingField(x, y, MV_DOWN);
7368         started_moving = TRUE;
7369
7370         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7371 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7372         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7373           Store[x][y] = EL_ROCK;
7374 #else
7375         Store[x][y] = EL_ROCK;
7376 #endif
7377
7378         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7379       }
7380       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7381       {
7382         if (!MovDelay[x][y])
7383         {
7384           MovDelay[x][y] = TILEY + 1;
7385
7386           ResetGfxAnimation(x, y);
7387           ResetGfxAnimation(x, y + 1);
7388         }
7389
7390         if (MovDelay[x][y])
7391         {
7392           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7393           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7394
7395           MovDelay[x][y]--;
7396           if (MovDelay[x][y])
7397             return;
7398         }
7399
7400         Feld[x][y] = EL_QUICKSAND_EMPTY;
7401         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7402         Store[x][y + 1] = Store[x][y];
7403         Store[x][y] = 0;
7404
7405         PlayLevelSoundAction(x, y, ACTION_FILLING);
7406       }
7407       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7408       {
7409         if (!MovDelay[x][y])
7410         {
7411           MovDelay[x][y] = TILEY + 1;
7412
7413           ResetGfxAnimation(x, y);
7414           ResetGfxAnimation(x, y + 1);
7415         }
7416
7417         if (MovDelay[x][y])
7418         {
7419           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7420           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7421
7422           MovDelay[x][y]--;
7423           if (MovDelay[x][y])
7424             return;
7425         }
7426
7427         Feld[x][y] = EL_QUICKSAND_EMPTY;
7428         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7429         Store[x][y + 1] = Store[x][y];
7430         Store[x][y] = 0;
7431
7432         PlayLevelSoundAction(x, y, ACTION_FILLING);
7433       }
7434     }
7435     else if (element == EL_QUICKSAND_FAST_FULL)
7436     {
7437       if (IS_FREE(x, y + 1))
7438       {
7439         InitMovingField(x, y, MV_DOWN);
7440         started_moving = TRUE;
7441
7442         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7443 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7444         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7445           Store[x][y] = EL_ROCK;
7446 #else
7447         Store[x][y] = EL_ROCK;
7448 #endif
7449
7450         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7451       }
7452       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7453       {
7454         if (!MovDelay[x][y])
7455         {
7456           MovDelay[x][y] = TILEY + 1;
7457
7458           ResetGfxAnimation(x, y);
7459           ResetGfxAnimation(x, y + 1);
7460         }
7461
7462         if (MovDelay[x][y])
7463         {
7464           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7465           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7466
7467           MovDelay[x][y]--;
7468           if (MovDelay[x][y])
7469             return;
7470         }
7471
7472         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7473         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7474         Store[x][y + 1] = Store[x][y];
7475         Store[x][y] = 0;
7476
7477         PlayLevelSoundAction(x, y, ACTION_FILLING);
7478       }
7479       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7480       {
7481         if (!MovDelay[x][y])
7482         {
7483           MovDelay[x][y] = TILEY + 1;
7484
7485           ResetGfxAnimation(x, y);
7486           ResetGfxAnimation(x, y + 1);
7487         }
7488
7489         if (MovDelay[x][y])
7490         {
7491           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7492           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7493
7494           MovDelay[x][y]--;
7495           if (MovDelay[x][y])
7496             return;
7497         }
7498
7499         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7500         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7501         Store[x][y + 1] = Store[x][y];
7502         Store[x][y] = 0;
7503
7504         PlayLevelSoundAction(x, y, ACTION_FILLING);
7505       }
7506     }
7507     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7508              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7509     {
7510       InitMovingField(x, y, MV_DOWN);
7511       started_moving = TRUE;
7512
7513       Feld[x][y] = EL_QUICKSAND_FILLING;
7514       Store[x][y] = element;
7515
7516       PlayLevelSoundAction(x, y, ACTION_FILLING);
7517     }
7518     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7519              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7520     {
7521       InitMovingField(x, y, MV_DOWN);
7522       started_moving = TRUE;
7523
7524       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7525       Store[x][y] = element;
7526
7527       PlayLevelSoundAction(x, y, ACTION_FILLING);
7528     }
7529     else if (element == EL_MAGIC_WALL_FULL)
7530     {
7531       if (IS_FREE(x, y + 1))
7532       {
7533         InitMovingField(x, y, MV_DOWN);
7534         started_moving = TRUE;
7535
7536         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7537         Store[x][y] = EL_CHANGED(Store[x][y]);
7538       }
7539       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7540       {
7541         if (!MovDelay[x][y])
7542           MovDelay[x][y] = TILEY / 4 + 1;
7543
7544         if (MovDelay[x][y])
7545         {
7546           MovDelay[x][y]--;
7547           if (MovDelay[x][y])
7548             return;
7549         }
7550
7551         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7552         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7553         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7554         Store[x][y] = 0;
7555       }
7556     }
7557     else if (element == EL_BD_MAGIC_WALL_FULL)
7558     {
7559       if (IS_FREE(x, y + 1))
7560       {
7561         InitMovingField(x, y, MV_DOWN);
7562         started_moving = TRUE;
7563
7564         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7565         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7566       }
7567       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7568       {
7569         if (!MovDelay[x][y])
7570           MovDelay[x][y] = TILEY / 4 + 1;
7571
7572         if (MovDelay[x][y])
7573         {
7574           MovDelay[x][y]--;
7575           if (MovDelay[x][y])
7576             return;
7577         }
7578
7579         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7580         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7581         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7582         Store[x][y] = 0;
7583       }
7584     }
7585     else if (element == EL_DC_MAGIC_WALL_FULL)
7586     {
7587       if (IS_FREE(x, y + 1))
7588       {
7589         InitMovingField(x, y, MV_DOWN);
7590         started_moving = TRUE;
7591
7592         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7593         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7594       }
7595       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7596       {
7597         if (!MovDelay[x][y])
7598           MovDelay[x][y] = TILEY / 4 + 1;
7599
7600         if (MovDelay[x][y])
7601         {
7602           MovDelay[x][y]--;
7603           if (MovDelay[x][y])
7604             return;
7605         }
7606
7607         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7608         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7609         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7610         Store[x][y] = 0;
7611       }
7612     }
7613     else if ((CAN_PASS_MAGIC_WALL(element) &&
7614               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7615                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7616              (CAN_PASS_DC_MAGIC_WALL(element) &&
7617               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7618
7619     {
7620       InitMovingField(x, y, MV_DOWN);
7621       started_moving = TRUE;
7622
7623       Feld[x][y] =
7624         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7625          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7626          EL_DC_MAGIC_WALL_FILLING);
7627       Store[x][y] = element;
7628     }
7629     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7630     {
7631       SplashAcid(x, y + 1);
7632
7633       InitMovingField(x, y, MV_DOWN);
7634       started_moving = TRUE;
7635
7636       Store[x][y] = EL_ACID;
7637     }
7638     else if (
7639              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7640               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7641              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7642               CAN_FALL(element) && WasJustFalling[x][y] &&
7643               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7644
7645              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7646               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7647               (Feld[x][y + 1] == EL_BLOCKED)))
7648     {
7649       /* this is needed for a special case not covered by calling "Impact()"
7650          from "ContinueMoving()": if an element moves to a tile directly below
7651          another element which was just falling on that tile (which was empty
7652          in the previous frame), the falling element above would just stop
7653          instead of smashing the element below (in previous version, the above
7654          element was just checked for "moving" instead of "falling", resulting
7655          in incorrect smashes caused by horizontal movement of the above
7656          element; also, the case of the player being the element to smash was
7657          simply not covered here... :-/ ) */
7658
7659       CheckCollision[x][y] = 0;
7660       CheckImpact[x][y] = 0;
7661
7662       Impact(x, y);
7663     }
7664     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7665     {
7666       if (MovDir[x][y] == MV_NONE)
7667       {
7668         InitMovingField(x, y, MV_DOWN);
7669         started_moving = TRUE;
7670       }
7671     }
7672     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7673     {
7674       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7675         MovDir[x][y] = MV_DOWN;
7676
7677       InitMovingField(x, y, MV_DOWN);
7678       started_moving = TRUE;
7679     }
7680     else if (element == EL_AMOEBA_DROP)
7681     {
7682       Feld[x][y] = EL_AMOEBA_GROWING;
7683       Store[x][y] = EL_AMOEBA_WET;
7684     }
7685     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7686               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7687              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7688              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7689     {
7690       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7691                                 (IS_FREE(x - 1, y + 1) ||
7692                                  Feld[x - 1][y + 1] == EL_ACID));
7693       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7694                                 (IS_FREE(x + 1, y + 1) ||
7695                                  Feld[x + 1][y + 1] == EL_ACID));
7696       boolean can_fall_any  = (can_fall_left || can_fall_right);
7697       boolean can_fall_both = (can_fall_left && can_fall_right);
7698       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7699
7700       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7701       {
7702         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7703           can_fall_right = FALSE;
7704         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7705           can_fall_left = FALSE;
7706         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7707           can_fall_right = FALSE;
7708         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7709           can_fall_left = FALSE;
7710
7711         can_fall_any  = (can_fall_left || can_fall_right);
7712         can_fall_both = FALSE;
7713       }
7714
7715       if (can_fall_both)
7716       {
7717         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7718           can_fall_right = FALSE;       /* slip down on left side */
7719         else
7720           can_fall_left = !(can_fall_right = RND(2));
7721
7722         can_fall_both = FALSE;
7723       }
7724
7725       if (can_fall_any)
7726       {
7727         /* if not determined otherwise, prefer left side for slipping down */
7728         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7729         started_moving = TRUE;
7730       }
7731     }
7732     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7733     {
7734       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7735       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7736       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7737       int belt_dir = game.belt_dir[belt_nr];
7738
7739       if ((belt_dir == MV_LEFT  && left_is_free) ||
7740           (belt_dir == MV_RIGHT && right_is_free))
7741       {
7742         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7743
7744         InitMovingField(x, y, belt_dir);
7745         started_moving = TRUE;
7746
7747         Pushed[x][y] = TRUE;
7748         Pushed[nextx][y] = TRUE;
7749
7750         GfxAction[x][y] = ACTION_DEFAULT;
7751       }
7752       else
7753       {
7754         MovDir[x][y] = 0;       /* if element was moving, stop it */
7755       }
7756     }
7757   }
7758
7759   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7760   if (CAN_MOVE(element) && !started_moving)
7761   {
7762     int move_pattern = element_info[element].move_pattern;
7763     int newx, newy;
7764
7765     Moving2Blocked(x, y, &newx, &newy);
7766
7767     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7768       return;
7769
7770     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7771         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7772     {
7773       WasJustMoving[x][y] = 0;
7774       CheckCollision[x][y] = 0;
7775
7776       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7777
7778       if (Feld[x][y] != element)        /* element has changed */
7779         return;
7780     }
7781
7782     if (!MovDelay[x][y])        /* start new movement phase */
7783     {
7784       /* all objects that can change their move direction after each step
7785          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7786
7787       if (element != EL_YAMYAM &&
7788           element != EL_DARK_YAMYAM &&
7789           element != EL_PACMAN &&
7790           !(move_pattern & MV_ANY_DIRECTION) &&
7791           move_pattern != MV_TURNING_LEFT &&
7792           move_pattern != MV_TURNING_RIGHT &&
7793           move_pattern != MV_TURNING_LEFT_RIGHT &&
7794           move_pattern != MV_TURNING_RIGHT_LEFT &&
7795           move_pattern != MV_TURNING_RANDOM)
7796       {
7797         TurnRound(x, y);
7798
7799         if (MovDelay[x][y] && (element == EL_BUG ||
7800                                element == EL_SPACESHIP ||
7801                                element == EL_SP_SNIKSNAK ||
7802                                element == EL_SP_ELECTRON ||
7803                                element == EL_MOLE))
7804           TEST_DrawLevelField(x, y);
7805       }
7806     }
7807
7808     if (MovDelay[x][y])         /* wait some time before next movement */
7809     {
7810       MovDelay[x][y]--;
7811
7812       if (element == EL_ROBOT ||
7813           element == EL_YAMYAM ||
7814           element == EL_DARK_YAMYAM)
7815       {
7816         DrawLevelElementAnimationIfNeeded(x, y, element);
7817         PlayLevelSoundAction(x, y, ACTION_WAITING);
7818       }
7819       else if (element == EL_SP_ELECTRON)
7820         DrawLevelElementAnimationIfNeeded(x, y, element);
7821       else if (element == EL_DRAGON)
7822       {
7823         int i;
7824         int dir = MovDir[x][y];
7825         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7826         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7827         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7828                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7829                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7830                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7831         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7832
7833         GfxAction[x][y] = ACTION_ATTACKING;
7834
7835         if (IS_PLAYER(x, y))
7836           DrawPlayerField(x, y);
7837         else
7838           TEST_DrawLevelField(x, y);
7839
7840         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7841
7842         for (i = 1; i <= 3; i++)
7843         {
7844           int xx = x + i * dx;
7845           int yy = y + i * dy;
7846           int sx = SCREENX(xx);
7847           int sy = SCREENY(yy);
7848           int flame_graphic = graphic + (i - 1);
7849
7850           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7851             break;
7852
7853           if (MovDelay[x][y])
7854           {
7855             int flamed = MovingOrBlocked2Element(xx, yy);
7856
7857             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7858               Bang(xx, yy);
7859             else
7860               RemoveMovingField(xx, yy);
7861
7862             ChangeDelay[xx][yy] = 0;
7863
7864             Feld[xx][yy] = EL_FLAMES;
7865
7866             if (IN_SCR_FIELD(sx, sy))
7867             {
7868               TEST_DrawLevelFieldCrumbled(xx, yy);
7869               DrawGraphic(sx, sy, flame_graphic, frame);
7870             }
7871           }
7872           else
7873           {
7874             if (Feld[xx][yy] == EL_FLAMES)
7875               Feld[xx][yy] = EL_EMPTY;
7876             TEST_DrawLevelField(xx, yy);
7877           }
7878         }
7879       }
7880
7881       if (MovDelay[x][y])       /* element still has to wait some time */
7882       {
7883         PlayLevelSoundAction(x, y, ACTION_WAITING);
7884
7885         return;
7886       }
7887     }
7888
7889     /* now make next step */
7890
7891     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7892
7893     if (DONT_COLLIDE_WITH(element) &&
7894         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7895         !PLAYER_ENEMY_PROTECTED(newx, newy))
7896     {
7897       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7898
7899       return;
7900     }
7901
7902     else if (CAN_MOVE_INTO_ACID(element) &&
7903              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7904              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7905              (MovDir[x][y] == MV_DOWN ||
7906               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7907     {
7908       SplashAcid(newx, newy);
7909       Store[x][y] = EL_ACID;
7910     }
7911     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7912     {
7913       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7914           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7915           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7916           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7917       {
7918         RemoveField(x, y);
7919         TEST_DrawLevelField(x, y);
7920
7921         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7922         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7923           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7924
7925         local_player->friends_still_needed--;
7926         if (!local_player->friends_still_needed &&
7927             !local_player->GameOver && AllPlayersGone)
7928           PlayerWins(local_player);
7929
7930         return;
7931       }
7932       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7933       {
7934         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7935           TEST_DrawLevelField(newx, newy);
7936         else
7937           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7938       }
7939       else if (!IS_FREE(newx, newy))
7940       {
7941         GfxAction[x][y] = ACTION_WAITING;
7942
7943         if (IS_PLAYER(x, y))
7944           DrawPlayerField(x, y);
7945         else
7946           TEST_DrawLevelField(x, y);
7947
7948         return;
7949       }
7950     }
7951     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7952     {
7953       if (IS_FOOD_PIG(Feld[newx][newy]))
7954       {
7955         if (IS_MOVING(newx, newy))
7956           RemoveMovingField(newx, newy);
7957         else
7958         {
7959           Feld[newx][newy] = EL_EMPTY;
7960           TEST_DrawLevelField(newx, newy);
7961         }
7962
7963         PlayLevelSound(x, y, SND_PIG_DIGGING);
7964       }
7965       else if (!IS_FREE(newx, newy))
7966       {
7967         if (IS_PLAYER(x, y))
7968           DrawPlayerField(x, y);
7969         else
7970           TEST_DrawLevelField(x, y);
7971
7972         return;
7973       }
7974     }
7975     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7976     {
7977       if (Store[x][y] != EL_EMPTY)
7978       {
7979         boolean can_clone = FALSE;
7980         int xx, yy;
7981
7982         /* check if element to clone is still there */
7983         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7984         {
7985           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7986           {
7987             can_clone = TRUE;
7988
7989             break;
7990           }
7991         }
7992
7993         /* cannot clone or target field not free anymore -- do not clone */
7994         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7995           Store[x][y] = EL_EMPTY;
7996       }
7997
7998       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7999       {
8000         if (IS_MV_DIAGONAL(MovDir[x][y]))
8001         {
8002           int diagonal_move_dir = MovDir[x][y];
8003           int stored = Store[x][y];
8004           int change_delay = 8;
8005           int graphic;
8006
8007           /* android is moving diagonally */
8008
8009           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8010
8011           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8012           GfxElement[x][y] = EL_EMC_ANDROID;
8013           GfxAction[x][y] = ACTION_SHRINKING;
8014           GfxDir[x][y] = diagonal_move_dir;
8015           ChangeDelay[x][y] = change_delay;
8016
8017           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8018                                    GfxDir[x][y]);
8019
8020           DrawLevelGraphicAnimation(x, y, graphic);
8021           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8022
8023           if (Feld[newx][newy] == EL_ACID)
8024           {
8025             SplashAcid(newx, newy);
8026
8027             return;
8028           }
8029
8030           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8031
8032           Store[newx][newy] = EL_EMC_ANDROID;
8033           GfxElement[newx][newy] = EL_EMC_ANDROID;
8034           GfxAction[newx][newy] = ACTION_GROWING;
8035           GfxDir[newx][newy] = diagonal_move_dir;
8036           ChangeDelay[newx][newy] = change_delay;
8037
8038           graphic = el_act_dir2img(GfxElement[newx][newy],
8039                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8040
8041           DrawLevelGraphicAnimation(newx, newy, graphic);
8042           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8043
8044           return;
8045         }
8046         else
8047         {
8048           Feld[newx][newy] = EL_EMPTY;
8049           TEST_DrawLevelField(newx, newy);
8050
8051           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8052         }
8053       }
8054       else if (!IS_FREE(newx, newy))
8055       {
8056         return;
8057       }
8058     }
8059     else if (IS_CUSTOM_ELEMENT(element) &&
8060              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8061     {
8062       if (!DigFieldByCE(newx, newy, element))
8063         return;
8064
8065       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8066       {
8067         RunnerVisit[x][y] = FrameCounter;
8068         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8069       }
8070     }
8071     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8072     {
8073       if (!IS_FREE(newx, newy))
8074       {
8075         if (IS_PLAYER(x, y))
8076           DrawPlayerField(x, y);
8077         else
8078           TEST_DrawLevelField(x, y);
8079
8080         return;
8081       }
8082       else
8083       {
8084         boolean wanna_flame = !RND(10);
8085         int dx = newx - x, dy = newy - y;
8086         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8087         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8088         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8089                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8090         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8091                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8092
8093         if ((wanna_flame ||
8094              IS_CLASSIC_ENEMY(element1) ||
8095              IS_CLASSIC_ENEMY(element2)) &&
8096             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8097             element1 != EL_FLAMES && element2 != EL_FLAMES)
8098         {
8099           ResetGfxAnimation(x, y);
8100           GfxAction[x][y] = ACTION_ATTACKING;
8101
8102           if (IS_PLAYER(x, y))
8103             DrawPlayerField(x, y);
8104           else
8105             TEST_DrawLevelField(x, y);
8106
8107           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8108
8109           MovDelay[x][y] = 50;
8110
8111           Feld[newx][newy] = EL_FLAMES;
8112           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8113             Feld[newx1][newy1] = EL_FLAMES;
8114           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8115             Feld[newx2][newy2] = EL_FLAMES;
8116
8117           return;
8118         }
8119       }
8120     }
8121     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8122              Feld[newx][newy] == EL_DIAMOND)
8123     {
8124       if (IS_MOVING(newx, newy))
8125         RemoveMovingField(newx, newy);
8126       else
8127       {
8128         Feld[newx][newy] = EL_EMPTY;
8129         TEST_DrawLevelField(newx, newy);
8130       }
8131
8132       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8133     }
8134     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8135              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8136     {
8137       if (AmoebaNr[newx][newy])
8138       {
8139         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8140         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8141             Feld[newx][newy] == EL_BD_AMOEBA)
8142           AmoebaCnt[AmoebaNr[newx][newy]]--;
8143       }
8144
8145       if (IS_MOVING(newx, newy))
8146       {
8147         RemoveMovingField(newx, newy);
8148       }
8149       else
8150       {
8151         Feld[newx][newy] = EL_EMPTY;
8152         TEST_DrawLevelField(newx, newy);
8153       }
8154
8155       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8156     }
8157     else if ((element == EL_PACMAN || element == EL_MOLE)
8158              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8159     {
8160       if (AmoebaNr[newx][newy])
8161       {
8162         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8163         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8164             Feld[newx][newy] == EL_BD_AMOEBA)
8165           AmoebaCnt[AmoebaNr[newx][newy]]--;
8166       }
8167
8168       if (element == EL_MOLE)
8169       {
8170         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8171         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8172
8173         ResetGfxAnimation(x, y);
8174         GfxAction[x][y] = ACTION_DIGGING;
8175         TEST_DrawLevelField(x, y);
8176
8177         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8178
8179         return;                         /* wait for shrinking amoeba */
8180       }
8181       else      /* element == EL_PACMAN */
8182       {
8183         Feld[newx][newy] = EL_EMPTY;
8184         TEST_DrawLevelField(newx, newy);
8185         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8186       }
8187     }
8188     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8189              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8190               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8191     {
8192       /* wait for shrinking amoeba to completely disappear */
8193       return;
8194     }
8195     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8196     {
8197       /* object was running against a wall */
8198
8199       TurnRound(x, y);
8200
8201       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8202         DrawLevelElementAnimation(x, y, element);
8203
8204       if (DONT_TOUCH(element))
8205         TestIfBadThingTouchesPlayer(x, y);
8206
8207       return;
8208     }
8209
8210     InitMovingField(x, y, MovDir[x][y]);
8211
8212     PlayLevelSoundAction(x, y, ACTION_MOVING);
8213   }
8214
8215   if (MovDir[x][y])
8216     ContinueMoving(x, y);
8217 }
8218
8219 void ContinueMoving(int x, int y)
8220 {
8221   int element = Feld[x][y];
8222   struct ElementInfo *ei = &element_info[element];
8223   int direction = MovDir[x][y];
8224   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8225   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8226   int newx = x + dx, newy = y + dy;
8227   int stored = Store[x][y];
8228   int stored_new = Store[newx][newy];
8229   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8230   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8231   boolean last_line = (newy == lev_fieldy - 1);
8232
8233   MovPos[x][y] += getElementMoveStepsize(x, y);
8234
8235   if (pushed_by_player) /* special case: moving object pushed by player */
8236     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8237
8238   if (ABS(MovPos[x][y]) < TILEX)
8239   {
8240     TEST_DrawLevelField(x, y);
8241
8242     return;     /* element is still moving */
8243   }
8244
8245   /* element reached destination field */
8246
8247   Feld[x][y] = EL_EMPTY;
8248   Feld[newx][newy] = element;
8249   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8250
8251   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8252   {
8253     element = Feld[newx][newy] = EL_ACID;
8254   }
8255   else if (element == EL_MOLE)
8256   {
8257     Feld[x][y] = EL_SAND;
8258
8259     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8260   }
8261   else if (element == EL_QUICKSAND_FILLING)
8262   {
8263     element = Feld[newx][newy] = get_next_element(element);
8264     Store[newx][newy] = Store[x][y];
8265   }
8266   else if (element == EL_QUICKSAND_EMPTYING)
8267   {
8268     Feld[x][y] = get_next_element(element);
8269     element = Feld[newx][newy] = Store[x][y];
8270   }
8271   else if (element == EL_QUICKSAND_FAST_FILLING)
8272   {
8273     element = Feld[newx][newy] = get_next_element(element);
8274     Store[newx][newy] = Store[x][y];
8275   }
8276   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8277   {
8278     Feld[x][y] = get_next_element(element);
8279     element = Feld[newx][newy] = Store[x][y];
8280   }
8281   else if (element == EL_MAGIC_WALL_FILLING)
8282   {
8283     element = Feld[newx][newy] = get_next_element(element);
8284     if (!game.magic_wall_active)
8285       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8286     Store[newx][newy] = Store[x][y];
8287   }
8288   else if (element == EL_MAGIC_WALL_EMPTYING)
8289   {
8290     Feld[x][y] = get_next_element(element);
8291     if (!game.magic_wall_active)
8292       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8293     element = Feld[newx][newy] = Store[x][y];
8294
8295     InitField(newx, newy, FALSE);
8296   }
8297   else if (element == EL_BD_MAGIC_WALL_FILLING)
8298   {
8299     element = Feld[newx][newy] = get_next_element(element);
8300     if (!game.magic_wall_active)
8301       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8302     Store[newx][newy] = Store[x][y];
8303   }
8304   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8305   {
8306     Feld[x][y] = get_next_element(element);
8307     if (!game.magic_wall_active)
8308       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8309     element = Feld[newx][newy] = Store[x][y];
8310
8311     InitField(newx, newy, FALSE);
8312   }
8313   else if (element == EL_DC_MAGIC_WALL_FILLING)
8314   {
8315     element = Feld[newx][newy] = get_next_element(element);
8316     if (!game.magic_wall_active)
8317       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8318     Store[newx][newy] = Store[x][y];
8319   }
8320   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8321   {
8322     Feld[x][y] = get_next_element(element);
8323     if (!game.magic_wall_active)
8324       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8325     element = Feld[newx][newy] = Store[x][y];
8326
8327     InitField(newx, newy, FALSE);
8328   }
8329   else if (element == EL_AMOEBA_DROPPING)
8330   {
8331     Feld[x][y] = get_next_element(element);
8332     element = Feld[newx][newy] = Store[x][y];
8333   }
8334   else if (element == EL_SOKOBAN_OBJECT)
8335   {
8336     if (Back[x][y])
8337       Feld[x][y] = Back[x][y];
8338
8339     if (Back[newx][newy])
8340       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8341
8342     Back[x][y] = Back[newx][newy] = 0;
8343   }
8344
8345   Store[x][y] = EL_EMPTY;
8346   MovPos[x][y] = 0;
8347   MovDir[x][y] = 0;
8348   MovDelay[x][y] = 0;
8349
8350   MovDelay[newx][newy] = 0;
8351
8352   if (CAN_CHANGE_OR_HAS_ACTION(element))
8353   {
8354     /* copy element change control values to new field */
8355     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8356     ChangePage[newx][newy]  = ChangePage[x][y];
8357     ChangeCount[newx][newy] = ChangeCount[x][y];
8358     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8359   }
8360
8361   CustomValue[newx][newy] = CustomValue[x][y];
8362
8363   ChangeDelay[x][y] = 0;
8364   ChangePage[x][y] = -1;
8365   ChangeCount[x][y] = 0;
8366   ChangeEvent[x][y] = -1;
8367
8368   CustomValue[x][y] = 0;
8369
8370   /* copy animation control values to new field */
8371   GfxFrame[newx][newy]  = GfxFrame[x][y];
8372   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8373   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8374   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8375
8376   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8377
8378   /* some elements can leave other elements behind after moving */
8379   if (ei->move_leave_element != EL_EMPTY &&
8380       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8381       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8382   {
8383     int move_leave_element = ei->move_leave_element;
8384
8385     /* this makes it possible to leave the removed element again */
8386     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8387       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8388
8389     Feld[x][y] = move_leave_element;
8390
8391     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8392       MovDir[x][y] = direction;
8393
8394     InitField(x, y, FALSE);
8395
8396     if (GFX_CRUMBLED(Feld[x][y]))
8397       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8398
8399     if (ELEM_IS_PLAYER(move_leave_element))
8400       RelocatePlayer(x, y, move_leave_element);
8401   }
8402
8403   /* do this after checking for left-behind element */
8404   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8405
8406   if (!CAN_MOVE(element) ||
8407       (CAN_FALL(element) && direction == MV_DOWN &&
8408        (element == EL_SPRING ||
8409         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8410         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8411     GfxDir[x][y] = MovDir[newx][newy] = 0;
8412
8413   TEST_DrawLevelField(x, y);
8414   TEST_DrawLevelField(newx, newy);
8415
8416   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8417
8418   /* prevent pushed element from moving on in pushed direction */
8419   if (pushed_by_player && CAN_MOVE(element) &&
8420       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8421       !(element_info[element].move_pattern & direction))
8422     TurnRound(newx, newy);
8423
8424   /* prevent elements on conveyor belt from moving on in last direction */
8425   if (pushed_by_conveyor && CAN_FALL(element) &&
8426       direction & MV_HORIZONTAL)
8427     MovDir[newx][newy] = 0;
8428
8429   if (!pushed_by_player)
8430   {
8431     int nextx = newx + dx, nexty = newy + dy;
8432     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8433
8434     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8435
8436     if (CAN_FALL(element) && direction == MV_DOWN)
8437       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8438
8439     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8440       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8441
8442     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8443       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8444   }
8445
8446   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8447   {
8448     TestIfBadThingTouchesPlayer(newx, newy);
8449     TestIfBadThingTouchesFriend(newx, newy);
8450
8451     if (!IS_CUSTOM_ELEMENT(element))
8452       TestIfBadThingTouchesOtherBadThing(newx, newy);
8453   }
8454   else if (element == EL_PENGUIN)
8455     TestIfFriendTouchesBadThing(newx, newy);
8456
8457   if (DONT_GET_HIT_BY(element))
8458   {
8459     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8460   }
8461
8462   /* give the player one last chance (one more frame) to move away */
8463   if (CAN_FALL(element) && direction == MV_DOWN &&
8464       (last_line || (!IS_FREE(x, newy + 1) &&
8465                      (!IS_PLAYER(x, newy + 1) ||
8466                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8467     Impact(x, newy);
8468
8469   if (pushed_by_player && !game.use_change_when_pushing_bug)
8470   {
8471     int push_side = MV_DIR_OPPOSITE(direction);
8472     struct PlayerInfo *player = PLAYERINFO(x, y);
8473
8474     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8475                                player->index_bit, push_side);
8476     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8477                                         player->index_bit, push_side);
8478   }
8479
8480   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8481     MovDelay[newx][newy] = 1;
8482
8483   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8484
8485   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8486   TestIfElementHitsCustomElement(newx, newy, direction);
8487   TestIfPlayerTouchesCustomElement(newx, newy);
8488   TestIfElementTouchesCustomElement(newx, newy);
8489
8490   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8491       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8492     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8493                              MV_DIR_OPPOSITE(direction));
8494 }
8495
8496 int AmoebeNachbarNr(int ax, int ay)
8497 {
8498   int i;
8499   int element = Feld[ax][ay];
8500   int group_nr = 0;
8501   static int xy[4][2] =
8502   {
8503     { 0, -1 },
8504     { -1, 0 },
8505     { +1, 0 },
8506     { 0, +1 }
8507   };
8508
8509   for (i = 0; i < NUM_DIRECTIONS; i++)
8510   {
8511     int x = ax + xy[i][0];
8512     int y = ay + xy[i][1];
8513
8514     if (!IN_LEV_FIELD(x, y))
8515       continue;
8516
8517     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8518       group_nr = AmoebaNr[x][y];
8519   }
8520
8521   return group_nr;
8522 }
8523
8524 static void AmoebenVereinigen(int ax, int ay)
8525 {
8526   int i, x, y, xx, yy;
8527   int new_group_nr = AmoebaNr[ax][ay];
8528   static int xy[4][2] =
8529   {
8530     { 0, -1 },
8531     { -1, 0 },
8532     { +1, 0 },
8533     { 0, +1 }
8534   };
8535
8536   if (new_group_nr == 0)
8537     return;
8538
8539   for (i = 0; i < NUM_DIRECTIONS; i++)
8540   {
8541     x = ax + xy[i][0];
8542     y = ay + xy[i][1];
8543
8544     if (!IN_LEV_FIELD(x, y))
8545       continue;
8546
8547     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8548          Feld[x][y] == EL_BD_AMOEBA ||
8549          Feld[x][y] == EL_AMOEBA_DEAD) &&
8550         AmoebaNr[x][y] != new_group_nr)
8551     {
8552       int old_group_nr = AmoebaNr[x][y];
8553
8554       if (old_group_nr == 0)
8555         return;
8556
8557       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8558       AmoebaCnt[old_group_nr] = 0;
8559       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8560       AmoebaCnt2[old_group_nr] = 0;
8561
8562       SCAN_PLAYFIELD(xx, yy)
8563       {
8564         if (AmoebaNr[xx][yy] == old_group_nr)
8565           AmoebaNr[xx][yy] = new_group_nr;
8566       }
8567     }
8568   }
8569 }
8570
8571 void AmoebeUmwandeln(int ax, int ay)
8572 {
8573   int i, x, y;
8574
8575   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8576   {
8577     int group_nr = AmoebaNr[ax][ay];
8578
8579 #ifdef DEBUG
8580     if (group_nr == 0)
8581     {
8582       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8583       printf("AmoebeUmwandeln(): This should never happen!\n");
8584       return;
8585     }
8586 #endif
8587
8588     SCAN_PLAYFIELD(x, y)
8589     {
8590       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8591       {
8592         AmoebaNr[x][y] = 0;
8593         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8594       }
8595     }
8596
8597     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8598                             SND_AMOEBA_TURNING_TO_GEM :
8599                             SND_AMOEBA_TURNING_TO_ROCK));
8600     Bang(ax, ay);
8601   }
8602   else
8603   {
8604     static int xy[4][2] =
8605     {
8606       { 0, -1 },
8607       { -1, 0 },
8608       { +1, 0 },
8609       { 0, +1 }
8610     };
8611
8612     for (i = 0; i < NUM_DIRECTIONS; i++)
8613     {
8614       x = ax + xy[i][0];
8615       y = ay + xy[i][1];
8616
8617       if (!IN_LEV_FIELD(x, y))
8618         continue;
8619
8620       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8621       {
8622         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8623                               SND_AMOEBA_TURNING_TO_GEM :
8624                               SND_AMOEBA_TURNING_TO_ROCK));
8625         Bang(x, y);
8626       }
8627     }
8628   }
8629 }
8630
8631 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8632 {
8633   int x, y;
8634   int group_nr = AmoebaNr[ax][ay];
8635   boolean done = FALSE;
8636
8637 #ifdef DEBUG
8638   if (group_nr == 0)
8639   {
8640     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8641     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8642     return;
8643   }
8644 #endif
8645
8646   SCAN_PLAYFIELD(x, y)
8647   {
8648     if (AmoebaNr[x][y] == group_nr &&
8649         (Feld[x][y] == EL_AMOEBA_DEAD ||
8650          Feld[x][y] == EL_BD_AMOEBA ||
8651          Feld[x][y] == EL_AMOEBA_GROWING))
8652     {
8653       AmoebaNr[x][y] = 0;
8654       Feld[x][y] = new_element;
8655       InitField(x, y, FALSE);
8656       TEST_DrawLevelField(x, y);
8657       done = TRUE;
8658     }
8659   }
8660
8661   if (done)
8662     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8663                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8664                             SND_BD_AMOEBA_TURNING_TO_GEM));
8665 }
8666
8667 static void AmoebeWaechst(int x, int y)
8668 {
8669   static unsigned int sound_delay = 0;
8670   static unsigned int sound_delay_value = 0;
8671
8672   if (!MovDelay[x][y])          /* start new growing cycle */
8673   {
8674     MovDelay[x][y] = 7;
8675
8676     if (DelayReached(&sound_delay, sound_delay_value))
8677     {
8678       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8679       sound_delay_value = 30;
8680     }
8681   }
8682
8683   if (MovDelay[x][y])           /* wait some time before growing bigger */
8684   {
8685     MovDelay[x][y]--;
8686     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8687     {
8688       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8689                                            6 - MovDelay[x][y]);
8690
8691       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8692     }
8693
8694     if (!MovDelay[x][y])
8695     {
8696       Feld[x][y] = Store[x][y];
8697       Store[x][y] = 0;
8698       TEST_DrawLevelField(x, y);
8699     }
8700   }
8701 }
8702
8703 static void AmoebaDisappearing(int x, int y)
8704 {
8705   static unsigned int sound_delay = 0;
8706   static unsigned int sound_delay_value = 0;
8707
8708   if (!MovDelay[x][y])          /* start new shrinking cycle */
8709   {
8710     MovDelay[x][y] = 7;
8711
8712     if (DelayReached(&sound_delay, sound_delay_value))
8713       sound_delay_value = 30;
8714   }
8715
8716   if (MovDelay[x][y])           /* wait some time before shrinking */
8717   {
8718     MovDelay[x][y]--;
8719     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8720     {
8721       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8722                                            6 - MovDelay[x][y]);
8723
8724       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8725     }
8726
8727     if (!MovDelay[x][y])
8728     {
8729       Feld[x][y] = EL_EMPTY;
8730       TEST_DrawLevelField(x, y);
8731
8732       /* don't let mole enter this field in this cycle;
8733          (give priority to objects falling to this field from above) */
8734       Stop[x][y] = TRUE;
8735     }
8736   }
8737 }
8738
8739 static void AmoebeAbleger(int ax, int ay)
8740 {
8741   int i;
8742   int element = Feld[ax][ay];
8743   int graphic = el2img(element);
8744   int newax = ax, neway = ay;
8745   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8746   static int xy[4][2] =
8747   {
8748     { 0, -1 },
8749     { -1, 0 },
8750     { +1, 0 },
8751     { 0, +1 }
8752   };
8753
8754   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8755   {
8756     Feld[ax][ay] = EL_AMOEBA_DEAD;
8757     TEST_DrawLevelField(ax, ay);
8758     return;
8759   }
8760
8761   if (IS_ANIMATED(graphic))
8762     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8763
8764   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8765     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8766
8767   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8768   {
8769     MovDelay[ax][ay]--;
8770     if (MovDelay[ax][ay])
8771       return;
8772   }
8773
8774   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8775   {
8776     int start = RND(4);
8777     int x = ax + xy[start][0];
8778     int y = ay + xy[start][1];
8779
8780     if (!IN_LEV_FIELD(x, y))
8781       return;
8782
8783     if (IS_FREE(x, y) ||
8784         CAN_GROW_INTO(Feld[x][y]) ||
8785         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8786         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8787     {
8788       newax = x;
8789       neway = y;
8790     }
8791
8792     if (newax == ax && neway == ay)
8793       return;
8794   }
8795   else                          /* normal or "filled" (BD style) amoeba */
8796   {
8797     int start = RND(4);
8798     boolean waiting_for_player = FALSE;
8799
8800     for (i = 0; i < NUM_DIRECTIONS; i++)
8801     {
8802       int j = (start + i) % 4;
8803       int x = ax + xy[j][0];
8804       int y = ay + xy[j][1];
8805
8806       if (!IN_LEV_FIELD(x, y))
8807         continue;
8808
8809       if (IS_FREE(x, y) ||
8810           CAN_GROW_INTO(Feld[x][y]) ||
8811           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8812           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8813       {
8814         newax = x;
8815         neway = y;
8816         break;
8817       }
8818       else if (IS_PLAYER(x, y))
8819         waiting_for_player = TRUE;
8820     }
8821
8822     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8823     {
8824       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8825       {
8826         Feld[ax][ay] = EL_AMOEBA_DEAD;
8827         TEST_DrawLevelField(ax, ay);
8828         AmoebaCnt[AmoebaNr[ax][ay]]--;
8829
8830         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8831         {
8832           if (element == EL_AMOEBA_FULL)
8833             AmoebeUmwandeln(ax, ay);
8834           else if (element == EL_BD_AMOEBA)
8835             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8836         }
8837       }
8838       return;
8839     }
8840     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8841     {
8842       /* amoeba gets larger by growing in some direction */
8843
8844       int new_group_nr = AmoebaNr[ax][ay];
8845
8846 #ifdef DEBUG
8847   if (new_group_nr == 0)
8848   {
8849     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8850     printf("AmoebeAbleger(): This should never happen!\n");
8851     return;
8852   }
8853 #endif
8854
8855       AmoebaNr[newax][neway] = new_group_nr;
8856       AmoebaCnt[new_group_nr]++;
8857       AmoebaCnt2[new_group_nr]++;
8858
8859       /* if amoeba touches other amoeba(s) after growing, unify them */
8860       AmoebenVereinigen(newax, neway);
8861
8862       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8863       {
8864         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8865         return;
8866       }
8867     }
8868   }
8869
8870   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8871       (neway == lev_fieldy - 1 && newax != ax))
8872   {
8873     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8874     Store[newax][neway] = element;
8875   }
8876   else if (neway == ay || element == EL_EMC_DRIPPER)
8877   {
8878     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8879
8880     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8881   }
8882   else
8883   {
8884     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8885     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8886     Store[ax][ay] = EL_AMOEBA_DROP;
8887     ContinueMoving(ax, ay);
8888     return;
8889   }
8890
8891   TEST_DrawLevelField(newax, neway);
8892 }
8893
8894 static void Life(int ax, int ay)
8895 {
8896   int x1, y1, x2, y2;
8897   int life_time = 40;
8898   int element = Feld[ax][ay];
8899   int graphic = el2img(element);
8900   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8901                          level.biomaze);
8902   boolean changed = FALSE;
8903
8904   if (IS_ANIMATED(graphic))
8905     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8906
8907   if (Stop[ax][ay])
8908     return;
8909
8910   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8911     MovDelay[ax][ay] = life_time;
8912
8913   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8914   {
8915     MovDelay[ax][ay]--;
8916     if (MovDelay[ax][ay])
8917       return;
8918   }
8919
8920   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8921   {
8922     int xx = ax+x1, yy = ay+y1;
8923     int old_element = Feld[xx][yy];
8924     int num_neighbours = 0;
8925
8926     if (!IN_LEV_FIELD(xx, yy))
8927       continue;
8928
8929     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8930     {
8931       int x = xx+x2, y = yy+y2;
8932
8933       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8934         continue;
8935
8936       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8937       boolean is_neighbour = FALSE;
8938
8939       if (level.use_life_bugs)
8940         is_neighbour =
8941           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8942            (IS_FREE(x, y)                             &&  Stop[x][y]));
8943       else
8944         is_neighbour =
8945           (Last[x][y] == element || is_player_cell);
8946
8947       if (is_neighbour)
8948         num_neighbours++;
8949     }
8950
8951     boolean is_free = FALSE;
8952
8953     if (level.use_life_bugs)
8954       is_free = (IS_FREE(xx, yy));
8955     else
8956       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8957
8958     if (xx == ax && yy == ay)           /* field in the middle */
8959     {
8960       if (num_neighbours < life_parameter[0] ||
8961           num_neighbours > life_parameter[1])
8962       {
8963         Feld[xx][yy] = EL_EMPTY;
8964         if (Feld[xx][yy] != old_element)
8965           TEST_DrawLevelField(xx, yy);
8966         Stop[xx][yy] = TRUE;
8967         changed = TRUE;
8968       }
8969     }
8970     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8971     {                                   /* free border field */
8972       if (num_neighbours >= life_parameter[2] &&
8973           num_neighbours <= life_parameter[3])
8974       {
8975         Feld[xx][yy] = element;
8976         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8977         if (Feld[xx][yy] != old_element)
8978           TEST_DrawLevelField(xx, yy);
8979         Stop[xx][yy] = TRUE;
8980         changed = TRUE;
8981       }
8982     }
8983   }
8984
8985   if (changed)
8986     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8987                    SND_GAME_OF_LIFE_GROWING);
8988 }
8989
8990 static void InitRobotWheel(int x, int y)
8991 {
8992   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8993 }
8994
8995 static void RunRobotWheel(int x, int y)
8996 {
8997   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8998 }
8999
9000 static void StopRobotWheel(int x, int y)
9001 {
9002   if (ZX == x && ZY == y)
9003   {
9004     ZX = ZY = -1;
9005
9006     game.robot_wheel_active = FALSE;
9007   }
9008 }
9009
9010 static void InitTimegateWheel(int x, int y)
9011 {
9012   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9013 }
9014
9015 static void RunTimegateWheel(int x, int y)
9016 {
9017   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9018 }
9019
9020 static void InitMagicBallDelay(int x, int y)
9021 {
9022   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9023 }
9024
9025 static void ActivateMagicBall(int bx, int by)
9026 {
9027   int x, y;
9028
9029   if (level.ball_random)
9030   {
9031     int pos_border = RND(8);    /* select one of the eight border elements */
9032     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9033     int xx = pos_content % 3;
9034     int yy = pos_content / 3;
9035
9036     x = bx - 1 + xx;
9037     y = by - 1 + yy;
9038
9039     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9040       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9041   }
9042   else
9043   {
9044     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9045     {
9046       int xx = x - bx + 1;
9047       int yy = y - by + 1;
9048
9049       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9050         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9051     }
9052   }
9053
9054   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9055 }
9056
9057 static void CheckExit(int x, int y)
9058 {
9059   if (local_player->gems_still_needed > 0 ||
9060       local_player->sokobanfields_still_needed > 0 ||
9061       local_player->lights_still_needed > 0)
9062   {
9063     int element = Feld[x][y];
9064     int graphic = el2img(element);
9065
9066     if (IS_ANIMATED(graphic))
9067       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9068
9069     return;
9070   }
9071
9072   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9073     return;
9074
9075   Feld[x][y] = EL_EXIT_OPENING;
9076
9077   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9078 }
9079
9080 static void CheckExitEM(int x, int y)
9081 {
9082   if (local_player->gems_still_needed > 0 ||
9083       local_player->sokobanfields_still_needed > 0 ||
9084       local_player->lights_still_needed > 0)
9085   {
9086     int element = Feld[x][y];
9087     int graphic = el2img(element);
9088
9089     if (IS_ANIMATED(graphic))
9090       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9091
9092     return;
9093   }
9094
9095   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9096     return;
9097
9098   Feld[x][y] = EL_EM_EXIT_OPENING;
9099
9100   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9101 }
9102
9103 static void CheckExitSteel(int x, int y)
9104 {
9105   if (local_player->gems_still_needed > 0 ||
9106       local_player->sokobanfields_still_needed > 0 ||
9107       local_player->lights_still_needed > 0)
9108   {
9109     int element = Feld[x][y];
9110     int graphic = el2img(element);
9111
9112     if (IS_ANIMATED(graphic))
9113       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9114
9115     return;
9116   }
9117
9118   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9119     return;
9120
9121   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9122
9123   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9124 }
9125
9126 static void CheckExitSteelEM(int x, int y)
9127 {
9128   if (local_player->gems_still_needed > 0 ||
9129       local_player->sokobanfields_still_needed > 0 ||
9130       local_player->lights_still_needed > 0)
9131   {
9132     int element = Feld[x][y];
9133     int graphic = el2img(element);
9134
9135     if (IS_ANIMATED(graphic))
9136       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9137
9138     return;
9139   }
9140
9141   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9142     return;
9143
9144   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9145
9146   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9147 }
9148
9149 static void CheckExitSP(int x, int y)
9150 {
9151   if (local_player->gems_still_needed > 0)
9152   {
9153     int element = Feld[x][y];
9154     int graphic = el2img(element);
9155
9156     if (IS_ANIMATED(graphic))
9157       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9158
9159     return;
9160   }
9161
9162   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9163     return;
9164
9165   Feld[x][y] = EL_SP_EXIT_OPENING;
9166
9167   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9168 }
9169
9170 static void CloseAllOpenTimegates(void)
9171 {
9172   int x, y;
9173
9174   SCAN_PLAYFIELD(x, y)
9175   {
9176     int element = Feld[x][y];
9177
9178     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9179     {
9180       Feld[x][y] = EL_TIMEGATE_CLOSING;
9181
9182       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9183     }
9184   }
9185 }
9186
9187 static void DrawTwinkleOnField(int x, int y)
9188 {
9189   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9190     return;
9191
9192   if (Feld[x][y] == EL_BD_DIAMOND)
9193     return;
9194
9195   if (MovDelay[x][y] == 0)      /* next animation frame */
9196     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9197
9198   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9199   {
9200     MovDelay[x][y]--;
9201
9202     DrawLevelElementAnimation(x, y, Feld[x][y]);
9203
9204     if (MovDelay[x][y] != 0)
9205     {
9206       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9207                                            10 - MovDelay[x][y]);
9208
9209       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9210     }
9211   }
9212 }
9213
9214 static void MauerWaechst(int x, int y)
9215 {
9216   int delay = 6;
9217
9218   if (!MovDelay[x][y])          /* next animation frame */
9219     MovDelay[x][y] = 3 * delay;
9220
9221   if (MovDelay[x][y])           /* wait some time before next frame */
9222   {
9223     MovDelay[x][y]--;
9224
9225     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9226     {
9227       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9228       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9229
9230       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9231     }
9232
9233     if (!MovDelay[x][y])
9234     {
9235       if (MovDir[x][y] == MV_LEFT)
9236       {
9237         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9238           TEST_DrawLevelField(x - 1, y);
9239       }
9240       else if (MovDir[x][y] == MV_RIGHT)
9241       {
9242         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9243           TEST_DrawLevelField(x + 1, y);
9244       }
9245       else if (MovDir[x][y] == MV_UP)
9246       {
9247         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9248           TEST_DrawLevelField(x, y - 1);
9249       }
9250       else
9251       {
9252         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9253           TEST_DrawLevelField(x, y + 1);
9254       }
9255
9256       Feld[x][y] = Store[x][y];
9257       Store[x][y] = 0;
9258       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9259       TEST_DrawLevelField(x, y);
9260     }
9261   }
9262 }
9263
9264 static void MauerAbleger(int ax, int ay)
9265 {
9266   int element = Feld[ax][ay];
9267   int graphic = el2img(element);
9268   boolean oben_frei = FALSE, unten_frei = FALSE;
9269   boolean links_frei = FALSE, rechts_frei = FALSE;
9270   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9271   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9272   boolean new_wall = FALSE;
9273
9274   if (IS_ANIMATED(graphic))
9275     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9276
9277   if (!MovDelay[ax][ay])        /* start building new wall */
9278     MovDelay[ax][ay] = 6;
9279
9280   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9281   {
9282     MovDelay[ax][ay]--;
9283     if (MovDelay[ax][ay])
9284       return;
9285   }
9286
9287   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9288     oben_frei = TRUE;
9289   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9290     unten_frei = TRUE;
9291   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9292     links_frei = TRUE;
9293   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9294     rechts_frei = TRUE;
9295
9296   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9297       element == EL_EXPANDABLE_WALL_ANY)
9298   {
9299     if (oben_frei)
9300     {
9301       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9302       Store[ax][ay-1] = element;
9303       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9304       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9305         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9306                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9307       new_wall = TRUE;
9308     }
9309     if (unten_frei)
9310     {
9311       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9312       Store[ax][ay+1] = element;
9313       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9314       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9315         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9316                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9317       new_wall = TRUE;
9318     }
9319   }
9320
9321   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9322       element == EL_EXPANDABLE_WALL_ANY ||
9323       element == EL_EXPANDABLE_WALL ||
9324       element == EL_BD_EXPANDABLE_WALL)
9325   {
9326     if (links_frei)
9327     {
9328       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9329       Store[ax-1][ay] = element;
9330       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9331       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9332         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9333                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9334       new_wall = TRUE;
9335     }
9336
9337     if (rechts_frei)
9338     {
9339       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9340       Store[ax+1][ay] = element;
9341       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9342       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9343         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9344                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9345       new_wall = TRUE;
9346     }
9347   }
9348
9349   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9350     TEST_DrawLevelField(ax, ay);
9351
9352   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9353     oben_massiv = TRUE;
9354   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9355     unten_massiv = TRUE;
9356   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9357     links_massiv = TRUE;
9358   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9359     rechts_massiv = TRUE;
9360
9361   if (((oben_massiv && unten_massiv) ||
9362        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9363        element == EL_EXPANDABLE_WALL) &&
9364       ((links_massiv && rechts_massiv) ||
9365        element == EL_EXPANDABLE_WALL_VERTICAL))
9366     Feld[ax][ay] = EL_WALL;
9367
9368   if (new_wall)
9369     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9370 }
9371
9372 static void MauerAblegerStahl(int ax, int ay)
9373 {
9374   int element = Feld[ax][ay];
9375   int graphic = el2img(element);
9376   boolean oben_frei = FALSE, unten_frei = FALSE;
9377   boolean links_frei = FALSE, rechts_frei = FALSE;
9378   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9379   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9380   boolean new_wall = FALSE;
9381
9382   if (IS_ANIMATED(graphic))
9383     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9384
9385   if (!MovDelay[ax][ay])        /* start building new wall */
9386     MovDelay[ax][ay] = 6;
9387
9388   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9389   {
9390     MovDelay[ax][ay]--;
9391     if (MovDelay[ax][ay])
9392       return;
9393   }
9394
9395   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9396     oben_frei = TRUE;
9397   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9398     unten_frei = TRUE;
9399   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9400     links_frei = TRUE;
9401   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9402     rechts_frei = TRUE;
9403
9404   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9405       element == EL_EXPANDABLE_STEELWALL_ANY)
9406   {
9407     if (oben_frei)
9408     {
9409       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9410       Store[ax][ay-1] = element;
9411       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9412       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9413         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9414                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9415       new_wall = TRUE;
9416     }
9417     if (unten_frei)
9418     {
9419       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9420       Store[ax][ay+1] = element;
9421       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9422       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9423         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9424                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9425       new_wall = TRUE;
9426     }
9427   }
9428
9429   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9430       element == EL_EXPANDABLE_STEELWALL_ANY)
9431   {
9432     if (links_frei)
9433     {
9434       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9435       Store[ax-1][ay] = element;
9436       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9437       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9438         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9439                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9440       new_wall = TRUE;
9441     }
9442
9443     if (rechts_frei)
9444     {
9445       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9446       Store[ax+1][ay] = element;
9447       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9448       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9449         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9450                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9451       new_wall = TRUE;
9452     }
9453   }
9454
9455   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9456     oben_massiv = TRUE;
9457   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9458     unten_massiv = TRUE;
9459   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9460     links_massiv = TRUE;
9461   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9462     rechts_massiv = TRUE;
9463
9464   if (((oben_massiv && unten_massiv) ||
9465        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9466       ((links_massiv && rechts_massiv) ||
9467        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9468     Feld[ax][ay] = EL_STEELWALL;
9469
9470   if (new_wall)
9471     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9472 }
9473
9474 static void CheckForDragon(int x, int y)
9475 {
9476   int i, j;
9477   boolean dragon_found = FALSE;
9478   static int xy[4][2] =
9479   {
9480     { 0, -1 },
9481     { -1, 0 },
9482     { +1, 0 },
9483     { 0, +1 }
9484   };
9485
9486   for (i = 0; i < NUM_DIRECTIONS; i++)
9487   {
9488     for (j = 0; j < 4; j++)
9489     {
9490       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9491
9492       if (IN_LEV_FIELD(xx, yy) &&
9493           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9494       {
9495         if (Feld[xx][yy] == EL_DRAGON)
9496           dragon_found = TRUE;
9497       }
9498       else
9499         break;
9500     }
9501   }
9502
9503   if (!dragon_found)
9504   {
9505     for (i = 0; i < NUM_DIRECTIONS; i++)
9506     {
9507       for (j = 0; j < 3; j++)
9508       {
9509         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9510   
9511         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9512         {
9513           Feld[xx][yy] = EL_EMPTY;
9514           TEST_DrawLevelField(xx, yy);
9515         }
9516         else
9517           break;
9518       }
9519     }
9520   }
9521 }
9522
9523 static void InitBuggyBase(int x, int y)
9524 {
9525   int element = Feld[x][y];
9526   int activating_delay = FRAMES_PER_SECOND / 4;
9527
9528   ChangeDelay[x][y] =
9529     (element == EL_SP_BUGGY_BASE ?
9530      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9531      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9532      activating_delay :
9533      element == EL_SP_BUGGY_BASE_ACTIVE ?
9534      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9535 }
9536
9537 static void WarnBuggyBase(int x, int y)
9538 {
9539   int i;
9540   static int xy[4][2] =
9541   {
9542     { 0, -1 },
9543     { -1, 0 },
9544     { +1, 0 },
9545     { 0, +1 }
9546   };
9547
9548   for (i = 0; i < NUM_DIRECTIONS; i++)
9549   {
9550     int xx = x + xy[i][0];
9551     int yy = y + xy[i][1];
9552
9553     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9554     {
9555       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9556
9557       break;
9558     }
9559   }
9560 }
9561
9562 static void InitTrap(int x, int y)
9563 {
9564   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9565 }
9566
9567 static void ActivateTrap(int x, int y)
9568 {
9569   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9570 }
9571
9572 static void ChangeActiveTrap(int x, int y)
9573 {
9574   int graphic = IMG_TRAP_ACTIVE;
9575
9576   /* if new animation frame was drawn, correct crumbled sand border */
9577   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9578     TEST_DrawLevelFieldCrumbled(x, y);
9579 }
9580
9581 static int getSpecialActionElement(int element, int number, int base_element)
9582 {
9583   return (element != EL_EMPTY ? element :
9584           number != -1 ? base_element + number - 1 :
9585           EL_EMPTY);
9586 }
9587
9588 static int getModifiedActionNumber(int value_old, int operator, int operand,
9589                                    int value_min, int value_max)
9590 {
9591   int value_new = (operator == CA_MODE_SET      ? operand :
9592                    operator == CA_MODE_ADD      ? value_old + operand :
9593                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9594                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9595                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9596                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9597                    value_old);
9598
9599   return (value_new < value_min ? value_min :
9600           value_new > value_max ? value_max :
9601           value_new);
9602 }
9603
9604 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9605 {
9606   struct ElementInfo *ei = &element_info[element];
9607   struct ElementChangeInfo *change = &ei->change_page[page];
9608   int target_element = change->target_element;
9609   int action_type = change->action_type;
9610   int action_mode = change->action_mode;
9611   int action_arg = change->action_arg;
9612   int action_element = change->action_element;
9613   int i;
9614
9615   if (!change->has_action)
9616     return;
9617
9618   /* ---------- determine action paramater values -------------------------- */
9619
9620   int level_time_value =
9621     (level.time > 0 ? TimeLeft :
9622      TimePlayed);
9623
9624   int action_arg_element_raw =
9625     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9626      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9627      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9628      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9629      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9630      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9631      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9632      EL_EMPTY);
9633   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9634
9635   int action_arg_direction =
9636     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9637      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9638      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9639      change->actual_trigger_side :
9640      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9641      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9642      MV_NONE);
9643
9644   int action_arg_number_min =
9645     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9646      CA_ARG_MIN);
9647
9648   int action_arg_number_max =
9649     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9650      action_type == CA_SET_LEVEL_GEMS ? 999 :
9651      action_type == CA_SET_LEVEL_TIME ? 9999 :
9652      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9653      action_type == CA_SET_CE_VALUE ? 9999 :
9654      action_type == CA_SET_CE_SCORE ? 9999 :
9655      CA_ARG_MAX);
9656
9657   int action_arg_number_reset =
9658     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9659      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9660      action_type == CA_SET_LEVEL_TIME ? level.time :
9661      action_type == CA_SET_LEVEL_SCORE ? 0 :
9662      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9663      action_type == CA_SET_CE_SCORE ? 0 :
9664      0);
9665
9666   int action_arg_number =
9667     (action_arg <= CA_ARG_MAX ? action_arg :
9668      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9669      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9670      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9671      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9672      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9673      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9674      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9675      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9676      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9677      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9678      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9679      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9680      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9681      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9682      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9683      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9684      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9685      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9686      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9687      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9688      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9689      -1);
9690
9691   int action_arg_number_old =
9692     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9693      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9694      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9695      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9696      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9697      0);
9698
9699   int action_arg_number_new =
9700     getModifiedActionNumber(action_arg_number_old,
9701                             action_mode, action_arg_number,
9702                             action_arg_number_min, action_arg_number_max);
9703
9704   int trigger_player_bits =
9705     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9706      change->actual_trigger_player_bits : change->trigger_player);
9707
9708   int action_arg_player_bits =
9709     (action_arg >= CA_ARG_PLAYER_1 &&
9710      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9711      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9712      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9713      PLAYER_BITS_ANY);
9714
9715   /* ---------- execute action  -------------------------------------------- */
9716
9717   switch (action_type)
9718   {
9719     case CA_NO_ACTION:
9720     {
9721       return;
9722     }
9723
9724     /* ---------- level actions  ------------------------------------------- */
9725
9726     case CA_RESTART_LEVEL:
9727     {
9728       game.restart_level = TRUE;
9729
9730       break;
9731     }
9732
9733     case CA_SHOW_ENVELOPE:
9734     {
9735       int element = getSpecialActionElement(action_arg_element,
9736                                             action_arg_number, EL_ENVELOPE_1);
9737
9738       if (IS_ENVELOPE(element))
9739         local_player->show_envelope = element;
9740
9741       break;
9742     }
9743
9744     case CA_SET_LEVEL_TIME:
9745     {
9746       if (level.time > 0)       /* only modify limited time value */
9747       {
9748         TimeLeft = action_arg_number_new;
9749
9750         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9751
9752         DisplayGameControlValues();
9753
9754         if (!TimeLeft && setup.time_limit)
9755           for (i = 0; i < MAX_PLAYERS; i++)
9756             KillPlayer(&stored_player[i]);
9757       }
9758
9759       break;
9760     }
9761
9762     case CA_SET_LEVEL_SCORE:
9763     {
9764       local_player->score = action_arg_number_new;
9765
9766       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9767
9768       DisplayGameControlValues();
9769
9770       break;
9771     }
9772
9773     case CA_SET_LEVEL_GEMS:
9774     {
9775       local_player->gems_still_needed = action_arg_number_new;
9776
9777       game.snapshot.collected_item = TRUE;
9778
9779       game_panel_controls[GAME_PANEL_GEMS].value =
9780         local_player->gems_still_needed;
9781
9782       DisplayGameControlValues();
9783
9784       break;
9785     }
9786
9787     case CA_SET_LEVEL_WIND:
9788     {
9789       game.wind_direction = action_arg_direction;
9790
9791       break;
9792     }
9793
9794     case CA_SET_LEVEL_RANDOM_SEED:
9795     {
9796       /* ensure that setting a new random seed while playing is predictable */
9797       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9798
9799       break;
9800     }
9801
9802     /* ---------- player actions  ------------------------------------------ */
9803
9804     case CA_MOVE_PLAYER:
9805     {
9806       /* automatically move to the next field in specified direction */
9807       for (i = 0; i < MAX_PLAYERS; i++)
9808         if (trigger_player_bits & (1 << i))
9809           stored_player[i].programmed_action = action_arg_direction;
9810
9811       break;
9812     }
9813
9814     case CA_EXIT_PLAYER:
9815     {
9816       for (i = 0; i < MAX_PLAYERS; i++)
9817         if (action_arg_player_bits & (1 << i))
9818           ExitPlayer(&stored_player[i]);
9819
9820       if (AllPlayersGone)
9821         PlayerWins(local_player);
9822
9823       break;
9824     }
9825
9826     case CA_KILL_PLAYER:
9827     {
9828       for (i = 0; i < MAX_PLAYERS; i++)
9829         if (action_arg_player_bits & (1 << i))
9830           KillPlayer(&stored_player[i]);
9831
9832       break;
9833     }
9834
9835     case CA_SET_PLAYER_KEYS:
9836     {
9837       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9838       int element = getSpecialActionElement(action_arg_element,
9839                                             action_arg_number, EL_KEY_1);
9840
9841       if (IS_KEY(element))
9842       {
9843         for (i = 0; i < MAX_PLAYERS; i++)
9844         {
9845           if (trigger_player_bits & (1 << i))
9846           {
9847             stored_player[i].key[KEY_NR(element)] = key_state;
9848
9849             DrawGameDoorValues();
9850           }
9851         }
9852       }
9853
9854       break;
9855     }
9856
9857     case CA_SET_PLAYER_SPEED:
9858     {
9859       for (i = 0; i < MAX_PLAYERS; i++)
9860       {
9861         if (trigger_player_bits & (1 << i))
9862         {
9863           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9864
9865           if (action_arg == CA_ARG_SPEED_FASTER &&
9866               stored_player[i].cannot_move)
9867           {
9868             action_arg_number = STEPSIZE_VERY_SLOW;
9869           }
9870           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9871                    action_arg == CA_ARG_SPEED_FASTER)
9872           {
9873             action_arg_number = 2;
9874             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9875                            CA_MODE_MULTIPLY);
9876           }
9877           else if (action_arg == CA_ARG_NUMBER_RESET)
9878           {
9879             action_arg_number = level.initial_player_stepsize[i];
9880           }
9881
9882           move_stepsize =
9883             getModifiedActionNumber(move_stepsize,
9884                                     action_mode,
9885                                     action_arg_number,
9886                                     action_arg_number_min,
9887                                     action_arg_number_max);
9888
9889           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9890         }
9891       }
9892
9893       break;
9894     }
9895
9896     case CA_SET_PLAYER_SHIELD:
9897     {
9898       for (i = 0; i < MAX_PLAYERS; i++)
9899       {
9900         if (trigger_player_bits & (1 << i))
9901         {
9902           if (action_arg == CA_ARG_SHIELD_OFF)
9903           {
9904             stored_player[i].shield_normal_time_left = 0;
9905             stored_player[i].shield_deadly_time_left = 0;
9906           }
9907           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9908           {
9909             stored_player[i].shield_normal_time_left = 999999;
9910           }
9911           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9912           {
9913             stored_player[i].shield_normal_time_left = 999999;
9914             stored_player[i].shield_deadly_time_left = 999999;
9915           }
9916         }
9917       }
9918
9919       break;
9920     }
9921
9922     case CA_SET_PLAYER_GRAVITY:
9923     {
9924       for (i = 0; i < MAX_PLAYERS; i++)
9925       {
9926         if (trigger_player_bits & (1 << i))
9927         {
9928           stored_player[i].gravity =
9929             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9930              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9931              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9932              stored_player[i].gravity);
9933         }
9934       }
9935
9936       break;
9937     }
9938
9939     case CA_SET_PLAYER_ARTWORK:
9940     {
9941       for (i = 0; i < MAX_PLAYERS; i++)
9942       {
9943         if (trigger_player_bits & (1 << i))
9944         {
9945           int artwork_element = action_arg_element;
9946
9947           if (action_arg == CA_ARG_ELEMENT_RESET)
9948             artwork_element =
9949               (level.use_artwork_element[i] ? level.artwork_element[i] :
9950                stored_player[i].element_nr);
9951
9952           if (stored_player[i].artwork_element != artwork_element)
9953             stored_player[i].Frame = 0;
9954
9955           stored_player[i].artwork_element = artwork_element;
9956
9957           SetPlayerWaiting(&stored_player[i], FALSE);
9958
9959           /* set number of special actions for bored and sleeping animation */
9960           stored_player[i].num_special_action_bored =
9961             get_num_special_action(artwork_element,
9962                                    ACTION_BORING_1, ACTION_BORING_LAST);
9963           stored_player[i].num_special_action_sleeping =
9964             get_num_special_action(artwork_element,
9965                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9966         }
9967       }
9968
9969       break;
9970     }
9971
9972     case CA_SET_PLAYER_INVENTORY:
9973     {
9974       for (i = 0; i < MAX_PLAYERS; i++)
9975       {
9976         struct PlayerInfo *player = &stored_player[i];
9977         int j, k;
9978
9979         if (trigger_player_bits & (1 << i))
9980         {
9981           int inventory_element = action_arg_element;
9982
9983           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9984               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9985               action_arg == CA_ARG_ELEMENT_ACTION)
9986           {
9987             int element = inventory_element;
9988             int collect_count = element_info[element].collect_count_initial;
9989
9990             if (!IS_CUSTOM_ELEMENT(element))
9991               collect_count = 1;
9992
9993             if (collect_count == 0)
9994               player->inventory_infinite_element = element;
9995             else
9996               for (k = 0; k < collect_count; k++)
9997                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9998                   player->inventory_element[player->inventory_size++] =
9999                     element;
10000           }
10001           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10002                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10003                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10004           {
10005             if (player->inventory_infinite_element != EL_UNDEFINED &&
10006                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10007                                      action_arg_element_raw))
10008               player->inventory_infinite_element = EL_UNDEFINED;
10009
10010             for (k = 0, j = 0; j < player->inventory_size; j++)
10011             {
10012               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10013                                         action_arg_element_raw))
10014                 player->inventory_element[k++] = player->inventory_element[j];
10015             }
10016
10017             player->inventory_size = k;
10018           }
10019           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10020           {
10021             if (player->inventory_size > 0)
10022             {
10023               for (j = 0; j < player->inventory_size - 1; j++)
10024                 player->inventory_element[j] = player->inventory_element[j + 1];
10025
10026               player->inventory_size--;
10027             }
10028           }
10029           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10030           {
10031             if (player->inventory_size > 0)
10032               player->inventory_size--;
10033           }
10034           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10035           {
10036             player->inventory_infinite_element = EL_UNDEFINED;
10037             player->inventory_size = 0;
10038           }
10039           else if (action_arg == CA_ARG_INVENTORY_RESET)
10040           {
10041             player->inventory_infinite_element = EL_UNDEFINED;
10042             player->inventory_size = 0;
10043
10044             if (level.use_initial_inventory[i])
10045             {
10046               for (j = 0; j < level.initial_inventory_size[i]; j++)
10047               {
10048                 int element = level.initial_inventory_content[i][j];
10049                 int collect_count = element_info[element].collect_count_initial;
10050
10051                 if (!IS_CUSTOM_ELEMENT(element))
10052                   collect_count = 1;
10053
10054                 if (collect_count == 0)
10055                   player->inventory_infinite_element = element;
10056                 else
10057                   for (k = 0; k < collect_count; k++)
10058                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10059                       player->inventory_element[player->inventory_size++] =
10060                         element;
10061               }
10062             }
10063           }
10064         }
10065       }
10066
10067       break;
10068     }
10069
10070     /* ---------- CE actions  ---------------------------------------------- */
10071
10072     case CA_SET_CE_VALUE:
10073     {
10074       int last_ce_value = CustomValue[x][y];
10075
10076       CustomValue[x][y] = action_arg_number_new;
10077
10078       if (CustomValue[x][y] != last_ce_value)
10079       {
10080         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10081         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10082
10083         if (CustomValue[x][y] == 0)
10084         {
10085           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10086           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10087         }
10088       }
10089
10090       break;
10091     }
10092
10093     case CA_SET_CE_SCORE:
10094     {
10095       int last_ce_score = ei->collect_score;
10096
10097       ei->collect_score = action_arg_number_new;
10098
10099       if (ei->collect_score != last_ce_score)
10100       {
10101         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10102         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10103
10104         if (ei->collect_score == 0)
10105         {
10106           int xx, yy;
10107
10108           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10109           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10110
10111           /*
10112             This is a very special case that seems to be a mixture between
10113             CheckElementChange() and CheckTriggeredElementChange(): while
10114             the first one only affects single elements that are triggered
10115             directly, the second one affects multiple elements in the playfield
10116             that are triggered indirectly by another element. This is a third
10117             case: Changing the CE score always affects multiple identical CEs,
10118             so every affected CE must be checked, not only the single CE for
10119             which the CE score was changed in the first place (as every instance
10120             of that CE shares the same CE score, and therefore also can change)!
10121           */
10122           SCAN_PLAYFIELD(xx, yy)
10123           {
10124             if (Feld[xx][yy] == element)
10125               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10126                                  CE_SCORE_GETS_ZERO);
10127           }
10128         }
10129       }
10130
10131       break;
10132     }
10133
10134     case CA_SET_CE_ARTWORK:
10135     {
10136       int artwork_element = action_arg_element;
10137       boolean reset_frame = FALSE;
10138       int xx, yy;
10139
10140       if (action_arg == CA_ARG_ELEMENT_RESET)
10141         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10142                            element);
10143
10144       if (ei->gfx_element != artwork_element)
10145         reset_frame = TRUE;
10146
10147       ei->gfx_element = artwork_element;
10148
10149       SCAN_PLAYFIELD(xx, yy)
10150       {
10151         if (Feld[xx][yy] == element)
10152         {
10153           if (reset_frame)
10154           {
10155             ResetGfxAnimation(xx, yy);
10156             ResetRandomAnimationValue(xx, yy);
10157           }
10158
10159           TEST_DrawLevelField(xx, yy);
10160         }
10161       }
10162
10163       break;
10164     }
10165
10166     /* ---------- engine actions  ------------------------------------------ */
10167
10168     case CA_SET_ENGINE_SCAN_MODE:
10169     {
10170       InitPlayfieldScanMode(action_arg);
10171
10172       break;
10173     }
10174
10175     default:
10176       break;
10177   }
10178 }
10179
10180 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10181 {
10182   int old_element = Feld[x][y];
10183   int new_element = GetElementFromGroupElement(element);
10184   int previous_move_direction = MovDir[x][y];
10185   int last_ce_value = CustomValue[x][y];
10186   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10187   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10188   boolean add_player_onto_element = (new_element_is_player &&
10189                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10190                                      IS_WALKABLE(old_element));
10191
10192   if (!add_player_onto_element)
10193   {
10194     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10195       RemoveMovingField(x, y);
10196     else
10197       RemoveField(x, y);
10198
10199     Feld[x][y] = new_element;
10200
10201     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10202       MovDir[x][y] = previous_move_direction;
10203
10204     if (element_info[new_element].use_last_ce_value)
10205       CustomValue[x][y] = last_ce_value;
10206
10207     InitField_WithBug1(x, y, FALSE);
10208
10209     new_element = Feld[x][y];   /* element may have changed */
10210
10211     ResetGfxAnimation(x, y);
10212     ResetRandomAnimationValue(x, y);
10213
10214     TEST_DrawLevelField(x, y);
10215
10216     if (GFX_CRUMBLED(new_element))
10217       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10218   }
10219
10220   /* check if element under the player changes from accessible to unaccessible
10221      (needed for special case of dropping element which then changes) */
10222   /* (must be checked after creating new element for walkable group elements) */
10223   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10224       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10225   {
10226     Bang(x, y);
10227
10228     return;
10229   }
10230
10231   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10232   if (new_element_is_player)
10233     RelocatePlayer(x, y, new_element);
10234
10235   if (is_change)
10236     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10237
10238   TestIfBadThingTouchesPlayer(x, y);
10239   TestIfPlayerTouchesCustomElement(x, y);
10240   TestIfElementTouchesCustomElement(x, y);
10241 }
10242
10243 static void CreateField(int x, int y, int element)
10244 {
10245   CreateFieldExt(x, y, element, FALSE);
10246 }
10247
10248 static void CreateElementFromChange(int x, int y, int element)
10249 {
10250   element = GET_VALID_RUNTIME_ELEMENT(element);
10251
10252   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10253   {
10254     int old_element = Feld[x][y];
10255
10256     /* prevent changed element from moving in same engine frame
10257        unless both old and new element can either fall or move */
10258     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10259         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10260       Stop[x][y] = TRUE;
10261   }
10262
10263   CreateFieldExt(x, y, element, TRUE);
10264 }
10265
10266 static boolean ChangeElement(int x, int y, int element, int page)
10267 {
10268   struct ElementInfo *ei = &element_info[element];
10269   struct ElementChangeInfo *change = &ei->change_page[page];
10270   int ce_value = CustomValue[x][y];
10271   int ce_score = ei->collect_score;
10272   int target_element;
10273   int old_element = Feld[x][y];
10274
10275   /* always use default change event to prevent running into a loop */
10276   if (ChangeEvent[x][y] == -1)
10277     ChangeEvent[x][y] = CE_DELAY;
10278
10279   if (ChangeEvent[x][y] == CE_DELAY)
10280   {
10281     /* reset actual trigger element, trigger player and action element */
10282     change->actual_trigger_element = EL_EMPTY;
10283     change->actual_trigger_player = EL_EMPTY;
10284     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10285     change->actual_trigger_side = CH_SIDE_NONE;
10286     change->actual_trigger_ce_value = 0;
10287     change->actual_trigger_ce_score = 0;
10288   }
10289
10290   /* do not change elements more than a specified maximum number of changes */
10291   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10292     return FALSE;
10293
10294   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10295
10296   if (change->explode)
10297   {
10298     Bang(x, y);
10299
10300     return TRUE;
10301   }
10302
10303   if (change->use_target_content)
10304   {
10305     boolean complete_replace = TRUE;
10306     boolean can_replace[3][3];
10307     int xx, yy;
10308
10309     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10310     {
10311       boolean is_empty;
10312       boolean is_walkable;
10313       boolean is_diggable;
10314       boolean is_collectible;
10315       boolean is_removable;
10316       boolean is_destructible;
10317       int ex = x + xx - 1;
10318       int ey = y + yy - 1;
10319       int content_element = change->target_content.e[xx][yy];
10320       int e;
10321
10322       can_replace[xx][yy] = TRUE;
10323
10324       if (ex == x && ey == y)   /* do not check changing element itself */
10325         continue;
10326
10327       if (content_element == EL_EMPTY_SPACE)
10328       {
10329         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10330
10331         continue;
10332       }
10333
10334       if (!IN_LEV_FIELD(ex, ey))
10335       {
10336         can_replace[xx][yy] = FALSE;
10337         complete_replace = FALSE;
10338
10339         continue;
10340       }
10341
10342       e = Feld[ex][ey];
10343
10344       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10345         e = MovingOrBlocked2Element(ex, ey);
10346
10347       is_empty = (IS_FREE(ex, ey) ||
10348                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10349
10350       is_walkable     = (is_empty || IS_WALKABLE(e));
10351       is_diggable     = (is_empty || IS_DIGGABLE(e));
10352       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10353       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10354       is_removable    = (is_diggable || is_collectible);
10355
10356       can_replace[xx][yy] =
10357         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10358           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10359           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10360           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10361           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10362           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10363          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10364
10365       if (!can_replace[xx][yy])
10366         complete_replace = FALSE;
10367     }
10368
10369     if (!change->only_if_complete || complete_replace)
10370     {
10371       boolean something_has_changed = FALSE;
10372
10373       if (change->only_if_complete && change->use_random_replace &&
10374           RND(100) < change->random_percentage)
10375         return FALSE;
10376
10377       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10378       {
10379         int ex = x + xx - 1;
10380         int ey = y + yy - 1;
10381         int content_element;
10382
10383         if (can_replace[xx][yy] && (!change->use_random_replace ||
10384                                     RND(100) < change->random_percentage))
10385         {
10386           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10387             RemoveMovingField(ex, ey);
10388
10389           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10390
10391           content_element = change->target_content.e[xx][yy];
10392           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10393                                               ce_value, ce_score);
10394
10395           CreateElementFromChange(ex, ey, target_element);
10396
10397           something_has_changed = TRUE;
10398
10399           /* for symmetry reasons, freeze newly created border elements */
10400           if (ex != x || ey != y)
10401             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10402         }
10403       }
10404
10405       if (something_has_changed)
10406       {
10407         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10408         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10409       }
10410     }
10411   }
10412   else
10413   {
10414     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10415                                         ce_value, ce_score);
10416
10417     if (element == EL_DIAGONAL_GROWING ||
10418         element == EL_DIAGONAL_SHRINKING)
10419     {
10420       target_element = Store[x][y];
10421
10422       Store[x][y] = EL_EMPTY;
10423     }
10424
10425     CreateElementFromChange(x, y, target_element);
10426
10427     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10428     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10429   }
10430
10431   /* this uses direct change before indirect change */
10432   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10433
10434   return TRUE;
10435 }
10436
10437 static void HandleElementChange(int x, int y, int page)
10438 {
10439   int element = MovingOrBlocked2Element(x, y);
10440   struct ElementInfo *ei = &element_info[element];
10441   struct ElementChangeInfo *change = &ei->change_page[page];
10442   boolean handle_action_before_change = FALSE;
10443
10444 #ifdef DEBUG
10445   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10446       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10447   {
10448     printf("\n\n");
10449     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10450            x, y, element, element_info[element].token_name);
10451     printf("HandleElementChange(): This should never happen!\n");
10452     printf("\n\n");
10453   }
10454 #endif
10455
10456   /* this can happen with classic bombs on walkable, changing elements */
10457   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10458   {
10459     return;
10460   }
10461
10462   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10463   {
10464     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10465
10466     if (change->can_change)
10467     {
10468       /* !!! not clear why graphic animation should be reset at all here !!! */
10469       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10470       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10471
10472       /*
10473         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10474
10475         When using an animation frame delay of 1 (this only happens with
10476         "sp_zonk.moving.left/right" in the classic graphics), the default
10477         (non-moving) animation shows wrong animation frames (while the
10478         moving animation, like "sp_zonk.moving.left/right", is correct,
10479         so this graphical bug never shows up with the classic graphics).
10480         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10481         be drawn instead of the correct frames 0,1,2,3. This is caused by
10482         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10483         an element change: First when the change delay ("ChangeDelay[][]")
10484         counter has reached zero after decrementing, then a second time in
10485         the next frame (after "GfxFrame[][]" was already incremented) when
10486         "ChangeDelay[][]" is reset to the initial delay value again.
10487
10488         This causes frame 0 to be drawn twice, while the last frame won't
10489         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10490
10491         As some animations may already be cleverly designed around this bug
10492         (at least the "Snake Bite" snake tail animation does this), it cannot
10493         simply be fixed here without breaking such existing animations.
10494         Unfortunately, it cannot easily be detected if a graphics set was
10495         designed "before" or "after" the bug was fixed. As a workaround,
10496         a new graphics set option "game.graphics_engine_version" was added
10497         to be able to specify the game's major release version for which the
10498         graphics set was designed, which can then be used to decide if the
10499         bugfix should be used (version 4 and above) or not (version 3 or
10500         below, or if no version was specified at all, as with old sets).
10501
10502         (The wrong/fixed animation frames can be tested with the test level set
10503         "test_gfxframe" and level "000", which contains a specially prepared
10504         custom element at level position (x/y) == (11/9) which uses the zonk
10505         animation mentioned above. Using "game.graphics_engine_version: 4"
10506         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10507         This can also be seen from the debug output for this test element.)
10508       */
10509
10510       /* when a custom element is about to change (for example by change delay),
10511          do not reset graphic animation when the custom element is moving */
10512       if (game.graphics_engine_version < 4 &&
10513           !IS_MOVING(x, y))
10514       {
10515         ResetGfxAnimation(x, y);
10516         ResetRandomAnimationValue(x, y);
10517       }
10518
10519       if (change->pre_change_function)
10520         change->pre_change_function(x, y);
10521     }
10522   }
10523
10524   ChangeDelay[x][y]--;
10525
10526   if (ChangeDelay[x][y] != 0)           /* continue element change */
10527   {
10528     if (change->can_change)
10529     {
10530       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10531
10532       if (IS_ANIMATED(graphic))
10533         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10534
10535       if (change->change_function)
10536         change->change_function(x, y);
10537     }
10538   }
10539   else                                  /* finish element change */
10540   {
10541     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10542     {
10543       page = ChangePage[x][y];
10544       ChangePage[x][y] = -1;
10545
10546       change = &ei->change_page[page];
10547     }
10548
10549     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10550     {
10551       ChangeDelay[x][y] = 1;            /* try change after next move step */
10552       ChangePage[x][y] = page;          /* remember page to use for change */
10553
10554       return;
10555     }
10556
10557     /* special case: set new level random seed before changing element */
10558     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10559       handle_action_before_change = TRUE;
10560
10561     if (change->has_action && handle_action_before_change)
10562       ExecuteCustomElementAction(x, y, element, page);
10563
10564     if (change->can_change)
10565     {
10566       if (ChangeElement(x, y, element, page))
10567       {
10568         if (change->post_change_function)
10569           change->post_change_function(x, y);
10570       }
10571     }
10572
10573     if (change->has_action && !handle_action_before_change)
10574       ExecuteCustomElementAction(x, y, element, page);
10575   }
10576 }
10577
10578 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10579                                               int trigger_element,
10580                                               int trigger_event,
10581                                               int trigger_player,
10582                                               int trigger_side,
10583                                               int trigger_page)
10584 {
10585   boolean change_done_any = FALSE;
10586   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10587   int i;
10588
10589   if (!(trigger_events[trigger_element][trigger_event]))
10590     return FALSE;
10591
10592   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10593
10594   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10595   {
10596     int element = EL_CUSTOM_START + i;
10597     boolean change_done = FALSE;
10598     int p;
10599
10600     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10601         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10602       continue;
10603
10604     for (p = 0; p < element_info[element].num_change_pages; p++)
10605     {
10606       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10607
10608       if (change->can_change_or_has_action &&
10609           change->has_event[trigger_event] &&
10610           change->trigger_side & trigger_side &&
10611           change->trigger_player & trigger_player &&
10612           change->trigger_page & trigger_page_bits &&
10613           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10614       {
10615         change->actual_trigger_element = trigger_element;
10616         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10617         change->actual_trigger_player_bits = trigger_player;
10618         change->actual_trigger_side = trigger_side;
10619         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10620         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10621
10622         if ((change->can_change && !change_done) || change->has_action)
10623         {
10624           int x, y;
10625
10626           SCAN_PLAYFIELD(x, y)
10627           {
10628             if (Feld[x][y] == element)
10629             {
10630               if (change->can_change && !change_done)
10631               {
10632                 /* if element already changed in this frame, not only prevent
10633                    another element change (checked in ChangeElement()), but
10634                    also prevent additional element actions for this element */
10635
10636                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10637                     !level.use_action_after_change_bug)
10638                   continue;
10639
10640                 ChangeDelay[x][y] = 1;
10641                 ChangeEvent[x][y] = trigger_event;
10642
10643                 HandleElementChange(x, y, p);
10644               }
10645               else if (change->has_action)
10646               {
10647                 /* if element already changed in this frame, not only prevent
10648                    another element change (checked in ChangeElement()), but
10649                    also prevent additional element actions for this element */
10650
10651                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10652                     !level.use_action_after_change_bug)
10653                   continue;
10654
10655                 ExecuteCustomElementAction(x, y, element, p);
10656                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10657               }
10658             }
10659           }
10660
10661           if (change->can_change)
10662           {
10663             change_done = TRUE;
10664             change_done_any = TRUE;
10665           }
10666         }
10667       }
10668     }
10669   }
10670
10671   RECURSION_LOOP_DETECTION_END();
10672
10673   return change_done_any;
10674 }
10675
10676 static boolean CheckElementChangeExt(int x, int y,
10677                                      int element,
10678                                      int trigger_element,
10679                                      int trigger_event,
10680                                      int trigger_player,
10681                                      int trigger_side)
10682 {
10683   boolean change_done = FALSE;
10684   int p;
10685
10686   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10687       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10688     return FALSE;
10689
10690   if (Feld[x][y] == EL_BLOCKED)
10691   {
10692     Blocked2Moving(x, y, &x, &y);
10693     element = Feld[x][y];
10694   }
10695
10696   /* check if element has already changed or is about to change after moving */
10697   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10698        Feld[x][y] != element) ||
10699
10700       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10701        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10702         ChangePage[x][y] != -1)))
10703     return FALSE;
10704
10705   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10706
10707   for (p = 0; p < element_info[element].num_change_pages; p++)
10708   {
10709     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10710
10711     /* check trigger element for all events where the element that is checked
10712        for changing interacts with a directly adjacent element -- this is
10713        different to element changes that affect other elements to change on the
10714        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10715     boolean check_trigger_element =
10716       (trigger_event == CE_TOUCHING_X ||
10717        trigger_event == CE_HITTING_X ||
10718        trigger_event == CE_HIT_BY_X ||
10719        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10720
10721     if (change->can_change_or_has_action &&
10722         change->has_event[trigger_event] &&
10723         change->trigger_side & trigger_side &&
10724         change->trigger_player & trigger_player &&
10725         (!check_trigger_element ||
10726          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10727     {
10728       change->actual_trigger_element = trigger_element;
10729       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10730       change->actual_trigger_player_bits = trigger_player;
10731       change->actual_trigger_side = trigger_side;
10732       change->actual_trigger_ce_value = CustomValue[x][y];
10733       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10734
10735       /* special case: trigger element not at (x,y) position for some events */
10736       if (check_trigger_element)
10737       {
10738         static struct
10739         {
10740           int dx, dy;
10741         } move_xy[] =
10742           {
10743             {  0,  0 },
10744             { -1,  0 },
10745             { +1,  0 },
10746             {  0,  0 },
10747             {  0, -1 },
10748             {  0,  0 }, { 0, 0 }, { 0, 0 },
10749             {  0, +1 }
10750           };
10751
10752         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10753         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10754
10755         change->actual_trigger_ce_value = CustomValue[xx][yy];
10756         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10757       }
10758
10759       if (change->can_change && !change_done)
10760       {
10761         ChangeDelay[x][y] = 1;
10762         ChangeEvent[x][y] = trigger_event;
10763
10764         HandleElementChange(x, y, p);
10765
10766         change_done = TRUE;
10767       }
10768       else if (change->has_action)
10769       {
10770         ExecuteCustomElementAction(x, y, element, p);
10771         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10772       }
10773     }
10774   }
10775
10776   RECURSION_LOOP_DETECTION_END();
10777
10778   return change_done;
10779 }
10780
10781 static void PlayPlayerSound(struct PlayerInfo *player)
10782 {
10783   int jx = player->jx, jy = player->jy;
10784   int sound_element = player->artwork_element;
10785   int last_action = player->last_action_waiting;
10786   int action = player->action_waiting;
10787
10788   if (player->is_waiting)
10789   {
10790     if (action != last_action)
10791       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10792     else
10793       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10794   }
10795   else
10796   {
10797     if (action != last_action)
10798       StopSound(element_info[sound_element].sound[last_action]);
10799
10800     if (last_action == ACTION_SLEEPING)
10801       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10802   }
10803 }
10804
10805 static void PlayAllPlayersSound(void)
10806 {
10807   int i;
10808
10809   for (i = 0; i < MAX_PLAYERS; i++)
10810     if (stored_player[i].active)
10811       PlayPlayerSound(&stored_player[i]);
10812 }
10813
10814 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10815 {
10816   boolean last_waiting = player->is_waiting;
10817   int move_dir = player->MovDir;
10818
10819   player->dir_waiting = move_dir;
10820   player->last_action_waiting = player->action_waiting;
10821
10822   if (is_waiting)
10823   {
10824     if (!last_waiting)          /* not waiting -> waiting */
10825     {
10826       player->is_waiting = TRUE;
10827
10828       player->frame_counter_bored =
10829         FrameCounter +
10830         game.player_boring_delay_fixed +
10831         GetSimpleRandom(game.player_boring_delay_random);
10832       player->frame_counter_sleeping =
10833         FrameCounter +
10834         game.player_sleeping_delay_fixed +
10835         GetSimpleRandom(game.player_sleeping_delay_random);
10836
10837       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10838     }
10839
10840     if (game.player_sleeping_delay_fixed +
10841         game.player_sleeping_delay_random > 0 &&
10842         player->anim_delay_counter == 0 &&
10843         player->post_delay_counter == 0 &&
10844         FrameCounter >= player->frame_counter_sleeping)
10845       player->is_sleeping = TRUE;
10846     else if (game.player_boring_delay_fixed +
10847              game.player_boring_delay_random > 0 &&
10848              FrameCounter >= player->frame_counter_bored)
10849       player->is_bored = TRUE;
10850
10851     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10852                               player->is_bored ? ACTION_BORING :
10853                               ACTION_WAITING);
10854
10855     if (player->is_sleeping && player->use_murphy)
10856     {
10857       /* special case for sleeping Murphy when leaning against non-free tile */
10858
10859       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10860           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10861            !IS_MOVING(player->jx - 1, player->jy)))
10862         move_dir = MV_LEFT;
10863       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10864                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10865                 !IS_MOVING(player->jx + 1, player->jy)))
10866         move_dir = MV_RIGHT;
10867       else
10868         player->is_sleeping = FALSE;
10869
10870       player->dir_waiting = move_dir;
10871     }
10872
10873     if (player->is_sleeping)
10874     {
10875       if (player->num_special_action_sleeping > 0)
10876       {
10877         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10878         {
10879           int last_special_action = player->special_action_sleeping;
10880           int num_special_action = player->num_special_action_sleeping;
10881           int special_action =
10882             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10883              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10884              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10885              last_special_action + 1 : ACTION_SLEEPING);
10886           int special_graphic =
10887             el_act_dir2img(player->artwork_element, special_action, move_dir);
10888
10889           player->anim_delay_counter =
10890             graphic_info[special_graphic].anim_delay_fixed +
10891             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10892           player->post_delay_counter =
10893             graphic_info[special_graphic].post_delay_fixed +
10894             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10895
10896           player->special_action_sleeping = special_action;
10897         }
10898
10899         if (player->anim_delay_counter > 0)
10900         {
10901           player->action_waiting = player->special_action_sleeping;
10902           player->anim_delay_counter--;
10903         }
10904         else if (player->post_delay_counter > 0)
10905         {
10906           player->post_delay_counter--;
10907         }
10908       }
10909     }
10910     else if (player->is_bored)
10911     {
10912       if (player->num_special_action_bored > 0)
10913       {
10914         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10915         {
10916           int special_action =
10917             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10918           int special_graphic =
10919             el_act_dir2img(player->artwork_element, special_action, move_dir);
10920
10921           player->anim_delay_counter =
10922             graphic_info[special_graphic].anim_delay_fixed +
10923             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10924           player->post_delay_counter =
10925             graphic_info[special_graphic].post_delay_fixed +
10926             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10927
10928           player->special_action_bored = special_action;
10929         }
10930
10931         if (player->anim_delay_counter > 0)
10932         {
10933           player->action_waiting = player->special_action_bored;
10934           player->anim_delay_counter--;
10935         }
10936         else if (player->post_delay_counter > 0)
10937         {
10938           player->post_delay_counter--;
10939         }
10940       }
10941     }
10942   }
10943   else if (last_waiting)        /* waiting -> not waiting */
10944   {
10945     player->is_waiting = FALSE;
10946     player->is_bored = FALSE;
10947     player->is_sleeping = FALSE;
10948
10949     player->frame_counter_bored = -1;
10950     player->frame_counter_sleeping = -1;
10951
10952     player->anim_delay_counter = 0;
10953     player->post_delay_counter = 0;
10954
10955     player->dir_waiting = player->MovDir;
10956     player->action_waiting = ACTION_DEFAULT;
10957
10958     player->special_action_bored = ACTION_DEFAULT;
10959     player->special_action_sleeping = ACTION_DEFAULT;
10960   }
10961 }
10962
10963 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10964 {
10965   if ((!player->is_moving  && player->was_moving) ||
10966       (player->MovPos == 0 && player->was_moving) ||
10967       (player->is_snapping && !player->was_snapping) ||
10968       (player->is_dropping && !player->was_dropping))
10969   {
10970     if (!CheckSaveEngineSnapshotToList())
10971       return;
10972
10973     player->was_moving = FALSE;
10974     player->was_snapping = TRUE;
10975     player->was_dropping = TRUE;
10976   }
10977   else
10978   {
10979     if (player->is_moving)
10980       player->was_moving = TRUE;
10981
10982     if (!player->is_snapping)
10983       player->was_snapping = FALSE;
10984
10985     if (!player->is_dropping)
10986       player->was_dropping = FALSE;
10987   }
10988 }
10989
10990 static void CheckSingleStepMode(struct PlayerInfo *player)
10991 {
10992   if (tape.single_step && tape.recording && !tape.pausing)
10993   {
10994     /* as it is called "single step mode", just return to pause mode when the
10995        player stopped moving after one tile (or never starts moving at all) */
10996     if (!player->is_moving &&
10997         !player->is_pushing &&
10998         !player->is_dropping_pressed)
10999     {
11000       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11001       SnapField(player, 0, 0);                  /* stop snapping */
11002     }
11003   }
11004
11005   CheckSaveEngineSnapshot(player);
11006 }
11007
11008 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11009 {
11010   int left      = player_action & JOY_LEFT;
11011   int right     = player_action & JOY_RIGHT;
11012   int up        = player_action & JOY_UP;
11013   int down      = player_action & JOY_DOWN;
11014   int button1   = player_action & JOY_BUTTON_1;
11015   int button2   = player_action & JOY_BUTTON_2;
11016   int dx        = (left ? -1 : right ? 1 : 0);
11017   int dy        = (up   ? -1 : down  ? 1 : 0);
11018
11019   if (!player->active || tape.pausing)
11020     return 0;
11021
11022   if (player_action)
11023   {
11024     if (button1)
11025       SnapField(player, dx, dy);
11026     else
11027     {
11028       if (button2)
11029         DropElement(player);
11030
11031       MovePlayer(player, dx, dy);
11032     }
11033
11034     CheckSingleStepMode(player);
11035
11036     SetPlayerWaiting(player, FALSE);
11037
11038     return player_action;
11039   }
11040   else
11041   {
11042     /* no actions for this player (no input at player's configured device) */
11043
11044     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11045     SnapField(player, 0, 0);
11046     CheckGravityMovementWhenNotMoving(player);
11047
11048     if (player->MovPos == 0)
11049       SetPlayerWaiting(player, TRUE);
11050
11051     if (player->MovPos == 0)    /* needed for tape.playing */
11052       player->is_moving = FALSE;
11053
11054     player->is_dropping = FALSE;
11055     player->is_dropping_pressed = FALSE;
11056     player->drop_pressed_delay = 0;
11057
11058     CheckSingleStepMode(player);
11059
11060     return 0;
11061   }
11062 }
11063
11064 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11065                                          byte *tape_action)
11066 {
11067   if (!tape.use_mouse)
11068     return;
11069
11070   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11071   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11072   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11073 }
11074
11075 static void SetTapeActionFromMouseAction(byte *tape_action,
11076                                          struct MouseActionInfo *mouse_action)
11077 {
11078   if (!tape.use_mouse)
11079     return;
11080
11081   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11082   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11083   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11084 }
11085
11086 static void CheckLevelSolved(void)
11087 {
11088   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11089   {
11090     if (level.native_em_level->lev->home == 0)  /* all players at home */
11091     {
11092       PlayerWins(local_player);
11093
11094       AllPlayersGone = TRUE;
11095
11096       level.native_em_level->lev->home = -1;
11097     }
11098
11099     if (level.native_em_level->ply[0]->alive == 0 &&
11100         level.native_em_level->ply[1]->alive == 0 &&
11101         level.native_em_level->ply[2]->alive == 0 &&
11102         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11103       AllPlayersGone = TRUE;
11104   }
11105   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11106   {
11107     if (game_sp.LevelSolved &&
11108         !game_sp.GameOver)                              /* game won */
11109     {
11110       PlayerWins(local_player);
11111
11112       game_sp.GameOver = TRUE;
11113
11114       AllPlayersGone = TRUE;
11115     }
11116
11117     if (game_sp.GameOver)                               /* game lost */
11118       AllPlayersGone = TRUE;
11119   }
11120   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11121   {
11122     if (game_mm.level_solved &&
11123         !game_mm.game_over)                             /* game won */
11124     {
11125       PlayerWins(local_player);
11126
11127       game_mm.game_over = TRUE;
11128
11129       AllPlayersGone = TRUE;
11130     }
11131
11132     if (game_mm.game_over)                              /* game lost */
11133       AllPlayersGone = TRUE;
11134   }
11135 }
11136
11137 static void CheckLevelTime(void)
11138 {
11139   int i;
11140
11141   if (TimeFrames >= FRAMES_PER_SECOND)
11142   {
11143     TimeFrames = 0;
11144     TapeTime++;
11145
11146     for (i = 0; i < MAX_PLAYERS; i++)
11147     {
11148       struct PlayerInfo *player = &stored_player[i];
11149
11150       if (SHIELD_ON(player))
11151       {
11152         player->shield_normal_time_left--;
11153
11154         if (player->shield_deadly_time_left > 0)
11155           player->shield_deadly_time_left--;
11156       }
11157     }
11158
11159     if (!local_player->LevelSolved && !level.use_step_counter)
11160     {
11161       TimePlayed++;
11162
11163       if (TimeLeft > 0)
11164       {
11165         TimeLeft--;
11166
11167         if (TimeLeft <= 10 && setup.time_limit)
11168           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11169
11170         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11171            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11172
11173         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11174
11175         if (!TimeLeft && setup.time_limit)
11176         {
11177           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11178             level.native_em_level->lev->killed_out_of_time = TRUE;
11179           else
11180             for (i = 0; i < MAX_PLAYERS; i++)
11181               KillPlayer(&stored_player[i]);
11182         }
11183       }
11184       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11185       {
11186         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11187       }
11188
11189       level.native_em_level->lev->time =
11190         (game.no_time_limit ? TimePlayed : TimeLeft);
11191     }
11192
11193     if (tape.recording || tape.playing)
11194       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11195   }
11196
11197   if (tape.recording || tape.playing)
11198     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11199
11200   UpdateAndDisplayGameControlValues();
11201 }
11202
11203 void AdvanceFrameAndPlayerCounters(int player_nr)
11204 {
11205   int i;
11206
11207   /* advance frame counters (global frame counter and time frame counter) */
11208   FrameCounter++;
11209   TimeFrames++;
11210
11211   /* advance player counters (counters for move delay, move animation etc.) */
11212   for (i = 0; i < MAX_PLAYERS; i++)
11213   {
11214     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11215     int move_delay_value = stored_player[i].move_delay_value;
11216     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11217
11218     if (!advance_player_counters)       /* not all players may be affected */
11219       continue;
11220
11221     if (move_frames == 0)       /* less than one move per game frame */
11222     {
11223       int stepsize = TILEX / move_delay_value;
11224       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11225       int count = (stored_player[i].is_moving ?
11226                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11227
11228       if (count % delay == 0)
11229         move_frames = 1;
11230     }
11231
11232     stored_player[i].Frame += move_frames;
11233
11234     if (stored_player[i].MovPos != 0)
11235       stored_player[i].StepFrame += move_frames;
11236
11237     if (stored_player[i].move_delay > 0)
11238       stored_player[i].move_delay--;
11239
11240     /* due to bugs in previous versions, counter must count up, not down */
11241     if (stored_player[i].push_delay != -1)
11242       stored_player[i].push_delay++;
11243
11244     if (stored_player[i].drop_delay > 0)
11245       stored_player[i].drop_delay--;
11246
11247     if (stored_player[i].is_dropping_pressed)
11248       stored_player[i].drop_pressed_delay++;
11249   }
11250 }
11251
11252 void StartGameActions(boolean init_network_game, boolean record_tape,
11253                       int random_seed)
11254 {
11255   unsigned int new_random_seed = InitRND(random_seed);
11256
11257   if (record_tape)
11258     TapeStartRecording(new_random_seed);
11259
11260   if (init_network_game)
11261   {
11262     SendToServer_LevelFile();
11263     SendToServer_StartPlaying();
11264
11265     return;
11266   }
11267
11268   InitGame();
11269 }
11270
11271 static void GameActionsExt(void)
11272 {
11273 #if 0
11274   static unsigned int game_frame_delay = 0;
11275 #endif
11276   unsigned int game_frame_delay_value;
11277   byte *recorded_player_action;
11278   byte summarized_player_action = 0;
11279   byte tape_action[MAX_PLAYERS];
11280   int i;
11281
11282   /* detect endless loops, caused by custom element programming */
11283   if (recursion_loop_detected && recursion_loop_depth == 0)
11284   {
11285     char *message = getStringCat3("Internal Error! Element ",
11286                                   EL_NAME(recursion_loop_element),
11287                                   " caused endless loop! Quit the game?");
11288
11289     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11290           EL_NAME(recursion_loop_element));
11291
11292     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11293
11294     recursion_loop_detected = FALSE;    /* if game should be continued */
11295
11296     free(message);
11297
11298     return;
11299   }
11300
11301   if (game.restart_level)
11302     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11303
11304   CheckLevelSolved();
11305
11306   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11307     GameWon();
11308
11309   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11310     TapeStop();
11311
11312   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11313     return;
11314
11315   game_frame_delay_value =
11316     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11317
11318   if (tape.playing && tape.warp_forward && !tape.pausing)
11319     game_frame_delay_value = 0;
11320
11321   SetVideoFrameDelay(game_frame_delay_value);
11322
11323 #if 0
11324 #if 0
11325   /* ---------- main game synchronization point ---------- */
11326
11327   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11328
11329   printf("::: skip == %d\n", skip);
11330
11331 #else
11332   /* ---------- main game synchronization point ---------- */
11333
11334   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11335 #endif
11336 #endif
11337
11338   if (network_playing && !network_player_action_received)
11339   {
11340     /* try to get network player actions in time */
11341
11342     /* last chance to get network player actions without main loop delay */
11343     HandleNetworking();
11344
11345     /* game was quit by network peer */
11346     if (game_status != GAME_MODE_PLAYING)
11347       return;
11348
11349     /* check if network player actions still missing and game still running */
11350     if (!network_player_action_received && !checkGameEnded())
11351       return;           /* failed to get network player actions in time */
11352
11353     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11354   }
11355
11356   if (tape.pausing)
11357     return;
11358
11359   /* at this point we know that we really continue executing the game */
11360
11361   network_player_action_received = FALSE;
11362
11363   /* when playing tape, read previously recorded player input from tape data */
11364   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11365
11366   local_player->effective_mouse_action = local_player->mouse_action;
11367
11368   if (recorded_player_action != NULL)
11369     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11370                                  recorded_player_action);
11371
11372   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11373   if (tape.pausing)
11374     return;
11375
11376   if (tape.set_centered_player)
11377   {
11378     game.centered_player_nr_next = tape.centered_player_nr_next;
11379     game.set_centered_player = TRUE;
11380   }
11381
11382   for (i = 0; i < MAX_PLAYERS; i++)
11383   {
11384     summarized_player_action |= stored_player[i].action;
11385
11386     if (!network_playing && (game.team_mode || tape.playing))
11387       stored_player[i].effective_action = stored_player[i].action;
11388   }
11389
11390   if (network_playing && !checkGameEnded())
11391     SendToServer_MovePlayer(summarized_player_action);
11392
11393   // summarize all actions at local players mapped input device position
11394   // (this allows using different input devices in single player mode)
11395   if (!network.enabled && !game.team_mode)
11396     stored_player[map_player_action[local_player->index_nr]].effective_action =
11397       summarized_player_action;
11398
11399   if (tape.recording &&
11400       setup.team_mode &&
11401       setup.input_on_focus &&
11402       game.centered_player_nr != -1)
11403   {
11404     for (i = 0; i < MAX_PLAYERS; i++)
11405       stored_player[i].effective_action =
11406         (i == game.centered_player_nr ? summarized_player_action : 0);
11407   }
11408
11409   if (recorded_player_action != NULL)
11410     for (i = 0; i < MAX_PLAYERS; i++)
11411       stored_player[i].effective_action = recorded_player_action[i];
11412
11413   for (i = 0; i < MAX_PLAYERS; i++)
11414   {
11415     tape_action[i] = stored_player[i].effective_action;
11416
11417     /* (this may happen in the RND game engine if a player was not present on
11418        the playfield on level start, but appeared later from a custom element */
11419     if (setup.team_mode &&
11420         tape.recording &&
11421         tape_action[i] &&
11422         !tape.player_participates[i])
11423       tape.player_participates[i] = TRUE;
11424   }
11425
11426   SetTapeActionFromMouseAction(tape_action,
11427                                &local_player->effective_mouse_action);
11428
11429   /* only record actions from input devices, but not programmed actions */
11430   if (tape.recording)
11431     TapeRecordAction(tape_action);
11432
11433 #if USE_NEW_PLAYER_ASSIGNMENTS
11434   // !!! also map player actions in single player mode !!!
11435   // if (game.team_mode)
11436   if (1)
11437   {
11438     byte mapped_action[MAX_PLAYERS];
11439
11440 #if DEBUG_PLAYER_ACTIONS
11441     printf(":::");
11442     for (i = 0; i < MAX_PLAYERS; i++)
11443       printf(" %d, ", stored_player[i].effective_action);
11444 #endif
11445
11446     for (i = 0; i < MAX_PLAYERS; i++)
11447       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11448
11449     for (i = 0; i < MAX_PLAYERS; i++)
11450       stored_player[i].effective_action = mapped_action[i];
11451
11452 #if DEBUG_PLAYER_ACTIONS
11453     printf(" =>");
11454     for (i = 0; i < MAX_PLAYERS; i++)
11455       printf(" %d, ", stored_player[i].effective_action);
11456     printf("\n");
11457 #endif
11458   }
11459 #if DEBUG_PLAYER_ACTIONS
11460   else
11461   {
11462     printf(":::");
11463     for (i = 0; i < MAX_PLAYERS; i++)
11464       printf(" %d, ", stored_player[i].effective_action);
11465     printf("\n");
11466   }
11467 #endif
11468 #endif
11469
11470   for (i = 0; i < MAX_PLAYERS; i++)
11471   {
11472     // allow engine snapshot in case of changed movement attempt
11473     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11474         (stored_player[i].effective_action & KEY_MOTION))
11475       game.snapshot.changed_action = TRUE;
11476
11477     // allow engine snapshot in case of snapping/dropping attempt
11478     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11479         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11480       game.snapshot.changed_action = TRUE;
11481
11482     game.snapshot.last_action[i] = stored_player[i].effective_action;
11483   }
11484
11485   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11486   {
11487     GameActions_EM_Main();
11488   }
11489   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11490   {
11491     GameActions_SP_Main();
11492   }
11493   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11494   {
11495     GameActions_MM_Main();
11496   }
11497   else
11498   {
11499     GameActions_RND_Main();
11500   }
11501
11502   BlitScreenToBitmap(backbuffer);
11503
11504   CheckLevelSolved();
11505   CheckLevelTime();
11506
11507   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11508
11509   if (global.show_frames_per_second)
11510   {
11511     static unsigned int fps_counter = 0;
11512     static int fps_frames = 0;
11513     unsigned int fps_delay_ms = Counter() - fps_counter;
11514
11515     fps_frames++;
11516
11517     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11518     {
11519       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11520
11521       fps_frames = 0;
11522       fps_counter = Counter();
11523
11524       /* always draw FPS to screen after FPS value was updated */
11525       redraw_mask |= REDRAW_FPS;
11526     }
11527
11528     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11529     if (GetDrawDeactivationMask() == REDRAW_NONE)
11530       redraw_mask |= REDRAW_FPS;
11531   }
11532 }
11533
11534 static void GameActions_CheckSaveEngineSnapshot(void)
11535 {
11536   if (!game.snapshot.save_snapshot)
11537     return;
11538
11539   // clear flag for saving snapshot _before_ saving snapshot
11540   game.snapshot.save_snapshot = FALSE;
11541
11542   SaveEngineSnapshotToList();
11543 }
11544
11545 void GameActions(void)
11546 {
11547   GameActionsExt();
11548
11549   GameActions_CheckSaveEngineSnapshot();
11550 }
11551
11552 void GameActions_EM_Main(void)
11553 {
11554   byte effective_action[MAX_PLAYERS];
11555   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11556   int i;
11557
11558   for (i = 0; i < MAX_PLAYERS; i++)
11559     effective_action[i] = stored_player[i].effective_action;
11560
11561   GameActions_EM(effective_action, warp_mode);
11562 }
11563
11564 void GameActions_SP_Main(void)
11565 {
11566   byte effective_action[MAX_PLAYERS];
11567   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11568   int i;
11569
11570   for (i = 0; i < MAX_PLAYERS; i++)
11571     effective_action[i] = stored_player[i].effective_action;
11572
11573   GameActions_SP(effective_action, warp_mode);
11574
11575   for (i = 0; i < MAX_PLAYERS; i++)
11576   {
11577     if (stored_player[i].force_dropping)
11578       stored_player[i].action |= KEY_BUTTON_DROP;
11579
11580     stored_player[i].force_dropping = FALSE;
11581   }
11582 }
11583
11584 void GameActions_MM_Main(void)
11585 {
11586   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11587
11588   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11589 }
11590
11591 void GameActions_RND_Main(void)
11592 {
11593   GameActions_RND();
11594 }
11595
11596 void GameActions_RND(void)
11597 {
11598   int magic_wall_x = 0, magic_wall_y = 0;
11599   int i, x, y, element, graphic, last_gfx_frame;
11600
11601   InitPlayfieldScanModeVars();
11602
11603   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11604   {
11605     SCAN_PLAYFIELD(x, y)
11606     {
11607       ChangeCount[x][y] = 0;
11608       ChangeEvent[x][y] = -1;
11609     }
11610   }
11611
11612   if (game.set_centered_player)
11613   {
11614     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11615
11616     /* switching to "all players" only possible if all players fit to screen */
11617     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11618     {
11619       game.centered_player_nr_next = game.centered_player_nr;
11620       game.set_centered_player = FALSE;
11621     }
11622
11623     /* do not switch focus to non-existing (or non-active) player */
11624     if (game.centered_player_nr_next >= 0 &&
11625         !stored_player[game.centered_player_nr_next].active)
11626     {
11627       game.centered_player_nr_next = game.centered_player_nr;
11628       game.set_centered_player = FALSE;
11629     }
11630   }
11631
11632   if (game.set_centered_player &&
11633       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11634   {
11635     int sx, sy;
11636
11637     if (game.centered_player_nr_next == -1)
11638     {
11639       setScreenCenteredToAllPlayers(&sx, &sy);
11640     }
11641     else
11642     {
11643       sx = stored_player[game.centered_player_nr_next].jx;
11644       sy = stored_player[game.centered_player_nr_next].jy;
11645     }
11646
11647     game.centered_player_nr = game.centered_player_nr_next;
11648     game.set_centered_player = FALSE;
11649
11650     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11651     DrawGameDoorValues();
11652   }
11653
11654   for (i = 0; i < MAX_PLAYERS; i++)
11655   {
11656     int actual_player_action = stored_player[i].effective_action;
11657
11658 #if 1
11659     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11660        - rnd_equinox_tetrachloride 048
11661        - rnd_equinox_tetrachloride_ii 096
11662        - rnd_emanuel_schmieg 002
11663        - doctor_sloan_ww 001, 020
11664     */
11665     if (stored_player[i].MovPos == 0)
11666       CheckGravityMovement(&stored_player[i]);
11667 #endif
11668
11669     /* overwrite programmed action with tape action */
11670     if (stored_player[i].programmed_action)
11671       actual_player_action = stored_player[i].programmed_action;
11672
11673     PlayerActions(&stored_player[i], actual_player_action);
11674
11675     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11676   }
11677
11678   ScrollScreen(NULL, SCROLL_GO_ON);
11679
11680   /* for backwards compatibility, the following code emulates a fixed bug that
11681      occured when pushing elements (causing elements that just made their last
11682      pushing step to already (if possible) make their first falling step in the
11683      same game frame, which is bad); this code is also needed to use the famous
11684      "spring push bug" which is used in older levels and might be wanted to be
11685      used also in newer levels, but in this case the buggy pushing code is only
11686      affecting the "spring" element and no other elements */
11687
11688   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11689   {
11690     for (i = 0; i < MAX_PLAYERS; i++)
11691     {
11692       struct PlayerInfo *player = &stored_player[i];
11693       int x = player->jx;
11694       int y = player->jy;
11695
11696       if (player->active && player->is_pushing && player->is_moving &&
11697           IS_MOVING(x, y) &&
11698           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11699            Feld[x][y] == EL_SPRING))
11700       {
11701         ContinueMoving(x, y);
11702
11703         /* continue moving after pushing (this is actually a bug) */
11704         if (!IS_MOVING(x, y))
11705           Stop[x][y] = FALSE;
11706       }
11707     }
11708   }
11709
11710   SCAN_PLAYFIELD(x, y)
11711   {
11712     Last[x][y] = Feld[x][y];
11713
11714     ChangeCount[x][y] = 0;
11715     ChangeEvent[x][y] = -1;
11716
11717     /* this must be handled before main playfield loop */
11718     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11719     {
11720       MovDelay[x][y]--;
11721       if (MovDelay[x][y] <= 0)
11722         RemoveField(x, y);
11723     }
11724
11725     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11726     {
11727       MovDelay[x][y]--;
11728       if (MovDelay[x][y] <= 0)
11729       {
11730         RemoveField(x, y);
11731         TEST_DrawLevelField(x, y);
11732
11733         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11734       }
11735     }
11736
11737 #if DEBUG
11738     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11739     {
11740       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11741       printf("GameActions(): This should never happen!\n");
11742
11743       ChangePage[x][y] = -1;
11744     }
11745 #endif
11746
11747     Stop[x][y] = FALSE;
11748     if (WasJustMoving[x][y] > 0)
11749       WasJustMoving[x][y]--;
11750     if (WasJustFalling[x][y] > 0)
11751       WasJustFalling[x][y]--;
11752     if (CheckCollision[x][y] > 0)
11753       CheckCollision[x][y]--;
11754     if (CheckImpact[x][y] > 0)
11755       CheckImpact[x][y]--;
11756
11757     GfxFrame[x][y]++;
11758
11759     /* reset finished pushing action (not done in ContinueMoving() to allow
11760        continuous pushing animation for elements with zero push delay) */
11761     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11762     {
11763       ResetGfxAnimation(x, y);
11764       TEST_DrawLevelField(x, y);
11765     }
11766
11767 #if DEBUG
11768     if (IS_BLOCKED(x, y))
11769     {
11770       int oldx, oldy;
11771
11772       Blocked2Moving(x, y, &oldx, &oldy);
11773       if (!IS_MOVING(oldx, oldy))
11774       {
11775         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11776         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11777         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11778         printf("GameActions(): This should never happen!\n");
11779       }
11780     }
11781 #endif
11782   }
11783
11784   SCAN_PLAYFIELD(x, y)
11785   {
11786     element = Feld[x][y];
11787     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11788     last_gfx_frame = GfxFrame[x][y];
11789
11790     ResetGfxFrame(x, y);
11791
11792     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11793       DrawLevelGraphicAnimation(x, y, graphic);
11794
11795     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11796         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11797       ResetRandomAnimationValue(x, y);
11798
11799     SetRandomAnimationValue(x, y);
11800
11801     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11802
11803     if (IS_INACTIVE(element))
11804     {
11805       if (IS_ANIMATED(graphic))
11806         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11807
11808       continue;
11809     }
11810
11811     /* this may take place after moving, so 'element' may have changed */
11812     if (IS_CHANGING(x, y) &&
11813         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11814     {
11815       int page = element_info[element].event_page_nr[CE_DELAY];
11816
11817       HandleElementChange(x, y, page);
11818
11819       element = Feld[x][y];
11820       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11821     }
11822
11823     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11824     {
11825       StartMoving(x, y);
11826
11827       element = Feld[x][y];
11828       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11829
11830       if (IS_ANIMATED(graphic) &&
11831           !IS_MOVING(x, y) &&
11832           !Stop[x][y])
11833         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11834
11835       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11836         TEST_DrawTwinkleOnField(x, y);
11837     }
11838     else if (element == EL_ACID)
11839     {
11840       if (!Stop[x][y])
11841         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11842     }
11843     else if ((element == EL_EXIT_OPEN ||
11844               element == EL_EM_EXIT_OPEN ||
11845               element == EL_SP_EXIT_OPEN ||
11846               element == EL_STEEL_EXIT_OPEN ||
11847               element == EL_EM_STEEL_EXIT_OPEN ||
11848               element == EL_SP_TERMINAL ||
11849               element == EL_SP_TERMINAL_ACTIVE ||
11850               element == EL_EXTRA_TIME ||
11851               element == EL_SHIELD_NORMAL ||
11852               element == EL_SHIELD_DEADLY) &&
11853              IS_ANIMATED(graphic))
11854       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11855     else if (IS_MOVING(x, y))
11856       ContinueMoving(x, y);
11857     else if (IS_ACTIVE_BOMB(element))
11858       CheckDynamite(x, y);
11859     else if (element == EL_AMOEBA_GROWING)
11860       AmoebeWaechst(x, y);
11861     else if (element == EL_AMOEBA_SHRINKING)
11862       AmoebaDisappearing(x, y);
11863
11864 #if !USE_NEW_AMOEBA_CODE
11865     else if (IS_AMOEBALIVE(element))
11866       AmoebeAbleger(x, y);
11867 #endif
11868
11869     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11870       Life(x, y);
11871     else if (element == EL_EXIT_CLOSED)
11872       CheckExit(x, y);
11873     else if (element == EL_EM_EXIT_CLOSED)
11874       CheckExitEM(x, y);
11875     else if (element == EL_STEEL_EXIT_CLOSED)
11876       CheckExitSteel(x, y);
11877     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11878       CheckExitSteelEM(x, y);
11879     else if (element == EL_SP_EXIT_CLOSED)
11880       CheckExitSP(x, y);
11881     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11882              element == EL_EXPANDABLE_STEELWALL_GROWING)
11883       MauerWaechst(x, y);
11884     else if (element == EL_EXPANDABLE_WALL ||
11885              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11886              element == EL_EXPANDABLE_WALL_VERTICAL ||
11887              element == EL_EXPANDABLE_WALL_ANY ||
11888              element == EL_BD_EXPANDABLE_WALL)
11889       MauerAbleger(x, y);
11890     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11891              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11892              element == EL_EXPANDABLE_STEELWALL_ANY)
11893       MauerAblegerStahl(x, y);
11894     else if (element == EL_FLAMES)
11895       CheckForDragon(x, y);
11896     else if (element == EL_EXPLOSION)
11897       ; /* drawing of correct explosion animation is handled separately */
11898     else if (element == EL_ELEMENT_SNAPPING ||
11899              element == EL_DIAGONAL_SHRINKING ||
11900              element == EL_DIAGONAL_GROWING)
11901     {
11902       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11903
11904       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11905     }
11906     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11907       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11908
11909     if (IS_BELT_ACTIVE(element))
11910       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11911
11912     if (game.magic_wall_active)
11913     {
11914       int jx = local_player->jx, jy = local_player->jy;
11915
11916       /* play the element sound at the position nearest to the player */
11917       if ((element == EL_MAGIC_WALL_FULL ||
11918            element == EL_MAGIC_WALL_ACTIVE ||
11919            element == EL_MAGIC_WALL_EMPTYING ||
11920            element == EL_BD_MAGIC_WALL_FULL ||
11921            element == EL_BD_MAGIC_WALL_ACTIVE ||
11922            element == EL_BD_MAGIC_WALL_EMPTYING ||
11923            element == EL_DC_MAGIC_WALL_FULL ||
11924            element == EL_DC_MAGIC_WALL_ACTIVE ||
11925            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11926           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11927       {
11928         magic_wall_x = x;
11929         magic_wall_y = y;
11930       }
11931     }
11932   }
11933
11934 #if USE_NEW_AMOEBA_CODE
11935   /* new experimental amoeba growth stuff */
11936   if (!(FrameCounter % 8))
11937   {
11938     static unsigned int random = 1684108901;
11939
11940     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11941     {
11942       x = RND(lev_fieldx);
11943       y = RND(lev_fieldy);
11944       element = Feld[x][y];
11945
11946       if (!IS_PLAYER(x,y) &&
11947           (element == EL_EMPTY ||
11948            CAN_GROW_INTO(element) ||
11949            element == EL_QUICKSAND_EMPTY ||
11950            element == EL_QUICKSAND_FAST_EMPTY ||
11951            element == EL_ACID_SPLASH_LEFT ||
11952            element == EL_ACID_SPLASH_RIGHT))
11953       {
11954         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11955             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11956             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11957             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11958           Feld[x][y] = EL_AMOEBA_DROP;
11959       }
11960
11961       random = random * 129 + 1;
11962     }
11963   }
11964 #endif
11965
11966   game.explosions_delayed = FALSE;
11967
11968   SCAN_PLAYFIELD(x, y)
11969   {
11970     element = Feld[x][y];
11971
11972     if (ExplodeField[x][y])
11973       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11974     else if (element == EL_EXPLOSION)
11975       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11976
11977     ExplodeField[x][y] = EX_TYPE_NONE;
11978   }
11979
11980   game.explosions_delayed = TRUE;
11981
11982   if (game.magic_wall_active)
11983   {
11984     if (!(game.magic_wall_time_left % 4))
11985     {
11986       int element = Feld[magic_wall_x][magic_wall_y];
11987
11988       if (element == EL_BD_MAGIC_WALL_FULL ||
11989           element == EL_BD_MAGIC_WALL_ACTIVE ||
11990           element == EL_BD_MAGIC_WALL_EMPTYING)
11991         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11992       else if (element == EL_DC_MAGIC_WALL_FULL ||
11993                element == EL_DC_MAGIC_WALL_ACTIVE ||
11994                element == EL_DC_MAGIC_WALL_EMPTYING)
11995         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11996       else
11997         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11998     }
11999
12000     if (game.magic_wall_time_left > 0)
12001     {
12002       game.magic_wall_time_left--;
12003
12004       if (!game.magic_wall_time_left)
12005       {
12006         SCAN_PLAYFIELD(x, y)
12007         {
12008           element = Feld[x][y];
12009
12010           if (element == EL_MAGIC_WALL_ACTIVE ||
12011               element == EL_MAGIC_WALL_FULL)
12012           {
12013             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12014             TEST_DrawLevelField(x, y);
12015           }
12016           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12017                    element == EL_BD_MAGIC_WALL_FULL)
12018           {
12019             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12020             TEST_DrawLevelField(x, y);
12021           }
12022           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12023                    element == EL_DC_MAGIC_WALL_FULL)
12024           {
12025             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12026             TEST_DrawLevelField(x, y);
12027           }
12028         }
12029
12030         game.magic_wall_active = FALSE;
12031       }
12032     }
12033   }
12034
12035   if (game.light_time_left > 0)
12036   {
12037     game.light_time_left--;
12038
12039     if (game.light_time_left == 0)
12040       RedrawAllLightSwitchesAndInvisibleElements();
12041   }
12042
12043   if (game.timegate_time_left > 0)
12044   {
12045     game.timegate_time_left--;
12046
12047     if (game.timegate_time_left == 0)
12048       CloseAllOpenTimegates();
12049   }
12050
12051   if (game.lenses_time_left > 0)
12052   {
12053     game.lenses_time_left--;
12054
12055     if (game.lenses_time_left == 0)
12056       RedrawAllInvisibleElementsForLenses();
12057   }
12058
12059   if (game.magnify_time_left > 0)
12060   {
12061     game.magnify_time_left--;
12062
12063     if (game.magnify_time_left == 0)
12064       RedrawAllInvisibleElementsForMagnifier();
12065   }
12066
12067   for (i = 0; i < MAX_PLAYERS; i++)
12068   {
12069     struct PlayerInfo *player = &stored_player[i];
12070
12071     if (SHIELD_ON(player))
12072     {
12073       if (player->shield_deadly_time_left)
12074         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12075       else if (player->shield_normal_time_left)
12076         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12077     }
12078   }
12079
12080 #if USE_DELAYED_GFX_REDRAW
12081   SCAN_PLAYFIELD(x, y)
12082   {
12083     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12084     {
12085       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12086          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12087
12088       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12089         DrawLevelField(x, y);
12090
12091       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12092         DrawLevelFieldCrumbled(x, y);
12093
12094       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12095         DrawLevelFieldCrumbledNeighbours(x, y);
12096
12097       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12098         DrawTwinkleOnField(x, y);
12099     }
12100
12101     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12102   }
12103 #endif
12104
12105   DrawAllPlayers();
12106   PlayAllPlayersSound();
12107
12108   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12109   {
12110     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12111
12112     local_player->show_envelope = 0;
12113   }
12114
12115   /* use random number generator in every frame to make it less predictable */
12116   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12117     RND(1);
12118 }
12119
12120 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12121 {
12122   int min_x = x, min_y = y, max_x = x, max_y = y;
12123   int i;
12124
12125   for (i = 0; i < MAX_PLAYERS; i++)
12126   {
12127     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12128
12129     if (!stored_player[i].active || &stored_player[i] == player)
12130       continue;
12131
12132     min_x = MIN(min_x, jx);
12133     min_y = MIN(min_y, jy);
12134     max_x = MAX(max_x, jx);
12135     max_y = MAX(max_y, jy);
12136   }
12137
12138   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12139 }
12140
12141 static boolean AllPlayersInVisibleScreen(void)
12142 {
12143   int i;
12144
12145   for (i = 0; i < MAX_PLAYERS; i++)
12146   {
12147     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12148
12149     if (!stored_player[i].active)
12150       continue;
12151
12152     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12153       return FALSE;
12154   }
12155
12156   return TRUE;
12157 }
12158
12159 void ScrollLevel(int dx, int dy)
12160 {
12161   int scroll_offset = 2 * TILEX_VAR;
12162   int x, y;
12163
12164   BlitBitmap(drawto_field, drawto_field,
12165              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12166              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12167              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12168              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12169              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12170              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12171
12172   if (dx != 0)
12173   {
12174     x = (dx == 1 ? BX1 : BX2);
12175     for (y = BY1; y <= BY2; y++)
12176       DrawScreenField(x, y);
12177   }
12178
12179   if (dy != 0)
12180   {
12181     y = (dy == 1 ? BY1 : BY2);
12182     for (x = BX1; x <= BX2; x++)
12183       DrawScreenField(x, y);
12184   }
12185
12186   redraw_mask |= REDRAW_FIELD;
12187 }
12188
12189 static boolean canFallDown(struct PlayerInfo *player)
12190 {
12191   int jx = player->jx, jy = player->jy;
12192
12193   return (IN_LEV_FIELD(jx, jy + 1) &&
12194           (IS_FREE(jx, jy + 1) ||
12195            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12196           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12197           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12198 }
12199
12200 static boolean canPassField(int x, int y, int move_dir)
12201 {
12202   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12203   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12204   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12205   int nextx = x + dx;
12206   int nexty = y + dy;
12207   int element = Feld[x][y];
12208
12209   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12210           !CAN_MOVE(element) &&
12211           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12212           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12213           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12214 }
12215
12216 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12217 {
12218   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12219   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12220   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12221   int newx = x + dx;
12222   int newy = y + dy;
12223
12224   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12225           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12226           (IS_DIGGABLE(Feld[newx][newy]) ||
12227            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12228            canPassField(newx, newy, move_dir)));
12229 }
12230
12231 static void CheckGravityMovement(struct PlayerInfo *player)
12232 {
12233   if (player->gravity && !player->programmed_action)
12234   {
12235     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12236     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12237     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12238     int jx = player->jx, jy = player->jy;
12239     boolean player_is_moving_to_valid_field =
12240       (!player_is_snapping &&
12241        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12242         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12243     boolean player_can_fall_down = canFallDown(player);
12244
12245     if (player_can_fall_down &&
12246         !player_is_moving_to_valid_field)
12247       player->programmed_action = MV_DOWN;
12248   }
12249 }
12250
12251 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12252 {
12253   return CheckGravityMovement(player);
12254
12255   if (player->gravity && !player->programmed_action)
12256   {
12257     int jx = player->jx, jy = player->jy;
12258     boolean field_under_player_is_free =
12259       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12260     boolean player_is_standing_on_valid_field =
12261       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12262        (IS_WALKABLE(Feld[jx][jy]) &&
12263         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12264
12265     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12266       player->programmed_action = MV_DOWN;
12267   }
12268 }
12269
12270 /*
12271   MovePlayerOneStep()
12272   -----------------------------------------------------------------------------
12273   dx, dy:               direction (non-diagonal) to try to move the player to
12274   real_dx, real_dy:     direction as read from input device (can be diagonal)
12275 */
12276
12277 boolean MovePlayerOneStep(struct PlayerInfo *player,
12278                           int dx, int dy, int real_dx, int real_dy)
12279 {
12280   int jx = player->jx, jy = player->jy;
12281   int new_jx = jx + dx, new_jy = jy + dy;
12282   int can_move;
12283   boolean player_can_move = !player->cannot_move;
12284
12285   if (!player->active || (!dx && !dy))
12286     return MP_NO_ACTION;
12287
12288   player->MovDir = (dx < 0 ? MV_LEFT :
12289                     dx > 0 ? MV_RIGHT :
12290                     dy < 0 ? MV_UP :
12291                     dy > 0 ? MV_DOWN :  MV_NONE);
12292
12293   if (!IN_LEV_FIELD(new_jx, new_jy))
12294     return MP_NO_ACTION;
12295
12296   if (!player_can_move)
12297   {
12298     if (player->MovPos == 0)
12299     {
12300       player->is_moving = FALSE;
12301       player->is_digging = FALSE;
12302       player->is_collecting = FALSE;
12303       player->is_snapping = FALSE;
12304       player->is_pushing = FALSE;
12305     }
12306   }
12307
12308   if (!network.enabled && game.centered_player_nr == -1 &&
12309       !AllPlayersInSight(player, new_jx, new_jy))
12310     return MP_NO_ACTION;
12311
12312   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12313   if (can_move != MP_MOVING)
12314     return can_move;
12315
12316   /* check if DigField() has caused relocation of the player */
12317   if (player->jx != jx || player->jy != jy)
12318     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12319
12320   StorePlayer[jx][jy] = 0;
12321   player->last_jx = jx;
12322   player->last_jy = jy;
12323   player->jx = new_jx;
12324   player->jy = new_jy;
12325   StorePlayer[new_jx][new_jy] = player->element_nr;
12326
12327   if (player->move_delay_value_next != -1)
12328   {
12329     player->move_delay_value = player->move_delay_value_next;
12330     player->move_delay_value_next = -1;
12331   }
12332
12333   player->MovPos =
12334     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12335
12336   player->step_counter++;
12337
12338   PlayerVisit[jx][jy] = FrameCounter;
12339
12340   player->is_moving = TRUE;
12341
12342 #if 1
12343   /* should better be called in MovePlayer(), but this breaks some tapes */
12344   ScrollPlayer(player, SCROLL_INIT);
12345 #endif
12346
12347   return MP_MOVING;
12348 }
12349
12350 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12351 {
12352   int jx = player->jx, jy = player->jy;
12353   int old_jx = jx, old_jy = jy;
12354   int moved = MP_NO_ACTION;
12355
12356   if (!player->active)
12357     return FALSE;
12358
12359   if (!dx && !dy)
12360   {
12361     if (player->MovPos == 0)
12362     {
12363       player->is_moving = FALSE;
12364       player->is_digging = FALSE;
12365       player->is_collecting = FALSE;
12366       player->is_snapping = FALSE;
12367       player->is_pushing = FALSE;
12368     }
12369
12370     return FALSE;
12371   }
12372
12373   if (player->move_delay > 0)
12374     return FALSE;
12375
12376   player->move_delay = -1;              /* set to "uninitialized" value */
12377
12378   /* store if player is automatically moved to next field */
12379   player->is_auto_moving = (player->programmed_action != MV_NONE);
12380
12381   /* remove the last programmed player action */
12382   player->programmed_action = 0;
12383
12384   if (player->MovPos)
12385   {
12386     /* should only happen if pre-1.2 tape recordings are played */
12387     /* this is only for backward compatibility */
12388
12389     int original_move_delay_value = player->move_delay_value;
12390
12391 #if DEBUG
12392     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12393            tape.counter);
12394 #endif
12395
12396     /* scroll remaining steps with finest movement resolution */
12397     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12398
12399     while (player->MovPos)
12400     {
12401       ScrollPlayer(player, SCROLL_GO_ON);
12402       ScrollScreen(NULL, SCROLL_GO_ON);
12403
12404       AdvanceFrameAndPlayerCounters(player->index_nr);
12405
12406       DrawAllPlayers();
12407       BackToFront_WithFrameDelay(0);
12408     }
12409
12410     player->move_delay_value = original_move_delay_value;
12411   }
12412
12413   player->is_active = FALSE;
12414
12415   if (player->last_move_dir & MV_HORIZONTAL)
12416   {
12417     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12418       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12419   }
12420   else
12421   {
12422     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12423       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12424   }
12425
12426   if (!moved && !player->is_active)
12427   {
12428     player->is_moving = FALSE;
12429     player->is_digging = FALSE;
12430     player->is_collecting = FALSE;
12431     player->is_snapping = FALSE;
12432     player->is_pushing = FALSE;
12433   }
12434
12435   jx = player->jx;
12436   jy = player->jy;
12437
12438   if (moved & MP_MOVING && !ScreenMovPos &&
12439       (player->index_nr == game.centered_player_nr ||
12440        game.centered_player_nr == -1))
12441   {
12442     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12443     int offset = game.scroll_delay_value;
12444
12445     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12446     {
12447       /* actual player has left the screen -- scroll in that direction */
12448       if (jx != old_jx)         /* player has moved horizontally */
12449         scroll_x += (jx - old_jx);
12450       else                      /* player has moved vertically */
12451         scroll_y += (jy - old_jy);
12452     }
12453     else
12454     {
12455       if (jx != old_jx)         /* player has moved horizontally */
12456       {
12457         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12458             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12459           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12460
12461         /* don't scroll over playfield boundaries */
12462         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12463           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12464
12465         /* don't scroll more than one field at a time */
12466         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12467
12468         /* don't scroll against the player's moving direction */
12469         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12470             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12471           scroll_x = old_scroll_x;
12472       }
12473       else                      /* player has moved vertically */
12474       {
12475         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12476             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12477           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12478
12479         /* don't scroll over playfield boundaries */
12480         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12481           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12482
12483         /* don't scroll more than one field at a time */
12484         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12485
12486         /* don't scroll against the player's moving direction */
12487         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12488             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12489           scroll_y = old_scroll_y;
12490       }
12491     }
12492
12493     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12494     {
12495       if (!network.enabled && game.centered_player_nr == -1 &&
12496           !AllPlayersInVisibleScreen())
12497       {
12498         scroll_x = old_scroll_x;
12499         scroll_y = old_scroll_y;
12500       }
12501       else
12502       {
12503         ScrollScreen(player, SCROLL_INIT);
12504         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12505       }
12506     }
12507   }
12508
12509   player->StepFrame = 0;
12510
12511   if (moved & MP_MOVING)
12512   {
12513     if (old_jx != jx && old_jy == jy)
12514       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12515     else if (old_jx == jx && old_jy != jy)
12516       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12517
12518     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12519
12520     player->last_move_dir = player->MovDir;
12521     player->is_moving = TRUE;
12522     player->is_snapping = FALSE;
12523     player->is_switching = FALSE;
12524     player->is_dropping = FALSE;
12525     player->is_dropping_pressed = FALSE;
12526     player->drop_pressed_delay = 0;
12527
12528 #if 0
12529     /* should better be called here than above, but this breaks some tapes */
12530     ScrollPlayer(player, SCROLL_INIT);
12531 #endif
12532   }
12533   else
12534   {
12535     CheckGravityMovementWhenNotMoving(player);
12536
12537     player->is_moving = FALSE;
12538
12539     /* at this point, the player is allowed to move, but cannot move right now
12540        (e.g. because of something blocking the way) -- ensure that the player
12541        is also allowed to move in the next frame (in old versions before 3.1.1,
12542        the player was forced to wait again for eight frames before next try) */
12543
12544     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12545       player->move_delay = 0;   /* allow direct movement in the next frame */
12546   }
12547
12548   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12549     player->move_delay = player->move_delay_value;
12550
12551   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12552   {
12553     TestIfPlayerTouchesBadThing(jx, jy);
12554     TestIfPlayerTouchesCustomElement(jx, jy);
12555   }
12556
12557   if (!player->active)
12558     RemovePlayer(player);
12559
12560   return moved;
12561 }
12562
12563 void ScrollPlayer(struct PlayerInfo *player, int mode)
12564 {
12565   int jx = player->jx, jy = player->jy;
12566   int last_jx = player->last_jx, last_jy = player->last_jy;
12567   int move_stepsize = TILEX / player->move_delay_value;
12568
12569   if (!player->active)
12570     return;
12571
12572   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12573     return;
12574
12575   if (mode == SCROLL_INIT)
12576   {
12577     player->actual_frame_counter = FrameCounter;
12578     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12579
12580     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12581         Feld[last_jx][last_jy] == EL_EMPTY)
12582     {
12583       int last_field_block_delay = 0;   /* start with no blocking at all */
12584       int block_delay_adjustment = player->block_delay_adjustment;
12585
12586       /* if player blocks last field, add delay for exactly one move */
12587       if (player->block_last_field)
12588       {
12589         last_field_block_delay += player->move_delay_value;
12590
12591         /* when blocking enabled, prevent moving up despite gravity */
12592         if (player->gravity && player->MovDir == MV_UP)
12593           block_delay_adjustment = -1;
12594       }
12595
12596       /* add block delay adjustment (also possible when not blocking) */
12597       last_field_block_delay += block_delay_adjustment;
12598
12599       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12600       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12601     }
12602
12603     if (player->MovPos != 0)    /* player has not yet reached destination */
12604       return;
12605   }
12606   else if (!FrameReached(&player->actual_frame_counter, 1))
12607     return;
12608
12609   if (player->MovPos != 0)
12610   {
12611     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12612     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12613
12614     /* before DrawPlayer() to draw correct player graphic for this case */
12615     if (player->MovPos == 0)
12616       CheckGravityMovement(player);
12617   }
12618
12619   if (player->MovPos == 0)      /* player reached destination field */
12620   {
12621     if (player->move_delay_reset_counter > 0)
12622     {
12623       player->move_delay_reset_counter--;
12624
12625       if (player->move_delay_reset_counter == 0)
12626       {
12627         /* continue with normal speed after quickly moving through gate */
12628         HALVE_PLAYER_SPEED(player);
12629
12630         /* be able to make the next move without delay */
12631         player->move_delay = 0;
12632       }
12633     }
12634
12635     player->last_jx = jx;
12636     player->last_jy = jy;
12637
12638     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12639         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12640         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12641         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12642         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12643         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12644         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12645         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12646     {
12647       ExitPlayer(player);
12648
12649       if ((local_player->friends_still_needed == 0 ||
12650            IS_SP_ELEMENT(Feld[jx][jy])) &&
12651           AllPlayersGone)
12652         PlayerWins(local_player);
12653     }
12654
12655     /* this breaks one level: "machine", level 000 */
12656     {
12657       int move_direction = player->MovDir;
12658       int enter_side = MV_DIR_OPPOSITE(move_direction);
12659       int leave_side = move_direction;
12660       int old_jx = last_jx;
12661       int old_jy = last_jy;
12662       int old_element = Feld[old_jx][old_jy];
12663       int new_element = Feld[jx][jy];
12664
12665       if (IS_CUSTOM_ELEMENT(old_element))
12666         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12667                                    CE_LEFT_BY_PLAYER,
12668                                    player->index_bit, leave_side);
12669
12670       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12671                                           CE_PLAYER_LEAVES_X,
12672                                           player->index_bit, leave_side);
12673
12674       if (IS_CUSTOM_ELEMENT(new_element))
12675         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12676                                    player->index_bit, enter_side);
12677
12678       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12679                                           CE_PLAYER_ENTERS_X,
12680                                           player->index_bit, enter_side);
12681
12682       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12683                                         CE_MOVE_OF_X, move_direction);
12684     }
12685
12686     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12687     {
12688       TestIfPlayerTouchesBadThing(jx, jy);
12689       TestIfPlayerTouchesCustomElement(jx, jy);
12690
12691       /* needed because pushed element has not yet reached its destination,
12692          so it would trigger a change event at its previous field location */
12693       if (!player->is_pushing)
12694         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12695
12696       if (!player->active)
12697         RemovePlayer(player);
12698     }
12699
12700     if (!local_player->LevelSolved && level.use_step_counter)
12701     {
12702       int i;
12703
12704       TimePlayed++;
12705
12706       if (TimeLeft > 0)
12707       {
12708         TimeLeft--;
12709
12710         if (TimeLeft <= 10 && setup.time_limit)
12711           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12712
12713         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12714
12715         DisplayGameControlValues();
12716
12717         if (!TimeLeft && setup.time_limit)
12718           for (i = 0; i < MAX_PLAYERS; i++)
12719             KillPlayer(&stored_player[i]);
12720       }
12721       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12722       {
12723         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12724
12725         DisplayGameControlValues();
12726       }
12727     }
12728
12729     if (tape.single_step && tape.recording && !tape.pausing &&
12730         !player->programmed_action)
12731       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12732
12733     if (!player->programmed_action)
12734       CheckSaveEngineSnapshot(player);
12735   }
12736 }
12737
12738 void ScrollScreen(struct PlayerInfo *player, int mode)
12739 {
12740   static unsigned int screen_frame_counter = 0;
12741
12742   if (mode == SCROLL_INIT)
12743   {
12744     /* set scrolling step size according to actual player's moving speed */
12745     ScrollStepSize = TILEX / player->move_delay_value;
12746
12747     screen_frame_counter = FrameCounter;
12748     ScreenMovDir = player->MovDir;
12749     ScreenMovPos = player->MovPos;
12750     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12751     return;
12752   }
12753   else if (!FrameReached(&screen_frame_counter, 1))
12754     return;
12755
12756   if (ScreenMovPos)
12757   {
12758     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12759     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12760     redraw_mask |= REDRAW_FIELD;
12761   }
12762   else
12763     ScreenMovDir = MV_NONE;
12764 }
12765
12766 void TestIfPlayerTouchesCustomElement(int x, int y)
12767 {
12768   static int xy[4][2] =
12769   {
12770     { 0, -1 },
12771     { -1, 0 },
12772     { +1, 0 },
12773     { 0, +1 }
12774   };
12775   static int trigger_sides[4][2] =
12776   {
12777     /* center side       border side */
12778     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12779     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12780     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12781     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12782   };
12783   static int touch_dir[4] =
12784   {
12785     MV_LEFT | MV_RIGHT,
12786     MV_UP   | MV_DOWN,
12787     MV_UP   | MV_DOWN,
12788     MV_LEFT | MV_RIGHT
12789   };
12790   int center_element = Feld[x][y];      /* should always be non-moving! */
12791   int i;
12792
12793   for (i = 0; i < NUM_DIRECTIONS; i++)
12794   {
12795     int xx = x + xy[i][0];
12796     int yy = y + xy[i][1];
12797     int center_side = trigger_sides[i][0];
12798     int border_side = trigger_sides[i][1];
12799     int border_element;
12800
12801     if (!IN_LEV_FIELD(xx, yy))
12802       continue;
12803
12804     if (IS_PLAYER(x, y))                /* player found at center element */
12805     {
12806       struct PlayerInfo *player = PLAYERINFO(x, y);
12807
12808       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12809         border_element = Feld[xx][yy];          /* may be moving! */
12810       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12811         border_element = Feld[xx][yy];
12812       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12813         border_element = MovingOrBlocked2Element(xx, yy);
12814       else
12815         continue;               /* center and border element do not touch */
12816
12817       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12818                                  player->index_bit, border_side);
12819       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12820                                           CE_PLAYER_TOUCHES_X,
12821                                           player->index_bit, border_side);
12822
12823       {
12824         /* use player element that is initially defined in the level playfield,
12825            not the player element that corresponds to the runtime player number
12826            (example: a level that contains EL_PLAYER_3 as the only player would
12827            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12828         int player_element = PLAYERINFO(x, y)->initial_element;
12829
12830         CheckElementChangeBySide(xx, yy, border_element, player_element,
12831                                  CE_TOUCHING_X, border_side);
12832       }
12833     }
12834     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12835     {
12836       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12837
12838       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12839       {
12840         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12841           continue;             /* center and border element do not touch */
12842       }
12843
12844       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12845                                  player->index_bit, center_side);
12846       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12847                                           CE_PLAYER_TOUCHES_X,
12848                                           player->index_bit, center_side);
12849
12850       {
12851         /* use player element that is initially defined in the level playfield,
12852            not the player element that corresponds to the runtime player number
12853            (example: a level that contains EL_PLAYER_3 as the only player would
12854            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12855         int player_element = PLAYERINFO(xx, yy)->initial_element;
12856
12857         CheckElementChangeBySide(x, y, center_element, player_element,
12858                                  CE_TOUCHING_X, center_side);
12859       }
12860
12861       break;
12862     }
12863   }
12864 }
12865
12866 void TestIfElementTouchesCustomElement(int x, int y)
12867 {
12868   static int xy[4][2] =
12869   {
12870     { 0, -1 },
12871     { -1, 0 },
12872     { +1, 0 },
12873     { 0, +1 }
12874   };
12875   static int trigger_sides[4][2] =
12876   {
12877     /* center side      border side */
12878     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12879     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12880     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12881     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12882   };
12883   static int touch_dir[4] =
12884   {
12885     MV_LEFT | MV_RIGHT,
12886     MV_UP   | MV_DOWN,
12887     MV_UP   | MV_DOWN,
12888     MV_LEFT | MV_RIGHT
12889   };
12890   boolean change_center_element = FALSE;
12891   int center_element = Feld[x][y];      /* should always be non-moving! */
12892   int border_element_old[NUM_DIRECTIONS];
12893   int i;
12894
12895   for (i = 0; i < NUM_DIRECTIONS; i++)
12896   {
12897     int xx = x + xy[i][0];
12898     int yy = y + xy[i][1];
12899     int border_element;
12900
12901     border_element_old[i] = -1;
12902
12903     if (!IN_LEV_FIELD(xx, yy))
12904       continue;
12905
12906     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12907       border_element = Feld[xx][yy];    /* may be moving! */
12908     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12909       border_element = Feld[xx][yy];
12910     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12911       border_element = MovingOrBlocked2Element(xx, yy);
12912     else
12913       continue;                 /* center and border element do not touch */
12914
12915     border_element_old[i] = border_element;
12916   }
12917
12918   for (i = 0; i < NUM_DIRECTIONS; i++)
12919   {
12920     int xx = x + xy[i][0];
12921     int yy = y + xy[i][1];
12922     int center_side = trigger_sides[i][0];
12923     int border_element = border_element_old[i];
12924
12925     if (border_element == -1)
12926       continue;
12927
12928     /* check for change of border element */
12929     CheckElementChangeBySide(xx, yy, border_element, center_element,
12930                              CE_TOUCHING_X, center_side);
12931
12932     /* (center element cannot be player, so we dont have to check this here) */
12933   }
12934
12935   for (i = 0; i < NUM_DIRECTIONS; i++)
12936   {
12937     int xx = x + xy[i][0];
12938     int yy = y + xy[i][1];
12939     int border_side = trigger_sides[i][1];
12940     int border_element = border_element_old[i];
12941
12942     if (border_element == -1)
12943       continue;
12944
12945     /* check for change of center element (but change it only once) */
12946     if (!change_center_element)
12947       change_center_element =
12948         CheckElementChangeBySide(x, y, center_element, border_element,
12949                                  CE_TOUCHING_X, border_side);
12950
12951     if (IS_PLAYER(xx, yy))
12952     {
12953       /* use player element that is initially defined in the level playfield,
12954          not the player element that corresponds to the runtime player number
12955          (example: a level that contains EL_PLAYER_3 as the only player would
12956          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12957       int player_element = PLAYERINFO(xx, yy)->initial_element;
12958
12959       CheckElementChangeBySide(x, y, center_element, player_element,
12960                                CE_TOUCHING_X, border_side);
12961     }
12962   }
12963 }
12964
12965 void TestIfElementHitsCustomElement(int x, int y, int direction)
12966 {
12967   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12968   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12969   int hitx = x + dx, hity = y + dy;
12970   int hitting_element = Feld[x][y];
12971   int touched_element;
12972
12973   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12974     return;
12975
12976   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12977                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12978
12979   if (IN_LEV_FIELD(hitx, hity))
12980   {
12981     int opposite_direction = MV_DIR_OPPOSITE(direction);
12982     int hitting_side = direction;
12983     int touched_side = opposite_direction;
12984     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12985                           MovDir[hitx][hity] != direction ||
12986                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12987
12988     object_hit = TRUE;
12989
12990     if (object_hit)
12991     {
12992       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12993                                CE_HITTING_X, touched_side);
12994
12995       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12996                                CE_HIT_BY_X, hitting_side);
12997
12998       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12999                                CE_HIT_BY_SOMETHING, opposite_direction);
13000
13001       if (IS_PLAYER(hitx, hity))
13002       {
13003         /* use player element that is initially defined in the level playfield,
13004            not the player element that corresponds to the runtime player number
13005            (example: a level that contains EL_PLAYER_3 as the only player would
13006            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13007         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13008
13009         CheckElementChangeBySide(x, y, hitting_element, player_element,
13010                                  CE_HITTING_X, touched_side);
13011       }
13012     }
13013   }
13014
13015   /* "hitting something" is also true when hitting the playfield border */
13016   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13017                            CE_HITTING_SOMETHING, direction);
13018 }
13019
13020 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13021 {
13022   int i, kill_x = -1, kill_y = -1;
13023
13024   int bad_element = -1;
13025   static int test_xy[4][2] =
13026   {
13027     { 0, -1 },
13028     { -1, 0 },
13029     { +1, 0 },
13030     { 0, +1 }
13031   };
13032   static int test_dir[4] =
13033   {
13034     MV_UP,
13035     MV_LEFT,
13036     MV_RIGHT,
13037     MV_DOWN
13038   };
13039
13040   for (i = 0; i < NUM_DIRECTIONS; i++)
13041   {
13042     int test_x, test_y, test_move_dir, test_element;
13043
13044     test_x = good_x + test_xy[i][0];
13045     test_y = good_y + test_xy[i][1];
13046
13047     if (!IN_LEV_FIELD(test_x, test_y))
13048       continue;
13049
13050     test_move_dir =
13051       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13052
13053     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13054
13055     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13056        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13057     */
13058     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13059         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13060     {
13061       kill_x = test_x;
13062       kill_y = test_y;
13063       bad_element = test_element;
13064
13065       break;
13066     }
13067   }
13068
13069   if (kill_x != -1 || kill_y != -1)
13070   {
13071     if (IS_PLAYER(good_x, good_y))
13072     {
13073       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13074
13075       if (player->shield_deadly_time_left > 0 &&
13076           !IS_INDESTRUCTIBLE(bad_element))
13077         Bang(kill_x, kill_y);
13078       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13079         KillPlayer(player);
13080     }
13081     else
13082       Bang(good_x, good_y);
13083   }
13084 }
13085
13086 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13087 {
13088   int i, kill_x = -1, kill_y = -1;
13089   int bad_element = Feld[bad_x][bad_y];
13090   static int test_xy[4][2] =
13091   {
13092     { 0, -1 },
13093     { -1, 0 },
13094     { +1, 0 },
13095     { 0, +1 }
13096   };
13097   static int touch_dir[4] =
13098   {
13099     MV_LEFT | MV_RIGHT,
13100     MV_UP   | MV_DOWN,
13101     MV_UP   | MV_DOWN,
13102     MV_LEFT | MV_RIGHT
13103   };
13104   static int test_dir[4] =
13105   {
13106     MV_UP,
13107     MV_LEFT,
13108     MV_RIGHT,
13109     MV_DOWN
13110   };
13111
13112   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13113     return;
13114
13115   for (i = 0; i < NUM_DIRECTIONS; i++)
13116   {
13117     int test_x, test_y, test_move_dir, test_element;
13118
13119     test_x = bad_x + test_xy[i][0];
13120     test_y = bad_y + test_xy[i][1];
13121
13122     if (!IN_LEV_FIELD(test_x, test_y))
13123       continue;
13124
13125     test_move_dir =
13126       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13127
13128     test_element = Feld[test_x][test_y];
13129
13130     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13131        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13132     */
13133     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13134         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13135     {
13136       /* good thing is player or penguin that does not move away */
13137       if (IS_PLAYER(test_x, test_y))
13138       {
13139         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13140
13141         if (bad_element == EL_ROBOT && player->is_moving)
13142           continue;     /* robot does not kill player if he is moving */
13143
13144         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13145         {
13146           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13147             continue;           /* center and border element do not touch */
13148         }
13149
13150         kill_x = test_x;
13151         kill_y = test_y;
13152
13153         break;
13154       }
13155       else if (test_element == EL_PENGUIN)
13156       {
13157         kill_x = test_x;
13158         kill_y = test_y;
13159
13160         break;
13161       }
13162     }
13163   }
13164
13165   if (kill_x != -1 || kill_y != -1)
13166   {
13167     if (IS_PLAYER(kill_x, kill_y))
13168     {
13169       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13170
13171       if (player->shield_deadly_time_left > 0 &&
13172           !IS_INDESTRUCTIBLE(bad_element))
13173         Bang(bad_x, bad_y);
13174       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13175         KillPlayer(player);
13176     }
13177     else
13178       Bang(kill_x, kill_y);
13179   }
13180 }
13181
13182 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13183 {
13184   int bad_element = Feld[bad_x][bad_y];
13185   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13186   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13187   int test_x = bad_x + dx, test_y = bad_y + dy;
13188   int test_move_dir, test_element;
13189   int kill_x = -1, kill_y = -1;
13190
13191   if (!IN_LEV_FIELD(test_x, test_y))
13192     return;
13193
13194   test_move_dir =
13195     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13196
13197   test_element = Feld[test_x][test_y];
13198
13199   if (test_move_dir != bad_move_dir)
13200   {
13201     /* good thing can be player or penguin that does not move away */
13202     if (IS_PLAYER(test_x, test_y))
13203     {
13204       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13205
13206       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13207          player as being hit when he is moving towards the bad thing, because
13208          the "get hit by" condition would be lost after the player stops) */
13209       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13210         return;         /* player moves away from bad thing */
13211
13212       kill_x = test_x;
13213       kill_y = test_y;
13214     }
13215     else if (test_element == EL_PENGUIN)
13216     {
13217       kill_x = test_x;
13218       kill_y = test_y;
13219     }
13220   }
13221
13222   if (kill_x != -1 || kill_y != -1)
13223   {
13224     if (IS_PLAYER(kill_x, kill_y))
13225     {
13226       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13227
13228       if (player->shield_deadly_time_left > 0 &&
13229           !IS_INDESTRUCTIBLE(bad_element))
13230         Bang(bad_x, bad_y);
13231       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13232         KillPlayer(player);
13233     }
13234     else
13235       Bang(kill_x, kill_y);
13236   }
13237 }
13238
13239 void TestIfPlayerTouchesBadThing(int x, int y)
13240 {
13241   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13242 }
13243
13244 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13245 {
13246   TestIfGoodThingHitsBadThing(x, y, move_dir);
13247 }
13248
13249 void TestIfBadThingTouchesPlayer(int x, int y)
13250 {
13251   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13252 }
13253
13254 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13255 {
13256   TestIfBadThingHitsGoodThing(x, y, move_dir);
13257 }
13258
13259 void TestIfFriendTouchesBadThing(int x, int y)
13260 {
13261   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13262 }
13263
13264 void TestIfBadThingTouchesFriend(int x, int y)
13265 {
13266   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13267 }
13268
13269 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13270 {
13271   int i, kill_x = bad_x, kill_y = bad_y;
13272   static int xy[4][2] =
13273   {
13274     { 0, -1 },
13275     { -1, 0 },
13276     { +1, 0 },
13277     { 0, +1 }
13278   };
13279
13280   for (i = 0; i < NUM_DIRECTIONS; i++)
13281   {
13282     int x, y, element;
13283
13284     x = bad_x + xy[i][0];
13285     y = bad_y + xy[i][1];
13286     if (!IN_LEV_FIELD(x, y))
13287       continue;
13288
13289     element = Feld[x][y];
13290     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13291         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13292     {
13293       kill_x = x;
13294       kill_y = y;
13295       break;
13296     }
13297   }
13298
13299   if (kill_x != bad_x || kill_y != bad_y)
13300     Bang(bad_x, bad_y);
13301 }
13302
13303 void KillPlayer(struct PlayerInfo *player)
13304 {
13305   int jx = player->jx, jy = player->jy;
13306
13307   if (!player->active)
13308     return;
13309
13310 #if 0
13311   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13312          player->killed, player->active, player->reanimated);
13313 #endif
13314
13315   /* the following code was introduced to prevent an infinite loop when calling
13316      -> Bang()
13317      -> CheckTriggeredElementChangeExt()
13318      -> ExecuteCustomElementAction()
13319      -> KillPlayer()
13320      -> (infinitely repeating the above sequence of function calls)
13321      which occurs when killing the player while having a CE with the setting
13322      "kill player X when explosion of <player X>"; the solution using a new
13323      field "player->killed" was chosen for backwards compatibility, although
13324      clever use of the fields "player->active" etc. would probably also work */
13325 #if 1
13326   if (player->killed)
13327     return;
13328 #endif
13329
13330   player->killed = TRUE;
13331
13332   /* remove accessible field at the player's position */
13333   Feld[jx][jy] = EL_EMPTY;
13334
13335   /* deactivate shield (else Bang()/Explode() would not work right) */
13336   player->shield_normal_time_left = 0;
13337   player->shield_deadly_time_left = 0;
13338
13339 #if 0
13340   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13341          player->killed, player->active, player->reanimated);
13342 #endif
13343
13344   Bang(jx, jy);
13345
13346 #if 0
13347   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13348          player->killed, player->active, player->reanimated);
13349 #endif
13350
13351   if (player->reanimated)       /* killed player may have been reanimated */
13352     player->killed = player->reanimated = FALSE;
13353   else
13354     BuryPlayer(player);
13355 }
13356
13357 static void KillPlayerUnlessEnemyProtected(int x, int y)
13358 {
13359   if (!PLAYER_ENEMY_PROTECTED(x, y))
13360     KillPlayer(PLAYERINFO(x, y));
13361 }
13362
13363 static void KillPlayerUnlessExplosionProtected(int x, int y)
13364 {
13365   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13366     KillPlayer(PLAYERINFO(x, y));
13367 }
13368
13369 void BuryPlayer(struct PlayerInfo *player)
13370 {
13371   int jx = player->jx, jy = player->jy;
13372
13373   if (!player->active)
13374     return;
13375
13376   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13377   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13378
13379   player->GameOver = TRUE;
13380   RemovePlayer(player);
13381 }
13382
13383 void RemovePlayer(struct PlayerInfo *player)
13384 {
13385   int jx = player->jx, jy = player->jy;
13386   int i, found = FALSE;
13387
13388   player->present = FALSE;
13389   player->active = FALSE;
13390
13391   if (!ExplodeField[jx][jy])
13392     StorePlayer[jx][jy] = 0;
13393
13394   if (player->is_moving)
13395     TEST_DrawLevelField(player->last_jx, player->last_jy);
13396
13397   for (i = 0; i < MAX_PLAYERS; i++)
13398     if (stored_player[i].active)
13399       found = TRUE;
13400
13401   if (!found)
13402     AllPlayersGone = TRUE;
13403
13404   ExitX = ZX = jx;
13405   ExitY = ZY = jy;
13406 }
13407
13408 void ExitPlayer(struct PlayerInfo *player)
13409 {
13410   DrawPlayer(player);   /* needed here only to cleanup last field */
13411   RemovePlayer(player);
13412
13413   if (local_player->players_still_needed > 0)
13414     local_player->players_still_needed--;
13415
13416   /* also set if some players not yet gone, but not needed to solve level */
13417   if (local_player->players_still_needed == 0)
13418     AllPlayersGone = TRUE;
13419 }
13420
13421 static void setFieldForSnapping(int x, int y, int element, int direction)
13422 {
13423   struct ElementInfo *ei = &element_info[element];
13424   int direction_bit = MV_DIR_TO_BIT(direction);
13425   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13426   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13427                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13428
13429   Feld[x][y] = EL_ELEMENT_SNAPPING;
13430   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13431
13432   ResetGfxAnimation(x, y);
13433
13434   GfxElement[x][y] = element;
13435   GfxAction[x][y] = action;
13436   GfxDir[x][y] = direction;
13437   GfxFrame[x][y] = -1;
13438 }
13439
13440 /*
13441   =============================================================================
13442   checkDiagonalPushing()
13443   -----------------------------------------------------------------------------
13444   check if diagonal input device direction results in pushing of object
13445   (by checking if the alternative direction is walkable, diggable, ...)
13446   =============================================================================
13447 */
13448
13449 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13450                                     int x, int y, int real_dx, int real_dy)
13451 {
13452   int jx, jy, dx, dy, xx, yy;
13453
13454   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13455     return TRUE;
13456
13457   /* diagonal direction: check alternative direction */
13458   jx = player->jx;
13459   jy = player->jy;
13460   dx = x - jx;
13461   dy = y - jy;
13462   xx = jx + (dx == 0 ? real_dx : 0);
13463   yy = jy + (dy == 0 ? real_dy : 0);
13464
13465   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13466 }
13467
13468 /*
13469   =============================================================================
13470   DigField()
13471   -----------------------------------------------------------------------------
13472   x, y:                 field next to player (non-diagonal) to try to dig to
13473   real_dx, real_dy:     direction as read from input device (can be diagonal)
13474   =============================================================================
13475 */
13476
13477 static int DigField(struct PlayerInfo *player,
13478                     int oldx, int oldy, int x, int y,
13479                     int real_dx, int real_dy, int mode)
13480 {
13481   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13482   boolean player_was_pushing = player->is_pushing;
13483   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13484   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13485   int jx = oldx, jy = oldy;
13486   int dx = x - jx, dy = y - jy;
13487   int nextx = x + dx, nexty = y + dy;
13488   int move_direction = (dx == -1 ? MV_LEFT  :
13489                         dx == +1 ? MV_RIGHT :
13490                         dy == -1 ? MV_UP    :
13491                         dy == +1 ? MV_DOWN  : MV_NONE);
13492   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13493   int dig_side = MV_DIR_OPPOSITE(move_direction);
13494   int old_element = Feld[jx][jy];
13495   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13496   int collect_count;
13497
13498   if (is_player)                /* function can also be called by EL_PENGUIN */
13499   {
13500     if (player->MovPos == 0)
13501     {
13502       player->is_digging = FALSE;
13503       player->is_collecting = FALSE;
13504     }
13505
13506     if (player->MovPos == 0)    /* last pushing move finished */
13507       player->is_pushing = FALSE;
13508
13509     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13510     {
13511       player->is_switching = FALSE;
13512       player->push_delay = -1;
13513
13514       return MP_NO_ACTION;
13515     }
13516   }
13517
13518   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13519     old_element = Back[jx][jy];
13520
13521   /* in case of element dropped at player position, check background */
13522   else if (Back[jx][jy] != EL_EMPTY &&
13523            game.engine_version >= VERSION_IDENT(2,2,0,0))
13524     old_element = Back[jx][jy];
13525
13526   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13527     return MP_NO_ACTION;        /* field has no opening in this direction */
13528
13529   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13530     return MP_NO_ACTION;        /* field has no opening in this direction */
13531
13532   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13533   {
13534     SplashAcid(x, y);
13535
13536     Feld[jx][jy] = player->artwork_element;
13537     InitMovingField(jx, jy, MV_DOWN);
13538     Store[jx][jy] = EL_ACID;
13539     ContinueMoving(jx, jy);
13540     BuryPlayer(player);
13541
13542     return MP_DONT_RUN_INTO;
13543   }
13544
13545   if (player_can_move && DONT_RUN_INTO(element))
13546   {
13547     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13548
13549     return MP_DONT_RUN_INTO;
13550   }
13551
13552   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13553     return MP_NO_ACTION;
13554
13555   collect_count = element_info[element].collect_count_initial;
13556
13557   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13558     return MP_NO_ACTION;
13559
13560   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13561     player_can_move = player_can_move_or_snap;
13562
13563   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13564       game.engine_version >= VERSION_IDENT(2,2,0,0))
13565   {
13566     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13567                                player->index_bit, dig_side);
13568     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13569                                         player->index_bit, dig_side);
13570
13571     if (element == EL_DC_LANDMINE)
13572       Bang(x, y);
13573
13574     if (Feld[x][y] != element)          /* field changed by snapping */
13575       return MP_ACTION;
13576
13577     return MP_NO_ACTION;
13578   }
13579
13580   if (player->gravity && is_player && !player->is_auto_moving &&
13581       canFallDown(player) && move_direction != MV_DOWN &&
13582       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13583     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13584
13585   if (player_can_move &&
13586       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13587   {
13588     int sound_element = SND_ELEMENT(element);
13589     int sound_action = ACTION_WALKING;
13590
13591     if (IS_RND_GATE(element))
13592     {
13593       if (!player->key[RND_GATE_NR(element)])
13594         return MP_NO_ACTION;
13595     }
13596     else if (IS_RND_GATE_GRAY(element))
13597     {
13598       if (!player->key[RND_GATE_GRAY_NR(element)])
13599         return MP_NO_ACTION;
13600     }
13601     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13602     {
13603       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13604         return MP_NO_ACTION;
13605     }
13606     else if (element == EL_EXIT_OPEN ||
13607              element == EL_EM_EXIT_OPEN ||
13608              element == EL_EM_EXIT_OPENING ||
13609              element == EL_STEEL_EXIT_OPEN ||
13610              element == EL_EM_STEEL_EXIT_OPEN ||
13611              element == EL_EM_STEEL_EXIT_OPENING ||
13612              element == EL_SP_EXIT_OPEN ||
13613              element == EL_SP_EXIT_OPENING)
13614     {
13615       sound_action = ACTION_PASSING;    /* player is passing exit */
13616     }
13617     else if (element == EL_EMPTY)
13618     {
13619       sound_action = ACTION_MOVING;             /* nothing to walk on */
13620     }
13621
13622     /* play sound from background or player, whatever is available */
13623     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13624       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13625     else
13626       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13627   }
13628   else if (player_can_move &&
13629            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13630   {
13631     if (!ACCESS_FROM(element, opposite_direction))
13632       return MP_NO_ACTION;      /* field not accessible from this direction */
13633
13634     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13635       return MP_NO_ACTION;
13636
13637     if (IS_EM_GATE(element))
13638     {
13639       if (!player->key[EM_GATE_NR(element)])
13640         return MP_NO_ACTION;
13641     }
13642     else if (IS_EM_GATE_GRAY(element))
13643     {
13644       if (!player->key[EM_GATE_GRAY_NR(element)])
13645         return MP_NO_ACTION;
13646     }
13647     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13648     {
13649       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13650         return MP_NO_ACTION;
13651     }
13652     else if (IS_EMC_GATE(element))
13653     {
13654       if (!player->key[EMC_GATE_NR(element)])
13655         return MP_NO_ACTION;
13656     }
13657     else if (IS_EMC_GATE_GRAY(element))
13658     {
13659       if (!player->key[EMC_GATE_GRAY_NR(element)])
13660         return MP_NO_ACTION;
13661     }
13662     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13663     {
13664       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13665         return MP_NO_ACTION;
13666     }
13667     else if (element == EL_DC_GATE_WHITE ||
13668              element == EL_DC_GATE_WHITE_GRAY ||
13669              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13670     {
13671       if (player->num_white_keys == 0)
13672         return MP_NO_ACTION;
13673
13674       player->num_white_keys--;
13675     }
13676     else if (IS_SP_PORT(element))
13677     {
13678       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13679           element == EL_SP_GRAVITY_PORT_RIGHT ||
13680           element == EL_SP_GRAVITY_PORT_UP ||
13681           element == EL_SP_GRAVITY_PORT_DOWN)
13682         player->gravity = !player->gravity;
13683       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13684                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13685                element == EL_SP_GRAVITY_ON_PORT_UP ||
13686                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13687         player->gravity = TRUE;
13688       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13689                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13690                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13691                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13692         player->gravity = FALSE;
13693     }
13694
13695     /* automatically move to the next field with double speed */
13696     player->programmed_action = move_direction;
13697
13698     if (player->move_delay_reset_counter == 0)
13699     {
13700       player->move_delay_reset_counter = 2;     /* two double speed steps */
13701
13702       DOUBLE_PLAYER_SPEED(player);
13703     }
13704
13705     PlayLevelSoundAction(x, y, ACTION_PASSING);
13706   }
13707   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13708   {
13709     RemoveField(x, y);
13710
13711     if (mode != DF_SNAP)
13712     {
13713       GfxElement[x][y] = GFX_ELEMENT(element);
13714       player->is_digging = TRUE;
13715     }
13716
13717     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13718
13719     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13720                                         player->index_bit, dig_side);
13721
13722     if (mode == DF_SNAP)
13723     {
13724       if (level.block_snap_field)
13725         setFieldForSnapping(x, y, element, move_direction);
13726       else
13727         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13728
13729       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13730                                           player->index_bit, dig_side);
13731     }
13732   }
13733   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13734   {
13735     RemoveField(x, y);
13736
13737     if (is_player && mode != DF_SNAP)
13738     {
13739       GfxElement[x][y] = element;
13740       player->is_collecting = TRUE;
13741     }
13742
13743     if (element == EL_SPEED_PILL)
13744     {
13745       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13746     }
13747     else if (element == EL_EXTRA_TIME && level.time > 0)
13748     {
13749       TimeLeft += level.extra_time;
13750
13751       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13752
13753       DisplayGameControlValues();
13754     }
13755     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13756     {
13757       player->shield_normal_time_left += level.shield_normal_time;
13758       if (element == EL_SHIELD_DEADLY)
13759         player->shield_deadly_time_left += level.shield_deadly_time;
13760     }
13761     else if (element == EL_DYNAMITE ||
13762              element == EL_EM_DYNAMITE ||
13763              element == EL_SP_DISK_RED)
13764     {
13765       if (player->inventory_size < MAX_INVENTORY_SIZE)
13766         player->inventory_element[player->inventory_size++] = element;
13767
13768       DrawGameDoorValues();
13769     }
13770     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13771     {
13772       player->dynabomb_count++;
13773       player->dynabombs_left++;
13774     }
13775     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13776     {
13777       player->dynabomb_size++;
13778     }
13779     else if (element == EL_DYNABOMB_INCREASE_POWER)
13780     {
13781       player->dynabomb_xl = TRUE;
13782     }
13783     else if (IS_KEY(element))
13784     {
13785       player->key[KEY_NR(element)] = TRUE;
13786
13787       DrawGameDoorValues();
13788     }
13789     else if (element == EL_DC_KEY_WHITE)
13790     {
13791       player->num_white_keys++;
13792
13793       /* display white keys? */
13794       /* DrawGameDoorValues(); */
13795     }
13796     else if (IS_ENVELOPE(element))
13797     {
13798       player->show_envelope = element;
13799     }
13800     else if (element == EL_EMC_LENSES)
13801     {
13802       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13803
13804       RedrawAllInvisibleElementsForLenses();
13805     }
13806     else if (element == EL_EMC_MAGNIFIER)
13807     {
13808       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13809
13810       RedrawAllInvisibleElementsForMagnifier();
13811     }
13812     else if (IS_DROPPABLE(element) ||
13813              IS_THROWABLE(element))     /* can be collected and dropped */
13814     {
13815       int i;
13816
13817       if (collect_count == 0)
13818         player->inventory_infinite_element = element;
13819       else
13820         for (i = 0; i < collect_count; i++)
13821           if (player->inventory_size < MAX_INVENTORY_SIZE)
13822             player->inventory_element[player->inventory_size++] = element;
13823
13824       DrawGameDoorValues();
13825     }
13826     else if (collect_count > 0)
13827     {
13828       local_player->gems_still_needed -= collect_count;
13829       if (local_player->gems_still_needed < 0)
13830         local_player->gems_still_needed = 0;
13831
13832       game.snapshot.collected_item = TRUE;
13833
13834       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13835
13836       DisplayGameControlValues();
13837     }
13838
13839     RaiseScoreElement(element);
13840     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13841
13842     if (is_player)
13843       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13844                                           player->index_bit, dig_side);
13845
13846     if (mode == DF_SNAP)
13847     {
13848       if (level.block_snap_field)
13849         setFieldForSnapping(x, y, element, move_direction);
13850       else
13851         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13852
13853       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13854                                           player->index_bit, dig_side);
13855     }
13856   }
13857   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13858   {
13859     if (mode == DF_SNAP && element != EL_BD_ROCK)
13860       return MP_NO_ACTION;
13861
13862     if (CAN_FALL(element) && dy)
13863       return MP_NO_ACTION;
13864
13865     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13866         !(element == EL_SPRING && level.use_spring_bug))
13867       return MP_NO_ACTION;
13868
13869     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13870         ((move_direction & MV_VERTICAL &&
13871           ((element_info[element].move_pattern & MV_LEFT &&
13872             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13873            (element_info[element].move_pattern & MV_RIGHT &&
13874             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13875          (move_direction & MV_HORIZONTAL &&
13876           ((element_info[element].move_pattern & MV_UP &&
13877             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13878            (element_info[element].move_pattern & MV_DOWN &&
13879             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13880       return MP_NO_ACTION;
13881
13882     /* do not push elements already moving away faster than player */
13883     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13884         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13885       return MP_NO_ACTION;
13886
13887     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13888     {
13889       if (player->push_delay_value == -1 || !player_was_pushing)
13890         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13891     }
13892     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13893     {
13894       if (player->push_delay_value == -1)
13895         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13896     }
13897     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13898     {
13899       if (!player->is_pushing)
13900         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13901     }
13902
13903     player->is_pushing = TRUE;
13904     player->is_active = TRUE;
13905
13906     if (!(IN_LEV_FIELD(nextx, nexty) &&
13907           (IS_FREE(nextx, nexty) ||
13908            (IS_SB_ELEMENT(element) &&
13909             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13910            (IS_CUSTOM_ELEMENT(element) &&
13911             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13912       return MP_NO_ACTION;
13913
13914     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13915       return MP_NO_ACTION;
13916
13917     if (player->push_delay == -1)       /* new pushing; restart delay */
13918       player->push_delay = 0;
13919
13920     if (player->push_delay < player->push_delay_value &&
13921         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13922         element != EL_SPRING && element != EL_BALLOON)
13923     {
13924       /* make sure that there is no move delay before next try to push */
13925       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13926         player->move_delay = 0;
13927
13928       return MP_NO_ACTION;
13929     }
13930
13931     if (IS_CUSTOM_ELEMENT(element) &&
13932         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13933     {
13934       if (!DigFieldByCE(nextx, nexty, element))
13935         return MP_NO_ACTION;
13936     }
13937
13938     if (IS_SB_ELEMENT(element))
13939     {
13940       if (element == EL_SOKOBAN_FIELD_FULL)
13941       {
13942         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13943         local_player->sokobanfields_still_needed++;
13944       }
13945
13946       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13947       {
13948         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13949         local_player->sokobanfields_still_needed--;
13950       }
13951
13952       Feld[x][y] = EL_SOKOBAN_OBJECT;
13953
13954       if (Back[x][y] == Back[nextx][nexty])
13955         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13956       else if (Back[x][y] != 0)
13957         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13958                                     ACTION_EMPTYING);
13959       else
13960         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13961                                     ACTION_FILLING);
13962
13963       if (local_player->sokobanfields_still_needed == 0 &&
13964           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13965       {
13966         local_player->players_still_needed = 0;
13967
13968         PlayerWins(player);
13969
13970         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13971       }
13972     }
13973     else
13974       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13975
13976     InitMovingField(x, y, move_direction);
13977     GfxAction[x][y] = ACTION_PUSHING;
13978
13979     if (mode == DF_SNAP)
13980       ContinueMoving(x, y);
13981     else
13982       MovPos[x][y] = (dx != 0 ? dx : dy);
13983
13984     Pushed[x][y] = TRUE;
13985     Pushed[nextx][nexty] = TRUE;
13986
13987     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13988       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13989     else
13990       player->push_delay_value = -1;    /* get new value later */
13991
13992     /* check for element change _after_ element has been pushed */
13993     if (game.use_change_when_pushing_bug)
13994     {
13995       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13996                                  player->index_bit, dig_side);
13997       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13998                                           player->index_bit, dig_side);
13999     }
14000   }
14001   else if (IS_SWITCHABLE(element))
14002   {
14003     if (PLAYER_SWITCHING(player, x, y))
14004     {
14005       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14006                                           player->index_bit, dig_side);
14007
14008       return MP_ACTION;
14009     }
14010
14011     player->is_switching = TRUE;
14012     player->switch_x = x;
14013     player->switch_y = y;
14014
14015     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14016
14017     if (element == EL_ROBOT_WHEEL)
14018     {
14019       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14020       ZX = x;
14021       ZY = y;
14022
14023       game.robot_wheel_active = TRUE;
14024
14025       TEST_DrawLevelField(x, y);
14026     }
14027     else if (element == EL_SP_TERMINAL)
14028     {
14029       int xx, yy;
14030
14031       SCAN_PLAYFIELD(xx, yy)
14032       {
14033         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14034         {
14035           Bang(xx, yy);
14036         }
14037         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14038         {
14039           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14040
14041           ResetGfxAnimation(xx, yy);
14042           TEST_DrawLevelField(xx, yy);
14043         }
14044       }
14045     }
14046     else if (IS_BELT_SWITCH(element))
14047     {
14048       ToggleBeltSwitch(x, y);
14049     }
14050     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14051              element == EL_SWITCHGATE_SWITCH_DOWN ||
14052              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14053              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14054     {
14055       ToggleSwitchgateSwitch(x, y);
14056     }
14057     else if (element == EL_LIGHT_SWITCH ||
14058              element == EL_LIGHT_SWITCH_ACTIVE)
14059     {
14060       ToggleLightSwitch(x, y);
14061     }
14062     else if (element == EL_TIMEGATE_SWITCH ||
14063              element == EL_DC_TIMEGATE_SWITCH)
14064     {
14065       ActivateTimegateSwitch(x, y);
14066     }
14067     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14068              element == EL_BALLOON_SWITCH_RIGHT ||
14069              element == EL_BALLOON_SWITCH_UP    ||
14070              element == EL_BALLOON_SWITCH_DOWN  ||
14071              element == EL_BALLOON_SWITCH_NONE  ||
14072              element == EL_BALLOON_SWITCH_ANY)
14073     {
14074       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14075                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14076                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14077                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14078                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14079                              move_direction);
14080     }
14081     else if (element == EL_LAMP)
14082     {
14083       Feld[x][y] = EL_LAMP_ACTIVE;
14084       local_player->lights_still_needed--;
14085
14086       ResetGfxAnimation(x, y);
14087       TEST_DrawLevelField(x, y);
14088     }
14089     else if (element == EL_TIME_ORB_FULL)
14090     {
14091       Feld[x][y] = EL_TIME_ORB_EMPTY;
14092
14093       if (level.time > 0 || level.use_time_orb_bug)
14094       {
14095         TimeLeft += level.time_orb_time;
14096         game.no_time_limit = FALSE;
14097
14098         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14099
14100         DisplayGameControlValues();
14101       }
14102
14103       ResetGfxAnimation(x, y);
14104       TEST_DrawLevelField(x, y);
14105     }
14106     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14107              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14108     {
14109       int xx, yy;
14110
14111       game.ball_state = !game.ball_state;
14112
14113       SCAN_PLAYFIELD(xx, yy)
14114       {
14115         int e = Feld[xx][yy];
14116
14117         if (game.ball_state)
14118         {
14119           if (e == EL_EMC_MAGIC_BALL)
14120             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14121           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14122             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14123         }
14124         else
14125         {
14126           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14127             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14128           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14129             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14130         }
14131       }
14132     }
14133
14134     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14135                                         player->index_bit, dig_side);
14136
14137     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14138                                         player->index_bit, dig_side);
14139
14140     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14141                                         player->index_bit, dig_side);
14142
14143     return MP_ACTION;
14144   }
14145   else
14146   {
14147     if (!PLAYER_SWITCHING(player, x, y))
14148     {
14149       player->is_switching = TRUE;
14150       player->switch_x = x;
14151       player->switch_y = y;
14152
14153       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14154                                  player->index_bit, dig_side);
14155       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14156                                           player->index_bit, dig_side);
14157
14158       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14159                                  player->index_bit, dig_side);
14160       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14161                                           player->index_bit, dig_side);
14162     }
14163
14164     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14165                                player->index_bit, dig_side);
14166     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14167                                         player->index_bit, dig_side);
14168
14169     return MP_NO_ACTION;
14170   }
14171
14172   player->push_delay = -1;
14173
14174   if (is_player)                /* function can also be called by EL_PENGUIN */
14175   {
14176     if (Feld[x][y] != element)          /* really digged/collected something */
14177     {
14178       player->is_collecting = !player->is_digging;
14179       player->is_active = TRUE;
14180     }
14181   }
14182
14183   return MP_MOVING;
14184 }
14185
14186 static boolean DigFieldByCE(int x, int y, int digging_element)
14187 {
14188   int element = Feld[x][y];
14189
14190   if (!IS_FREE(x, y))
14191   {
14192     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14193                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14194                   ACTION_BREAKING);
14195
14196     /* no element can dig solid indestructible elements */
14197     if (IS_INDESTRUCTIBLE(element) &&
14198         !IS_DIGGABLE(element) &&
14199         !IS_COLLECTIBLE(element))
14200       return FALSE;
14201
14202     if (AmoebaNr[x][y] &&
14203         (element == EL_AMOEBA_FULL ||
14204          element == EL_BD_AMOEBA ||
14205          element == EL_AMOEBA_GROWING))
14206     {
14207       AmoebaCnt[AmoebaNr[x][y]]--;
14208       AmoebaCnt2[AmoebaNr[x][y]]--;
14209     }
14210
14211     if (IS_MOVING(x, y))
14212       RemoveMovingField(x, y);
14213     else
14214     {
14215       RemoveField(x, y);
14216       TEST_DrawLevelField(x, y);
14217     }
14218
14219     /* if digged element was about to explode, prevent the explosion */
14220     ExplodeField[x][y] = EX_TYPE_NONE;
14221
14222     PlayLevelSoundAction(x, y, action);
14223   }
14224
14225   Store[x][y] = EL_EMPTY;
14226
14227   /* this makes it possible to leave the removed element again */
14228   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14229     Store[x][y] = element;
14230
14231   return TRUE;
14232 }
14233
14234 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14235 {
14236   int jx = player->jx, jy = player->jy;
14237   int x = jx + dx, y = jy + dy;
14238   int snap_direction = (dx == -1 ? MV_LEFT  :
14239                         dx == +1 ? MV_RIGHT :
14240                         dy == -1 ? MV_UP    :
14241                         dy == +1 ? MV_DOWN  : MV_NONE);
14242   boolean can_continue_snapping = (level.continuous_snapping &&
14243                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14244
14245   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14246     return FALSE;
14247
14248   if (!player->active || !IN_LEV_FIELD(x, y))
14249     return FALSE;
14250
14251   if (dx && dy)
14252     return FALSE;
14253
14254   if (!dx && !dy)
14255   {
14256     if (player->MovPos == 0)
14257       player->is_pushing = FALSE;
14258
14259     player->is_snapping = FALSE;
14260
14261     if (player->MovPos == 0)
14262     {
14263       player->is_moving = FALSE;
14264       player->is_digging = FALSE;
14265       player->is_collecting = FALSE;
14266     }
14267
14268     return FALSE;
14269   }
14270
14271   /* prevent snapping with already pressed snap key when not allowed */
14272   if (player->is_snapping && !can_continue_snapping)
14273     return FALSE;
14274
14275   player->MovDir = snap_direction;
14276
14277   if (player->MovPos == 0)
14278   {
14279     player->is_moving = FALSE;
14280     player->is_digging = FALSE;
14281     player->is_collecting = FALSE;
14282   }
14283
14284   player->is_dropping = FALSE;
14285   player->is_dropping_pressed = FALSE;
14286   player->drop_pressed_delay = 0;
14287
14288   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14289     return FALSE;
14290
14291   player->is_snapping = TRUE;
14292   player->is_active = TRUE;
14293
14294   if (player->MovPos == 0)
14295   {
14296     player->is_moving = FALSE;
14297     player->is_digging = FALSE;
14298     player->is_collecting = FALSE;
14299   }
14300
14301   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14302     TEST_DrawLevelField(player->last_jx, player->last_jy);
14303
14304   TEST_DrawLevelField(x, y);
14305
14306   return TRUE;
14307 }
14308
14309 static boolean DropElement(struct PlayerInfo *player)
14310 {
14311   int old_element, new_element;
14312   int dropx = player->jx, dropy = player->jy;
14313   int drop_direction = player->MovDir;
14314   int drop_side = drop_direction;
14315   int drop_element = get_next_dropped_element(player);
14316
14317   /* do not drop an element on top of another element; when holding drop key
14318      pressed without moving, dropped element must move away before the next
14319      element can be dropped (this is especially important if the next element
14320      is dynamite, which can be placed on background for historical reasons) */
14321   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14322     return MP_ACTION;
14323
14324   if (IS_THROWABLE(drop_element))
14325   {
14326     dropx += GET_DX_FROM_DIR(drop_direction);
14327     dropy += GET_DY_FROM_DIR(drop_direction);
14328
14329     if (!IN_LEV_FIELD(dropx, dropy))
14330       return FALSE;
14331   }
14332
14333   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14334   new_element = drop_element;           /* default: no change when dropping */
14335
14336   /* check if player is active, not moving and ready to drop */
14337   if (!player->active || player->MovPos || player->drop_delay > 0)
14338     return FALSE;
14339
14340   /* check if player has anything that can be dropped */
14341   if (new_element == EL_UNDEFINED)
14342     return FALSE;
14343
14344   /* only set if player has anything that can be dropped */
14345   player->is_dropping_pressed = TRUE;
14346
14347   /* check if drop key was pressed long enough for EM style dynamite */
14348   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14349     return FALSE;
14350
14351   /* check if anything can be dropped at the current position */
14352   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14353     return FALSE;
14354
14355   /* collected custom elements can only be dropped on empty fields */
14356   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14357     return FALSE;
14358
14359   if (old_element != EL_EMPTY)
14360     Back[dropx][dropy] = old_element;   /* store old element on this field */
14361
14362   ResetGfxAnimation(dropx, dropy);
14363   ResetRandomAnimationValue(dropx, dropy);
14364
14365   if (player->inventory_size > 0 ||
14366       player->inventory_infinite_element != EL_UNDEFINED)
14367   {
14368     if (player->inventory_size > 0)
14369     {
14370       player->inventory_size--;
14371
14372       DrawGameDoorValues();
14373
14374       if (new_element == EL_DYNAMITE)
14375         new_element = EL_DYNAMITE_ACTIVE;
14376       else if (new_element == EL_EM_DYNAMITE)
14377         new_element = EL_EM_DYNAMITE_ACTIVE;
14378       else if (new_element == EL_SP_DISK_RED)
14379         new_element = EL_SP_DISK_RED_ACTIVE;
14380     }
14381
14382     Feld[dropx][dropy] = new_element;
14383
14384     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14385       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14386                           el2img(Feld[dropx][dropy]), 0);
14387
14388     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14389
14390     /* needed if previous element just changed to "empty" in the last frame */
14391     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14392
14393     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14394                                player->index_bit, drop_side);
14395     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14396                                         CE_PLAYER_DROPS_X,
14397                                         player->index_bit, drop_side);
14398
14399     TestIfElementTouchesCustomElement(dropx, dropy);
14400   }
14401   else          /* player is dropping a dyna bomb */
14402   {
14403     player->dynabombs_left--;
14404
14405     Feld[dropx][dropy] = new_element;
14406
14407     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14408       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14409                           el2img(Feld[dropx][dropy]), 0);
14410
14411     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14412   }
14413
14414   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14415     InitField_WithBug1(dropx, dropy, FALSE);
14416
14417   new_element = Feld[dropx][dropy];     /* element might have changed */
14418
14419   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14420       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14421   {
14422     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14423       MovDir[dropx][dropy] = drop_direction;
14424
14425     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14426
14427     /* do not cause impact style collision by dropping elements that can fall */
14428     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14429   }
14430
14431   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14432   player->is_dropping = TRUE;
14433
14434   player->drop_pressed_delay = 0;
14435   player->is_dropping_pressed = FALSE;
14436
14437   player->drop_x = dropx;
14438   player->drop_y = dropy;
14439
14440   return TRUE;
14441 }
14442
14443 /* ------------------------------------------------------------------------- */
14444 /* game sound playing functions                                              */
14445 /* ------------------------------------------------------------------------- */
14446
14447 static int *loop_sound_frame = NULL;
14448 static int *loop_sound_volume = NULL;
14449
14450 void InitPlayLevelSound(void)
14451 {
14452   int num_sounds = getSoundListSize();
14453
14454   checked_free(loop_sound_frame);
14455   checked_free(loop_sound_volume);
14456
14457   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14458   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14459 }
14460
14461 static void PlayLevelSound(int x, int y, int nr)
14462 {
14463   int sx = SCREENX(x), sy = SCREENY(y);
14464   int volume, stereo_position;
14465   int max_distance = 8;
14466   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14467
14468   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14469       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14470     return;
14471
14472   if (!IN_LEV_FIELD(x, y) ||
14473       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14474       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14475     return;
14476
14477   volume = SOUND_MAX_VOLUME;
14478
14479   if (!IN_SCR_FIELD(sx, sy))
14480   {
14481     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14482     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14483
14484     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14485   }
14486
14487   stereo_position = (SOUND_MAX_LEFT +
14488                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14489                      (SCR_FIELDX + 2 * max_distance));
14490
14491   if (IS_LOOP_SOUND(nr))
14492   {
14493     /* This assures that quieter loop sounds do not overwrite louder ones,
14494        while restarting sound volume comparison with each new game frame. */
14495
14496     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14497       return;
14498
14499     loop_sound_volume[nr] = volume;
14500     loop_sound_frame[nr] = FrameCounter;
14501   }
14502
14503   PlaySoundExt(nr, volume, stereo_position, type);
14504 }
14505
14506 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14507 {
14508   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14509                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14510                  y < LEVELY(BY1) ? LEVELY(BY1) :
14511                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14512                  sound_action);
14513 }
14514
14515 static void PlayLevelSoundAction(int x, int y, int action)
14516 {
14517   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14518 }
14519
14520 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14521 {
14522   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14523
14524   if (sound_effect != SND_UNDEFINED)
14525     PlayLevelSound(x, y, sound_effect);
14526 }
14527
14528 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14529                                               int action)
14530 {
14531   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14532
14533   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14534     PlayLevelSound(x, y, sound_effect);
14535 }
14536
14537 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14538 {
14539   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14540
14541   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14542     PlayLevelSound(x, y, sound_effect);
14543 }
14544
14545 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14546 {
14547   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14548
14549   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14550     StopSound(sound_effect);
14551 }
14552
14553 static int getLevelMusicNr(void)
14554 {
14555   if (levelset.music[level_nr] != MUS_UNDEFINED)
14556     return levelset.music[level_nr];            /* from config file */
14557   else
14558     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14559 }
14560
14561 static void FadeLevelSounds(void)
14562 {
14563   FadeSounds();
14564 }
14565
14566 static void FadeLevelMusic(void)
14567 {
14568   int music_nr = getLevelMusicNr();
14569   char *curr_music = getCurrentlyPlayingMusicFilename();
14570   char *next_music = getMusicInfoEntryFilename(music_nr);
14571
14572   if (!strEqual(curr_music, next_music))
14573     FadeMusic();
14574 }
14575
14576 void FadeLevelSoundsAndMusic(void)
14577 {
14578   FadeLevelSounds();
14579   FadeLevelMusic();
14580 }
14581
14582 static void PlayLevelMusic(void)
14583 {
14584   int music_nr = getLevelMusicNr();
14585   char *curr_music = getCurrentlyPlayingMusicFilename();
14586   char *next_music = getMusicInfoEntryFilename(music_nr);
14587
14588   if (!strEqual(curr_music, next_music))
14589     PlayMusicLoop(music_nr);
14590 }
14591
14592 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14593 {
14594   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14595   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14596   int x = xx - 1 - offset;
14597   int y = yy - 1 - offset;
14598
14599   switch (sample)
14600   {
14601     case SAMPLE_blank:
14602       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14603       break;
14604
14605     case SAMPLE_roll:
14606       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14607       break;
14608
14609     case SAMPLE_stone:
14610       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14611       break;
14612
14613     case SAMPLE_nut:
14614       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14615       break;
14616
14617     case SAMPLE_crack:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14619       break;
14620
14621     case SAMPLE_bug:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14623       break;
14624
14625     case SAMPLE_tank:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14627       break;
14628
14629     case SAMPLE_android_clone:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14631       break;
14632
14633     case SAMPLE_android_move:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14635       break;
14636
14637     case SAMPLE_spring:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14639       break;
14640
14641     case SAMPLE_slurp:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14643       break;
14644
14645     case SAMPLE_eater:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14647       break;
14648
14649     case SAMPLE_eater_eat:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14651       break;
14652
14653     case SAMPLE_alien:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14655       break;
14656
14657     case SAMPLE_collect:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14659       break;
14660
14661     case SAMPLE_diamond:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14663       break;
14664
14665     case SAMPLE_squash:
14666       /* !!! CHECK THIS !!! */
14667 #if 1
14668       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14669 #else
14670       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14671 #endif
14672       break;
14673
14674     case SAMPLE_wonderfall:
14675       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14676       break;
14677
14678     case SAMPLE_drip:
14679       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14680       break;
14681
14682     case SAMPLE_push:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14684       break;
14685
14686     case SAMPLE_dirt:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14688       break;
14689
14690     case SAMPLE_acid:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14692       break;
14693
14694     case SAMPLE_ball:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14696       break;
14697
14698     case SAMPLE_grow:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14700       break;
14701
14702     case SAMPLE_wonder:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14704       break;
14705
14706     case SAMPLE_door:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14708       break;
14709
14710     case SAMPLE_exit_open:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14712       break;
14713
14714     case SAMPLE_exit_leave:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14716       break;
14717
14718     case SAMPLE_dynamite:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14720       break;
14721
14722     case SAMPLE_tick:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14724       break;
14725
14726     case SAMPLE_press:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14728       break;
14729
14730     case SAMPLE_wheel:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14732       break;
14733
14734     case SAMPLE_boom:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14736       break;
14737
14738     case SAMPLE_die:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14740       break;
14741
14742     case SAMPLE_time:
14743       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14744       break;
14745
14746     default:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14748       break;
14749   }
14750 }
14751
14752 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14753 {
14754   int element = map_element_SP_to_RND(element_sp);
14755   int action = map_action_SP_to_RND(action_sp);
14756   int offset = (setup.sp_show_border_elements ? 0 : 1);
14757   int x = xx - offset;
14758   int y = yy - offset;
14759
14760   PlayLevelSoundElementAction(x, y, element, action);
14761 }
14762
14763 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14764 {
14765   int element = map_element_MM_to_RND(element_mm);
14766   int action = map_action_MM_to_RND(action_mm);
14767   int offset = 0;
14768   int x = xx - offset;
14769   int y = yy - offset;
14770
14771   if (!IS_MM_ELEMENT(element))
14772     element = EL_MM_DEFAULT;
14773
14774   PlayLevelSoundElementAction(x, y, element, action);
14775 }
14776
14777 void PlaySound_MM(int sound_mm)
14778 {
14779   int sound = map_sound_MM_to_RND(sound_mm);
14780
14781   if (sound == SND_UNDEFINED)
14782     return;
14783
14784   PlaySound(sound);
14785 }
14786
14787 void PlaySoundLoop_MM(int sound_mm)
14788 {
14789   int sound = map_sound_MM_to_RND(sound_mm);
14790
14791   if (sound == SND_UNDEFINED)
14792     return;
14793
14794   PlaySoundLoop(sound);
14795 }
14796
14797 void StopSound_MM(int sound_mm)
14798 {
14799   int sound = map_sound_MM_to_RND(sound_mm);
14800
14801   if (sound == SND_UNDEFINED)
14802     return;
14803
14804   StopSound(sound);
14805 }
14806
14807 void RaiseScore(int value)
14808 {
14809   local_player->score += value;
14810
14811   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14812
14813   DisplayGameControlValues();
14814 }
14815
14816 void RaiseScoreElement(int element)
14817 {
14818   switch (element)
14819   {
14820     case EL_EMERALD:
14821     case EL_BD_DIAMOND:
14822     case EL_EMERALD_YELLOW:
14823     case EL_EMERALD_RED:
14824     case EL_EMERALD_PURPLE:
14825     case EL_SP_INFOTRON:
14826       RaiseScore(level.score[SC_EMERALD]);
14827       break;
14828     case EL_DIAMOND:
14829       RaiseScore(level.score[SC_DIAMOND]);
14830       break;
14831     case EL_CRYSTAL:
14832       RaiseScore(level.score[SC_CRYSTAL]);
14833       break;
14834     case EL_PEARL:
14835       RaiseScore(level.score[SC_PEARL]);
14836       break;
14837     case EL_BUG:
14838     case EL_BD_BUTTERFLY:
14839     case EL_SP_ELECTRON:
14840       RaiseScore(level.score[SC_BUG]);
14841       break;
14842     case EL_SPACESHIP:
14843     case EL_BD_FIREFLY:
14844     case EL_SP_SNIKSNAK:
14845       RaiseScore(level.score[SC_SPACESHIP]);
14846       break;
14847     case EL_YAMYAM:
14848     case EL_DARK_YAMYAM:
14849       RaiseScore(level.score[SC_YAMYAM]);
14850       break;
14851     case EL_ROBOT:
14852       RaiseScore(level.score[SC_ROBOT]);
14853       break;
14854     case EL_PACMAN:
14855       RaiseScore(level.score[SC_PACMAN]);
14856       break;
14857     case EL_NUT:
14858       RaiseScore(level.score[SC_NUT]);
14859       break;
14860     case EL_DYNAMITE:
14861     case EL_EM_DYNAMITE:
14862     case EL_SP_DISK_RED:
14863     case EL_DYNABOMB_INCREASE_NUMBER:
14864     case EL_DYNABOMB_INCREASE_SIZE:
14865     case EL_DYNABOMB_INCREASE_POWER:
14866       RaiseScore(level.score[SC_DYNAMITE]);
14867       break;
14868     case EL_SHIELD_NORMAL:
14869     case EL_SHIELD_DEADLY:
14870       RaiseScore(level.score[SC_SHIELD]);
14871       break;
14872     case EL_EXTRA_TIME:
14873       RaiseScore(level.extra_time_score);
14874       break;
14875     case EL_KEY_1:
14876     case EL_KEY_2:
14877     case EL_KEY_3:
14878     case EL_KEY_4:
14879     case EL_EM_KEY_1:
14880     case EL_EM_KEY_2:
14881     case EL_EM_KEY_3:
14882     case EL_EM_KEY_4:
14883     case EL_EMC_KEY_5:
14884     case EL_EMC_KEY_6:
14885     case EL_EMC_KEY_7:
14886     case EL_EMC_KEY_8:
14887     case EL_DC_KEY_WHITE:
14888       RaiseScore(level.score[SC_KEY]);
14889       break;
14890     default:
14891       RaiseScore(element_info[element].collect_score);
14892       break;
14893   }
14894 }
14895
14896 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14897 {
14898   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14899   {
14900     /* closing door required in case of envelope style request dialogs */
14901     if (!skip_request)
14902       CloseDoor(DOOR_CLOSE_1);
14903
14904     if (network.enabled)
14905       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14906     else
14907     {
14908       if (quick_quit)
14909         FadeSkipNextFadeIn();
14910
14911       SetGameStatus(GAME_MODE_MAIN);
14912
14913       DrawMainMenu();
14914     }
14915   }
14916   else          /* continue playing the game */
14917   {
14918     if (tape.playing && tape.deactivate_display)
14919       TapeDeactivateDisplayOff(TRUE);
14920
14921     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14922
14923     if (tape.playing && tape.deactivate_display)
14924       TapeDeactivateDisplayOn();
14925   }
14926 }
14927
14928 void RequestQuitGame(boolean ask_if_really_quit)
14929 {
14930   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14931   boolean skip_request = AllPlayersGone || quick_quit;
14932
14933   RequestQuitGameExt(skip_request, quick_quit,
14934                      "Do you really want to quit the game?");
14935 }
14936
14937 void RequestRestartGame(char *message)
14938 {
14939   game.restart_game_message = NULL;
14940
14941   boolean has_started_game = hasStartedNetworkGame();
14942   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
14943
14944   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
14945   {
14946     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14947   }
14948   else
14949   {
14950     SetGameStatus(GAME_MODE_MAIN);
14951
14952     DrawMainMenu();
14953   }
14954 }
14955
14956 void CheckGameOver(void)
14957 {
14958   static boolean last_game_over = FALSE;
14959   static int game_over_delay = 0;
14960   int game_over_delay_value = 50;
14961   boolean game_over = checkGameFailed();
14962
14963   /* do not handle game over if request dialog is already active */
14964   if (game.request_active)
14965     return;
14966
14967   if (!game_over)
14968   {
14969     last_game_over = FALSE;
14970     game_over_delay = game_over_delay_value;
14971
14972     return;
14973   }
14974
14975   if (game_over_delay > 0)
14976   {
14977     game_over_delay--;
14978
14979     return;
14980   }
14981
14982   if (last_game_over != game_over)
14983     game.restart_game_message = (hasStartedNetworkGame() ?
14984                                  "Game over! Play it again?" :
14985                                  "Game over!");
14986
14987   last_game_over = game_over;
14988 }
14989
14990 boolean checkGameSolved(void)
14991 {
14992   /* set for all game engines if level was solved */
14993   return local_player->LevelSolved_GameEnd;
14994 }
14995
14996 boolean checkGameFailed(void)
14997 {
14998   if (!AllPlayersGone)
14999     return FALSE;
15000
15001   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15002     return (level.native_em_level->lev->home > 0);
15003   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15004     return (game_sp.GameOver && !game_sp.LevelSolved);
15005   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15006     return (game_mm.game_over && !game_mm.level_solved);
15007   else                          /* GAME_ENGINE_TYPE_RND */
15008     return (local_player->GameOver && !local_player->LevelSolved);
15009 }
15010
15011 boolean checkGameEnded(void)
15012 {
15013   return (checkGameSolved() || checkGameFailed());
15014 }
15015
15016
15017 /* ------------------------------------------------------------------------- */
15018 /* random generator functions                                                */
15019 /* ------------------------------------------------------------------------- */
15020
15021 unsigned int InitEngineRandom_RND(int seed)
15022 {
15023   game.num_random_calls = 0;
15024
15025   return InitEngineRandom(seed);
15026 }
15027
15028 unsigned int RND(int max)
15029 {
15030   if (max > 0)
15031   {
15032     game.num_random_calls++;
15033
15034     return GetEngineRandom(max);
15035   }
15036
15037   return 0;
15038 }
15039
15040
15041 /* ------------------------------------------------------------------------- */
15042 /* game engine snapshot handling functions                                   */
15043 /* ------------------------------------------------------------------------- */
15044
15045 struct EngineSnapshotInfo
15046 {
15047   /* runtime values for custom element collect score */
15048   int collect_score[NUM_CUSTOM_ELEMENTS];
15049
15050   /* runtime values for group element choice position */
15051   int choice_pos[NUM_GROUP_ELEMENTS];
15052
15053   /* runtime values for belt position animations */
15054   int belt_graphic[4][NUM_BELT_PARTS];
15055   int belt_anim_mode[4][NUM_BELT_PARTS];
15056 };
15057
15058 static struct EngineSnapshotInfo engine_snapshot_rnd;
15059 static char *snapshot_level_identifier = NULL;
15060 static int snapshot_level_nr = -1;
15061
15062 static void SaveEngineSnapshotValues_RND(void)
15063 {
15064   static int belt_base_active_element[4] =
15065   {
15066     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15067     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15068     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15069     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15070   };
15071   int i, j;
15072
15073   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15074   {
15075     int element = EL_CUSTOM_START + i;
15076
15077     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15078   }
15079
15080   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15081   {
15082     int element = EL_GROUP_START + i;
15083
15084     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15085   }
15086
15087   for (i = 0; i < 4; i++)
15088   {
15089     for (j = 0; j < NUM_BELT_PARTS; j++)
15090     {
15091       int element = belt_base_active_element[i] + j;
15092       int graphic = el2img(element);
15093       int anim_mode = graphic_info[graphic].anim_mode;
15094
15095       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15096       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15097     }
15098   }
15099 }
15100
15101 static void LoadEngineSnapshotValues_RND(void)
15102 {
15103   unsigned int num_random_calls = game.num_random_calls;
15104   int i, j;
15105
15106   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15107   {
15108     int element = EL_CUSTOM_START + i;
15109
15110     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15111   }
15112
15113   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15114   {
15115     int element = EL_GROUP_START + i;
15116
15117     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15118   }
15119
15120   for (i = 0; i < 4; i++)
15121   {
15122     for (j = 0; j < NUM_BELT_PARTS; j++)
15123     {
15124       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15125       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15126
15127       graphic_info[graphic].anim_mode = anim_mode;
15128     }
15129   }
15130
15131   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15132   {
15133     InitRND(tape.random_seed);
15134     for (i = 0; i < num_random_calls; i++)
15135       RND(1);
15136   }
15137
15138   if (game.num_random_calls != num_random_calls)
15139   {
15140     Error(ERR_INFO, "number of random calls out of sync");
15141     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15142     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15143     Error(ERR_EXIT, "this should not happen -- please debug");
15144   }
15145 }
15146
15147 void FreeEngineSnapshotSingle(void)
15148 {
15149   FreeSnapshotSingle();
15150
15151   setString(&snapshot_level_identifier, NULL);
15152   snapshot_level_nr = -1;
15153 }
15154
15155 void FreeEngineSnapshotList(void)
15156 {
15157   FreeSnapshotList();
15158 }
15159
15160 static ListNode *SaveEngineSnapshotBuffers(void)
15161 {
15162   ListNode *buffers = NULL;
15163
15164   /* copy some special values to a structure better suited for the snapshot */
15165
15166   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15167     SaveEngineSnapshotValues_RND();
15168   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15169     SaveEngineSnapshotValues_EM();
15170   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15171     SaveEngineSnapshotValues_SP(&buffers);
15172   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15173     SaveEngineSnapshotValues_MM(&buffers);
15174
15175   /* save values stored in special snapshot structure */
15176
15177   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15178     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15179   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15180     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15181   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15182     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15183   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15184     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15185
15186   /* save further RND engine values */
15187
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15191
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15196
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15202
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15206
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15208
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15210
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15213
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15216   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15218   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15219   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15221   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15222   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15224   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15225   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15226   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15227   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15228   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15229   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15230   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15231   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15232
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15235
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15237   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15238   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15239
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15242
15243   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15244   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15247   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15248
15249   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15250   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15251
15252 #if 0
15253   ListNode *node = engine_snapshot_list_rnd;
15254   int num_bytes = 0;
15255
15256   while (node != NULL)
15257   {
15258     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15259
15260     node = node->next;
15261   }
15262
15263   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15264 #endif
15265
15266   return buffers;
15267 }
15268
15269 void SaveEngineSnapshotSingle(void)
15270 {
15271   ListNode *buffers = SaveEngineSnapshotBuffers();
15272
15273   /* finally save all snapshot buffers to single snapshot */
15274   SaveSnapshotSingle(buffers);
15275
15276   /* save level identification information */
15277   setString(&snapshot_level_identifier, leveldir_current->identifier);
15278   snapshot_level_nr = level_nr;
15279 }
15280
15281 boolean CheckSaveEngineSnapshotToList(void)
15282 {
15283   boolean save_snapshot =
15284     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15285      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15286       game.snapshot.changed_action) ||
15287      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15288       game.snapshot.collected_item));
15289
15290   game.snapshot.changed_action = FALSE;
15291   game.snapshot.collected_item = FALSE;
15292   game.snapshot.save_snapshot = save_snapshot;
15293
15294   return save_snapshot;
15295 }
15296
15297 void SaveEngineSnapshotToList(void)
15298 {
15299   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15300       tape.quick_resume)
15301     return;
15302
15303   ListNode *buffers = SaveEngineSnapshotBuffers();
15304
15305   /* finally save all snapshot buffers to snapshot list */
15306   SaveSnapshotToList(buffers);
15307 }
15308
15309 void SaveEngineSnapshotToListInitial(void)
15310 {
15311   FreeEngineSnapshotList();
15312
15313   SaveEngineSnapshotToList();
15314 }
15315
15316 static void LoadEngineSnapshotValues(void)
15317 {
15318   /* restore special values from snapshot structure */
15319
15320   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15321     LoadEngineSnapshotValues_RND();
15322   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15323     LoadEngineSnapshotValues_EM();
15324   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15325     LoadEngineSnapshotValues_SP();
15326   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15327     LoadEngineSnapshotValues_MM();
15328 }
15329
15330 void LoadEngineSnapshotSingle(void)
15331 {
15332   LoadSnapshotSingle();
15333
15334   LoadEngineSnapshotValues();
15335 }
15336
15337 static void LoadEngineSnapshot_Undo(int steps)
15338 {
15339   LoadSnapshotFromList_Older(steps);
15340
15341   LoadEngineSnapshotValues();
15342 }
15343
15344 static void LoadEngineSnapshot_Redo(int steps)
15345 {
15346   LoadSnapshotFromList_Newer(steps);
15347
15348   LoadEngineSnapshotValues();
15349 }
15350
15351 boolean CheckEngineSnapshotSingle(void)
15352 {
15353   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15354           snapshot_level_nr == level_nr);
15355 }
15356
15357 boolean CheckEngineSnapshotList(void)
15358 {
15359   return CheckSnapshotList();
15360 }
15361
15362
15363 /* ---------- new game button stuff ---------------------------------------- */
15364
15365 static struct
15366 {
15367   int graphic;
15368   struct XY *pos;
15369   int gadget_id;
15370   boolean *setup_value;
15371   boolean allowed_on_tape;
15372   char *infotext;
15373 } gamebutton_info[NUM_GAME_BUTTONS] =
15374 {
15375   {
15376     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15377     GAME_CTRL_ID_STOP,                          NULL,
15378     TRUE,                                       "stop game"
15379   },
15380   {
15381     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15382     GAME_CTRL_ID_PAUSE,                         NULL,
15383     TRUE,                                       "pause game"
15384   },
15385   {
15386     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15387     GAME_CTRL_ID_PLAY,                          NULL,
15388     TRUE,                                       "play game"
15389   },
15390   {
15391     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15392     GAME_CTRL_ID_UNDO,                          NULL,
15393     TRUE,                                       "undo step"
15394   },
15395   {
15396     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15397     GAME_CTRL_ID_REDO,                          NULL,
15398     TRUE,                                       "redo step"
15399   },
15400   {
15401     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15402     GAME_CTRL_ID_SAVE,                          NULL,
15403     TRUE,                                       "save game"
15404   },
15405   {
15406     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15407     GAME_CTRL_ID_PAUSE2,                        NULL,
15408     TRUE,                                       "pause game"
15409   },
15410   {
15411     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15412     GAME_CTRL_ID_LOAD,                          NULL,
15413     TRUE,                                       "load game"
15414   },
15415   {
15416     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15417     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15418     FALSE,                                      "stop game"
15419   },
15420   {
15421     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15422     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15423     FALSE,                                      "pause game"
15424   },
15425   {
15426     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15427     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15428     FALSE,                                      "play game"
15429   },
15430   {
15431     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15432     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15433     TRUE,                                       "background music on/off"
15434   },
15435   {
15436     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15437     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15438     TRUE,                                       "sound loops on/off"
15439   },
15440   {
15441     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15442     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15443     TRUE,                                       "normal sounds on/off"
15444   },
15445   {
15446     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15447     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15448     FALSE,                                      "background music on/off"
15449   },
15450   {
15451     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15452     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15453     FALSE,                                      "sound loops on/off"
15454   },
15455   {
15456     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15457     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15458     FALSE,                                      "normal sounds on/off"
15459   }
15460 };
15461
15462 void CreateGameButtons(void)
15463 {
15464   int i;
15465
15466   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15467   {
15468     int graphic = gamebutton_info[i].graphic;
15469     struct GraphicInfo *gfx = &graphic_info[graphic];
15470     struct XY *pos = gamebutton_info[i].pos;
15471     struct GadgetInfo *gi;
15472     int button_type;
15473     boolean checked;
15474     unsigned int event_mask;
15475     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15476     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15477     int base_x = (on_tape ? VX : DX);
15478     int base_y = (on_tape ? VY : DY);
15479     int gd_x   = gfx->src_x;
15480     int gd_y   = gfx->src_y;
15481     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15482     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15483     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15484     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15485     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15486     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15487     int id = i;
15488
15489     if (gfx->bitmap == NULL)
15490     {
15491       game_gadget[id] = NULL;
15492
15493       continue;
15494     }
15495
15496     if (id == GAME_CTRL_ID_STOP ||
15497         id == GAME_CTRL_ID_PANEL_STOP ||
15498         id == GAME_CTRL_ID_PLAY ||
15499         id == GAME_CTRL_ID_PANEL_PLAY ||
15500         id == GAME_CTRL_ID_SAVE ||
15501         id == GAME_CTRL_ID_LOAD)
15502     {
15503       button_type = GD_TYPE_NORMAL_BUTTON;
15504       checked = FALSE;
15505       event_mask = GD_EVENT_RELEASED;
15506     }
15507     else if (id == GAME_CTRL_ID_UNDO ||
15508              id == GAME_CTRL_ID_REDO)
15509     {
15510       button_type = GD_TYPE_NORMAL_BUTTON;
15511       checked = FALSE;
15512       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15513     }
15514     else
15515     {
15516       button_type = GD_TYPE_CHECK_BUTTON;
15517       checked = (gamebutton_info[i].setup_value != NULL ?
15518                  *gamebutton_info[i].setup_value : FALSE);
15519       event_mask = GD_EVENT_PRESSED;
15520     }
15521
15522     gi = CreateGadget(GDI_CUSTOM_ID, id,
15523                       GDI_IMAGE_ID, graphic,
15524                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15525                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15526                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15527                       GDI_WIDTH, gfx->width,
15528                       GDI_HEIGHT, gfx->height,
15529                       GDI_TYPE, button_type,
15530                       GDI_STATE, GD_BUTTON_UNPRESSED,
15531                       GDI_CHECKED, checked,
15532                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15533                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15534                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15535                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15536                       GDI_DIRECT_DRAW, FALSE,
15537                       GDI_EVENT_MASK, event_mask,
15538                       GDI_CALLBACK_ACTION, HandleGameButtons,
15539                       GDI_END);
15540
15541     if (gi == NULL)
15542       Error(ERR_EXIT, "cannot create gadget");
15543
15544     game_gadget[id] = gi;
15545   }
15546 }
15547
15548 void FreeGameButtons(void)
15549 {
15550   int i;
15551
15552   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15553     FreeGadget(game_gadget[i]);
15554 }
15555
15556 static void UnmapGameButtonsAtSamePosition(int id)
15557 {
15558   int i;
15559
15560   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15561     if (i != id &&
15562         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15563         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15564       UnmapGadget(game_gadget[i]);
15565 }
15566
15567 static void UnmapGameButtonsAtSamePosition_All(void)
15568 {
15569   if (setup.show_snapshot_buttons)
15570   {
15571     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15572     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15573     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15574   }
15575   else
15576   {
15577     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15578     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15579     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15580
15581     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15582     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15583     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15584   }
15585 }
15586
15587 static void MapGameButtonsAtSamePosition(int id)
15588 {
15589   int i;
15590
15591   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15592     if (i != id &&
15593         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15594         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15595       MapGadget(game_gadget[i]);
15596
15597   UnmapGameButtonsAtSamePosition_All();
15598 }
15599
15600 void MapUndoRedoButtons(void)
15601 {
15602   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15603   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15604
15605   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15606   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15607
15608   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15609 }
15610
15611 void UnmapUndoRedoButtons(void)
15612 {
15613   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15614   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15615
15616   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15617   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15618
15619   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15620 }
15621
15622 static void MapGameButtonsExt(boolean on_tape)
15623 {
15624   int i;
15625
15626   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15627     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15628         i != GAME_CTRL_ID_UNDO &&
15629         i != GAME_CTRL_ID_REDO)
15630       MapGadget(game_gadget[i]);
15631
15632   UnmapGameButtonsAtSamePosition_All();
15633
15634   RedrawGameButtons();
15635 }
15636
15637 static void UnmapGameButtonsExt(boolean on_tape)
15638 {
15639   int i;
15640
15641   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15642     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15643       UnmapGadget(game_gadget[i]);
15644 }
15645
15646 static void RedrawGameButtonsExt(boolean on_tape)
15647 {
15648   int i;
15649
15650   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15651     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15652       RedrawGadget(game_gadget[i]);
15653
15654   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15655   redraw_mask &= ~REDRAW_ALL;
15656 }
15657
15658 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15659 {
15660   if (gi == NULL)
15661     return;
15662
15663   gi->checked = state;
15664 }
15665
15666 static void RedrawSoundButtonGadget(int id)
15667 {
15668   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15669              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15670              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15671              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15672              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15673              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15674              id);
15675
15676   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15677   RedrawGadget(game_gadget[id2]);
15678 }
15679
15680 void MapGameButtons(void)
15681 {
15682   MapGameButtonsExt(FALSE);
15683 }
15684
15685 void UnmapGameButtons(void)
15686 {
15687   UnmapGameButtonsExt(FALSE);
15688 }
15689
15690 void RedrawGameButtons(void)
15691 {
15692   RedrawGameButtonsExt(FALSE);
15693 }
15694
15695 void MapGameButtonsOnTape(void)
15696 {
15697   MapGameButtonsExt(TRUE);
15698 }
15699
15700 void UnmapGameButtonsOnTape(void)
15701 {
15702   UnmapGameButtonsExt(TRUE);
15703 }
15704
15705 void RedrawGameButtonsOnTape(void)
15706 {
15707   RedrawGameButtonsExt(TRUE);
15708 }
15709
15710 static void GameUndoRedoExt(void)
15711 {
15712   ClearPlayerAction();
15713
15714   tape.pausing = TRUE;
15715
15716   RedrawPlayfield();
15717   UpdateAndDisplayGameControlValues();
15718
15719   DrawCompleteVideoDisplay();
15720   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15721   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15722   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15723
15724   BackToFront();
15725 }
15726
15727 static void GameUndo(int steps)
15728 {
15729   if (!CheckEngineSnapshotList())
15730     return;
15731
15732   LoadEngineSnapshot_Undo(steps);
15733
15734   GameUndoRedoExt();
15735 }
15736
15737 static void GameRedo(int steps)
15738 {
15739   if (!CheckEngineSnapshotList())
15740     return;
15741
15742   LoadEngineSnapshot_Redo(steps);
15743
15744   GameUndoRedoExt();
15745 }
15746
15747 static void HandleGameButtonsExt(int id, int button)
15748 {
15749   static boolean game_undo_executed = FALSE;
15750   int steps = BUTTON_STEPSIZE(button);
15751   boolean handle_game_buttons =
15752     (game_status == GAME_MODE_PLAYING ||
15753      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15754
15755   if (!handle_game_buttons)
15756     return;
15757
15758   switch (id)
15759   {
15760     case GAME_CTRL_ID_STOP:
15761     case GAME_CTRL_ID_PANEL_STOP:
15762       if (game_status == GAME_MODE_MAIN)
15763         break;
15764
15765       if (tape.playing)
15766         TapeStop();
15767       else
15768         RequestQuitGame(TRUE);
15769
15770       break;
15771
15772     case GAME_CTRL_ID_PAUSE:
15773     case GAME_CTRL_ID_PAUSE2:
15774     case GAME_CTRL_ID_PANEL_PAUSE:
15775       if (network.enabled && game_status == GAME_MODE_PLAYING)
15776       {
15777         if (tape.pausing)
15778           SendToServer_ContinuePlaying();
15779         else
15780           SendToServer_PausePlaying();
15781       }
15782       else
15783         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15784
15785       game_undo_executed = FALSE;
15786
15787       break;
15788
15789     case GAME_CTRL_ID_PLAY:
15790     case GAME_CTRL_ID_PANEL_PLAY:
15791       if (game_status == GAME_MODE_MAIN)
15792       {
15793         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15794       }
15795       else if (tape.pausing)
15796       {
15797         if (network.enabled)
15798           SendToServer_ContinuePlaying();
15799         else
15800           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15801       }
15802       break;
15803
15804     case GAME_CTRL_ID_UNDO:
15805       // Important: When using "save snapshot when collecting an item" mode,
15806       // load last (current) snapshot for first "undo" after pressing "pause"
15807       // (else the last-but-one snapshot would be loaded, because the snapshot
15808       // pointer already points to the last snapshot when pressing "pause",
15809       // which is fine for "every step/move" mode, but not for "every collect")
15810       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15811           !game_undo_executed)
15812         steps--;
15813
15814       game_undo_executed = TRUE;
15815
15816       GameUndo(steps);
15817       break;
15818
15819     case GAME_CTRL_ID_REDO:
15820       GameRedo(steps);
15821       break;
15822
15823     case GAME_CTRL_ID_SAVE:
15824       TapeQuickSave();
15825       break;
15826
15827     case GAME_CTRL_ID_LOAD:
15828       TapeQuickLoad();
15829       break;
15830
15831     case SOUND_CTRL_ID_MUSIC:
15832     case SOUND_CTRL_ID_PANEL_MUSIC:
15833       if (setup.sound_music)
15834       { 
15835         setup.sound_music = FALSE;
15836
15837         FadeMusic();
15838       }
15839       else if (audio.music_available)
15840       { 
15841         setup.sound = setup.sound_music = TRUE;
15842
15843         SetAudioMode(setup.sound);
15844
15845         if (game_status == GAME_MODE_PLAYING)
15846           PlayLevelMusic();
15847       }
15848
15849       RedrawSoundButtonGadget(id);
15850
15851       break;
15852
15853     case SOUND_CTRL_ID_LOOPS:
15854     case SOUND_CTRL_ID_PANEL_LOOPS:
15855       if (setup.sound_loops)
15856         setup.sound_loops = FALSE;
15857       else if (audio.loops_available)
15858       {
15859         setup.sound = setup.sound_loops = TRUE;
15860
15861         SetAudioMode(setup.sound);
15862       }
15863
15864       RedrawSoundButtonGadget(id);
15865
15866       break;
15867
15868     case SOUND_CTRL_ID_SIMPLE:
15869     case SOUND_CTRL_ID_PANEL_SIMPLE:
15870       if (setup.sound_simple)
15871         setup.sound_simple = FALSE;
15872       else if (audio.sound_available)
15873       {
15874         setup.sound = setup.sound_simple = TRUE;
15875
15876         SetAudioMode(setup.sound);
15877       }
15878
15879       RedrawSoundButtonGadget(id);
15880
15881       break;
15882
15883     default:
15884       break;
15885   }
15886 }
15887
15888 static void HandleGameButtons(struct GadgetInfo *gi)
15889 {
15890   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15891 }
15892
15893 void HandleSoundButtonKeys(Key key)
15894 {
15895   if (key == setup.shortcut.sound_simple)
15896     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15897   else if (key == setup.shortcut.sound_loops)
15898     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15899   else if (key == setup.shortcut.sound_music)
15900     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15901 }