fixed network games, part 1: separate locally and network connected players
[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()             (local_player->LevelSolved_PanelOff)
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();
1089 static void FadeLevelSoundsAndMusic();
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(void);
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
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!options.network || player->connected_network)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 void InitGame()
3277 {
3278   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280   int fade_mask = REDRAW_FIELD;
3281
3282   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3283   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3284   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3285   int initial_move_dir = MV_DOWN;
3286   int i, j, x, y;
3287
3288   // required here to update video display before fading (FIX THIS)
3289   DrawMaskedBorder(REDRAW_DOOR_2);
3290
3291   if (!game.restart_level)
3292     CloseDoor(DOOR_CLOSE_1);
3293
3294   SetGameStatus(GAME_MODE_PLAYING);
3295
3296   if (level_editor_test_game)
3297     FadeSkipNextFadeIn();
3298   else
3299     FadeSetEnterScreen();
3300
3301   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302     fade_mask = REDRAW_ALL;
3303
3304   FadeLevelSoundsAndMusic();
3305
3306   ExpireSoundLoops(TRUE);
3307
3308   if (!level_editor_test_game)
3309     FadeOut(fade_mask);
3310
3311   /* needed if different viewport properties defined for playing */
3312   ChangeViewportPropertiesIfNeeded();
3313
3314   ClearField();
3315
3316   DrawCompleteVideoDisplay();
3317
3318   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3319
3320   InitGameEngine();
3321   InitGameControlValues();
3322
3323   /* don't play tapes over network */
3324   network_playing = (options.network && !tape.playing);
3325
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327   {
3328     struct PlayerInfo *player = &stored_player[i];
3329
3330     player->index_nr = i;
3331     player->index_bit = (1 << i);
3332     player->element_nr = EL_PLAYER_1 + i;
3333
3334     player->present = FALSE;
3335     player->active = FALSE;
3336     player->mapped = FALSE;
3337
3338     player->killed = FALSE;
3339     player->reanimated = FALSE;
3340
3341     player->action = 0;
3342     player->effective_action = 0;
3343     player->programmed_action = 0;
3344
3345     player->mouse_action.lx = 0;
3346     player->mouse_action.ly = 0;
3347     player->mouse_action.button = 0;
3348     player->mouse_action.button_hint = 0;
3349
3350     player->effective_mouse_action.lx = 0;
3351     player->effective_mouse_action.ly = 0;
3352     player->effective_mouse_action.button = 0;
3353     player->effective_mouse_action.button_hint = 0;
3354
3355     player->score = 0;
3356     player->score_final = 0;
3357
3358     player->health = MAX_HEALTH;
3359     player->health_final = MAX_HEALTH;
3360
3361     player->gems_still_needed = level.gems_needed;
3362     player->sokobanfields_still_needed = 0;
3363     player->lights_still_needed = 0;
3364     player->friends_still_needed = 0;
3365
3366     for (j = 0; j < MAX_NUM_KEYS; j++)
3367       player->key[j] = FALSE;
3368
3369     player->num_white_keys = 0;
3370
3371     player->dynabomb_count = 0;
3372     player->dynabomb_size = 1;
3373     player->dynabombs_left = 0;
3374     player->dynabomb_xl = FALSE;
3375
3376     player->MovDir = initial_move_dir;
3377     player->MovPos = 0;
3378     player->GfxPos = 0;
3379     player->GfxDir = initial_move_dir;
3380     player->GfxAction = ACTION_DEFAULT;
3381     player->Frame = 0;
3382     player->StepFrame = 0;
3383
3384     player->initial_element = player->element_nr;
3385     player->artwork_element =
3386       (level.use_artwork_element[i] ? level.artwork_element[i] :
3387        player->element_nr);
3388     player->use_murphy = FALSE;
3389
3390     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3391     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3392
3393     player->gravity = level.initial_player_gravity[i];
3394
3395     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3396
3397     player->actual_frame_counter = 0;
3398
3399     player->step_counter = 0;
3400
3401     player->last_move_dir = initial_move_dir;
3402
3403     player->is_active = FALSE;
3404
3405     player->is_waiting = FALSE;
3406     player->is_moving = FALSE;
3407     player->is_auto_moving = FALSE;
3408     player->is_digging = FALSE;
3409     player->is_snapping = FALSE;
3410     player->is_collecting = FALSE;
3411     player->is_pushing = FALSE;
3412     player->is_switching = FALSE;
3413     player->is_dropping = FALSE;
3414     player->is_dropping_pressed = FALSE;
3415
3416     player->is_bored = FALSE;
3417     player->is_sleeping = FALSE;
3418
3419     player->was_waiting = TRUE;
3420     player->was_moving = FALSE;
3421     player->was_snapping = FALSE;
3422     player->was_dropping = FALSE;
3423
3424     player->force_dropping = FALSE;
3425
3426     player->frame_counter_bored = -1;
3427     player->frame_counter_sleeping = -1;
3428
3429     player->anim_delay_counter = 0;
3430     player->post_delay_counter = 0;
3431
3432     player->dir_waiting = initial_move_dir;
3433     player->action_waiting = ACTION_DEFAULT;
3434     player->last_action_waiting = ACTION_DEFAULT;
3435     player->special_action_bored = ACTION_DEFAULT;
3436     player->special_action_sleeping = ACTION_DEFAULT;
3437
3438     player->switch_x = -1;
3439     player->switch_y = -1;
3440
3441     player->drop_x = -1;
3442     player->drop_y = -1;
3443
3444     player->show_envelope = 0;
3445
3446     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3447
3448     player->push_delay       = -1;      /* initialized when pushing starts */
3449     player->push_delay_value = game.initial_push_delay_value;
3450
3451     player->drop_delay = 0;
3452     player->drop_pressed_delay = 0;
3453
3454     player->last_jx = -1;
3455     player->last_jy = -1;
3456     player->jx = -1;
3457     player->jy = -1;
3458
3459     player->shield_normal_time_left = 0;
3460     player->shield_deadly_time_left = 0;
3461
3462     player->inventory_infinite_element = EL_UNDEFINED;
3463     player->inventory_size = 0;
3464
3465     if (level.use_initial_inventory[i])
3466     {
3467       for (j = 0; j < level.initial_inventory_size[i]; j++)
3468       {
3469         int element = level.initial_inventory_content[i][j];
3470         int collect_count = element_info[element].collect_count_initial;
3471         int k;
3472
3473         if (!IS_CUSTOM_ELEMENT(element))
3474           collect_count = 1;
3475
3476         if (collect_count == 0)
3477           player->inventory_infinite_element = element;
3478         else
3479           for (k = 0; k < collect_count; k++)
3480             if (player->inventory_size < MAX_INVENTORY_SIZE)
3481               player->inventory_element[player->inventory_size++] = element;
3482       }
3483     }
3484
3485     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486     SnapField(player, 0, 0);
3487
3488     player->LevelSolved = FALSE;
3489     player->GameOver = FALSE;
3490
3491     player->LevelSolved_GameWon = FALSE;
3492     player->LevelSolved_GameEnd = FALSE;
3493     player->LevelSolved_PanelOff = FALSE;
3494     player->LevelSolved_SaveTape = FALSE;
3495     player->LevelSolved_SaveScore = FALSE;
3496
3497     player->LevelSolved_CountingTime = 0;
3498     player->LevelSolved_CountingScore = 0;
3499     player->LevelSolved_CountingHealth = 0;
3500
3501     map_player_action[i] = i;
3502   }
3503
3504   network_player_action_received = FALSE;
3505
3506 #if defined(NETWORK_AVALIABLE)
3507   /* initial null action */
3508   if (network_playing)
3509     SendToServer_MovePlayer(MV_NONE);
3510 #endif
3511
3512   ZX = ZY = -1;
3513   ExitX = ExitY = -1;
3514
3515   FrameCounter = 0;
3516   TimeFrames = 0;
3517   TimePlayed = 0;
3518   TimeLeft = level.time;
3519   TapeTime = 0;
3520
3521   ScreenMovDir = MV_NONE;
3522   ScreenMovPos = 0;
3523   ScreenGfxPos = 0;
3524
3525   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3526
3527   AllPlayersGone = FALSE;
3528
3529   game.no_time_limit = (level.time == 0);
3530
3531   game.yamyam_content_nr = 0;
3532   game.robot_wheel_active = FALSE;
3533   game.magic_wall_active = FALSE;
3534   game.magic_wall_time_left = 0;
3535   game.light_time_left = 0;
3536   game.timegate_time_left = 0;
3537   game.switchgate_pos = 0;
3538   game.wind_direction = level.wind_direction_initial;
3539
3540   game.lenses_time_left = 0;
3541   game.magnify_time_left = 0;
3542
3543   game.ball_state = level.ball_state_initial;
3544   game.ball_content_nr = 0;
3545
3546   game.envelope_active = FALSE;
3547
3548   /* set focus to local player for network games, else to all players */
3549   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3550   game.centered_player_nr_next = game.centered_player_nr;
3551   game.set_centered_player = FALSE;
3552
3553   if (network_playing && tape.recording)
3554   {
3555     /* store client dependent player focus when recording network games */
3556     tape.centered_player_nr_next = game.centered_player_nr_next;
3557     tape.set_centered_player = TRUE;
3558   }
3559
3560   for (i = 0; i < NUM_BELTS; i++)
3561   {
3562     game.belt_dir[i] = MV_NONE;
3563     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3564   }
3565
3566   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3567     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3568
3569 #if DEBUG_INIT_PLAYER
3570   if (options.debug)
3571   {
3572     printf("Player status at level initialization:\n");
3573   }
3574 #endif
3575
3576   SCAN_PLAYFIELD(x, y)
3577   {
3578     Feld[x][y] = level.field[x][y];
3579     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3580     ChangeDelay[x][y] = 0;
3581     ChangePage[x][y] = -1;
3582     CustomValue[x][y] = 0;              /* initialized in InitField() */
3583     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3584     AmoebaNr[x][y] = 0;
3585     WasJustMoving[x][y] = 0;
3586     WasJustFalling[x][y] = 0;
3587     CheckCollision[x][y] = 0;
3588     CheckImpact[x][y] = 0;
3589     Stop[x][y] = FALSE;
3590     Pushed[x][y] = FALSE;
3591
3592     ChangeCount[x][y] = 0;
3593     ChangeEvent[x][y] = -1;
3594
3595     ExplodePhase[x][y] = 0;
3596     ExplodeDelay[x][y] = 0;
3597     ExplodeField[x][y] = EX_TYPE_NONE;
3598
3599     RunnerVisit[x][y] = 0;
3600     PlayerVisit[x][y] = 0;
3601
3602     GfxFrame[x][y] = 0;
3603     GfxRandom[x][y] = INIT_GFX_RANDOM();
3604     GfxElement[x][y] = EL_UNDEFINED;
3605     GfxAction[x][y] = ACTION_DEFAULT;
3606     GfxDir[x][y] = MV_NONE;
3607     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3608   }
3609
3610   SCAN_PLAYFIELD(x, y)
3611   {
3612     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3613       emulate_bd = FALSE;
3614     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3615       emulate_sb = FALSE;
3616     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3617       emulate_sp = FALSE;
3618
3619     InitField(x, y, TRUE);
3620
3621     ResetGfxAnimation(x, y);
3622   }
3623
3624   InitBeltMovement();
3625
3626   for (i = 0; i < MAX_PLAYERS; i++)
3627   {
3628     struct PlayerInfo *player = &stored_player[i];
3629
3630     /* set number of special actions for bored and sleeping animation */
3631     player->num_special_action_bored =
3632       get_num_special_action(player->artwork_element,
3633                              ACTION_BORING_1, ACTION_BORING_LAST);
3634     player->num_special_action_sleeping =
3635       get_num_special_action(player->artwork_element,
3636                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3637   }
3638
3639   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3640                     emulate_sb ? EMU_SOKOBAN :
3641                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3642
3643   /* initialize type of slippery elements */
3644   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3645   {
3646     if (!IS_CUSTOM_ELEMENT(i))
3647     {
3648       /* default: elements slip down either to the left or right randomly */
3649       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3650
3651       /* SP style elements prefer to slip down on the left side */
3652       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3653         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3654
3655       /* BD style elements prefer to slip down on the left side */
3656       if (game.emulation == EMU_BOULDERDASH)
3657         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3658     }
3659   }
3660
3661   /* initialize explosion and ignition delay */
3662   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3663   {
3664     if (!IS_CUSTOM_ELEMENT(i))
3665     {
3666       int num_phase = 8;
3667       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3668                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3669                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3670       int last_phase = (num_phase + 1) * delay;
3671       int half_phase = (num_phase / 2) * delay;
3672
3673       element_info[i].explosion_delay = last_phase - 1;
3674       element_info[i].ignition_delay = half_phase;
3675
3676       if (i == EL_BLACK_ORB)
3677         element_info[i].ignition_delay = 1;
3678     }
3679   }
3680
3681   /* correct non-moving belts to start moving left */
3682   for (i = 0; i < NUM_BELTS; i++)
3683     if (game.belt_dir[i] == MV_NONE)
3684       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3685
3686 #if USE_NEW_PLAYER_ASSIGNMENTS
3687   for (i = 0; i < MAX_PLAYERS; i++)
3688   {
3689     stored_player[i].connected = FALSE;
3690
3691     /* in network game mode, the local player might not be the first player */
3692     if (stored_player[i].connected_locally)
3693       local_player = &stored_player[i];
3694   }
3695
3696   if (!options.network)
3697     local_player->connected = TRUE;
3698
3699   if (tape.playing)
3700   {
3701     for (i = 0; i < MAX_PLAYERS; i++)
3702       stored_player[i].connected = tape.player_participates[i];
3703   }
3704   else if (options.network)
3705   {
3706     /* add team mode players connected over the network (needed for correct
3707        assignment of player figures from level to locally playing players) */
3708
3709     for (i = 0; i < MAX_PLAYERS; i++)
3710       if (stored_player[i].connected_network)
3711         stored_player[i].connected = TRUE;
3712   }
3713   else if (game.team_mode)
3714   {
3715     /* try to guess locally connected team mode players (needed for correct
3716        assignment of player figures from level to locally playing players) */
3717
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719       if (setup.input[i].use_joystick ||
3720           setup.input[i].key.left != KSYM_UNDEFINED)
3721         stored_player[i].connected = TRUE;
3722   }
3723
3724 #if DEBUG_INIT_PLAYER
3725   if (options.debug)
3726   {
3727     printf("Player status after level initialization:\n");
3728
3729     for (i = 0; i < MAX_PLAYERS; i++)
3730     {
3731       struct PlayerInfo *player = &stored_player[i];
3732
3733       printf("- player %d: present == %d, connected == %d, active == %d",
3734              i + 1,
3735              player->present,
3736              player->connected,
3737              player->active);
3738
3739       if (local_player == player)
3740         printf(" (local player)");
3741
3742       printf("\n");
3743     }
3744   }
3745 #endif
3746
3747 #if DEBUG_INIT_PLAYER
3748   if (options.debug)
3749     printf("Reassigning players ...\n");
3750 #endif
3751
3752   /* check if any connected player was not found in playfield */
3753   for (i = 0; i < MAX_PLAYERS; i++)
3754   {
3755     struct PlayerInfo *player = &stored_player[i];
3756
3757     if (player->connected && !player->present)
3758     {
3759       struct PlayerInfo *field_player = NULL;
3760
3761 #if DEBUG_INIT_PLAYER
3762       if (options.debug)
3763         printf("- looking for field player for player %d ...\n", i + 1);
3764 #endif
3765
3766       /* assign first free player found that is present in the playfield */
3767
3768       /* first try: look for unmapped playfield player that is not connected */
3769       for (j = 0; j < MAX_PLAYERS; j++)
3770         if (field_player == NULL &&
3771             stored_player[j].present &&
3772             !stored_player[j].mapped &&
3773             !stored_player[j].connected)
3774           field_player = &stored_player[j];
3775
3776       /* second try: look for *any* unmapped playfield player */
3777       for (j = 0; j < MAX_PLAYERS; j++)
3778         if (field_player == NULL &&
3779             stored_player[j].present &&
3780             !stored_player[j].mapped)
3781           field_player = &stored_player[j];
3782
3783       if (field_player != NULL)
3784       {
3785         int jx = field_player->jx, jy = field_player->jy;
3786
3787 #if DEBUG_INIT_PLAYER
3788         if (options.debug)
3789           printf("- found player %d\n", field_player->index_nr + 1);
3790 #endif
3791
3792         player->present = FALSE;
3793         player->active = FALSE;
3794
3795         field_player->present = TRUE;
3796         field_player->active = TRUE;
3797
3798         /*
3799         player->initial_element = field_player->initial_element;
3800         player->artwork_element = field_player->artwork_element;
3801
3802         player->block_last_field       = field_player->block_last_field;
3803         player->block_delay_adjustment = field_player->block_delay_adjustment;
3804         */
3805
3806         StorePlayer[jx][jy] = field_player->element_nr;
3807
3808         field_player->jx = field_player->last_jx = jx;
3809         field_player->jy = field_player->last_jy = jy;
3810
3811         if (local_player == player)
3812           local_player = field_player;
3813
3814         map_player_action[field_player->index_nr] = i;
3815
3816         field_player->mapped = TRUE;
3817
3818 #if DEBUG_INIT_PLAYER
3819         if (options.debug)
3820           printf("- map_player_action[%d] == %d\n",
3821                  field_player->index_nr + 1, i + 1);
3822 #endif
3823       }
3824     }
3825
3826     if (player->connected && player->present)
3827       player->mapped = TRUE;
3828   }
3829
3830 #if DEBUG_INIT_PLAYER
3831   if (options.debug)
3832   {
3833     printf("Player status after player assignment (first stage):\n");
3834
3835     for (i = 0; i < MAX_PLAYERS; i++)
3836     {
3837       struct PlayerInfo *player = &stored_player[i];
3838
3839       printf("- player %d: present == %d, connected == %d, active == %d",
3840              i + 1,
3841              player->present,
3842              player->connected,
3843              player->active);
3844
3845       if (local_player == player)
3846         printf(" (local player)");
3847
3848       printf("\n");
3849     }
3850   }
3851 #endif
3852
3853 #else
3854
3855   /* check if any connected player was not found in playfield */
3856   for (i = 0; i < MAX_PLAYERS; i++)
3857   {
3858     struct PlayerInfo *player = &stored_player[i];
3859
3860     if (player->connected && !player->present)
3861     {
3862       for (j = 0; j < MAX_PLAYERS; j++)
3863       {
3864         struct PlayerInfo *field_player = &stored_player[j];
3865         int jx = field_player->jx, jy = field_player->jy;
3866
3867         /* assign first free player found that is present in the playfield */
3868         if (field_player->present && !field_player->connected)
3869         {
3870           player->present = TRUE;
3871           player->active = TRUE;
3872
3873           field_player->present = FALSE;
3874           field_player->active = FALSE;
3875
3876           player->initial_element = field_player->initial_element;
3877           player->artwork_element = field_player->artwork_element;
3878
3879           player->block_last_field       = field_player->block_last_field;
3880           player->block_delay_adjustment = field_player->block_delay_adjustment;
3881
3882           StorePlayer[jx][jy] = player->element_nr;
3883
3884           player->jx = player->last_jx = jx;
3885           player->jy = player->last_jy = jy;
3886
3887           break;
3888         }
3889       }
3890     }
3891   }
3892 #endif
3893
3894 #if 0
3895   printf("::: local_player->present == %d\n", local_player->present);
3896 #endif
3897
3898   if (tape.playing)
3899   {
3900     /* when playing a tape, eliminate all players who do not participate */
3901
3902 #if USE_NEW_PLAYER_ASSIGNMENTS
3903
3904     if (!game.team_mode)
3905     {
3906       for (i = 0; i < MAX_PLAYERS; i++)
3907       {
3908         if (stored_player[i].active &&
3909             !tape.player_participates[map_player_action[i]])
3910         {
3911           struct PlayerInfo *player = &stored_player[i];
3912           int jx = player->jx, jy = player->jy;
3913
3914 #if DEBUG_INIT_PLAYER
3915           if (options.debug)
3916             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3917 #endif
3918
3919           player->active = FALSE;
3920           StorePlayer[jx][jy] = 0;
3921           Feld[jx][jy] = EL_EMPTY;
3922         }
3923       }
3924     }
3925
3926 #else
3927
3928     for (i = 0; i < MAX_PLAYERS; i++)
3929     {
3930       if (stored_player[i].active &&
3931           !tape.player_participates[i])
3932       {
3933         struct PlayerInfo *player = &stored_player[i];
3934         int jx = player->jx, jy = player->jy;
3935
3936         player->active = FALSE;
3937         StorePlayer[jx][jy] = 0;
3938         Feld[jx][jy] = EL_EMPTY;
3939       }
3940     }
3941 #endif
3942   }
3943   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3944   {
3945     /* when in single player mode, eliminate all but the first active player */
3946
3947     for (i = 0; i < MAX_PLAYERS; i++)
3948     {
3949       if (stored_player[i].active)
3950       {
3951         for (j = i + 1; j < MAX_PLAYERS; j++)
3952         {
3953           if (stored_player[j].active)
3954           {
3955             struct PlayerInfo *player = &stored_player[j];
3956             int jx = player->jx, jy = player->jy;
3957
3958             player->active = FALSE;
3959             player->present = FALSE;
3960
3961             StorePlayer[jx][jy] = 0;
3962             Feld[jx][jy] = EL_EMPTY;
3963           }
3964         }
3965       }
3966     }
3967   }
3968
3969   /* when recording the game, store which players take part in the game */
3970   if (tape.recording)
3971   {
3972 #if USE_NEW_PLAYER_ASSIGNMENTS
3973     for (i = 0; i < MAX_PLAYERS; i++)
3974       if (stored_player[i].connected)
3975         tape.player_participates[i] = TRUE;
3976 #else
3977     for (i = 0; i < MAX_PLAYERS; i++)
3978       if (stored_player[i].active)
3979         tape.player_participates[i] = TRUE;
3980 #endif
3981   }
3982
3983 #if DEBUG_INIT_PLAYER
3984   if (options.debug)
3985   {
3986     printf("Player status after player assignment (final stage):\n");
3987
3988     for (i = 0; i < MAX_PLAYERS; i++)
3989     {
3990       struct PlayerInfo *player = &stored_player[i];
3991
3992       printf("- player %d: present == %d, connected == %d, active == %d",
3993              i + 1,
3994              player->present,
3995              player->connected,
3996              player->active);
3997
3998       if (local_player == player)
3999         printf(" (local player)");
4000
4001       printf("\n");
4002     }
4003   }
4004 #endif
4005
4006   if (BorderElement == EL_EMPTY)
4007   {
4008     SBX_Left = 0;
4009     SBX_Right = lev_fieldx - SCR_FIELDX;
4010     SBY_Upper = 0;
4011     SBY_Lower = lev_fieldy - SCR_FIELDY;
4012   }
4013   else
4014   {
4015     SBX_Left = -1;
4016     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4017     SBY_Upper = -1;
4018     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4019   }
4020
4021   if (full_lev_fieldx <= SCR_FIELDX)
4022     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4023   if (full_lev_fieldy <= SCR_FIELDY)
4024     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4025
4026   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4027     SBX_Left--;
4028   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4029     SBY_Upper--;
4030
4031   /* if local player not found, look for custom element that might create
4032      the player (make some assumptions about the right custom element) */
4033   if (!local_player->present)
4034   {
4035     int start_x = 0, start_y = 0;
4036     int found_rating = 0;
4037     int found_element = EL_UNDEFINED;
4038     int player_nr = local_player->index_nr;
4039
4040     SCAN_PLAYFIELD(x, y)
4041     {
4042       int element = Feld[x][y];
4043       int content;
4044       int xx, yy;
4045       boolean is_player;
4046
4047       if (level.use_start_element[player_nr] &&
4048           level.start_element[player_nr] == element &&
4049           found_rating < 4)
4050       {
4051         start_x = x;
4052         start_y = y;
4053
4054         found_rating = 4;
4055         found_element = element;
4056       }
4057
4058       if (!IS_CUSTOM_ELEMENT(element))
4059         continue;
4060
4061       if (CAN_CHANGE(element))
4062       {
4063         for (i = 0; i < element_info[element].num_change_pages; i++)
4064         {
4065           /* check for player created from custom element as single target */
4066           content = element_info[element].change_page[i].target_element;
4067           is_player = ELEM_IS_PLAYER(content);
4068
4069           if (is_player && (found_rating < 3 ||
4070                             (found_rating == 3 && element < found_element)))
4071           {
4072             start_x = x;
4073             start_y = y;
4074
4075             found_rating = 3;
4076             found_element = element;
4077           }
4078         }
4079       }
4080
4081       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4082       {
4083         /* check for player created from custom element as explosion content */
4084         content = element_info[element].content.e[xx][yy];
4085         is_player = ELEM_IS_PLAYER(content);
4086
4087         if (is_player && (found_rating < 2 ||
4088                           (found_rating == 2 && element < found_element)))
4089         {
4090           start_x = x + xx - 1;
4091           start_y = y + yy - 1;
4092
4093           found_rating = 2;
4094           found_element = element;
4095         }
4096
4097         if (!CAN_CHANGE(element))
4098           continue;
4099
4100         for (i = 0; i < element_info[element].num_change_pages; i++)
4101         {
4102           /* check for player created from custom element as extended target */
4103           content =
4104             element_info[element].change_page[i].target_content.e[xx][yy];
4105
4106           is_player = ELEM_IS_PLAYER(content);
4107
4108           if (is_player && (found_rating < 1 ||
4109                             (found_rating == 1 && element < found_element)))
4110           {
4111             start_x = x + xx - 1;
4112             start_y = y + yy - 1;
4113
4114             found_rating = 1;
4115             found_element = element;
4116           }
4117         }
4118       }
4119     }
4120
4121     scroll_x = SCROLL_POSITION_X(start_x);
4122     scroll_y = SCROLL_POSITION_Y(start_y);
4123   }
4124   else
4125   {
4126     scroll_x = SCROLL_POSITION_X(local_player->jx);
4127     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4128   }
4129
4130   /* !!! FIX THIS (START) !!! */
4131   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4132   {
4133     InitGameEngine_EM();
4134   }
4135   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4136   {
4137     InitGameEngine_SP();
4138   }
4139   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4140   {
4141     InitGameEngine_MM();
4142   }
4143   else
4144   {
4145     DrawLevel(REDRAW_FIELD);
4146     DrawAllPlayers();
4147
4148     /* after drawing the level, correct some elements */
4149     if (game.timegate_time_left == 0)
4150       CloseAllOpenTimegates();
4151   }
4152
4153   /* blit playfield from scroll buffer to normal back buffer for fading in */
4154   BlitScreenToBitmap(backbuffer);
4155   /* !!! FIX THIS (END) !!! */
4156
4157   DrawMaskedBorder(fade_mask);
4158
4159   FadeIn(fade_mask);
4160
4161 #if 1
4162   // full screen redraw is required at this point in the following cases:
4163   // - special editor door undrawn when game was started from level editor
4164   // - drawing area (playfield) was changed and has to be removed completely
4165   redraw_mask = REDRAW_ALL;
4166   BackToFront();
4167 #endif
4168
4169   if (!game.restart_level)
4170   {
4171     /* copy default game door content to main double buffer */
4172
4173     /* !!! CHECK AGAIN !!! */
4174     SetPanelBackground();
4175     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4176     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4177   }
4178
4179   SetPanelBackground();
4180   SetDrawBackgroundMask(REDRAW_DOOR_1);
4181
4182   UpdateAndDisplayGameControlValues();
4183
4184   if (!game.restart_level)
4185   {
4186     UnmapGameButtons();
4187     UnmapTapeButtons();
4188
4189     FreeGameButtons();
4190     CreateGameButtons();
4191
4192     MapGameButtons();
4193     MapTapeButtons();
4194
4195     /* copy actual game door content to door double buffer for OpenDoor() */
4196     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4197
4198     OpenDoor(DOOR_OPEN_ALL);
4199
4200     KeyboardAutoRepeatOffUnlessAutoplay();
4201
4202 #if DEBUG_INIT_PLAYER
4203     if (options.debug)
4204     {
4205       printf("Player status (final):\n");
4206
4207       for (i = 0; i < MAX_PLAYERS; i++)
4208       {
4209         struct PlayerInfo *player = &stored_player[i];
4210
4211         printf("- player %d: present == %d, connected == %d, active == %d",
4212                i + 1,
4213                player->present,
4214                player->connected,
4215                player->active);
4216
4217         if (local_player == player)
4218           printf(" (local player)");
4219
4220         printf("\n");
4221       }
4222     }
4223 #endif
4224   }
4225
4226   UnmapAllGadgets();
4227
4228   MapGameButtons();
4229   MapTapeButtons();
4230
4231   if (!game.restart_level && !tape.playing)
4232   {
4233     LevelStats_incPlayed(level_nr);
4234
4235     SaveLevelSetup_SeriesInfo();
4236   }
4237
4238   game.restart_level = FALSE;
4239   game.restart_game_message = NULL;
4240
4241   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4242     InitGameActions_MM();
4243
4244   SaveEngineSnapshotToListInitial();
4245
4246   if (!game.restart_level)
4247   {
4248     PlaySound(SND_GAME_STARTING);
4249
4250     if (setup.sound_music)
4251       PlayLevelMusic();
4252   }
4253 }
4254
4255 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4256                         int actual_player_x, int actual_player_y)
4257 {
4258   /* this is used for non-R'n'D game engines to update certain engine values */
4259
4260   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4261   {
4262     actual_player_x = correctLevelPosX_EM(actual_player_x);
4263     actual_player_y = correctLevelPosY_EM(actual_player_y);
4264   }
4265
4266   /* needed to determine if sounds are played within the visible screen area */
4267   scroll_x = actual_scroll_x;
4268   scroll_y = actual_scroll_y;
4269
4270   /* needed to get player position for "follow finger" playing input method */
4271   local_player->jx = actual_player_x;
4272   local_player->jy = actual_player_y;
4273 }
4274
4275 void InitMovDir(int x, int y)
4276 {
4277   int i, element = Feld[x][y];
4278   static int xy[4][2] =
4279   {
4280     {  0, +1 },
4281     { +1,  0 },
4282     {  0, -1 },
4283     { -1,  0 }
4284   };
4285   static int direction[3][4] =
4286   {
4287     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4288     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4289     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4290   };
4291
4292   switch (element)
4293   {
4294     case EL_BUG_RIGHT:
4295     case EL_BUG_UP:
4296     case EL_BUG_LEFT:
4297     case EL_BUG_DOWN:
4298       Feld[x][y] = EL_BUG;
4299       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4300       break;
4301
4302     case EL_SPACESHIP_RIGHT:
4303     case EL_SPACESHIP_UP:
4304     case EL_SPACESHIP_LEFT:
4305     case EL_SPACESHIP_DOWN:
4306       Feld[x][y] = EL_SPACESHIP;
4307       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4308       break;
4309
4310     case EL_BD_BUTTERFLY_RIGHT:
4311     case EL_BD_BUTTERFLY_UP:
4312     case EL_BD_BUTTERFLY_LEFT:
4313     case EL_BD_BUTTERFLY_DOWN:
4314       Feld[x][y] = EL_BD_BUTTERFLY;
4315       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4316       break;
4317
4318     case EL_BD_FIREFLY_RIGHT:
4319     case EL_BD_FIREFLY_UP:
4320     case EL_BD_FIREFLY_LEFT:
4321     case EL_BD_FIREFLY_DOWN:
4322       Feld[x][y] = EL_BD_FIREFLY;
4323       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4324       break;
4325
4326     case EL_PACMAN_RIGHT:
4327     case EL_PACMAN_UP:
4328     case EL_PACMAN_LEFT:
4329     case EL_PACMAN_DOWN:
4330       Feld[x][y] = EL_PACMAN;
4331       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4332       break;
4333
4334     case EL_YAMYAM_LEFT:
4335     case EL_YAMYAM_RIGHT:
4336     case EL_YAMYAM_UP:
4337     case EL_YAMYAM_DOWN:
4338       Feld[x][y] = EL_YAMYAM;
4339       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4340       break;
4341
4342     case EL_SP_SNIKSNAK:
4343       MovDir[x][y] = MV_UP;
4344       break;
4345
4346     case EL_SP_ELECTRON:
4347       MovDir[x][y] = MV_LEFT;
4348       break;
4349
4350     case EL_MOLE_LEFT:
4351     case EL_MOLE_RIGHT:
4352     case EL_MOLE_UP:
4353     case EL_MOLE_DOWN:
4354       Feld[x][y] = EL_MOLE;
4355       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4356       break;
4357
4358     default:
4359       if (IS_CUSTOM_ELEMENT(element))
4360       {
4361         struct ElementInfo *ei = &element_info[element];
4362         int move_direction_initial = ei->move_direction_initial;
4363         int move_pattern = ei->move_pattern;
4364
4365         if (move_direction_initial == MV_START_PREVIOUS)
4366         {
4367           if (MovDir[x][y] != MV_NONE)
4368             return;
4369
4370           move_direction_initial = MV_START_AUTOMATIC;
4371         }
4372
4373         if (move_direction_initial == MV_START_RANDOM)
4374           MovDir[x][y] = 1 << RND(4);
4375         else if (move_direction_initial & MV_ANY_DIRECTION)
4376           MovDir[x][y] = move_direction_initial;
4377         else if (move_pattern == MV_ALL_DIRECTIONS ||
4378                  move_pattern == MV_TURNING_LEFT ||
4379                  move_pattern == MV_TURNING_RIGHT ||
4380                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4381                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4382                  move_pattern == MV_TURNING_RANDOM)
4383           MovDir[x][y] = 1 << RND(4);
4384         else if (move_pattern == MV_HORIZONTAL)
4385           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4386         else if (move_pattern == MV_VERTICAL)
4387           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4388         else if (move_pattern & MV_ANY_DIRECTION)
4389           MovDir[x][y] = element_info[element].move_pattern;
4390         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4391                  move_pattern == MV_ALONG_RIGHT_SIDE)
4392         {
4393           /* use random direction as default start direction */
4394           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4395             MovDir[x][y] = 1 << RND(4);
4396
4397           for (i = 0; i < NUM_DIRECTIONS; i++)
4398           {
4399             int x1 = x + xy[i][0];
4400             int y1 = y + xy[i][1];
4401
4402             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4403             {
4404               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4405                 MovDir[x][y] = direction[0][i];
4406               else
4407                 MovDir[x][y] = direction[1][i];
4408
4409               break;
4410             }
4411           }
4412         }                
4413       }
4414       else
4415       {
4416         MovDir[x][y] = 1 << RND(4);
4417
4418         if (element != EL_BUG &&
4419             element != EL_SPACESHIP &&
4420             element != EL_BD_BUTTERFLY &&
4421             element != EL_BD_FIREFLY)
4422           break;
4423
4424         for (i = 0; i < NUM_DIRECTIONS; i++)
4425         {
4426           int x1 = x + xy[i][0];
4427           int y1 = y + xy[i][1];
4428
4429           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4430           {
4431             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4432             {
4433               MovDir[x][y] = direction[0][i];
4434               break;
4435             }
4436             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4437                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4438             {
4439               MovDir[x][y] = direction[1][i];
4440               break;
4441             }
4442           }
4443         }
4444       }
4445       break;
4446   }
4447
4448   GfxDir[x][y] = MovDir[x][y];
4449 }
4450
4451 void InitAmoebaNr(int x, int y)
4452 {
4453   int i;
4454   int group_nr = AmoebeNachbarNr(x, y);
4455
4456   if (group_nr == 0)
4457   {
4458     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4459     {
4460       if (AmoebaCnt[i] == 0)
4461       {
4462         group_nr = i;
4463         break;
4464       }
4465     }
4466   }
4467
4468   AmoebaNr[x][y] = group_nr;
4469   AmoebaCnt[group_nr]++;
4470   AmoebaCnt2[group_nr]++;
4471 }
4472
4473 static void PlayerWins(struct PlayerInfo *player)
4474 {
4475   player->LevelSolved = TRUE;
4476   player->GameOver = TRUE;
4477
4478   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4479                          level.native_em_level->lev->score :
4480                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4481                          game_mm.score :
4482                          player->score);
4483   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4484                           MM_HEALTH(game_mm.laser_overload_value) :
4485                           player->health);
4486
4487   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4488                                       TimeLeft);
4489   player->LevelSolved_CountingScore = player->score_final;
4490   player->LevelSolved_CountingHealth = player->health_final;
4491 }
4492
4493 void GameWon()
4494 {
4495   static int time_count_steps;
4496   static int time, time_final;
4497   static int score, score_final;
4498   static int health, health_final;
4499   static int game_over_delay_1 = 0;
4500   static int game_over_delay_2 = 0;
4501   static int game_over_delay_3 = 0;
4502   int game_over_delay_value_1 = 50;
4503   int game_over_delay_value_2 = 25;
4504   int game_over_delay_value_3 = 50;
4505
4506   if (!local_player->LevelSolved_GameWon)
4507   {
4508     int i;
4509
4510     /* do not start end game actions before the player stops moving (to exit) */
4511     if (local_player->MovPos)
4512       return;
4513
4514     local_player->LevelSolved_GameWon = TRUE;
4515     local_player->LevelSolved_SaveTape = tape.recording;
4516     local_player->LevelSolved_SaveScore = !tape.playing;
4517
4518     if (!tape.playing)
4519     {
4520       LevelStats_incSolved(level_nr);
4521
4522       SaveLevelSetup_SeriesInfo();
4523     }
4524
4525     if (tape.auto_play)         /* tape might already be stopped here */
4526       tape.auto_play_level_solved = TRUE;
4527
4528     TapeStop();
4529
4530     game_over_delay_1 = 0;
4531     game_over_delay_2 = 0;
4532     game_over_delay_3 = game_over_delay_value_3;
4533
4534     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4535     score = score_final = local_player->score_final;
4536     health = health_final = local_player->health_final;
4537
4538     if (level.score[SC_TIME_BONUS] > 0)
4539     {
4540       if (TimeLeft > 0)
4541       {
4542         time_final = 0;
4543         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4544       }
4545       else if (game.no_time_limit && TimePlayed < 999)
4546       {
4547         time_final = 999;
4548         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4549       }
4550
4551       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4552
4553       game_over_delay_1 = game_over_delay_value_1;
4554
4555       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4556       {
4557         health_final = 0;
4558         score_final += health * level.score[SC_TIME_BONUS];
4559
4560         game_over_delay_2 = game_over_delay_value_2;
4561       }
4562
4563       local_player->score_final = score_final;
4564       local_player->health_final = health_final;
4565     }
4566
4567     if (level_editor_test_game)
4568     {
4569       time = time_final;
4570       score = score_final;
4571
4572       local_player->LevelSolved_CountingTime = time;
4573       local_player->LevelSolved_CountingScore = score;
4574
4575       game_panel_controls[GAME_PANEL_TIME].value = time;
4576       game_panel_controls[GAME_PANEL_SCORE].value = score;
4577
4578       DisplayGameControlValues();
4579     }
4580
4581     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4582     {
4583       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4584       {
4585         /* close exit door after last player */
4586         if ((AllPlayersGone &&
4587              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4588               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4589               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4590             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4591             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4592         {
4593           int element = Feld[ExitX][ExitY];
4594
4595           Feld[ExitX][ExitY] =
4596             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4597              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4598              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4599              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4600              EL_EM_STEEL_EXIT_CLOSING);
4601
4602           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4603         }
4604
4605         /* player disappears */
4606         DrawLevelField(ExitX, ExitY);
4607       }
4608
4609       for (i = 0; i < MAX_PLAYERS; i++)
4610       {
4611         struct PlayerInfo *player = &stored_player[i];
4612
4613         if (player->present)
4614         {
4615           RemovePlayer(player);
4616
4617           /* player disappears */
4618           DrawLevelField(player->jx, player->jy);
4619         }
4620       }
4621     }
4622
4623     PlaySound(SND_GAME_WINNING);
4624   }
4625
4626   if (game_over_delay_1 > 0)
4627   {
4628     game_over_delay_1--;
4629
4630     return;
4631   }
4632
4633   if (time != time_final)
4634   {
4635     int time_to_go = ABS(time_final - time);
4636     int time_count_dir = (time < time_final ? +1 : -1);
4637
4638     if (time_to_go < time_count_steps)
4639       time_count_steps = 1;
4640
4641     time  += time_count_steps * time_count_dir;
4642     score += time_count_steps * level.score[SC_TIME_BONUS];
4643
4644     local_player->LevelSolved_CountingTime = time;
4645     local_player->LevelSolved_CountingScore = score;
4646
4647     game_panel_controls[GAME_PANEL_TIME].value = time;
4648     game_panel_controls[GAME_PANEL_SCORE].value = score;
4649
4650     DisplayGameControlValues();
4651
4652     if (time == time_final)
4653       StopSound(SND_GAME_LEVELTIME_BONUS);
4654     else if (setup.sound_loops)
4655       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4656     else
4657       PlaySound(SND_GAME_LEVELTIME_BONUS);
4658
4659     return;
4660   }
4661
4662   if (game_over_delay_2 > 0)
4663   {
4664     game_over_delay_2--;
4665
4666     return;
4667   }
4668
4669   if (health != health_final)
4670   {
4671     int health_count_dir = (health < health_final ? +1 : -1);
4672
4673     health += health_count_dir;
4674     score  += level.score[SC_TIME_BONUS];
4675
4676     local_player->LevelSolved_CountingHealth = health;
4677     local_player->LevelSolved_CountingScore = score;
4678
4679     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4680     game_panel_controls[GAME_PANEL_SCORE].value = score;
4681
4682     DisplayGameControlValues();
4683
4684     if (health == health_final)
4685       StopSound(SND_GAME_LEVELTIME_BONUS);
4686     else if (setup.sound_loops)
4687       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4688     else
4689       PlaySound(SND_GAME_LEVELTIME_BONUS);
4690
4691     return;
4692   }
4693
4694   local_player->LevelSolved_PanelOff = TRUE;
4695
4696   if (game_over_delay_3 > 0)
4697   {
4698     game_over_delay_3--;
4699
4700     return;
4701   }
4702
4703   GameEnd();
4704 }
4705
4706 void GameEnd()
4707 {
4708   int hi_pos;
4709   boolean raise_level = FALSE;
4710
4711   local_player->LevelSolved_GameEnd = TRUE;
4712
4713   if (local_player->LevelSolved_SaveTape)
4714   {
4715     /* make sure that request dialog to save tape does not open door again */
4716     if (!global.use_envelope_request)
4717       CloseDoor(DOOR_CLOSE_1);
4718
4719     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4720   }
4721
4722   /* if no tape is to be saved, close both doors simultaneously */
4723   CloseDoor(DOOR_CLOSE_ALL);
4724
4725   if (level_editor_test_game)
4726   {
4727     SetGameStatus(GAME_MODE_MAIN);
4728
4729     DrawMainMenu();
4730
4731     return;
4732   }
4733
4734   if (!local_player->LevelSolved_SaveScore)
4735   {
4736     SetGameStatus(GAME_MODE_MAIN);
4737
4738     DrawMainMenu();
4739
4740     return;
4741   }
4742
4743   if (level_nr == leveldir_current->handicap_level)
4744   {
4745     leveldir_current->handicap_level++;
4746
4747     SaveLevelSetup_SeriesInfo();
4748   }
4749
4750   if (setup.increment_levels &&
4751       level_nr < leveldir_current->last_level)
4752     raise_level = TRUE;                 /* advance to next level */
4753
4754   if ((hi_pos = NewHiScore()) >= 0) 
4755   {
4756     SetGameStatus(GAME_MODE_SCORES);
4757
4758     DrawHallOfFame(hi_pos);
4759
4760     if (raise_level)
4761     {
4762       level_nr++;
4763       TapeErase();
4764     }
4765   }
4766   else
4767   {
4768     SetGameStatus(GAME_MODE_MAIN);
4769
4770     if (raise_level)
4771     {
4772       level_nr++;
4773       TapeErase();
4774     }
4775
4776     DrawMainMenu();
4777   }
4778 }
4779
4780 int NewHiScore()
4781 {
4782   int k, l;
4783   int position = -1;
4784   boolean one_score_entry_per_name = !program.many_scores_per_name;
4785
4786   LoadScore(level_nr);
4787
4788   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4789       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4790     return -1;
4791
4792   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4793   {
4794     if (local_player->score_final > highscore[k].Score)
4795     {
4796       /* player has made it to the hall of fame */
4797
4798       if (k < MAX_SCORE_ENTRIES - 1)
4799       {
4800         int m = MAX_SCORE_ENTRIES - 1;
4801
4802         if (one_score_entry_per_name)
4803         {
4804           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4805             if (strEqual(setup.player_name, highscore[l].Name))
4806               m = l;
4807
4808           if (m == k)   /* player's new highscore overwrites his old one */
4809             goto put_into_list;
4810         }
4811
4812         for (l = m; l > k; l--)
4813         {
4814           strcpy(highscore[l].Name, highscore[l - 1].Name);
4815           highscore[l].Score = highscore[l - 1].Score;
4816         }
4817       }
4818
4819       put_into_list:
4820
4821       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4822       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4823       highscore[k].Score = local_player->score_final; 
4824       position = k;
4825
4826       break;
4827     }
4828     else if (one_score_entry_per_name &&
4829              !strncmp(setup.player_name, highscore[k].Name,
4830                       MAX_PLAYER_NAME_LEN))
4831       break;    /* player already there with a higher score */
4832   }
4833
4834   if (position >= 0) 
4835     SaveScore(level_nr);
4836
4837   return position;
4838 }
4839
4840 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4841 {
4842   int element = Feld[x][y];
4843   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4844   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4845   int horiz_move = (dx != 0);
4846   int sign = (horiz_move ? dx : dy);
4847   int step = sign * element_info[element].move_stepsize;
4848
4849   /* special values for move stepsize for spring and things on conveyor belt */
4850   if (horiz_move)
4851   {
4852     if (CAN_FALL(element) &&
4853         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4854       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4855     else if (element == EL_SPRING)
4856       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4857   }
4858
4859   return step;
4860 }
4861
4862 inline static int getElementMoveStepsize(int x, int y)
4863 {
4864   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4865 }
4866
4867 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4868 {
4869   if (player->GfxAction != action || player->GfxDir != dir)
4870   {
4871     player->GfxAction = action;
4872     player->GfxDir = dir;
4873     player->Frame = 0;
4874     player->StepFrame = 0;
4875   }
4876 }
4877
4878 static void ResetGfxFrame(int x, int y)
4879 {
4880   // profiling showed that "autotest" spends 10~20% of its time in this function
4881   if (DrawingDeactivatedField())
4882     return;
4883
4884   int element = Feld[x][y];
4885   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4886
4887   if (graphic_info[graphic].anim_global_sync)
4888     GfxFrame[x][y] = FrameCounter;
4889   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4890     GfxFrame[x][y] = CustomValue[x][y];
4891   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4892     GfxFrame[x][y] = element_info[element].collect_score;
4893   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4894     GfxFrame[x][y] = ChangeDelay[x][y];
4895 }
4896
4897 static void ResetGfxAnimation(int x, int y)
4898 {
4899   GfxAction[x][y] = ACTION_DEFAULT;
4900   GfxDir[x][y] = MovDir[x][y];
4901   GfxFrame[x][y] = 0;
4902
4903   ResetGfxFrame(x, y);
4904 }
4905
4906 static void ResetRandomAnimationValue(int x, int y)
4907 {
4908   GfxRandom[x][y] = INIT_GFX_RANDOM();
4909 }
4910
4911 void InitMovingField(int x, int y, int direction)
4912 {
4913   int element = Feld[x][y];
4914   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4915   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4916   int newx = x + dx;
4917   int newy = y + dy;
4918   boolean is_moving_before, is_moving_after;
4919
4920   /* check if element was/is moving or being moved before/after mode change */
4921   is_moving_before = (WasJustMoving[x][y] != 0);
4922   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4923
4924   /* reset animation only for moving elements which change direction of moving
4925      or which just started or stopped moving
4926      (else CEs with property "can move" / "not moving" are reset each frame) */
4927   if (is_moving_before != is_moving_after ||
4928       direction != MovDir[x][y])
4929     ResetGfxAnimation(x, y);
4930
4931   MovDir[x][y] = direction;
4932   GfxDir[x][y] = direction;
4933
4934   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4935                      direction == MV_DOWN && CAN_FALL(element) ?
4936                      ACTION_FALLING : ACTION_MOVING);
4937
4938   /* this is needed for CEs with property "can move" / "not moving" */
4939
4940   if (is_moving_after)
4941   {
4942     if (Feld[newx][newy] == EL_EMPTY)
4943       Feld[newx][newy] = EL_BLOCKED;
4944
4945     MovDir[newx][newy] = MovDir[x][y];
4946
4947     CustomValue[newx][newy] = CustomValue[x][y];
4948
4949     GfxFrame[newx][newy] = GfxFrame[x][y];
4950     GfxRandom[newx][newy] = GfxRandom[x][y];
4951     GfxAction[newx][newy] = GfxAction[x][y];
4952     GfxDir[newx][newy] = GfxDir[x][y];
4953   }
4954 }
4955
4956 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4957 {
4958   int direction = MovDir[x][y];
4959   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4960   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4961
4962   *goes_to_x = newx;
4963   *goes_to_y = newy;
4964 }
4965
4966 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4967 {
4968   int oldx = x, oldy = y;
4969   int direction = MovDir[x][y];
4970
4971   if (direction == MV_LEFT)
4972     oldx++;
4973   else if (direction == MV_RIGHT)
4974     oldx--;
4975   else if (direction == MV_UP)
4976     oldy++;
4977   else if (direction == MV_DOWN)
4978     oldy--;
4979
4980   *comes_from_x = oldx;
4981   *comes_from_y = oldy;
4982 }
4983
4984 int MovingOrBlocked2Element(int x, int y)
4985 {
4986   int element = Feld[x][y];
4987
4988   if (element == EL_BLOCKED)
4989   {
4990     int oldx, oldy;
4991
4992     Blocked2Moving(x, y, &oldx, &oldy);
4993     return Feld[oldx][oldy];
4994   }
4995   else
4996     return element;
4997 }
4998
4999 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5000 {
5001   /* like MovingOrBlocked2Element(), but if element is moving
5002      and (x,y) is the field the moving element is just leaving,
5003      return EL_BLOCKED instead of the element value */
5004   int element = Feld[x][y];
5005
5006   if (IS_MOVING(x, y))
5007   {
5008     if (element == EL_BLOCKED)
5009     {
5010       int oldx, oldy;
5011
5012       Blocked2Moving(x, y, &oldx, &oldy);
5013       return Feld[oldx][oldy];
5014     }
5015     else
5016       return EL_BLOCKED;
5017   }
5018   else
5019     return element;
5020 }
5021
5022 static void RemoveField(int x, int y)
5023 {
5024   Feld[x][y] = EL_EMPTY;
5025
5026   MovPos[x][y] = 0;
5027   MovDir[x][y] = 0;
5028   MovDelay[x][y] = 0;
5029
5030   CustomValue[x][y] = 0;
5031
5032   AmoebaNr[x][y] = 0;
5033   ChangeDelay[x][y] = 0;
5034   ChangePage[x][y] = -1;
5035   Pushed[x][y] = FALSE;
5036
5037   GfxElement[x][y] = EL_UNDEFINED;
5038   GfxAction[x][y] = ACTION_DEFAULT;
5039   GfxDir[x][y] = MV_NONE;
5040 }
5041
5042 void RemoveMovingField(int x, int y)
5043 {
5044   int oldx = x, oldy = y, newx = x, newy = y;
5045   int element = Feld[x][y];
5046   int next_element = EL_UNDEFINED;
5047
5048   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5049     return;
5050
5051   if (IS_MOVING(x, y))
5052   {
5053     Moving2Blocked(x, y, &newx, &newy);
5054
5055     if (Feld[newx][newy] != EL_BLOCKED)
5056     {
5057       /* element is moving, but target field is not free (blocked), but
5058          already occupied by something different (example: acid pool);
5059          in this case, only remove the moving field, but not the target */
5060
5061       RemoveField(oldx, oldy);
5062
5063       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5064
5065       TEST_DrawLevelField(oldx, oldy);
5066
5067       return;
5068     }
5069   }
5070   else if (element == EL_BLOCKED)
5071   {
5072     Blocked2Moving(x, y, &oldx, &oldy);
5073     if (!IS_MOVING(oldx, oldy))
5074       return;
5075   }
5076
5077   if (element == EL_BLOCKED &&
5078       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5079        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5080        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5081        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5082        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5083        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5084     next_element = get_next_element(Feld[oldx][oldy]);
5085
5086   RemoveField(oldx, oldy);
5087   RemoveField(newx, newy);
5088
5089   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5090
5091   if (next_element != EL_UNDEFINED)
5092     Feld[oldx][oldy] = next_element;
5093
5094   TEST_DrawLevelField(oldx, oldy);
5095   TEST_DrawLevelField(newx, newy);
5096 }
5097
5098 void DrawDynamite(int x, int y)
5099 {
5100   int sx = SCREENX(x), sy = SCREENY(y);
5101   int graphic = el2img(Feld[x][y]);
5102   int frame;
5103
5104   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5105     return;
5106
5107   if (IS_WALKABLE_INSIDE(Back[x][y]))
5108     return;
5109
5110   if (Back[x][y])
5111     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5112   else if (Store[x][y])
5113     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5114
5115   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5116
5117   if (Back[x][y] || Store[x][y])
5118     DrawGraphicThruMask(sx, sy, graphic, frame);
5119   else
5120     DrawGraphic(sx, sy, graphic, frame);
5121 }
5122
5123 void CheckDynamite(int x, int y)
5124 {
5125   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5126   {
5127     MovDelay[x][y]--;
5128
5129     if (MovDelay[x][y] != 0)
5130     {
5131       DrawDynamite(x, y);
5132       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5133
5134       return;
5135     }
5136   }
5137
5138   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5139
5140   Bang(x, y);
5141 }
5142
5143 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5144 {
5145   boolean num_checked_players = 0;
5146   int i;
5147
5148   for (i = 0; i < MAX_PLAYERS; i++)
5149   {
5150     if (stored_player[i].active)
5151     {
5152       int sx = stored_player[i].jx;
5153       int sy = stored_player[i].jy;
5154
5155       if (num_checked_players == 0)
5156       {
5157         *sx1 = *sx2 = sx;
5158         *sy1 = *sy2 = sy;
5159       }
5160       else
5161       {
5162         *sx1 = MIN(*sx1, sx);
5163         *sy1 = MIN(*sy1, sy);
5164         *sx2 = MAX(*sx2, sx);
5165         *sy2 = MAX(*sy2, sy);
5166       }
5167
5168       num_checked_players++;
5169     }
5170   }
5171 }
5172
5173 static boolean checkIfAllPlayersFitToScreen_RND()
5174 {
5175   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5176
5177   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5178
5179   return (sx2 - sx1 < SCR_FIELDX &&
5180           sy2 - sy1 < SCR_FIELDY);
5181 }
5182
5183 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5184 {
5185   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5186
5187   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5188
5189   *sx = (sx1 + sx2) / 2;
5190   *sy = (sy1 + sy2) / 2;
5191 }
5192
5193 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5194                         boolean center_screen, boolean quick_relocation)
5195 {
5196   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5197   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5198   boolean no_delay = (tape.warp_forward);
5199   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5200   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5201   int new_scroll_x, new_scroll_y;
5202
5203   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5204   {
5205     /* case 1: quick relocation inside visible screen (without scrolling) */
5206
5207     RedrawPlayfield();
5208
5209     return;
5210   }
5211
5212   if (!level.shifted_relocation || center_screen)
5213   {
5214     /* relocation _with_ centering of screen */
5215
5216     new_scroll_x = SCROLL_POSITION_X(x);
5217     new_scroll_y = SCROLL_POSITION_Y(y);
5218   }
5219   else
5220   {
5221     /* relocation _without_ centering of screen */
5222
5223     int center_scroll_x = SCROLL_POSITION_X(old_x);
5224     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5225     int offset_x = x + (scroll_x - center_scroll_x);
5226     int offset_y = y + (scroll_y - center_scroll_y);
5227
5228     /* for new screen position, apply previous offset to center position */
5229     new_scroll_x = SCROLL_POSITION_X(offset_x);
5230     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5231   }
5232
5233   if (quick_relocation)
5234   {
5235     /* case 2: quick relocation (redraw without visible scrolling) */
5236
5237     scroll_x = new_scroll_x;
5238     scroll_y = new_scroll_y;
5239
5240     RedrawPlayfield();
5241
5242     return;
5243   }
5244
5245   /* case 3: visible relocation (with scrolling to new position) */
5246
5247   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5248
5249   SetVideoFrameDelay(wait_delay_value);
5250
5251   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5252   {
5253     int dx = 0, dy = 0;
5254     int fx = FX, fy = FY;
5255
5256     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5257     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5258
5259     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5260       break;
5261
5262     scroll_x -= dx;
5263     scroll_y -= dy;
5264
5265     fx += dx * TILEX / 2;
5266     fy += dy * TILEY / 2;
5267
5268     ScrollLevel(dx, dy);
5269     DrawAllPlayers();
5270
5271     /* scroll in two steps of half tile size to make things smoother */
5272     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5273
5274     /* scroll second step to align at full tile size */
5275     BlitScreenToBitmap(window);
5276   }
5277
5278   DrawAllPlayers();
5279   BackToFront();
5280
5281   SetVideoFrameDelay(frame_delay_value_old);
5282 }
5283
5284 void RelocatePlayer(int jx, int jy, int el_player_raw)
5285 {
5286   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5287   int player_nr = GET_PLAYER_NR(el_player);
5288   struct PlayerInfo *player = &stored_player[player_nr];
5289   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5290   boolean no_delay = (tape.warp_forward);
5291   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5292   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5293   int old_jx = player->jx;
5294   int old_jy = player->jy;
5295   int old_element = Feld[old_jx][old_jy];
5296   int element = Feld[jx][jy];
5297   boolean player_relocated = (old_jx != jx || old_jy != jy);
5298
5299   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5300   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5301   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5302   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5303   int leave_side_horiz = move_dir_horiz;
5304   int leave_side_vert  = move_dir_vert;
5305   int enter_side = enter_side_horiz | enter_side_vert;
5306   int leave_side = leave_side_horiz | leave_side_vert;
5307
5308   if (player->GameOver)         /* do not reanimate dead player */
5309     return;
5310
5311   if (!player_relocated)        /* no need to relocate the player */
5312     return;
5313
5314   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5315   {
5316     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5317     DrawLevelField(jx, jy);
5318   }
5319
5320   if (player->present)
5321   {
5322     while (player->MovPos)
5323     {
5324       ScrollPlayer(player, SCROLL_GO_ON);
5325       ScrollScreen(NULL, SCROLL_GO_ON);
5326
5327       AdvanceFrameAndPlayerCounters(player->index_nr);
5328
5329       DrawPlayer(player);
5330
5331       BackToFront_WithFrameDelay(wait_delay_value);
5332     }
5333
5334     DrawPlayer(player);         /* needed here only to cleanup last field */
5335     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5336
5337     player->is_moving = FALSE;
5338   }
5339
5340   if (IS_CUSTOM_ELEMENT(old_element))
5341     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5342                                CE_LEFT_BY_PLAYER,
5343                                player->index_bit, leave_side);
5344
5345   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5346                                       CE_PLAYER_LEAVES_X,
5347                                       player->index_bit, leave_side);
5348
5349   Feld[jx][jy] = el_player;
5350   InitPlayerField(jx, jy, el_player, TRUE);
5351
5352   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5353      possible that the relocation target field did not contain a player element,
5354      but a walkable element, to which the new player was relocated -- in this
5355      case, restore that (already initialized!) element on the player field */
5356   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5357   {
5358     Feld[jx][jy] = element;     /* restore previously existing element */
5359   }
5360
5361   /* only visually relocate centered player */
5362   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5363                      FALSE, level.instant_relocation);
5364
5365   TestIfPlayerTouchesBadThing(jx, jy);
5366   TestIfPlayerTouchesCustomElement(jx, jy);
5367
5368   if (IS_CUSTOM_ELEMENT(element))
5369     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5370                                player->index_bit, enter_side);
5371
5372   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5373                                       player->index_bit, enter_side);
5374
5375   if (player->is_switching)
5376   {
5377     /* ensure that relocation while still switching an element does not cause
5378        a new element to be treated as also switched directly after relocation
5379        (this is important for teleporter switches that teleport the player to
5380        a place where another teleporter switch is in the same direction, which
5381        would then incorrectly be treated as immediately switched before the
5382        direction key that caused the switch was released) */
5383
5384     player->switch_x += jx - old_jx;
5385     player->switch_y += jy - old_jy;
5386   }
5387 }
5388
5389 void Explode(int ex, int ey, int phase, int mode)
5390 {
5391   int x, y;
5392   int last_phase;
5393   int border_element;
5394
5395   /* !!! eliminate this variable !!! */
5396   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5397
5398   if (game.explosions_delayed)
5399   {
5400     ExplodeField[ex][ey] = mode;
5401     return;
5402   }
5403
5404   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5405   {
5406     int center_element = Feld[ex][ey];
5407     int artwork_element, explosion_element;     /* set these values later */
5408
5409     /* remove things displayed in background while burning dynamite */
5410     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5411       Back[ex][ey] = 0;
5412
5413     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5414     {
5415       /* put moving element to center field (and let it explode there) */
5416       center_element = MovingOrBlocked2Element(ex, ey);
5417       RemoveMovingField(ex, ey);
5418       Feld[ex][ey] = center_element;
5419     }
5420
5421     /* now "center_element" is finally determined -- set related values now */
5422     artwork_element = center_element;           /* for custom player artwork */
5423     explosion_element = center_element;         /* for custom player artwork */
5424
5425     if (IS_PLAYER(ex, ey))
5426     {
5427       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5428
5429       artwork_element = stored_player[player_nr].artwork_element;
5430
5431       if (level.use_explosion_element[player_nr])
5432       {
5433         explosion_element = level.explosion_element[player_nr];
5434         artwork_element = explosion_element;
5435       }
5436     }
5437
5438     if (mode == EX_TYPE_NORMAL ||
5439         mode == EX_TYPE_CENTER ||
5440         mode == EX_TYPE_CROSS)
5441       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5442
5443     last_phase = element_info[explosion_element].explosion_delay + 1;
5444
5445     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5446     {
5447       int xx = x - ex + 1;
5448       int yy = y - ey + 1;
5449       int element;
5450
5451       if (!IN_LEV_FIELD(x, y) ||
5452           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5453           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5454         continue;
5455
5456       element = Feld[x][y];
5457
5458       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5459       {
5460         element = MovingOrBlocked2Element(x, y);
5461
5462         if (!IS_EXPLOSION_PROOF(element))
5463           RemoveMovingField(x, y);
5464       }
5465
5466       /* indestructible elements can only explode in center (but not flames) */
5467       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5468                                            mode == EX_TYPE_BORDER)) ||
5469           element == EL_FLAMES)
5470         continue;
5471
5472       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5473          behaviour, for example when touching a yamyam that explodes to rocks
5474          with active deadly shield, a rock is created under the player !!! */
5475       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5476 #if 0
5477       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5478           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5479            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5480 #else
5481       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5482 #endif
5483       {
5484         if (IS_ACTIVE_BOMB(element))
5485         {
5486           /* re-activate things under the bomb like gate or penguin */
5487           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5488           Back[x][y] = 0;
5489         }
5490
5491         continue;
5492       }
5493
5494       /* save walkable background elements while explosion on same tile */
5495       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5496           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5497         Back[x][y] = element;
5498
5499       /* ignite explodable elements reached by other explosion */
5500       if (element == EL_EXPLOSION)
5501         element = Store2[x][y];
5502
5503       if (AmoebaNr[x][y] &&
5504           (element == EL_AMOEBA_FULL ||
5505            element == EL_BD_AMOEBA ||
5506            element == EL_AMOEBA_GROWING))
5507       {
5508         AmoebaCnt[AmoebaNr[x][y]]--;
5509         AmoebaCnt2[AmoebaNr[x][y]]--;
5510       }
5511
5512       RemoveField(x, y);
5513
5514       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5515       {
5516         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5517
5518         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5519
5520         if (PLAYERINFO(ex, ey)->use_murphy)
5521           Store[x][y] = EL_EMPTY;
5522       }
5523
5524       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5525          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5526       else if (ELEM_IS_PLAYER(center_element))
5527         Store[x][y] = EL_EMPTY;
5528       else if (center_element == EL_YAMYAM)
5529         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5530       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5531         Store[x][y] = element_info[center_element].content.e[xx][yy];
5532 #if 1
5533       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5534          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5535          otherwise) -- FIX THIS !!! */
5536       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5537         Store[x][y] = element_info[element].content.e[1][1];
5538 #else
5539       else if (!CAN_EXPLODE(element))
5540         Store[x][y] = element_info[element].content.e[1][1];
5541 #endif
5542       else
5543         Store[x][y] = EL_EMPTY;
5544
5545       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5546           center_element == EL_AMOEBA_TO_DIAMOND)
5547         Store2[x][y] = element;
5548
5549       Feld[x][y] = EL_EXPLOSION;
5550       GfxElement[x][y] = artwork_element;
5551
5552       ExplodePhase[x][y] = 1;
5553       ExplodeDelay[x][y] = last_phase;
5554
5555       Stop[x][y] = TRUE;
5556     }
5557
5558     if (center_element == EL_YAMYAM)
5559       game.yamyam_content_nr =
5560         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5561
5562     return;
5563   }
5564
5565   if (Stop[ex][ey])
5566     return;
5567
5568   x = ex;
5569   y = ey;
5570
5571   if (phase == 1)
5572     GfxFrame[x][y] = 0;         /* restart explosion animation */
5573
5574   last_phase = ExplodeDelay[x][y];
5575
5576   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5577
5578   /* this can happen if the player leaves an explosion just in time */
5579   if (GfxElement[x][y] == EL_UNDEFINED)
5580     GfxElement[x][y] = EL_EMPTY;
5581
5582   border_element = Store2[x][y];
5583   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5584     border_element = StorePlayer[x][y];
5585
5586   if (phase == element_info[border_element].ignition_delay ||
5587       phase == last_phase)
5588   {
5589     boolean border_explosion = FALSE;
5590
5591     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5592         !PLAYER_EXPLOSION_PROTECTED(x, y))
5593     {
5594       KillPlayerUnlessExplosionProtected(x, y);
5595       border_explosion = TRUE;
5596     }
5597     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5598     {
5599       Feld[x][y] = Store2[x][y];
5600       Store2[x][y] = 0;
5601       Bang(x, y);
5602       border_explosion = TRUE;
5603     }
5604     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5605     {
5606       AmoebeUmwandeln(x, y);
5607       Store2[x][y] = 0;
5608       border_explosion = TRUE;
5609     }
5610
5611     /* if an element just explodes due to another explosion (chain-reaction),
5612        do not immediately end the new explosion when it was the last frame of
5613        the explosion (as it would be done in the following "if"-statement!) */
5614     if (border_explosion && phase == last_phase)
5615       return;
5616   }
5617
5618   if (phase == last_phase)
5619   {
5620     int element;
5621
5622     element = Feld[x][y] = Store[x][y];
5623     Store[x][y] = Store2[x][y] = 0;
5624     GfxElement[x][y] = EL_UNDEFINED;
5625
5626     /* player can escape from explosions and might therefore be still alive */
5627     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5628         element <= EL_PLAYER_IS_EXPLODING_4)
5629     {
5630       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5631       int explosion_element = EL_PLAYER_1 + player_nr;
5632       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5633       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5634
5635       if (level.use_explosion_element[player_nr])
5636         explosion_element = level.explosion_element[player_nr];
5637
5638       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5639                     element_info[explosion_element].content.e[xx][yy]);
5640     }
5641
5642     /* restore probably existing indestructible background element */
5643     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5644       element = Feld[x][y] = Back[x][y];
5645     Back[x][y] = 0;
5646
5647     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5648     GfxDir[x][y] = MV_NONE;
5649     ChangeDelay[x][y] = 0;
5650     ChangePage[x][y] = -1;
5651
5652     CustomValue[x][y] = 0;
5653
5654     InitField_WithBug2(x, y, FALSE);
5655
5656     TEST_DrawLevelField(x, y);
5657
5658     TestIfElementTouchesCustomElement(x, y);
5659
5660     if (GFX_CRUMBLED(element))
5661       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5662
5663     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5664       StorePlayer[x][y] = 0;
5665
5666     if (ELEM_IS_PLAYER(element))
5667       RelocatePlayer(x, y, element);
5668   }
5669   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5670   {
5671     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5672     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5673
5674     if (phase == delay)
5675       TEST_DrawLevelFieldCrumbled(x, y);
5676
5677     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5678     {
5679       DrawLevelElement(x, y, Back[x][y]);
5680       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5681     }
5682     else if (IS_WALKABLE_UNDER(Back[x][y]))
5683     {
5684       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5685       DrawLevelElementThruMask(x, y, Back[x][y]);
5686     }
5687     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5688       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5689   }
5690 }
5691
5692 void DynaExplode(int ex, int ey)
5693 {
5694   int i, j;
5695   int dynabomb_element = Feld[ex][ey];
5696   int dynabomb_size = 1;
5697   boolean dynabomb_xl = FALSE;
5698   struct PlayerInfo *player;
5699   static int xy[4][2] =
5700   {
5701     { 0, -1 },
5702     { -1, 0 },
5703     { +1, 0 },
5704     { 0, +1 }
5705   };
5706
5707   if (IS_ACTIVE_BOMB(dynabomb_element))
5708   {
5709     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5710     dynabomb_size = player->dynabomb_size;
5711     dynabomb_xl = player->dynabomb_xl;
5712     player->dynabombs_left++;
5713   }
5714
5715   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5716
5717   for (i = 0; i < NUM_DIRECTIONS; i++)
5718   {
5719     for (j = 1; j <= dynabomb_size; j++)
5720     {
5721       int x = ex + j * xy[i][0];
5722       int y = ey + j * xy[i][1];
5723       int element;
5724
5725       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5726         break;
5727
5728       element = Feld[x][y];
5729
5730       /* do not restart explosions of fields with active bombs */
5731       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5732         continue;
5733
5734       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5735
5736       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5737           !IS_DIGGABLE(element) && !dynabomb_xl)
5738         break;
5739     }
5740   }
5741 }
5742
5743 void Bang(int x, int y)
5744 {
5745   int element = MovingOrBlocked2Element(x, y);
5746   int explosion_type = EX_TYPE_NORMAL;
5747
5748   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5749   {
5750     struct PlayerInfo *player = PLAYERINFO(x, y);
5751
5752     element = Feld[x][y] = player->initial_element;
5753
5754     if (level.use_explosion_element[player->index_nr])
5755     {
5756       int explosion_element = level.explosion_element[player->index_nr];
5757
5758       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5759         explosion_type = EX_TYPE_CROSS;
5760       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5761         explosion_type = EX_TYPE_CENTER;
5762     }
5763   }
5764
5765   switch (element)
5766   {
5767     case EL_BUG:
5768     case EL_SPACESHIP:
5769     case EL_BD_BUTTERFLY:
5770     case EL_BD_FIREFLY:
5771     case EL_YAMYAM:
5772     case EL_DARK_YAMYAM:
5773     case EL_ROBOT:
5774     case EL_PACMAN:
5775     case EL_MOLE:
5776       RaiseScoreElement(element);
5777       break;
5778
5779     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5780     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5781     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5782     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5783     case EL_DYNABOMB_INCREASE_NUMBER:
5784     case EL_DYNABOMB_INCREASE_SIZE:
5785     case EL_DYNABOMB_INCREASE_POWER:
5786       explosion_type = EX_TYPE_DYNA;
5787       break;
5788
5789     case EL_DC_LANDMINE:
5790       explosion_type = EX_TYPE_CENTER;
5791       break;
5792
5793     case EL_PENGUIN:
5794     case EL_LAMP:
5795     case EL_LAMP_ACTIVE:
5796     case EL_AMOEBA_TO_DIAMOND:
5797       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5798         explosion_type = EX_TYPE_CENTER;
5799       break;
5800
5801     default:
5802       if (element_info[element].explosion_type == EXPLODES_CROSS)
5803         explosion_type = EX_TYPE_CROSS;
5804       else if (element_info[element].explosion_type == EXPLODES_1X1)
5805         explosion_type = EX_TYPE_CENTER;
5806       break;
5807   }
5808
5809   if (explosion_type == EX_TYPE_DYNA)
5810     DynaExplode(x, y);
5811   else
5812     Explode(x, y, EX_PHASE_START, explosion_type);
5813
5814   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5815 }
5816
5817 void SplashAcid(int x, int y)
5818 {
5819   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5820       (!IN_LEV_FIELD(x - 1, y - 2) ||
5821        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5822     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5823
5824   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5825       (!IN_LEV_FIELD(x + 1, y - 2) ||
5826        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5827     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5828
5829   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5830 }
5831
5832 static void InitBeltMovement()
5833 {
5834   static int belt_base_element[4] =
5835   {
5836     EL_CONVEYOR_BELT_1_LEFT,
5837     EL_CONVEYOR_BELT_2_LEFT,
5838     EL_CONVEYOR_BELT_3_LEFT,
5839     EL_CONVEYOR_BELT_4_LEFT
5840   };
5841   static int belt_base_active_element[4] =
5842   {
5843     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5844     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5845     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5846     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5847   };
5848
5849   int x, y, i, j;
5850
5851   /* set frame order for belt animation graphic according to belt direction */
5852   for (i = 0; i < NUM_BELTS; i++)
5853   {
5854     int belt_nr = i;
5855
5856     for (j = 0; j < NUM_BELT_PARTS; j++)
5857     {
5858       int element = belt_base_active_element[belt_nr] + j;
5859       int graphic_1 = el2img(element);
5860       int graphic_2 = el2panelimg(element);
5861
5862       if (game.belt_dir[i] == MV_LEFT)
5863       {
5864         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5865         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5866       }
5867       else
5868       {
5869         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5870         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5871       }
5872     }
5873   }
5874
5875   SCAN_PLAYFIELD(x, y)
5876   {
5877     int element = Feld[x][y];
5878
5879     for (i = 0; i < NUM_BELTS; i++)
5880     {
5881       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5882       {
5883         int e_belt_nr = getBeltNrFromBeltElement(element);
5884         int belt_nr = i;
5885
5886         if (e_belt_nr == belt_nr)
5887         {
5888           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5889
5890           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5891         }
5892       }
5893     }
5894   }
5895 }
5896
5897 static void ToggleBeltSwitch(int x, int y)
5898 {
5899   static int belt_base_element[4] =
5900   {
5901     EL_CONVEYOR_BELT_1_LEFT,
5902     EL_CONVEYOR_BELT_2_LEFT,
5903     EL_CONVEYOR_BELT_3_LEFT,
5904     EL_CONVEYOR_BELT_4_LEFT
5905   };
5906   static int belt_base_active_element[4] =
5907   {
5908     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5909     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5910     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5911     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5912   };
5913   static int belt_base_switch_element[4] =
5914   {
5915     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5916     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5917     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5918     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5919   };
5920   static int belt_move_dir[4] =
5921   {
5922     MV_LEFT,
5923     MV_NONE,
5924     MV_RIGHT,
5925     MV_NONE,
5926   };
5927
5928   int element = Feld[x][y];
5929   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5930   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5931   int belt_dir = belt_move_dir[belt_dir_nr];
5932   int xx, yy, i;
5933
5934   if (!IS_BELT_SWITCH(element))
5935     return;
5936
5937   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5938   game.belt_dir[belt_nr] = belt_dir;
5939
5940   if (belt_dir_nr == 3)
5941     belt_dir_nr = 1;
5942
5943   /* set frame order for belt animation graphic according to belt direction */
5944   for (i = 0; i < NUM_BELT_PARTS; i++)
5945   {
5946     int element = belt_base_active_element[belt_nr] + i;
5947     int graphic_1 = el2img(element);
5948     int graphic_2 = el2panelimg(element);
5949
5950     if (belt_dir == MV_LEFT)
5951     {
5952       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5953       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5954     }
5955     else
5956     {
5957       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5958       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5959     }
5960   }
5961
5962   SCAN_PLAYFIELD(xx, yy)
5963   {
5964     int element = Feld[xx][yy];
5965
5966     if (IS_BELT_SWITCH(element))
5967     {
5968       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5969
5970       if (e_belt_nr == belt_nr)
5971       {
5972         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5973         TEST_DrawLevelField(xx, yy);
5974       }
5975     }
5976     else if (IS_BELT(element) && belt_dir != MV_NONE)
5977     {
5978       int e_belt_nr = getBeltNrFromBeltElement(element);
5979
5980       if (e_belt_nr == belt_nr)
5981       {
5982         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5983
5984         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5985         TEST_DrawLevelField(xx, yy);
5986       }
5987     }
5988     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5989     {
5990       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5991
5992       if (e_belt_nr == belt_nr)
5993       {
5994         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5995
5996         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5997         TEST_DrawLevelField(xx, yy);
5998       }
5999     }
6000   }
6001 }
6002
6003 static void ToggleSwitchgateSwitch(int x, int y)
6004 {
6005   int xx, yy;
6006
6007   game.switchgate_pos = !game.switchgate_pos;
6008
6009   SCAN_PLAYFIELD(xx, yy)
6010   {
6011     int element = Feld[xx][yy];
6012
6013     if (element == EL_SWITCHGATE_SWITCH_UP)
6014     {
6015       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6016       TEST_DrawLevelField(xx, yy);
6017     }
6018     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6019     {
6020       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6021       TEST_DrawLevelField(xx, yy);
6022     }
6023     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6024     {
6025       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6026       TEST_DrawLevelField(xx, yy);
6027     }
6028     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6029     {
6030       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6031       TEST_DrawLevelField(xx, yy);
6032     }
6033     else if (element == EL_SWITCHGATE_OPEN ||
6034              element == EL_SWITCHGATE_OPENING)
6035     {
6036       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6037
6038       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6039     }
6040     else if (element == EL_SWITCHGATE_CLOSED ||
6041              element == EL_SWITCHGATE_CLOSING)
6042     {
6043       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6044
6045       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6046     }
6047   }
6048 }
6049
6050 static int getInvisibleActiveFromInvisibleElement(int element)
6051 {
6052   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6053           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6054           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6055           element);
6056 }
6057
6058 static int getInvisibleFromInvisibleActiveElement(int element)
6059 {
6060   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6061           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6062           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6063           element);
6064 }
6065
6066 static void RedrawAllLightSwitchesAndInvisibleElements()
6067 {
6068   int x, y;
6069
6070   SCAN_PLAYFIELD(x, y)
6071   {
6072     int element = Feld[x][y];
6073
6074     if (element == EL_LIGHT_SWITCH &&
6075         game.light_time_left > 0)
6076     {
6077       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6078       TEST_DrawLevelField(x, y);
6079     }
6080     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6081              game.light_time_left == 0)
6082     {
6083       Feld[x][y] = EL_LIGHT_SWITCH;
6084       TEST_DrawLevelField(x, y);
6085     }
6086     else if (element == EL_EMC_DRIPPER &&
6087              game.light_time_left > 0)
6088     {
6089       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6090       TEST_DrawLevelField(x, y);
6091     }
6092     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6093              game.light_time_left == 0)
6094     {
6095       Feld[x][y] = EL_EMC_DRIPPER;
6096       TEST_DrawLevelField(x, y);
6097     }
6098     else if (element == EL_INVISIBLE_STEELWALL ||
6099              element == EL_INVISIBLE_WALL ||
6100              element == EL_INVISIBLE_SAND)
6101     {
6102       if (game.light_time_left > 0)
6103         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6104
6105       TEST_DrawLevelField(x, y);
6106
6107       /* uncrumble neighbour fields, if needed */
6108       if (element == EL_INVISIBLE_SAND)
6109         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6110     }
6111     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6112              element == EL_INVISIBLE_WALL_ACTIVE ||
6113              element == EL_INVISIBLE_SAND_ACTIVE)
6114     {
6115       if (game.light_time_left == 0)
6116         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6117
6118       TEST_DrawLevelField(x, y);
6119
6120       /* re-crumble neighbour fields, if needed */
6121       if (element == EL_INVISIBLE_SAND)
6122         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6123     }
6124   }
6125 }
6126
6127 static void RedrawAllInvisibleElementsForLenses()
6128 {
6129   int x, y;
6130
6131   SCAN_PLAYFIELD(x, y)
6132   {
6133     int element = Feld[x][y];
6134
6135     if (element == EL_EMC_DRIPPER &&
6136         game.lenses_time_left > 0)
6137     {
6138       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6139       TEST_DrawLevelField(x, y);
6140     }
6141     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6142              game.lenses_time_left == 0)
6143     {
6144       Feld[x][y] = EL_EMC_DRIPPER;
6145       TEST_DrawLevelField(x, y);
6146     }
6147     else if (element == EL_INVISIBLE_STEELWALL ||
6148              element == EL_INVISIBLE_WALL ||
6149              element == EL_INVISIBLE_SAND)
6150     {
6151       if (game.lenses_time_left > 0)
6152         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6153
6154       TEST_DrawLevelField(x, y);
6155
6156       /* uncrumble neighbour fields, if needed */
6157       if (element == EL_INVISIBLE_SAND)
6158         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6159     }
6160     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6161              element == EL_INVISIBLE_WALL_ACTIVE ||
6162              element == EL_INVISIBLE_SAND_ACTIVE)
6163     {
6164       if (game.lenses_time_left == 0)
6165         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6166
6167       TEST_DrawLevelField(x, y);
6168
6169       /* re-crumble neighbour fields, if needed */
6170       if (element == EL_INVISIBLE_SAND)
6171         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6172     }
6173   }
6174 }
6175
6176 static void RedrawAllInvisibleElementsForMagnifier()
6177 {
6178   int x, y;
6179
6180   SCAN_PLAYFIELD(x, y)
6181   {
6182     int element = Feld[x][y];
6183
6184     if (element == EL_EMC_FAKE_GRASS &&
6185         game.magnify_time_left > 0)
6186     {
6187       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6188       TEST_DrawLevelField(x, y);
6189     }
6190     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6191              game.magnify_time_left == 0)
6192     {
6193       Feld[x][y] = EL_EMC_FAKE_GRASS;
6194       TEST_DrawLevelField(x, y);
6195     }
6196     else if (IS_GATE_GRAY(element) &&
6197              game.magnify_time_left > 0)
6198     {
6199       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6200                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6201                     IS_EM_GATE_GRAY(element) ?
6202                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6203                     IS_EMC_GATE_GRAY(element) ?
6204                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6205                     IS_DC_GATE_GRAY(element) ?
6206                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6207                     element);
6208       TEST_DrawLevelField(x, y);
6209     }
6210     else if (IS_GATE_GRAY_ACTIVE(element) &&
6211              game.magnify_time_left == 0)
6212     {
6213       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6214                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6215                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6216                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6217                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6218                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6219                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6220                     EL_DC_GATE_WHITE_GRAY :
6221                     element);
6222       TEST_DrawLevelField(x, y);
6223     }
6224   }
6225 }
6226
6227 static void ToggleLightSwitch(int x, int y)
6228 {
6229   int element = Feld[x][y];
6230
6231   game.light_time_left =
6232     (element == EL_LIGHT_SWITCH ?
6233      level.time_light * FRAMES_PER_SECOND : 0);
6234
6235   RedrawAllLightSwitchesAndInvisibleElements();
6236 }
6237
6238 static void ActivateTimegateSwitch(int x, int y)
6239 {
6240   int xx, yy;
6241
6242   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6243
6244   SCAN_PLAYFIELD(xx, yy)
6245   {
6246     int element = Feld[xx][yy];
6247
6248     if (element == EL_TIMEGATE_CLOSED ||
6249         element == EL_TIMEGATE_CLOSING)
6250     {
6251       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6252       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6253     }
6254
6255     /*
6256     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6257     {
6258       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6259       TEST_DrawLevelField(xx, yy);
6260     }
6261     */
6262
6263   }
6264
6265   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6266                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6267 }
6268
6269 void Impact(int x, int y)
6270 {
6271   boolean last_line = (y == lev_fieldy - 1);
6272   boolean object_hit = FALSE;
6273   boolean impact = (last_line || object_hit);
6274   int element = Feld[x][y];
6275   int smashed = EL_STEELWALL;
6276
6277   if (!last_line)       /* check if element below was hit */
6278   {
6279     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6280       return;
6281
6282     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6283                                          MovDir[x][y + 1] != MV_DOWN ||
6284                                          MovPos[x][y + 1] <= TILEY / 2));
6285
6286     /* do not smash moving elements that left the smashed field in time */
6287     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6288         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6289       object_hit = FALSE;
6290
6291 #if USE_QUICKSAND_IMPACT_BUGFIX
6292     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6293     {
6294       RemoveMovingField(x, y + 1);
6295       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6296       Feld[x][y + 2] = EL_ROCK;
6297       TEST_DrawLevelField(x, y + 2);
6298
6299       object_hit = TRUE;
6300     }
6301
6302     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6303     {
6304       RemoveMovingField(x, y + 1);
6305       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6306       Feld[x][y + 2] = EL_ROCK;
6307       TEST_DrawLevelField(x, y + 2);
6308
6309       object_hit = TRUE;
6310     }
6311 #endif
6312
6313     if (object_hit)
6314       smashed = MovingOrBlocked2Element(x, y + 1);
6315
6316     impact = (last_line || object_hit);
6317   }
6318
6319   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6320   {
6321     SplashAcid(x, y + 1);
6322     return;
6323   }
6324
6325   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6326   /* only reset graphic animation if graphic really changes after impact */
6327   if (impact &&
6328       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6329   {
6330     ResetGfxAnimation(x, y);
6331     TEST_DrawLevelField(x, y);
6332   }
6333
6334   if (impact && CAN_EXPLODE_IMPACT(element))
6335   {
6336     Bang(x, y);
6337     return;
6338   }
6339   else if (impact && element == EL_PEARL &&
6340            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6341   {
6342     ResetGfxAnimation(x, y);
6343
6344     Feld[x][y] = EL_PEARL_BREAKING;
6345     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6346     return;
6347   }
6348   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6349   {
6350     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6351
6352     return;
6353   }
6354
6355   if (impact && element == EL_AMOEBA_DROP)
6356   {
6357     if (object_hit && IS_PLAYER(x, y + 1))
6358       KillPlayerUnlessEnemyProtected(x, y + 1);
6359     else if (object_hit && smashed == EL_PENGUIN)
6360       Bang(x, y + 1);
6361     else
6362     {
6363       Feld[x][y] = EL_AMOEBA_GROWING;
6364       Store[x][y] = EL_AMOEBA_WET;
6365
6366       ResetRandomAnimationValue(x, y);
6367     }
6368     return;
6369   }
6370
6371   if (object_hit)               /* check which object was hit */
6372   {
6373     if ((CAN_PASS_MAGIC_WALL(element) && 
6374          (smashed == EL_MAGIC_WALL ||
6375           smashed == EL_BD_MAGIC_WALL)) ||
6376         (CAN_PASS_DC_MAGIC_WALL(element) &&
6377          smashed == EL_DC_MAGIC_WALL))
6378     {
6379       int xx, yy;
6380       int activated_magic_wall =
6381         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6382          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6383          EL_DC_MAGIC_WALL_ACTIVE);
6384
6385       /* activate magic wall / mill */
6386       SCAN_PLAYFIELD(xx, yy)
6387       {
6388         if (Feld[xx][yy] == smashed)
6389           Feld[xx][yy] = activated_magic_wall;
6390       }
6391
6392       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6393       game.magic_wall_active = TRUE;
6394
6395       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6396                             SND_MAGIC_WALL_ACTIVATING :
6397                             smashed == EL_BD_MAGIC_WALL ?
6398                             SND_BD_MAGIC_WALL_ACTIVATING :
6399                             SND_DC_MAGIC_WALL_ACTIVATING));
6400     }
6401
6402     if (IS_PLAYER(x, y + 1))
6403     {
6404       if (CAN_SMASH_PLAYER(element))
6405       {
6406         KillPlayerUnlessEnemyProtected(x, y + 1);
6407         return;
6408       }
6409     }
6410     else if (smashed == EL_PENGUIN)
6411     {
6412       if (CAN_SMASH_PLAYER(element))
6413       {
6414         Bang(x, y + 1);
6415         return;
6416       }
6417     }
6418     else if (element == EL_BD_DIAMOND)
6419     {
6420       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6421       {
6422         Bang(x, y + 1);
6423         return;
6424       }
6425     }
6426     else if (((element == EL_SP_INFOTRON ||
6427                element == EL_SP_ZONK) &&
6428               (smashed == EL_SP_SNIKSNAK ||
6429                smashed == EL_SP_ELECTRON ||
6430                smashed == EL_SP_DISK_ORANGE)) ||
6431              (element == EL_SP_INFOTRON &&
6432               smashed == EL_SP_DISK_YELLOW))
6433     {
6434       Bang(x, y + 1);
6435       return;
6436     }
6437     else if (CAN_SMASH_EVERYTHING(element))
6438     {
6439       if (IS_CLASSIC_ENEMY(smashed) ||
6440           CAN_EXPLODE_SMASHED(smashed))
6441       {
6442         Bang(x, y + 1);
6443         return;
6444       }
6445       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6446       {
6447         if (smashed == EL_LAMP ||
6448             smashed == EL_LAMP_ACTIVE)
6449         {
6450           Bang(x, y + 1);
6451           return;
6452         }
6453         else if (smashed == EL_NUT)
6454         {
6455           Feld[x][y + 1] = EL_NUT_BREAKING;
6456           PlayLevelSound(x, y, SND_NUT_BREAKING);
6457           RaiseScoreElement(EL_NUT);
6458           return;
6459         }
6460         else if (smashed == EL_PEARL)
6461         {
6462           ResetGfxAnimation(x, y);
6463
6464           Feld[x][y + 1] = EL_PEARL_BREAKING;
6465           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6466           return;
6467         }
6468         else if (smashed == EL_DIAMOND)
6469         {
6470           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6471           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6472           return;
6473         }
6474         else if (IS_BELT_SWITCH(smashed))
6475         {
6476           ToggleBeltSwitch(x, y + 1);
6477         }
6478         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6479                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6480                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6481                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6482         {
6483           ToggleSwitchgateSwitch(x, y + 1);
6484         }
6485         else if (smashed == EL_LIGHT_SWITCH ||
6486                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6487         {
6488           ToggleLightSwitch(x, y + 1);
6489         }
6490         else
6491         {
6492           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6493
6494           CheckElementChangeBySide(x, y + 1, smashed, element,
6495                                    CE_SWITCHED, CH_SIDE_TOP);
6496           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6497                                             CH_SIDE_TOP);
6498         }
6499       }
6500       else
6501       {
6502         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6503       }
6504     }
6505   }
6506
6507   /* play sound of magic wall / mill */
6508   if (!last_line &&
6509       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6510        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6511        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6512   {
6513     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6514       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6515     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6516       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6517     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6518       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6519
6520     return;
6521   }
6522
6523   /* play sound of object that hits the ground */
6524   if (last_line || object_hit)
6525     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6526 }
6527
6528 inline static void TurnRoundExt(int x, int y)
6529 {
6530   static struct
6531   {
6532     int dx, dy;
6533   } move_xy[] =
6534   {
6535     {  0,  0 },
6536     { -1,  0 },
6537     { +1,  0 },
6538     {  0,  0 },
6539     {  0, -1 },
6540     {  0,  0 }, { 0, 0 }, { 0, 0 },
6541     {  0, +1 }
6542   };
6543   static struct
6544   {
6545     int left, right, back;
6546   } turn[] =
6547   {
6548     { 0,        0,              0        },
6549     { MV_DOWN,  MV_UP,          MV_RIGHT },
6550     { MV_UP,    MV_DOWN,        MV_LEFT  },
6551     { 0,        0,              0        },
6552     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6553     { 0,        0,              0        },
6554     { 0,        0,              0        },
6555     { 0,        0,              0        },
6556     { MV_RIGHT, MV_LEFT,        MV_UP    }
6557   };
6558
6559   int element = Feld[x][y];
6560   int move_pattern = element_info[element].move_pattern;
6561
6562   int old_move_dir = MovDir[x][y];
6563   int left_dir  = turn[old_move_dir].left;
6564   int right_dir = turn[old_move_dir].right;
6565   int back_dir  = turn[old_move_dir].back;
6566
6567   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6568   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6569   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6570   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6571
6572   int left_x  = x + left_dx,  left_y  = y + left_dy;
6573   int right_x = x + right_dx, right_y = y + right_dy;
6574   int move_x  = x + move_dx,  move_y  = y + move_dy;
6575
6576   int xx, yy;
6577
6578   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6579   {
6580     TestIfBadThingTouchesOtherBadThing(x, y);
6581
6582     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6583       MovDir[x][y] = right_dir;
6584     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6585       MovDir[x][y] = left_dir;
6586
6587     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6588       MovDelay[x][y] = 9;
6589     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6590       MovDelay[x][y] = 1;
6591   }
6592   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6593   {
6594     TestIfBadThingTouchesOtherBadThing(x, y);
6595
6596     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6597       MovDir[x][y] = left_dir;
6598     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6599       MovDir[x][y] = right_dir;
6600
6601     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6602       MovDelay[x][y] = 9;
6603     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6604       MovDelay[x][y] = 1;
6605   }
6606   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6607   {
6608     TestIfBadThingTouchesOtherBadThing(x, y);
6609
6610     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6611       MovDir[x][y] = left_dir;
6612     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6613       MovDir[x][y] = right_dir;
6614
6615     if (MovDir[x][y] != old_move_dir)
6616       MovDelay[x][y] = 9;
6617   }
6618   else if (element == EL_YAMYAM)
6619   {
6620     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6621     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6622
6623     if (can_turn_left && can_turn_right)
6624       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6625     else if (can_turn_left)
6626       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6627     else if (can_turn_right)
6628       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6629     else
6630       MovDir[x][y] = back_dir;
6631
6632     MovDelay[x][y] = 16 + 16 * RND(3);
6633   }
6634   else if (element == EL_DARK_YAMYAM)
6635   {
6636     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6637                                                          left_x, left_y);
6638     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6639                                                          right_x, right_y);
6640
6641     if (can_turn_left && can_turn_right)
6642       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6643     else if (can_turn_left)
6644       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6645     else if (can_turn_right)
6646       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6647     else
6648       MovDir[x][y] = back_dir;
6649
6650     MovDelay[x][y] = 16 + 16 * RND(3);
6651   }
6652   else if (element == EL_PACMAN)
6653   {
6654     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6655     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6656
6657     if (can_turn_left && can_turn_right)
6658       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6659     else if (can_turn_left)
6660       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6661     else if (can_turn_right)
6662       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6663     else
6664       MovDir[x][y] = back_dir;
6665
6666     MovDelay[x][y] = 6 + RND(40);
6667   }
6668   else if (element == EL_PIG)
6669   {
6670     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6671     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6672     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6673     boolean should_turn_left, should_turn_right, should_move_on;
6674     int rnd_value = 24;
6675     int rnd = RND(rnd_value);
6676
6677     should_turn_left = (can_turn_left &&
6678                         (!can_move_on ||
6679                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6680                                                    y + back_dy + left_dy)));
6681     should_turn_right = (can_turn_right &&
6682                          (!can_move_on ||
6683                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6684                                                     y + back_dy + right_dy)));
6685     should_move_on = (can_move_on &&
6686                       (!can_turn_left ||
6687                        !can_turn_right ||
6688                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6689                                                  y + move_dy + left_dy) ||
6690                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6691                                                  y + move_dy + right_dy)));
6692
6693     if (should_turn_left || should_turn_right || should_move_on)
6694     {
6695       if (should_turn_left && should_turn_right && should_move_on)
6696         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6697                         rnd < 2 * rnd_value / 3 ? right_dir :
6698                         old_move_dir);
6699       else if (should_turn_left && should_turn_right)
6700         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6701       else if (should_turn_left && should_move_on)
6702         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6703       else if (should_turn_right && should_move_on)
6704         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6705       else if (should_turn_left)
6706         MovDir[x][y] = left_dir;
6707       else if (should_turn_right)
6708         MovDir[x][y] = right_dir;
6709       else if (should_move_on)
6710         MovDir[x][y] = old_move_dir;
6711     }
6712     else 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(xx, yy) ||
6727         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6728       MovDir[x][y] = old_move_dir;
6729
6730     MovDelay[x][y] = 0;
6731   }
6732   else if (element == EL_DRAGON)
6733   {
6734     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6735     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6736     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6737     int rnd_value = 24;
6738     int rnd = RND(rnd_value);
6739
6740     if (can_move_on && rnd > rnd_value / 8)
6741       MovDir[x][y] = old_move_dir;
6742     else if (can_turn_left && can_turn_right)
6743       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6744     else if (can_turn_left && rnd > rnd_value / 8)
6745       MovDir[x][y] = left_dir;
6746     else if (can_turn_right && rnd > rnd_value / 8)
6747       MovDir[x][y] = right_dir;
6748     else
6749       MovDir[x][y] = back_dir;
6750
6751     xx = x + move_xy[MovDir[x][y]].dx;
6752     yy = y + move_xy[MovDir[x][y]].dy;
6753
6754     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6755       MovDir[x][y] = old_move_dir;
6756
6757     MovDelay[x][y] = 0;
6758   }
6759   else if (element == EL_MOLE)
6760   {
6761     boolean can_move_on =
6762       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6763                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6764                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6765     if (!can_move_on)
6766     {
6767       boolean can_turn_left =
6768         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6769                               IS_AMOEBOID(Feld[left_x][left_y])));
6770
6771       boolean can_turn_right =
6772         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6773                               IS_AMOEBOID(Feld[right_x][right_y])));
6774
6775       if (can_turn_left && can_turn_right)
6776         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6777       else if (can_turn_left)
6778         MovDir[x][y] = left_dir;
6779       else
6780         MovDir[x][y] = right_dir;
6781     }
6782
6783     if (MovDir[x][y] != old_move_dir)
6784       MovDelay[x][y] = 9;
6785   }
6786   else if (element == EL_BALLOON)
6787   {
6788     MovDir[x][y] = game.wind_direction;
6789     MovDelay[x][y] = 0;
6790   }
6791   else if (element == EL_SPRING)
6792   {
6793     if (MovDir[x][y] & MV_HORIZONTAL)
6794     {
6795       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6796           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6797       {
6798         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6799         ResetGfxAnimation(move_x, move_y);
6800         TEST_DrawLevelField(move_x, move_y);
6801
6802         MovDir[x][y] = back_dir;
6803       }
6804       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6805                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6806         MovDir[x][y] = MV_NONE;
6807     }
6808
6809     MovDelay[x][y] = 0;
6810   }
6811   else if (element == EL_ROBOT ||
6812            element == EL_SATELLITE ||
6813            element == EL_PENGUIN ||
6814            element == EL_EMC_ANDROID)
6815   {
6816     int attr_x = -1, attr_y = -1;
6817
6818     if (AllPlayersGone)
6819     {
6820       attr_x = ExitX;
6821       attr_y = ExitY;
6822     }
6823     else
6824     {
6825       int i;
6826
6827       for (i = 0; i < MAX_PLAYERS; i++)
6828       {
6829         struct PlayerInfo *player = &stored_player[i];
6830         int jx = player->jx, jy = player->jy;
6831
6832         if (!player->active)
6833           continue;
6834
6835         if (attr_x == -1 ||
6836             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6837         {
6838           attr_x = jx;
6839           attr_y = jy;
6840         }
6841       }
6842     }
6843
6844     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6845         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6846          game.engine_version < VERSION_IDENT(3,1,0,0)))
6847     {
6848       attr_x = ZX;
6849       attr_y = ZY;
6850     }
6851
6852     if (element == EL_PENGUIN)
6853     {
6854       int i;
6855       static int xy[4][2] =
6856       {
6857         { 0, -1 },
6858         { -1, 0 },
6859         { +1, 0 },
6860         { 0, +1 }
6861       };
6862
6863       for (i = 0; i < NUM_DIRECTIONS; i++)
6864       {
6865         int ex = x + xy[i][0];
6866         int ey = y + xy[i][1];
6867
6868         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6869                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6870                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6871                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6872         {
6873           attr_x = ex;
6874           attr_y = ey;
6875           break;
6876         }
6877       }
6878     }
6879
6880     MovDir[x][y] = MV_NONE;
6881     if (attr_x < x)
6882       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6883     else if (attr_x > x)
6884       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6885     if (attr_y < y)
6886       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6887     else if (attr_y > y)
6888       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6889
6890     if (element == EL_ROBOT)
6891     {
6892       int newx, newy;
6893
6894       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6895         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6896       Moving2Blocked(x, y, &newx, &newy);
6897
6898       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6899         MovDelay[x][y] = 8 + 8 * !RND(3);
6900       else
6901         MovDelay[x][y] = 16;
6902     }
6903     else if (element == EL_PENGUIN)
6904     {
6905       int newx, newy;
6906
6907       MovDelay[x][y] = 1;
6908
6909       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6910       {
6911         boolean first_horiz = RND(2);
6912         int new_move_dir = MovDir[x][y];
6913
6914         MovDir[x][y] =
6915           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6916         Moving2Blocked(x, y, &newx, &newy);
6917
6918         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6919           return;
6920
6921         MovDir[x][y] =
6922           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6923         Moving2Blocked(x, y, &newx, &newy);
6924
6925         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6926           return;
6927
6928         MovDir[x][y] = old_move_dir;
6929         return;
6930       }
6931     }
6932     else if (element == EL_SATELLITE)
6933     {
6934       int newx, newy;
6935
6936       MovDelay[x][y] = 1;
6937
6938       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6939       {
6940         boolean first_horiz = RND(2);
6941         int new_move_dir = MovDir[x][y];
6942
6943         MovDir[x][y] =
6944           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6945         Moving2Blocked(x, y, &newx, &newy);
6946
6947         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6948           return;
6949
6950         MovDir[x][y] =
6951           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6952         Moving2Blocked(x, y, &newx, &newy);
6953
6954         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6955           return;
6956
6957         MovDir[x][y] = old_move_dir;
6958         return;
6959       }
6960     }
6961     else if (element == EL_EMC_ANDROID)
6962     {
6963       static int check_pos[16] =
6964       {
6965         -1,             /*  0 => (invalid)          */
6966         7,              /*  1 => MV_LEFT            */
6967         3,              /*  2 => MV_RIGHT           */
6968         -1,             /*  3 => (invalid)          */
6969         1,              /*  4 =>            MV_UP   */
6970         0,              /*  5 => MV_LEFT  | MV_UP   */
6971         2,              /*  6 => MV_RIGHT | MV_UP   */
6972         -1,             /*  7 => (invalid)          */
6973         5,              /*  8 =>            MV_DOWN */
6974         6,              /*  9 => MV_LEFT  | MV_DOWN */
6975         4,              /* 10 => MV_RIGHT | MV_DOWN */
6976         -1,             /* 11 => (invalid)          */
6977         -1,             /* 12 => (invalid)          */
6978         -1,             /* 13 => (invalid)          */
6979         -1,             /* 14 => (invalid)          */
6980         -1,             /* 15 => (invalid)          */
6981       };
6982       static struct
6983       {
6984         int dx, dy;
6985         int dir;
6986       } check_xy[8] =
6987       {
6988         { -1, -1,       MV_LEFT  | MV_UP   },
6989         {  0, -1,                  MV_UP   },
6990         { +1, -1,       MV_RIGHT | MV_UP   },
6991         { +1,  0,       MV_RIGHT           },
6992         { +1, +1,       MV_RIGHT | MV_DOWN },
6993         {  0, +1,                  MV_DOWN },
6994         { -1, +1,       MV_LEFT  | MV_DOWN },
6995         { -1,  0,       MV_LEFT            },
6996       };
6997       int start_pos, check_order;
6998       boolean can_clone = FALSE;
6999       int i;
7000
7001       /* check if there is any free field around current position */
7002       for (i = 0; i < 8; i++)
7003       {
7004         int newx = x + check_xy[i].dx;
7005         int newy = y + check_xy[i].dy;
7006
7007         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7008         {
7009           can_clone = TRUE;
7010
7011           break;
7012         }
7013       }
7014
7015       if (can_clone)            /* randomly find an element to clone */
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
7029           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7030           {
7031             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7032             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7033
7034             Store[x][y] = Feld[newx][newy];
7035
7036             can_clone = TRUE;
7037
7038             break;
7039           }
7040         }
7041       }
7042
7043       if (can_clone)            /* randomly find a direction to move */
7044       {
7045         can_clone = FALSE;
7046
7047         start_pos = check_pos[RND(8)];
7048         check_order = (RND(2) ? -1 : +1);
7049
7050         for (i = 0; i < 8; i++)
7051         {
7052           int pos_raw = start_pos + i * check_order;
7053           int pos = (pos_raw + 8) % 8;
7054           int newx = x + check_xy[pos].dx;
7055           int newy = y + check_xy[pos].dy;
7056           int new_move_dir = check_xy[pos].dir;
7057
7058           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7059           {
7060             MovDir[x][y] = new_move_dir;
7061             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7062
7063             can_clone = TRUE;
7064
7065             break;
7066           }
7067         }
7068       }
7069
7070       if (can_clone)            /* cloning and moving successful */
7071         return;
7072
7073       /* cannot clone -- try to move towards player */
7074
7075       start_pos = check_pos[MovDir[x][y] & 0x0f];
7076       check_order = (RND(2) ? -1 : +1);
7077
7078       for (i = 0; i < 3; i++)
7079       {
7080         /* first check start_pos, then previous/next or (next/previous) pos */
7081         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7082         int pos = (pos_raw + 8) % 8;
7083         int newx = x + check_xy[pos].dx;
7084         int newy = y + check_xy[pos].dy;
7085         int new_move_dir = check_xy[pos].dir;
7086
7087         if (IS_PLAYER(newx, newy))
7088           break;
7089
7090         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7091         {
7092           MovDir[x][y] = new_move_dir;
7093           MovDelay[x][y] = level.android_move_time * 8 + 1;
7094
7095           break;
7096         }
7097       }
7098     }
7099   }
7100   else if (move_pattern == MV_TURNING_LEFT ||
7101            move_pattern == MV_TURNING_RIGHT ||
7102            move_pattern == MV_TURNING_LEFT_RIGHT ||
7103            move_pattern == MV_TURNING_RIGHT_LEFT ||
7104            move_pattern == MV_TURNING_RANDOM ||
7105            move_pattern == MV_ALL_DIRECTIONS)
7106   {
7107     boolean can_turn_left =
7108       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7109     boolean can_turn_right =
7110       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7111
7112     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7113       return;
7114
7115     if (move_pattern == MV_TURNING_LEFT)
7116       MovDir[x][y] = left_dir;
7117     else if (move_pattern == MV_TURNING_RIGHT)
7118       MovDir[x][y] = right_dir;
7119     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7120       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7121     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7122       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7123     else if (move_pattern == MV_TURNING_RANDOM)
7124       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7125                       can_turn_right && !can_turn_left ? right_dir :
7126                       RND(2) ? left_dir : right_dir);
7127     else if (can_turn_left && can_turn_right)
7128       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7129     else if (can_turn_left)
7130       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7131     else if (can_turn_right)
7132       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7133     else
7134       MovDir[x][y] = back_dir;
7135
7136     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7137   }
7138   else if (move_pattern == MV_HORIZONTAL ||
7139            move_pattern == MV_VERTICAL)
7140   {
7141     if (move_pattern & old_move_dir)
7142       MovDir[x][y] = back_dir;
7143     else if (move_pattern == MV_HORIZONTAL)
7144       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7145     else if (move_pattern == MV_VERTICAL)
7146       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7147
7148     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7149   }
7150   else if (move_pattern & MV_ANY_DIRECTION)
7151   {
7152     MovDir[x][y] = move_pattern;
7153     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7154   }
7155   else if (move_pattern & MV_WIND_DIRECTION)
7156   {
7157     MovDir[x][y] = game.wind_direction;
7158     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7159   }
7160   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7161   {
7162     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7163       MovDir[x][y] = left_dir;
7164     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7165       MovDir[x][y] = right_dir;
7166
7167     if (MovDir[x][y] != old_move_dir)
7168       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7169   }
7170   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7171   {
7172     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7173       MovDir[x][y] = right_dir;
7174     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7175       MovDir[x][y] = left_dir;
7176
7177     if (MovDir[x][y] != old_move_dir)
7178       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7179   }
7180   else if (move_pattern == MV_TOWARDS_PLAYER ||
7181            move_pattern == MV_AWAY_FROM_PLAYER)
7182   {
7183     int attr_x = -1, attr_y = -1;
7184     int newx, newy;
7185     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7186
7187     if (AllPlayersGone)
7188     {
7189       attr_x = ExitX;
7190       attr_y = ExitY;
7191     }
7192     else
7193     {
7194       int i;
7195
7196       for (i = 0; i < MAX_PLAYERS; i++)
7197       {
7198         struct PlayerInfo *player = &stored_player[i];
7199         int jx = player->jx, jy = player->jy;
7200
7201         if (!player->active)
7202           continue;
7203
7204         if (attr_x == -1 ||
7205             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7206         {
7207           attr_x = jx;
7208           attr_y = jy;
7209         }
7210       }
7211     }
7212
7213     MovDir[x][y] = MV_NONE;
7214     if (attr_x < x)
7215       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7216     else if (attr_x > x)
7217       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7218     if (attr_y < y)
7219       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7220     else if (attr_y > y)
7221       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7222
7223     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7224
7225     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7226     {
7227       boolean first_horiz = RND(2);
7228       int new_move_dir = MovDir[x][y];
7229
7230       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7231       {
7232         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7233         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7234
7235         return;
7236       }
7237
7238       MovDir[x][y] =
7239         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7240       Moving2Blocked(x, y, &newx, &newy);
7241
7242       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7243         return;
7244
7245       MovDir[x][y] =
7246         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7247       Moving2Blocked(x, y, &newx, &newy);
7248
7249       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7250         return;
7251
7252       MovDir[x][y] = old_move_dir;
7253     }
7254   }
7255   else if (move_pattern == MV_WHEN_PUSHED ||
7256            move_pattern == MV_WHEN_DROPPED)
7257   {
7258     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7259       MovDir[x][y] = MV_NONE;
7260
7261     MovDelay[x][y] = 0;
7262   }
7263   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7264   {
7265     static int test_xy[7][2] =
7266     {
7267       { 0, -1 },
7268       { -1, 0 },
7269       { +1, 0 },
7270       { 0, +1 },
7271       { 0, -1 },
7272       { -1, 0 },
7273       { +1, 0 },
7274     };
7275     static int test_dir[7] =
7276     {
7277       MV_UP,
7278       MV_LEFT,
7279       MV_RIGHT,
7280       MV_DOWN,
7281       MV_UP,
7282       MV_LEFT,
7283       MV_RIGHT,
7284     };
7285     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7286     int move_preference = -1000000;     /* start with very low preference */
7287     int new_move_dir = MV_NONE;
7288     int start_test = RND(4);
7289     int i;
7290
7291     for (i = 0; i < NUM_DIRECTIONS; i++)
7292     {
7293       int move_dir = test_dir[start_test + i];
7294       int move_dir_preference;
7295
7296       xx = x + test_xy[start_test + i][0];
7297       yy = y + test_xy[start_test + i][1];
7298
7299       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7300           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7301       {
7302         new_move_dir = move_dir;
7303
7304         break;
7305       }
7306
7307       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7308         continue;
7309
7310       move_dir_preference = -1 * RunnerVisit[xx][yy];
7311       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7312         move_dir_preference = PlayerVisit[xx][yy];
7313
7314       if (move_dir_preference > move_preference)
7315       {
7316         /* prefer field that has not been visited for the longest time */
7317         move_preference = move_dir_preference;
7318         new_move_dir = move_dir;
7319       }
7320       else if (move_dir_preference == move_preference &&
7321                move_dir == old_move_dir)
7322       {
7323         /* prefer last direction when all directions are preferred equally */
7324         move_preference = move_dir_preference;
7325         new_move_dir = move_dir;
7326       }
7327     }
7328
7329     MovDir[x][y] = new_move_dir;
7330     if (old_move_dir != new_move_dir)
7331       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7332   }
7333 }
7334
7335 static void TurnRound(int x, int y)
7336 {
7337   int direction = MovDir[x][y];
7338
7339   TurnRoundExt(x, y);
7340
7341   GfxDir[x][y] = MovDir[x][y];
7342
7343   if (direction != MovDir[x][y])
7344     GfxFrame[x][y] = 0;
7345
7346   if (MovDelay[x][y])
7347     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7348
7349   ResetGfxFrame(x, y);
7350 }
7351
7352 static boolean JustBeingPushed(int x, int y)
7353 {
7354   int i;
7355
7356   for (i = 0; i < MAX_PLAYERS; i++)
7357   {
7358     struct PlayerInfo *player = &stored_player[i];
7359
7360     if (player->active && player->is_pushing && player->MovPos)
7361     {
7362       int next_jx = player->jx + (player->jx - player->last_jx);
7363       int next_jy = player->jy + (player->jy - player->last_jy);
7364
7365       if (x == next_jx && y == next_jy)
7366         return TRUE;
7367     }
7368   }
7369
7370   return FALSE;
7371 }
7372
7373 void StartMoving(int x, int y)
7374 {
7375   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7376   int element = Feld[x][y];
7377
7378   if (Stop[x][y])
7379     return;
7380
7381   if (MovDelay[x][y] == 0)
7382     GfxAction[x][y] = ACTION_DEFAULT;
7383
7384   if (CAN_FALL(element) && y < lev_fieldy - 1)
7385   {
7386     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7387         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7388       if (JustBeingPushed(x, y))
7389         return;
7390
7391     if (element == EL_QUICKSAND_FULL)
7392     {
7393       if (IS_FREE(x, y + 1))
7394       {
7395         InitMovingField(x, y, MV_DOWN);
7396         started_moving = TRUE;
7397
7398         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7399 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7400         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7401           Store[x][y] = EL_ROCK;
7402 #else
7403         Store[x][y] = EL_ROCK;
7404 #endif
7405
7406         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7407       }
7408       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7409       {
7410         if (!MovDelay[x][y])
7411         {
7412           MovDelay[x][y] = TILEY + 1;
7413
7414           ResetGfxAnimation(x, y);
7415           ResetGfxAnimation(x, y + 1);
7416         }
7417
7418         if (MovDelay[x][y])
7419         {
7420           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7421           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7422
7423           MovDelay[x][y]--;
7424           if (MovDelay[x][y])
7425             return;
7426         }
7427
7428         Feld[x][y] = EL_QUICKSAND_EMPTY;
7429         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7430         Store[x][y + 1] = Store[x][y];
7431         Store[x][y] = 0;
7432
7433         PlayLevelSoundAction(x, y, ACTION_FILLING);
7434       }
7435       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7436       {
7437         if (!MovDelay[x][y])
7438         {
7439           MovDelay[x][y] = TILEY + 1;
7440
7441           ResetGfxAnimation(x, y);
7442           ResetGfxAnimation(x, y + 1);
7443         }
7444
7445         if (MovDelay[x][y])
7446         {
7447           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7448           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7449
7450           MovDelay[x][y]--;
7451           if (MovDelay[x][y])
7452             return;
7453         }
7454
7455         Feld[x][y] = EL_QUICKSAND_EMPTY;
7456         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7457         Store[x][y + 1] = Store[x][y];
7458         Store[x][y] = 0;
7459
7460         PlayLevelSoundAction(x, y, ACTION_FILLING);
7461       }
7462     }
7463     else if (element == EL_QUICKSAND_FAST_FULL)
7464     {
7465       if (IS_FREE(x, y + 1))
7466       {
7467         InitMovingField(x, y, MV_DOWN);
7468         started_moving = TRUE;
7469
7470         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7471 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7472         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7473           Store[x][y] = EL_ROCK;
7474 #else
7475         Store[x][y] = EL_ROCK;
7476 #endif
7477
7478         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7479       }
7480       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7481       {
7482         if (!MovDelay[x][y])
7483         {
7484           MovDelay[x][y] = TILEY + 1;
7485
7486           ResetGfxAnimation(x, y);
7487           ResetGfxAnimation(x, y + 1);
7488         }
7489
7490         if (MovDelay[x][y])
7491         {
7492           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7493           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7494
7495           MovDelay[x][y]--;
7496           if (MovDelay[x][y])
7497             return;
7498         }
7499
7500         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7501         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7502         Store[x][y + 1] = Store[x][y];
7503         Store[x][y] = 0;
7504
7505         PlayLevelSoundAction(x, y, ACTION_FILLING);
7506       }
7507       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7508       {
7509         if (!MovDelay[x][y])
7510         {
7511           MovDelay[x][y] = TILEY + 1;
7512
7513           ResetGfxAnimation(x, y);
7514           ResetGfxAnimation(x, y + 1);
7515         }
7516
7517         if (MovDelay[x][y])
7518         {
7519           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7520           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7521
7522           MovDelay[x][y]--;
7523           if (MovDelay[x][y])
7524             return;
7525         }
7526
7527         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7528         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7529         Store[x][y + 1] = Store[x][y];
7530         Store[x][y] = 0;
7531
7532         PlayLevelSoundAction(x, y, ACTION_FILLING);
7533       }
7534     }
7535     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7536              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7537     {
7538       InitMovingField(x, y, MV_DOWN);
7539       started_moving = TRUE;
7540
7541       Feld[x][y] = EL_QUICKSAND_FILLING;
7542       Store[x][y] = element;
7543
7544       PlayLevelSoundAction(x, y, ACTION_FILLING);
7545     }
7546     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7547              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7548     {
7549       InitMovingField(x, y, MV_DOWN);
7550       started_moving = TRUE;
7551
7552       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7553       Store[x][y] = element;
7554
7555       PlayLevelSoundAction(x, y, ACTION_FILLING);
7556     }
7557     else if (element == EL_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_MAGIC_WALL_EMPTYING;
7565         Store[x][y] = EL_CHANGED(Store[x][y]);
7566       }
7567       else if (Feld[x][y + 1] == EL_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_MAGIC_WALL_ACTIVE;
7580         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7581         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7582         Store[x][y] = 0;
7583       }
7584     }
7585     else if (element == EL_BD_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_BD_MAGIC_WALL_EMPTYING;
7593         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7594       }
7595       else if (Feld[x][y + 1] == EL_BD_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_BD_MAGIC_WALL_ACTIVE;
7608         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7609         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7610         Store[x][y] = 0;
7611       }
7612     }
7613     else if (element == EL_DC_MAGIC_WALL_FULL)
7614     {
7615       if (IS_FREE(x, y + 1))
7616       {
7617         InitMovingField(x, y, MV_DOWN);
7618         started_moving = TRUE;
7619
7620         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7621         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7622       }
7623       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7624       {
7625         if (!MovDelay[x][y])
7626           MovDelay[x][y] = TILEY / 4 + 1;
7627
7628         if (MovDelay[x][y])
7629         {
7630           MovDelay[x][y]--;
7631           if (MovDelay[x][y])
7632             return;
7633         }
7634
7635         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7636         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7637         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7638         Store[x][y] = 0;
7639       }
7640     }
7641     else if ((CAN_PASS_MAGIC_WALL(element) &&
7642               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7643                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7644              (CAN_PASS_DC_MAGIC_WALL(element) &&
7645               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7646
7647     {
7648       InitMovingField(x, y, MV_DOWN);
7649       started_moving = TRUE;
7650
7651       Feld[x][y] =
7652         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7653          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7654          EL_DC_MAGIC_WALL_FILLING);
7655       Store[x][y] = element;
7656     }
7657     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7658     {
7659       SplashAcid(x, y + 1);
7660
7661       InitMovingField(x, y, MV_DOWN);
7662       started_moving = TRUE;
7663
7664       Store[x][y] = EL_ACID;
7665     }
7666     else if (
7667              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7668               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7669              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7670               CAN_FALL(element) && WasJustFalling[x][y] &&
7671               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7672
7673              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7674               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7675               (Feld[x][y + 1] == EL_BLOCKED)))
7676     {
7677       /* this is needed for a special case not covered by calling "Impact()"
7678          from "ContinueMoving()": if an element moves to a tile directly below
7679          another element which was just falling on that tile (which was empty
7680          in the previous frame), the falling element above would just stop
7681          instead of smashing the element below (in previous version, the above
7682          element was just checked for "moving" instead of "falling", resulting
7683          in incorrect smashes caused by horizontal movement of the above
7684          element; also, the case of the player being the element to smash was
7685          simply not covered here... :-/ ) */
7686
7687       CheckCollision[x][y] = 0;
7688       CheckImpact[x][y] = 0;
7689
7690       Impact(x, y);
7691     }
7692     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7693     {
7694       if (MovDir[x][y] == MV_NONE)
7695       {
7696         InitMovingField(x, y, MV_DOWN);
7697         started_moving = TRUE;
7698       }
7699     }
7700     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7701     {
7702       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7703         MovDir[x][y] = MV_DOWN;
7704
7705       InitMovingField(x, y, MV_DOWN);
7706       started_moving = TRUE;
7707     }
7708     else if (element == EL_AMOEBA_DROP)
7709     {
7710       Feld[x][y] = EL_AMOEBA_GROWING;
7711       Store[x][y] = EL_AMOEBA_WET;
7712     }
7713     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7714               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7715              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7716              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7717     {
7718       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7719                                 (IS_FREE(x - 1, y + 1) ||
7720                                  Feld[x - 1][y + 1] == EL_ACID));
7721       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7722                                 (IS_FREE(x + 1, y + 1) ||
7723                                  Feld[x + 1][y + 1] == EL_ACID));
7724       boolean can_fall_any  = (can_fall_left || can_fall_right);
7725       boolean can_fall_both = (can_fall_left && can_fall_right);
7726       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7727
7728       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7729       {
7730         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7731           can_fall_right = FALSE;
7732         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7733           can_fall_left = FALSE;
7734         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7735           can_fall_right = FALSE;
7736         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7737           can_fall_left = FALSE;
7738
7739         can_fall_any  = (can_fall_left || can_fall_right);
7740         can_fall_both = FALSE;
7741       }
7742
7743       if (can_fall_both)
7744       {
7745         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7746           can_fall_right = FALSE;       /* slip down on left side */
7747         else
7748           can_fall_left = !(can_fall_right = RND(2));
7749
7750         can_fall_both = FALSE;
7751       }
7752
7753       if (can_fall_any)
7754       {
7755         /* if not determined otherwise, prefer left side for slipping down */
7756         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7757         started_moving = TRUE;
7758       }
7759     }
7760     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7761     {
7762       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7763       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7764       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7765       int belt_dir = game.belt_dir[belt_nr];
7766
7767       if ((belt_dir == MV_LEFT  && left_is_free) ||
7768           (belt_dir == MV_RIGHT && right_is_free))
7769       {
7770         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7771
7772         InitMovingField(x, y, belt_dir);
7773         started_moving = TRUE;
7774
7775         Pushed[x][y] = TRUE;
7776         Pushed[nextx][y] = TRUE;
7777
7778         GfxAction[x][y] = ACTION_DEFAULT;
7779       }
7780       else
7781       {
7782         MovDir[x][y] = 0;       /* if element was moving, stop it */
7783       }
7784     }
7785   }
7786
7787   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7788   if (CAN_MOVE(element) && !started_moving)
7789   {
7790     int move_pattern = element_info[element].move_pattern;
7791     int newx, newy;
7792
7793     Moving2Blocked(x, y, &newx, &newy);
7794
7795     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7796       return;
7797
7798     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7799         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7800     {
7801       WasJustMoving[x][y] = 0;
7802       CheckCollision[x][y] = 0;
7803
7804       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7805
7806       if (Feld[x][y] != element)        /* element has changed */
7807         return;
7808     }
7809
7810     if (!MovDelay[x][y])        /* start new movement phase */
7811     {
7812       /* all objects that can change their move direction after each step
7813          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7814
7815       if (element != EL_YAMYAM &&
7816           element != EL_DARK_YAMYAM &&
7817           element != EL_PACMAN &&
7818           !(move_pattern & MV_ANY_DIRECTION) &&
7819           move_pattern != MV_TURNING_LEFT &&
7820           move_pattern != MV_TURNING_RIGHT &&
7821           move_pattern != MV_TURNING_LEFT_RIGHT &&
7822           move_pattern != MV_TURNING_RIGHT_LEFT &&
7823           move_pattern != MV_TURNING_RANDOM)
7824       {
7825         TurnRound(x, y);
7826
7827         if (MovDelay[x][y] && (element == EL_BUG ||
7828                                element == EL_SPACESHIP ||
7829                                element == EL_SP_SNIKSNAK ||
7830                                element == EL_SP_ELECTRON ||
7831                                element == EL_MOLE))
7832           TEST_DrawLevelField(x, y);
7833       }
7834     }
7835
7836     if (MovDelay[x][y])         /* wait some time before next movement */
7837     {
7838       MovDelay[x][y]--;
7839
7840       if (element == EL_ROBOT ||
7841           element == EL_YAMYAM ||
7842           element == EL_DARK_YAMYAM)
7843       {
7844         DrawLevelElementAnimationIfNeeded(x, y, element);
7845         PlayLevelSoundAction(x, y, ACTION_WAITING);
7846       }
7847       else if (element == EL_SP_ELECTRON)
7848         DrawLevelElementAnimationIfNeeded(x, y, element);
7849       else if (element == EL_DRAGON)
7850       {
7851         int i;
7852         int dir = MovDir[x][y];
7853         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7854         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7855         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7856                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7857                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7858                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7859         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7860
7861         GfxAction[x][y] = ACTION_ATTACKING;
7862
7863         if (IS_PLAYER(x, y))
7864           DrawPlayerField(x, y);
7865         else
7866           TEST_DrawLevelField(x, y);
7867
7868         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7869
7870         for (i = 1; i <= 3; i++)
7871         {
7872           int xx = x + i * dx;
7873           int yy = y + i * dy;
7874           int sx = SCREENX(xx);
7875           int sy = SCREENY(yy);
7876           int flame_graphic = graphic + (i - 1);
7877
7878           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7879             break;
7880
7881           if (MovDelay[x][y])
7882           {
7883             int flamed = MovingOrBlocked2Element(xx, yy);
7884
7885             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7886               Bang(xx, yy);
7887             else
7888               RemoveMovingField(xx, yy);
7889
7890             ChangeDelay[xx][yy] = 0;
7891
7892             Feld[xx][yy] = EL_FLAMES;
7893
7894             if (IN_SCR_FIELD(sx, sy))
7895             {
7896               TEST_DrawLevelFieldCrumbled(xx, yy);
7897               DrawGraphic(sx, sy, flame_graphic, frame);
7898             }
7899           }
7900           else
7901           {
7902             if (Feld[xx][yy] == EL_FLAMES)
7903               Feld[xx][yy] = EL_EMPTY;
7904             TEST_DrawLevelField(xx, yy);
7905           }
7906         }
7907       }
7908
7909       if (MovDelay[x][y])       /* element still has to wait some time */
7910       {
7911         PlayLevelSoundAction(x, y, ACTION_WAITING);
7912
7913         return;
7914       }
7915     }
7916
7917     /* now make next step */
7918
7919     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7920
7921     if (DONT_COLLIDE_WITH(element) &&
7922         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7923         !PLAYER_ENEMY_PROTECTED(newx, newy))
7924     {
7925       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7926
7927       return;
7928     }
7929
7930     else if (CAN_MOVE_INTO_ACID(element) &&
7931              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7932              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7933              (MovDir[x][y] == MV_DOWN ||
7934               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7935     {
7936       SplashAcid(newx, newy);
7937       Store[x][y] = EL_ACID;
7938     }
7939     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7940     {
7941       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7942           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7943           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7944           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7945       {
7946         RemoveField(x, y);
7947         TEST_DrawLevelField(x, y);
7948
7949         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7950         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7951           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7952
7953         local_player->friends_still_needed--;
7954         if (!local_player->friends_still_needed &&
7955             !local_player->GameOver && AllPlayersGone)
7956           PlayerWins(local_player);
7957
7958         return;
7959       }
7960       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7961       {
7962         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7963           TEST_DrawLevelField(newx, newy);
7964         else
7965           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7966       }
7967       else if (!IS_FREE(newx, newy))
7968       {
7969         GfxAction[x][y] = ACTION_WAITING;
7970
7971         if (IS_PLAYER(x, y))
7972           DrawPlayerField(x, y);
7973         else
7974           TEST_DrawLevelField(x, y);
7975
7976         return;
7977       }
7978     }
7979     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7980     {
7981       if (IS_FOOD_PIG(Feld[newx][newy]))
7982       {
7983         if (IS_MOVING(newx, newy))
7984           RemoveMovingField(newx, newy);
7985         else
7986         {
7987           Feld[newx][newy] = EL_EMPTY;
7988           TEST_DrawLevelField(newx, newy);
7989         }
7990
7991         PlayLevelSound(x, y, SND_PIG_DIGGING);
7992       }
7993       else if (!IS_FREE(newx, newy))
7994       {
7995         if (IS_PLAYER(x, y))
7996           DrawPlayerField(x, y);
7997         else
7998           TEST_DrawLevelField(x, y);
7999
8000         return;
8001       }
8002     }
8003     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8004     {
8005       if (Store[x][y] != EL_EMPTY)
8006       {
8007         boolean can_clone = FALSE;
8008         int xx, yy;
8009
8010         /* check if element to clone is still there */
8011         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8012         {
8013           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8014           {
8015             can_clone = TRUE;
8016
8017             break;
8018           }
8019         }
8020
8021         /* cannot clone or target field not free anymore -- do not clone */
8022         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8023           Store[x][y] = EL_EMPTY;
8024       }
8025
8026       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8027       {
8028         if (IS_MV_DIAGONAL(MovDir[x][y]))
8029         {
8030           int diagonal_move_dir = MovDir[x][y];
8031           int stored = Store[x][y];
8032           int change_delay = 8;
8033           int graphic;
8034
8035           /* android is moving diagonally */
8036
8037           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8038
8039           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8040           GfxElement[x][y] = EL_EMC_ANDROID;
8041           GfxAction[x][y] = ACTION_SHRINKING;
8042           GfxDir[x][y] = diagonal_move_dir;
8043           ChangeDelay[x][y] = change_delay;
8044
8045           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8046                                    GfxDir[x][y]);
8047
8048           DrawLevelGraphicAnimation(x, y, graphic);
8049           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8050
8051           if (Feld[newx][newy] == EL_ACID)
8052           {
8053             SplashAcid(newx, newy);
8054
8055             return;
8056           }
8057
8058           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8059
8060           Store[newx][newy] = EL_EMC_ANDROID;
8061           GfxElement[newx][newy] = EL_EMC_ANDROID;
8062           GfxAction[newx][newy] = ACTION_GROWING;
8063           GfxDir[newx][newy] = diagonal_move_dir;
8064           ChangeDelay[newx][newy] = change_delay;
8065
8066           graphic = el_act_dir2img(GfxElement[newx][newy],
8067                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8068
8069           DrawLevelGraphicAnimation(newx, newy, graphic);
8070           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8071
8072           return;
8073         }
8074         else
8075         {
8076           Feld[newx][newy] = EL_EMPTY;
8077           TEST_DrawLevelField(newx, newy);
8078
8079           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8080         }
8081       }
8082       else if (!IS_FREE(newx, newy))
8083       {
8084         return;
8085       }
8086     }
8087     else if (IS_CUSTOM_ELEMENT(element) &&
8088              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8089     {
8090       if (!DigFieldByCE(newx, newy, element))
8091         return;
8092
8093       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8094       {
8095         RunnerVisit[x][y] = FrameCounter;
8096         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8097       }
8098     }
8099     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8100     {
8101       if (!IS_FREE(newx, newy))
8102       {
8103         if (IS_PLAYER(x, y))
8104           DrawPlayerField(x, y);
8105         else
8106           TEST_DrawLevelField(x, y);
8107
8108         return;
8109       }
8110       else
8111       {
8112         boolean wanna_flame = !RND(10);
8113         int dx = newx - x, dy = newy - y;
8114         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8115         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8116         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8117                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8118         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8119                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8120
8121         if ((wanna_flame ||
8122              IS_CLASSIC_ENEMY(element1) ||
8123              IS_CLASSIC_ENEMY(element2)) &&
8124             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8125             element1 != EL_FLAMES && element2 != EL_FLAMES)
8126         {
8127           ResetGfxAnimation(x, y);
8128           GfxAction[x][y] = ACTION_ATTACKING;
8129
8130           if (IS_PLAYER(x, y))
8131             DrawPlayerField(x, y);
8132           else
8133             TEST_DrawLevelField(x, y);
8134
8135           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8136
8137           MovDelay[x][y] = 50;
8138
8139           Feld[newx][newy] = EL_FLAMES;
8140           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8141             Feld[newx1][newy1] = EL_FLAMES;
8142           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8143             Feld[newx2][newy2] = EL_FLAMES;
8144
8145           return;
8146         }
8147       }
8148     }
8149     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8150              Feld[newx][newy] == EL_DIAMOND)
8151     {
8152       if (IS_MOVING(newx, newy))
8153         RemoveMovingField(newx, newy);
8154       else
8155       {
8156         Feld[newx][newy] = EL_EMPTY;
8157         TEST_DrawLevelField(newx, newy);
8158       }
8159
8160       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8161     }
8162     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8163              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8164     {
8165       if (AmoebaNr[newx][newy])
8166       {
8167         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8168         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8169             Feld[newx][newy] == EL_BD_AMOEBA)
8170           AmoebaCnt[AmoebaNr[newx][newy]]--;
8171       }
8172
8173       if (IS_MOVING(newx, newy))
8174       {
8175         RemoveMovingField(newx, newy);
8176       }
8177       else
8178       {
8179         Feld[newx][newy] = EL_EMPTY;
8180         TEST_DrawLevelField(newx, newy);
8181       }
8182
8183       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8184     }
8185     else if ((element == EL_PACMAN || element == EL_MOLE)
8186              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8187     {
8188       if (AmoebaNr[newx][newy])
8189       {
8190         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8191         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8192             Feld[newx][newy] == EL_BD_AMOEBA)
8193           AmoebaCnt[AmoebaNr[newx][newy]]--;
8194       }
8195
8196       if (element == EL_MOLE)
8197       {
8198         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8199         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8200
8201         ResetGfxAnimation(x, y);
8202         GfxAction[x][y] = ACTION_DIGGING;
8203         TEST_DrawLevelField(x, y);
8204
8205         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8206
8207         return;                         /* wait for shrinking amoeba */
8208       }
8209       else      /* element == EL_PACMAN */
8210       {
8211         Feld[newx][newy] = EL_EMPTY;
8212         TEST_DrawLevelField(newx, newy);
8213         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8214       }
8215     }
8216     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8217              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8218               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8219     {
8220       /* wait for shrinking amoeba to completely disappear */
8221       return;
8222     }
8223     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8224     {
8225       /* object was running against a wall */
8226
8227       TurnRound(x, y);
8228
8229       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8230         DrawLevelElementAnimation(x, y, element);
8231
8232       if (DONT_TOUCH(element))
8233         TestIfBadThingTouchesPlayer(x, y);
8234
8235       return;
8236     }
8237
8238     InitMovingField(x, y, MovDir[x][y]);
8239
8240     PlayLevelSoundAction(x, y, ACTION_MOVING);
8241   }
8242
8243   if (MovDir[x][y])
8244     ContinueMoving(x, y);
8245 }
8246
8247 void ContinueMoving(int x, int y)
8248 {
8249   int element = Feld[x][y];
8250   struct ElementInfo *ei = &element_info[element];
8251   int direction = MovDir[x][y];
8252   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8253   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8254   int newx = x + dx, newy = y + dy;
8255   int stored = Store[x][y];
8256   int stored_new = Store[newx][newy];
8257   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8258   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8259   boolean last_line = (newy == lev_fieldy - 1);
8260
8261   MovPos[x][y] += getElementMoveStepsize(x, y);
8262
8263   if (pushed_by_player) /* special case: moving object pushed by player */
8264     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8265
8266   if (ABS(MovPos[x][y]) < TILEX)
8267   {
8268     TEST_DrawLevelField(x, y);
8269
8270     return;     /* element is still moving */
8271   }
8272
8273   /* element reached destination field */
8274
8275   Feld[x][y] = EL_EMPTY;
8276   Feld[newx][newy] = element;
8277   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8278
8279   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8280   {
8281     element = Feld[newx][newy] = EL_ACID;
8282   }
8283   else if (element == EL_MOLE)
8284   {
8285     Feld[x][y] = EL_SAND;
8286
8287     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8288   }
8289   else if (element == EL_QUICKSAND_FILLING)
8290   {
8291     element = Feld[newx][newy] = get_next_element(element);
8292     Store[newx][newy] = Store[x][y];
8293   }
8294   else if (element == EL_QUICKSAND_EMPTYING)
8295   {
8296     Feld[x][y] = get_next_element(element);
8297     element = Feld[newx][newy] = Store[x][y];
8298   }
8299   else if (element == EL_QUICKSAND_FAST_FILLING)
8300   {
8301     element = Feld[newx][newy] = get_next_element(element);
8302     Store[newx][newy] = Store[x][y];
8303   }
8304   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8305   {
8306     Feld[x][y] = get_next_element(element);
8307     element = Feld[newx][newy] = Store[x][y];
8308   }
8309   else if (element == EL_MAGIC_WALL_FILLING)
8310   {
8311     element = Feld[newx][newy] = get_next_element(element);
8312     if (!game.magic_wall_active)
8313       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8314     Store[newx][newy] = Store[x][y];
8315   }
8316   else if (element == EL_MAGIC_WALL_EMPTYING)
8317   {
8318     Feld[x][y] = get_next_element(element);
8319     if (!game.magic_wall_active)
8320       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8321     element = Feld[newx][newy] = Store[x][y];
8322
8323     InitField(newx, newy, FALSE);
8324   }
8325   else if (element == EL_BD_MAGIC_WALL_FILLING)
8326   {
8327     element = Feld[newx][newy] = get_next_element(element);
8328     if (!game.magic_wall_active)
8329       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8330     Store[newx][newy] = Store[x][y];
8331   }
8332   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8333   {
8334     Feld[x][y] = get_next_element(element);
8335     if (!game.magic_wall_active)
8336       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8337     element = Feld[newx][newy] = Store[x][y];
8338
8339     InitField(newx, newy, FALSE);
8340   }
8341   else if (element == EL_DC_MAGIC_WALL_FILLING)
8342   {
8343     element = Feld[newx][newy] = get_next_element(element);
8344     if (!game.magic_wall_active)
8345       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8346     Store[newx][newy] = Store[x][y];
8347   }
8348   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8349   {
8350     Feld[x][y] = get_next_element(element);
8351     if (!game.magic_wall_active)
8352       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8353     element = Feld[newx][newy] = Store[x][y];
8354
8355     InitField(newx, newy, FALSE);
8356   }
8357   else if (element == EL_AMOEBA_DROPPING)
8358   {
8359     Feld[x][y] = get_next_element(element);
8360     element = Feld[newx][newy] = Store[x][y];
8361   }
8362   else if (element == EL_SOKOBAN_OBJECT)
8363   {
8364     if (Back[x][y])
8365       Feld[x][y] = Back[x][y];
8366
8367     if (Back[newx][newy])
8368       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8369
8370     Back[x][y] = Back[newx][newy] = 0;
8371   }
8372
8373   Store[x][y] = EL_EMPTY;
8374   MovPos[x][y] = 0;
8375   MovDir[x][y] = 0;
8376   MovDelay[x][y] = 0;
8377
8378   MovDelay[newx][newy] = 0;
8379
8380   if (CAN_CHANGE_OR_HAS_ACTION(element))
8381   {
8382     /* copy element change control values to new field */
8383     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8384     ChangePage[newx][newy]  = ChangePage[x][y];
8385     ChangeCount[newx][newy] = ChangeCount[x][y];
8386     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8387   }
8388
8389   CustomValue[newx][newy] = CustomValue[x][y];
8390
8391   ChangeDelay[x][y] = 0;
8392   ChangePage[x][y] = -1;
8393   ChangeCount[x][y] = 0;
8394   ChangeEvent[x][y] = -1;
8395
8396   CustomValue[x][y] = 0;
8397
8398   /* copy animation control values to new field */
8399   GfxFrame[newx][newy]  = GfxFrame[x][y];
8400   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8401   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8402   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8403
8404   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8405
8406   /* some elements can leave other elements behind after moving */
8407   if (ei->move_leave_element != EL_EMPTY &&
8408       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8409       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8410   {
8411     int move_leave_element = ei->move_leave_element;
8412
8413     /* this makes it possible to leave the removed element again */
8414     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8415       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8416
8417     Feld[x][y] = move_leave_element;
8418
8419     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8420       MovDir[x][y] = direction;
8421
8422     InitField(x, y, FALSE);
8423
8424     if (GFX_CRUMBLED(Feld[x][y]))
8425       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8426
8427     if (ELEM_IS_PLAYER(move_leave_element))
8428       RelocatePlayer(x, y, move_leave_element);
8429   }
8430
8431   /* do this after checking for left-behind element */
8432   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8433
8434   if (!CAN_MOVE(element) ||
8435       (CAN_FALL(element) && direction == MV_DOWN &&
8436        (element == EL_SPRING ||
8437         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8438         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8439     GfxDir[x][y] = MovDir[newx][newy] = 0;
8440
8441   TEST_DrawLevelField(x, y);
8442   TEST_DrawLevelField(newx, newy);
8443
8444   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8445
8446   /* prevent pushed element from moving on in pushed direction */
8447   if (pushed_by_player && CAN_MOVE(element) &&
8448       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8449       !(element_info[element].move_pattern & direction))
8450     TurnRound(newx, newy);
8451
8452   /* prevent elements on conveyor belt from moving on in last direction */
8453   if (pushed_by_conveyor && CAN_FALL(element) &&
8454       direction & MV_HORIZONTAL)
8455     MovDir[newx][newy] = 0;
8456
8457   if (!pushed_by_player)
8458   {
8459     int nextx = newx + dx, nexty = newy + dy;
8460     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8461
8462     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8463
8464     if (CAN_FALL(element) && direction == MV_DOWN)
8465       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8466
8467     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8468       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8469
8470     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8471       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8472   }
8473
8474   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8475   {
8476     TestIfBadThingTouchesPlayer(newx, newy);
8477     TestIfBadThingTouchesFriend(newx, newy);
8478
8479     if (!IS_CUSTOM_ELEMENT(element))
8480       TestIfBadThingTouchesOtherBadThing(newx, newy);
8481   }
8482   else if (element == EL_PENGUIN)
8483     TestIfFriendTouchesBadThing(newx, newy);
8484
8485   if (DONT_GET_HIT_BY(element))
8486   {
8487     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8488   }
8489
8490   /* give the player one last chance (one more frame) to move away */
8491   if (CAN_FALL(element) && direction == MV_DOWN &&
8492       (last_line || (!IS_FREE(x, newy + 1) &&
8493                      (!IS_PLAYER(x, newy + 1) ||
8494                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8495     Impact(x, newy);
8496
8497   if (pushed_by_player && !game.use_change_when_pushing_bug)
8498   {
8499     int push_side = MV_DIR_OPPOSITE(direction);
8500     struct PlayerInfo *player = PLAYERINFO(x, y);
8501
8502     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8503                                player->index_bit, push_side);
8504     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8505                                         player->index_bit, push_side);
8506   }
8507
8508   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8509     MovDelay[newx][newy] = 1;
8510
8511   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8512
8513   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8514   TestIfElementHitsCustomElement(newx, newy, direction);
8515   TestIfPlayerTouchesCustomElement(newx, newy);
8516   TestIfElementTouchesCustomElement(newx, newy);
8517
8518   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8519       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8520     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8521                              MV_DIR_OPPOSITE(direction));
8522 }
8523
8524 int AmoebeNachbarNr(int ax, int ay)
8525 {
8526   int i;
8527   int element = Feld[ax][ay];
8528   int group_nr = 0;
8529   static int xy[4][2] =
8530   {
8531     { 0, -1 },
8532     { -1, 0 },
8533     { +1, 0 },
8534     { 0, +1 }
8535   };
8536
8537   for (i = 0; i < NUM_DIRECTIONS; i++)
8538   {
8539     int x = ax + xy[i][0];
8540     int y = ay + xy[i][1];
8541
8542     if (!IN_LEV_FIELD(x, y))
8543       continue;
8544
8545     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8546       group_nr = AmoebaNr[x][y];
8547   }
8548
8549   return group_nr;
8550 }
8551
8552 void AmoebenVereinigen(int ax, int ay)
8553 {
8554   int i, x, y, xx, yy;
8555   int new_group_nr = AmoebaNr[ax][ay];
8556   static int xy[4][2] =
8557   {
8558     { 0, -1 },
8559     { -1, 0 },
8560     { +1, 0 },
8561     { 0, +1 }
8562   };
8563
8564   if (new_group_nr == 0)
8565     return;
8566
8567   for (i = 0; i < NUM_DIRECTIONS; i++)
8568   {
8569     x = ax + xy[i][0];
8570     y = ay + xy[i][1];
8571
8572     if (!IN_LEV_FIELD(x, y))
8573       continue;
8574
8575     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8576          Feld[x][y] == EL_BD_AMOEBA ||
8577          Feld[x][y] == EL_AMOEBA_DEAD) &&
8578         AmoebaNr[x][y] != new_group_nr)
8579     {
8580       int old_group_nr = AmoebaNr[x][y];
8581
8582       if (old_group_nr == 0)
8583         return;
8584
8585       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8586       AmoebaCnt[old_group_nr] = 0;
8587       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8588       AmoebaCnt2[old_group_nr] = 0;
8589
8590       SCAN_PLAYFIELD(xx, yy)
8591       {
8592         if (AmoebaNr[xx][yy] == old_group_nr)
8593           AmoebaNr[xx][yy] = new_group_nr;
8594       }
8595     }
8596   }
8597 }
8598
8599 void AmoebeUmwandeln(int ax, int ay)
8600 {
8601   int i, x, y;
8602
8603   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8604   {
8605     int group_nr = AmoebaNr[ax][ay];
8606
8607 #ifdef DEBUG
8608     if (group_nr == 0)
8609     {
8610       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8611       printf("AmoebeUmwandeln(): This should never happen!\n");
8612       return;
8613     }
8614 #endif
8615
8616     SCAN_PLAYFIELD(x, y)
8617     {
8618       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8619       {
8620         AmoebaNr[x][y] = 0;
8621         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8622       }
8623     }
8624
8625     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8626                             SND_AMOEBA_TURNING_TO_GEM :
8627                             SND_AMOEBA_TURNING_TO_ROCK));
8628     Bang(ax, ay);
8629   }
8630   else
8631   {
8632     static int xy[4][2] =
8633     {
8634       { 0, -1 },
8635       { -1, 0 },
8636       { +1, 0 },
8637       { 0, +1 }
8638     };
8639
8640     for (i = 0; i < NUM_DIRECTIONS; i++)
8641     {
8642       x = ax + xy[i][0];
8643       y = ay + xy[i][1];
8644
8645       if (!IN_LEV_FIELD(x, y))
8646         continue;
8647
8648       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8649       {
8650         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8651                               SND_AMOEBA_TURNING_TO_GEM :
8652                               SND_AMOEBA_TURNING_TO_ROCK));
8653         Bang(x, y);
8654       }
8655     }
8656   }
8657 }
8658
8659 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8660 {
8661   int x, y;
8662   int group_nr = AmoebaNr[ax][ay];
8663   boolean done = FALSE;
8664
8665 #ifdef DEBUG
8666   if (group_nr == 0)
8667   {
8668     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8669     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8670     return;
8671   }
8672 #endif
8673
8674   SCAN_PLAYFIELD(x, y)
8675   {
8676     if (AmoebaNr[x][y] == group_nr &&
8677         (Feld[x][y] == EL_AMOEBA_DEAD ||
8678          Feld[x][y] == EL_BD_AMOEBA ||
8679          Feld[x][y] == EL_AMOEBA_GROWING))
8680     {
8681       AmoebaNr[x][y] = 0;
8682       Feld[x][y] = new_element;
8683       InitField(x, y, FALSE);
8684       TEST_DrawLevelField(x, y);
8685       done = TRUE;
8686     }
8687   }
8688
8689   if (done)
8690     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8691                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8692                             SND_BD_AMOEBA_TURNING_TO_GEM));
8693 }
8694
8695 void AmoebeWaechst(int x, int y)
8696 {
8697   static unsigned int sound_delay = 0;
8698   static unsigned int sound_delay_value = 0;
8699
8700   if (!MovDelay[x][y])          /* start new growing cycle */
8701   {
8702     MovDelay[x][y] = 7;
8703
8704     if (DelayReached(&sound_delay, sound_delay_value))
8705     {
8706       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8707       sound_delay_value = 30;
8708     }
8709   }
8710
8711   if (MovDelay[x][y])           /* wait some time before growing bigger */
8712   {
8713     MovDelay[x][y]--;
8714     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8715     {
8716       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8717                                            6 - MovDelay[x][y]);
8718
8719       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8720     }
8721
8722     if (!MovDelay[x][y])
8723     {
8724       Feld[x][y] = Store[x][y];
8725       Store[x][y] = 0;
8726       TEST_DrawLevelField(x, y);
8727     }
8728   }
8729 }
8730
8731 void AmoebaDisappearing(int x, int y)
8732 {
8733   static unsigned int sound_delay = 0;
8734   static unsigned int sound_delay_value = 0;
8735
8736   if (!MovDelay[x][y])          /* start new shrinking cycle */
8737   {
8738     MovDelay[x][y] = 7;
8739
8740     if (DelayReached(&sound_delay, sound_delay_value))
8741       sound_delay_value = 30;
8742   }
8743
8744   if (MovDelay[x][y])           /* wait some time before shrinking */
8745   {
8746     MovDelay[x][y]--;
8747     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8748     {
8749       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8750                                            6 - MovDelay[x][y]);
8751
8752       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8753     }
8754
8755     if (!MovDelay[x][y])
8756     {
8757       Feld[x][y] = EL_EMPTY;
8758       TEST_DrawLevelField(x, y);
8759
8760       /* don't let mole enter this field in this cycle;
8761          (give priority to objects falling to this field from above) */
8762       Stop[x][y] = TRUE;
8763     }
8764   }
8765 }
8766
8767 void AmoebeAbleger(int ax, int ay)
8768 {
8769   int i;
8770   int element = Feld[ax][ay];
8771   int graphic = el2img(element);
8772   int newax = ax, neway = ay;
8773   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8774   static int xy[4][2] =
8775   {
8776     { 0, -1 },
8777     { -1, 0 },
8778     { +1, 0 },
8779     { 0, +1 }
8780   };
8781
8782   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8783   {
8784     Feld[ax][ay] = EL_AMOEBA_DEAD;
8785     TEST_DrawLevelField(ax, ay);
8786     return;
8787   }
8788
8789   if (IS_ANIMATED(graphic))
8790     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8791
8792   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8793     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8794
8795   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8796   {
8797     MovDelay[ax][ay]--;
8798     if (MovDelay[ax][ay])
8799       return;
8800   }
8801
8802   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8803   {
8804     int start = RND(4);
8805     int x = ax + xy[start][0];
8806     int y = ay + xy[start][1];
8807
8808     if (!IN_LEV_FIELD(x, y))
8809       return;
8810
8811     if (IS_FREE(x, y) ||
8812         CAN_GROW_INTO(Feld[x][y]) ||
8813         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8814         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8815     {
8816       newax = x;
8817       neway = y;
8818     }
8819
8820     if (newax == ax && neway == ay)
8821       return;
8822   }
8823   else                          /* normal or "filled" (BD style) amoeba */
8824   {
8825     int start = RND(4);
8826     boolean waiting_for_player = FALSE;
8827
8828     for (i = 0; i < NUM_DIRECTIONS; i++)
8829     {
8830       int j = (start + i) % 4;
8831       int x = ax + xy[j][0];
8832       int y = ay + xy[j][1];
8833
8834       if (!IN_LEV_FIELD(x, y))
8835         continue;
8836
8837       if (IS_FREE(x, y) ||
8838           CAN_GROW_INTO(Feld[x][y]) ||
8839           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8840           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8841       {
8842         newax = x;
8843         neway = y;
8844         break;
8845       }
8846       else if (IS_PLAYER(x, y))
8847         waiting_for_player = TRUE;
8848     }
8849
8850     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8851     {
8852       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8853       {
8854         Feld[ax][ay] = EL_AMOEBA_DEAD;
8855         TEST_DrawLevelField(ax, ay);
8856         AmoebaCnt[AmoebaNr[ax][ay]]--;
8857
8858         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8859         {
8860           if (element == EL_AMOEBA_FULL)
8861             AmoebeUmwandeln(ax, ay);
8862           else if (element == EL_BD_AMOEBA)
8863             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8864         }
8865       }
8866       return;
8867     }
8868     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8869     {
8870       /* amoeba gets larger by growing in some direction */
8871
8872       int new_group_nr = AmoebaNr[ax][ay];
8873
8874 #ifdef DEBUG
8875   if (new_group_nr == 0)
8876   {
8877     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8878     printf("AmoebeAbleger(): This should never happen!\n");
8879     return;
8880   }
8881 #endif
8882
8883       AmoebaNr[newax][neway] = new_group_nr;
8884       AmoebaCnt[new_group_nr]++;
8885       AmoebaCnt2[new_group_nr]++;
8886
8887       /* if amoeba touches other amoeba(s) after growing, unify them */
8888       AmoebenVereinigen(newax, neway);
8889
8890       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8891       {
8892         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8893         return;
8894       }
8895     }
8896   }
8897
8898   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8899       (neway == lev_fieldy - 1 && newax != ax))
8900   {
8901     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8902     Store[newax][neway] = element;
8903   }
8904   else if (neway == ay || element == EL_EMC_DRIPPER)
8905   {
8906     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8907
8908     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8909   }
8910   else
8911   {
8912     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8913     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8914     Store[ax][ay] = EL_AMOEBA_DROP;
8915     ContinueMoving(ax, ay);
8916     return;
8917   }
8918
8919   TEST_DrawLevelField(newax, neway);
8920 }
8921
8922 void Life(int ax, int ay)
8923 {
8924   int x1, y1, x2, y2;
8925   int life_time = 40;
8926   int element = Feld[ax][ay];
8927   int graphic = el2img(element);
8928   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8929                          level.biomaze);
8930   boolean changed = FALSE;
8931
8932   if (IS_ANIMATED(graphic))
8933     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8934
8935   if (Stop[ax][ay])
8936     return;
8937
8938   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8939     MovDelay[ax][ay] = life_time;
8940
8941   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8942   {
8943     MovDelay[ax][ay]--;
8944     if (MovDelay[ax][ay])
8945       return;
8946   }
8947
8948   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8949   {
8950     int xx = ax+x1, yy = ay+y1;
8951     int nachbarn = 0;
8952
8953     if (!IN_LEV_FIELD(xx, yy))
8954       continue;
8955
8956     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8957     {
8958       int x = xx+x2, y = yy+y2;
8959
8960       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8961         continue;
8962
8963       if (((Feld[x][y] == element ||
8964             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8965            !Stop[x][y]) ||
8966           (IS_FREE(x, y) && Stop[x][y]))
8967         nachbarn++;
8968     }
8969
8970     if (xx == ax && yy == ay)           /* field in the middle */
8971     {
8972       if (nachbarn < life_parameter[0] ||
8973           nachbarn > life_parameter[1])
8974       {
8975         Feld[xx][yy] = EL_EMPTY;
8976         if (!Stop[xx][yy])
8977           TEST_DrawLevelField(xx, yy);
8978         Stop[xx][yy] = TRUE;
8979         changed = TRUE;
8980       }
8981     }
8982     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8983     {                                   /* free border field */
8984       if (nachbarn >= life_parameter[2] &&
8985           nachbarn <= life_parameter[3])
8986       {
8987         Feld[xx][yy] = element;
8988         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8989         if (!Stop[xx][yy])
8990           TEST_DrawLevelField(xx, yy);
8991         Stop[xx][yy] = TRUE;
8992         changed = TRUE;
8993       }
8994     }
8995   }
8996
8997   if (changed)
8998     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8999                    SND_GAME_OF_LIFE_GROWING);
9000 }
9001
9002 static void InitRobotWheel(int x, int y)
9003 {
9004   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9005 }
9006
9007 static void RunRobotWheel(int x, int y)
9008 {
9009   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9010 }
9011
9012 static void StopRobotWheel(int x, int y)
9013 {
9014   if (ZX == x && ZY == y)
9015   {
9016     ZX = ZY = -1;
9017
9018     game.robot_wheel_active = FALSE;
9019   }
9020 }
9021
9022 static void InitTimegateWheel(int x, int y)
9023 {
9024   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9025 }
9026
9027 static void RunTimegateWheel(int x, int y)
9028 {
9029   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9030 }
9031
9032 static void InitMagicBallDelay(int x, int y)
9033 {
9034   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9035 }
9036
9037 static void ActivateMagicBall(int bx, int by)
9038 {
9039   int x, y;
9040
9041   if (level.ball_random)
9042   {
9043     int pos_border = RND(8);    /* select one of the eight border elements */
9044     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9045     int xx = pos_content % 3;
9046     int yy = pos_content / 3;
9047
9048     x = bx - 1 + xx;
9049     y = by - 1 + yy;
9050
9051     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9052       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9053   }
9054   else
9055   {
9056     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9057     {
9058       int xx = x - bx + 1;
9059       int yy = y - by + 1;
9060
9061       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9062         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9063     }
9064   }
9065
9066   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9067 }
9068
9069 void CheckExit(int x, int y)
9070 {
9071   if (local_player->gems_still_needed > 0 ||
9072       local_player->sokobanfields_still_needed > 0 ||
9073       local_player->lights_still_needed > 0)
9074   {
9075     int element = Feld[x][y];
9076     int graphic = el2img(element);
9077
9078     if (IS_ANIMATED(graphic))
9079       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9080
9081     return;
9082   }
9083
9084   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9085     return;
9086
9087   Feld[x][y] = EL_EXIT_OPENING;
9088
9089   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9090 }
9091
9092 void CheckExitEM(int x, int y)
9093 {
9094   if (local_player->gems_still_needed > 0 ||
9095       local_player->sokobanfields_still_needed > 0 ||
9096       local_player->lights_still_needed > 0)
9097   {
9098     int element = Feld[x][y];
9099     int graphic = el2img(element);
9100
9101     if (IS_ANIMATED(graphic))
9102       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9103
9104     return;
9105   }
9106
9107   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9108     return;
9109
9110   Feld[x][y] = EL_EM_EXIT_OPENING;
9111
9112   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9113 }
9114
9115 void CheckExitSteel(int x, int y)
9116 {
9117   if (local_player->gems_still_needed > 0 ||
9118       local_player->sokobanfields_still_needed > 0 ||
9119       local_player->lights_still_needed > 0)
9120   {
9121     int element = Feld[x][y];
9122     int graphic = el2img(element);
9123
9124     if (IS_ANIMATED(graphic))
9125       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9126
9127     return;
9128   }
9129
9130   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9131     return;
9132
9133   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9134
9135   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9136 }
9137
9138 void CheckExitSteelEM(int x, int y)
9139 {
9140   if (local_player->gems_still_needed > 0 ||
9141       local_player->sokobanfields_still_needed > 0 ||
9142       local_player->lights_still_needed > 0)
9143   {
9144     int element = Feld[x][y];
9145     int graphic = el2img(element);
9146
9147     if (IS_ANIMATED(graphic))
9148       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9149
9150     return;
9151   }
9152
9153   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9154     return;
9155
9156   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9157
9158   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9159 }
9160
9161 void CheckExitSP(int x, int y)
9162 {
9163   if (local_player->gems_still_needed > 0)
9164   {
9165     int element = Feld[x][y];
9166     int graphic = el2img(element);
9167
9168     if (IS_ANIMATED(graphic))
9169       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9170
9171     return;
9172   }
9173
9174   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9175     return;
9176
9177   Feld[x][y] = EL_SP_EXIT_OPENING;
9178
9179   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9180 }
9181
9182 static void CloseAllOpenTimegates()
9183 {
9184   int x, y;
9185
9186   SCAN_PLAYFIELD(x, y)
9187   {
9188     int element = Feld[x][y];
9189
9190     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9191     {
9192       Feld[x][y] = EL_TIMEGATE_CLOSING;
9193
9194       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9195     }
9196   }
9197 }
9198
9199 void DrawTwinkleOnField(int x, int y)
9200 {
9201   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9202     return;
9203
9204   if (Feld[x][y] == EL_BD_DIAMOND)
9205     return;
9206
9207   if (MovDelay[x][y] == 0)      /* next animation frame */
9208     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9209
9210   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9211   {
9212     MovDelay[x][y]--;
9213
9214     DrawLevelElementAnimation(x, y, Feld[x][y]);
9215
9216     if (MovDelay[x][y] != 0)
9217     {
9218       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9219                                            10 - MovDelay[x][y]);
9220
9221       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9222     }
9223   }
9224 }
9225
9226 void MauerWaechst(int x, int y)
9227 {
9228   int delay = 6;
9229
9230   if (!MovDelay[x][y])          /* next animation frame */
9231     MovDelay[x][y] = 3 * delay;
9232
9233   if (MovDelay[x][y])           /* wait some time before next frame */
9234   {
9235     MovDelay[x][y]--;
9236
9237     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9238     {
9239       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9240       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9241
9242       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9243     }
9244
9245     if (!MovDelay[x][y])
9246     {
9247       if (MovDir[x][y] == MV_LEFT)
9248       {
9249         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9250           TEST_DrawLevelField(x - 1, y);
9251       }
9252       else if (MovDir[x][y] == MV_RIGHT)
9253       {
9254         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9255           TEST_DrawLevelField(x + 1, y);
9256       }
9257       else if (MovDir[x][y] == MV_UP)
9258       {
9259         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9260           TEST_DrawLevelField(x, y - 1);
9261       }
9262       else
9263       {
9264         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9265           TEST_DrawLevelField(x, y + 1);
9266       }
9267
9268       Feld[x][y] = Store[x][y];
9269       Store[x][y] = 0;
9270       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9271       TEST_DrawLevelField(x, y);
9272     }
9273   }
9274 }
9275
9276 void MauerAbleger(int ax, int ay)
9277 {
9278   int element = Feld[ax][ay];
9279   int graphic = el2img(element);
9280   boolean oben_frei = FALSE, unten_frei = FALSE;
9281   boolean links_frei = FALSE, rechts_frei = FALSE;
9282   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9283   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9284   boolean new_wall = FALSE;
9285
9286   if (IS_ANIMATED(graphic))
9287     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9288
9289   if (!MovDelay[ax][ay])        /* start building new wall */
9290     MovDelay[ax][ay] = 6;
9291
9292   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9293   {
9294     MovDelay[ax][ay]--;
9295     if (MovDelay[ax][ay])
9296       return;
9297   }
9298
9299   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9300     oben_frei = TRUE;
9301   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9302     unten_frei = TRUE;
9303   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9304     links_frei = TRUE;
9305   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9306     rechts_frei = TRUE;
9307
9308   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9309       element == EL_EXPANDABLE_WALL_ANY)
9310   {
9311     if (oben_frei)
9312     {
9313       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9314       Store[ax][ay-1] = element;
9315       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9316       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9317         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9318                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9319       new_wall = TRUE;
9320     }
9321     if (unten_frei)
9322     {
9323       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9324       Store[ax][ay+1] = element;
9325       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9326       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9327         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9328                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9329       new_wall = TRUE;
9330     }
9331   }
9332
9333   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9334       element == EL_EXPANDABLE_WALL_ANY ||
9335       element == EL_EXPANDABLE_WALL ||
9336       element == EL_BD_EXPANDABLE_WALL)
9337   {
9338     if (links_frei)
9339     {
9340       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9341       Store[ax-1][ay] = element;
9342       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9343       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9344         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9345                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9346       new_wall = TRUE;
9347     }
9348
9349     if (rechts_frei)
9350     {
9351       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9352       Store[ax+1][ay] = element;
9353       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9354       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9355         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9356                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9357       new_wall = TRUE;
9358     }
9359   }
9360
9361   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9362     TEST_DrawLevelField(ax, ay);
9363
9364   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9365     oben_massiv = TRUE;
9366   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9367     unten_massiv = TRUE;
9368   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9369     links_massiv = TRUE;
9370   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9371     rechts_massiv = TRUE;
9372
9373   if (((oben_massiv && unten_massiv) ||
9374        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9375        element == EL_EXPANDABLE_WALL) &&
9376       ((links_massiv && rechts_massiv) ||
9377        element == EL_EXPANDABLE_WALL_VERTICAL))
9378     Feld[ax][ay] = EL_WALL;
9379
9380   if (new_wall)
9381     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9382 }
9383
9384 void MauerAblegerStahl(int ax, int ay)
9385 {
9386   int element = Feld[ax][ay];
9387   int graphic = el2img(element);
9388   boolean oben_frei = FALSE, unten_frei = FALSE;
9389   boolean links_frei = FALSE, rechts_frei = FALSE;
9390   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9391   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9392   boolean new_wall = FALSE;
9393
9394   if (IS_ANIMATED(graphic))
9395     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9396
9397   if (!MovDelay[ax][ay])        /* start building new wall */
9398     MovDelay[ax][ay] = 6;
9399
9400   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9401   {
9402     MovDelay[ax][ay]--;
9403     if (MovDelay[ax][ay])
9404       return;
9405   }
9406
9407   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9408     oben_frei = TRUE;
9409   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9410     unten_frei = TRUE;
9411   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9412     links_frei = TRUE;
9413   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9414     rechts_frei = TRUE;
9415
9416   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9417       element == EL_EXPANDABLE_STEELWALL_ANY)
9418   {
9419     if (oben_frei)
9420     {
9421       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9422       Store[ax][ay-1] = element;
9423       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9424       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9425         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9426                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9427       new_wall = TRUE;
9428     }
9429     if (unten_frei)
9430     {
9431       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9432       Store[ax][ay+1] = element;
9433       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9434       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9435         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9436                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9437       new_wall = TRUE;
9438     }
9439   }
9440
9441   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9442       element == EL_EXPANDABLE_STEELWALL_ANY)
9443   {
9444     if (links_frei)
9445     {
9446       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9447       Store[ax-1][ay] = element;
9448       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9449       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9450         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9451                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9452       new_wall = TRUE;
9453     }
9454
9455     if (rechts_frei)
9456     {
9457       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9458       Store[ax+1][ay] = element;
9459       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9460       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9461         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9462                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9463       new_wall = TRUE;
9464     }
9465   }
9466
9467   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9468     oben_massiv = TRUE;
9469   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9470     unten_massiv = TRUE;
9471   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9472     links_massiv = TRUE;
9473   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9474     rechts_massiv = TRUE;
9475
9476   if (((oben_massiv && unten_massiv) ||
9477        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9478       ((links_massiv && rechts_massiv) ||
9479        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9480     Feld[ax][ay] = EL_STEELWALL;
9481
9482   if (new_wall)
9483     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9484 }
9485
9486 void CheckForDragon(int x, int y)
9487 {
9488   int i, j;
9489   boolean dragon_found = FALSE;
9490   static int xy[4][2] =
9491   {
9492     { 0, -1 },
9493     { -1, 0 },
9494     { +1, 0 },
9495     { 0, +1 }
9496   };
9497
9498   for (i = 0; i < NUM_DIRECTIONS; i++)
9499   {
9500     for (j = 0; j < 4; j++)
9501     {
9502       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9503
9504       if (IN_LEV_FIELD(xx, yy) &&
9505           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9506       {
9507         if (Feld[xx][yy] == EL_DRAGON)
9508           dragon_found = TRUE;
9509       }
9510       else
9511         break;
9512     }
9513   }
9514
9515   if (!dragon_found)
9516   {
9517     for (i = 0; i < NUM_DIRECTIONS; i++)
9518     {
9519       for (j = 0; j < 3; j++)
9520       {
9521         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9522   
9523         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9524         {
9525           Feld[xx][yy] = EL_EMPTY;
9526           TEST_DrawLevelField(xx, yy);
9527         }
9528         else
9529           break;
9530       }
9531     }
9532   }
9533 }
9534
9535 static void InitBuggyBase(int x, int y)
9536 {
9537   int element = Feld[x][y];
9538   int activating_delay = FRAMES_PER_SECOND / 4;
9539
9540   ChangeDelay[x][y] =
9541     (element == EL_SP_BUGGY_BASE ?
9542      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9543      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9544      activating_delay :
9545      element == EL_SP_BUGGY_BASE_ACTIVE ?
9546      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9547 }
9548
9549 static void WarnBuggyBase(int x, int y)
9550 {
9551   int i;
9552   static int xy[4][2] =
9553   {
9554     { 0, -1 },
9555     { -1, 0 },
9556     { +1, 0 },
9557     { 0, +1 }
9558   };
9559
9560   for (i = 0; i < NUM_DIRECTIONS; i++)
9561   {
9562     int xx = x + xy[i][0];
9563     int yy = y + xy[i][1];
9564
9565     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9566     {
9567       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9568
9569       break;
9570     }
9571   }
9572 }
9573
9574 static void InitTrap(int x, int y)
9575 {
9576   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9577 }
9578
9579 static void ActivateTrap(int x, int y)
9580 {
9581   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9582 }
9583
9584 static void ChangeActiveTrap(int x, int y)
9585 {
9586   int graphic = IMG_TRAP_ACTIVE;
9587
9588   /* if new animation frame was drawn, correct crumbled sand border */
9589   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9590     TEST_DrawLevelFieldCrumbled(x, y);
9591 }
9592
9593 static int getSpecialActionElement(int element, int number, int base_element)
9594 {
9595   return (element != EL_EMPTY ? element :
9596           number != -1 ? base_element + number - 1 :
9597           EL_EMPTY);
9598 }
9599
9600 static int getModifiedActionNumber(int value_old, int operator, int operand,
9601                                    int value_min, int value_max)
9602 {
9603   int value_new = (operator == CA_MODE_SET      ? operand :
9604                    operator == CA_MODE_ADD      ? value_old + operand :
9605                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9606                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9607                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9608                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9609                    value_old);
9610
9611   return (value_new < value_min ? value_min :
9612           value_new > value_max ? value_max :
9613           value_new);
9614 }
9615
9616 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9617 {
9618   struct ElementInfo *ei = &element_info[element];
9619   struct ElementChangeInfo *change = &ei->change_page[page];
9620   int target_element = change->target_element;
9621   int action_type = change->action_type;
9622   int action_mode = change->action_mode;
9623   int action_arg = change->action_arg;
9624   int action_element = change->action_element;
9625   int i;
9626
9627   if (!change->has_action)
9628     return;
9629
9630   /* ---------- determine action paramater values -------------------------- */
9631
9632   int level_time_value =
9633     (level.time > 0 ? TimeLeft :
9634      TimePlayed);
9635
9636   int action_arg_element_raw =
9637     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9638      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9639      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9640      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9641      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9642      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9643      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9644      EL_EMPTY);
9645   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9646
9647   int action_arg_direction =
9648     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9649      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9650      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9651      change->actual_trigger_side :
9652      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9653      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9654      MV_NONE);
9655
9656   int action_arg_number_min =
9657     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9658      CA_ARG_MIN);
9659
9660   int action_arg_number_max =
9661     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9662      action_type == CA_SET_LEVEL_GEMS ? 999 :
9663      action_type == CA_SET_LEVEL_TIME ? 9999 :
9664      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9665      action_type == CA_SET_CE_VALUE ? 9999 :
9666      action_type == CA_SET_CE_SCORE ? 9999 :
9667      CA_ARG_MAX);
9668
9669   int action_arg_number_reset =
9670     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9671      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9672      action_type == CA_SET_LEVEL_TIME ? level.time :
9673      action_type == CA_SET_LEVEL_SCORE ? 0 :
9674      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9675      action_type == CA_SET_CE_SCORE ? 0 :
9676      0);
9677
9678   int action_arg_number =
9679     (action_arg <= CA_ARG_MAX ? action_arg :
9680      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9681      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9682      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9683      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9684      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9685      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9686      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9687      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9688      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9689      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9690      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9691      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9692      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9693      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9694      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9695      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9696      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9697      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9698      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9699      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9700      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9701      -1);
9702
9703   int action_arg_number_old =
9704     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9705      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9706      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9707      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9708      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9709      0);
9710
9711   int action_arg_number_new =
9712     getModifiedActionNumber(action_arg_number_old,
9713                             action_mode, action_arg_number,
9714                             action_arg_number_min, action_arg_number_max);
9715
9716   int trigger_player_bits =
9717     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9718      change->actual_trigger_player_bits : change->trigger_player);
9719
9720   int action_arg_player_bits =
9721     (action_arg >= CA_ARG_PLAYER_1 &&
9722      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9723      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9724      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9725      PLAYER_BITS_ANY);
9726
9727   /* ---------- execute action  -------------------------------------------- */
9728
9729   switch (action_type)
9730   {
9731     case CA_NO_ACTION:
9732     {
9733       return;
9734     }
9735
9736     /* ---------- level actions  ------------------------------------------- */
9737
9738     case CA_RESTART_LEVEL:
9739     {
9740       game.restart_level = TRUE;
9741
9742       break;
9743     }
9744
9745     case CA_SHOW_ENVELOPE:
9746     {
9747       int element = getSpecialActionElement(action_arg_element,
9748                                             action_arg_number, EL_ENVELOPE_1);
9749
9750       if (IS_ENVELOPE(element))
9751         local_player->show_envelope = element;
9752
9753       break;
9754     }
9755
9756     case CA_SET_LEVEL_TIME:
9757     {
9758       if (level.time > 0)       /* only modify limited time value */
9759       {
9760         TimeLeft = action_arg_number_new;
9761
9762         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9763
9764         DisplayGameControlValues();
9765
9766         if (!TimeLeft && setup.time_limit)
9767           for (i = 0; i < MAX_PLAYERS; i++)
9768             KillPlayer(&stored_player[i]);
9769       }
9770
9771       break;
9772     }
9773
9774     case CA_SET_LEVEL_SCORE:
9775     {
9776       local_player->score = action_arg_number_new;
9777
9778       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9779
9780       DisplayGameControlValues();
9781
9782       break;
9783     }
9784
9785     case CA_SET_LEVEL_GEMS:
9786     {
9787       local_player->gems_still_needed = action_arg_number_new;
9788
9789       game.snapshot.collected_item = TRUE;
9790
9791       game_panel_controls[GAME_PANEL_GEMS].value =
9792         local_player->gems_still_needed;
9793
9794       DisplayGameControlValues();
9795
9796       break;
9797     }
9798
9799     case CA_SET_LEVEL_WIND:
9800     {
9801       game.wind_direction = action_arg_direction;
9802
9803       break;
9804     }
9805
9806     case CA_SET_LEVEL_RANDOM_SEED:
9807     {
9808       /* ensure that setting a new random seed while playing is predictable */
9809       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9810
9811       break;
9812     }
9813
9814     /* ---------- player actions  ------------------------------------------ */
9815
9816     case CA_MOVE_PLAYER:
9817     {
9818       /* automatically move to the next field in specified direction */
9819       for (i = 0; i < MAX_PLAYERS; i++)
9820         if (trigger_player_bits & (1 << i))
9821           stored_player[i].programmed_action = action_arg_direction;
9822
9823       break;
9824     }
9825
9826     case CA_EXIT_PLAYER:
9827     {
9828       for (i = 0; i < MAX_PLAYERS; i++)
9829         if (action_arg_player_bits & (1 << i))
9830           PlayerWins(&stored_player[i]);
9831
9832       break;
9833     }
9834
9835     case CA_KILL_PLAYER:
9836     {
9837       for (i = 0; i < MAX_PLAYERS; i++)
9838         if (action_arg_player_bits & (1 << i))
9839           KillPlayer(&stored_player[i]);
9840
9841       break;
9842     }
9843
9844     case CA_SET_PLAYER_KEYS:
9845     {
9846       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9847       int element = getSpecialActionElement(action_arg_element,
9848                                             action_arg_number, EL_KEY_1);
9849
9850       if (IS_KEY(element))
9851       {
9852         for (i = 0; i < MAX_PLAYERS; i++)
9853         {
9854           if (trigger_player_bits & (1 << i))
9855           {
9856             stored_player[i].key[KEY_NR(element)] = key_state;
9857
9858             DrawGameDoorValues();
9859           }
9860         }
9861       }
9862
9863       break;
9864     }
9865
9866     case CA_SET_PLAYER_SPEED:
9867     {
9868       for (i = 0; i < MAX_PLAYERS; i++)
9869       {
9870         if (trigger_player_bits & (1 << i))
9871         {
9872           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9873
9874           if (action_arg == CA_ARG_SPEED_FASTER &&
9875               stored_player[i].cannot_move)
9876           {
9877             action_arg_number = STEPSIZE_VERY_SLOW;
9878           }
9879           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9880                    action_arg == CA_ARG_SPEED_FASTER)
9881           {
9882             action_arg_number = 2;
9883             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9884                            CA_MODE_MULTIPLY);
9885           }
9886           else if (action_arg == CA_ARG_NUMBER_RESET)
9887           {
9888             action_arg_number = level.initial_player_stepsize[i];
9889           }
9890
9891           move_stepsize =
9892             getModifiedActionNumber(move_stepsize,
9893                                     action_mode,
9894                                     action_arg_number,
9895                                     action_arg_number_min,
9896                                     action_arg_number_max);
9897
9898           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9899         }
9900       }
9901
9902       break;
9903     }
9904
9905     case CA_SET_PLAYER_SHIELD:
9906     {
9907       for (i = 0; i < MAX_PLAYERS; i++)
9908       {
9909         if (trigger_player_bits & (1 << i))
9910         {
9911           if (action_arg == CA_ARG_SHIELD_OFF)
9912           {
9913             stored_player[i].shield_normal_time_left = 0;
9914             stored_player[i].shield_deadly_time_left = 0;
9915           }
9916           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9917           {
9918             stored_player[i].shield_normal_time_left = 999999;
9919           }
9920           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9921           {
9922             stored_player[i].shield_normal_time_left = 999999;
9923             stored_player[i].shield_deadly_time_left = 999999;
9924           }
9925         }
9926       }
9927
9928       break;
9929     }
9930
9931     case CA_SET_PLAYER_GRAVITY:
9932     {
9933       for (i = 0; i < MAX_PLAYERS; i++)
9934       {
9935         if (trigger_player_bits & (1 << i))
9936         {
9937           stored_player[i].gravity =
9938             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9939              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9940              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9941              stored_player[i].gravity);
9942         }
9943       }
9944
9945       break;
9946     }
9947
9948     case CA_SET_PLAYER_ARTWORK:
9949     {
9950       for (i = 0; i < MAX_PLAYERS; i++)
9951       {
9952         if (trigger_player_bits & (1 << i))
9953         {
9954           int artwork_element = action_arg_element;
9955
9956           if (action_arg == CA_ARG_ELEMENT_RESET)
9957             artwork_element =
9958               (level.use_artwork_element[i] ? level.artwork_element[i] :
9959                stored_player[i].element_nr);
9960
9961           if (stored_player[i].artwork_element != artwork_element)
9962             stored_player[i].Frame = 0;
9963
9964           stored_player[i].artwork_element = artwork_element;
9965
9966           SetPlayerWaiting(&stored_player[i], FALSE);
9967
9968           /* set number of special actions for bored and sleeping animation */
9969           stored_player[i].num_special_action_bored =
9970             get_num_special_action(artwork_element,
9971                                    ACTION_BORING_1, ACTION_BORING_LAST);
9972           stored_player[i].num_special_action_sleeping =
9973             get_num_special_action(artwork_element,
9974                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9975         }
9976       }
9977
9978       break;
9979     }
9980
9981     case CA_SET_PLAYER_INVENTORY:
9982     {
9983       for (i = 0; i < MAX_PLAYERS; i++)
9984       {
9985         struct PlayerInfo *player = &stored_player[i];
9986         int j, k;
9987
9988         if (trigger_player_bits & (1 << i))
9989         {
9990           int inventory_element = action_arg_element;
9991
9992           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9993               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9994               action_arg == CA_ARG_ELEMENT_ACTION)
9995           {
9996             int element = inventory_element;
9997             int collect_count = element_info[element].collect_count_initial;
9998
9999             if (!IS_CUSTOM_ELEMENT(element))
10000               collect_count = 1;
10001
10002             if (collect_count == 0)
10003               player->inventory_infinite_element = element;
10004             else
10005               for (k = 0; k < collect_count; k++)
10006                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10007                   player->inventory_element[player->inventory_size++] =
10008                     element;
10009           }
10010           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10011                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10012                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10013           {
10014             if (player->inventory_infinite_element != EL_UNDEFINED &&
10015                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10016                                      action_arg_element_raw))
10017               player->inventory_infinite_element = EL_UNDEFINED;
10018
10019             for (k = 0, j = 0; j < player->inventory_size; j++)
10020             {
10021               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10022                                         action_arg_element_raw))
10023                 player->inventory_element[k++] = player->inventory_element[j];
10024             }
10025
10026             player->inventory_size = k;
10027           }
10028           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10029           {
10030             if (player->inventory_size > 0)
10031             {
10032               for (j = 0; j < player->inventory_size - 1; j++)
10033                 player->inventory_element[j] = player->inventory_element[j + 1];
10034
10035               player->inventory_size--;
10036             }
10037           }
10038           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10039           {
10040             if (player->inventory_size > 0)
10041               player->inventory_size--;
10042           }
10043           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10044           {
10045             player->inventory_infinite_element = EL_UNDEFINED;
10046             player->inventory_size = 0;
10047           }
10048           else if (action_arg == CA_ARG_INVENTORY_RESET)
10049           {
10050             player->inventory_infinite_element = EL_UNDEFINED;
10051             player->inventory_size = 0;
10052
10053             if (level.use_initial_inventory[i])
10054             {
10055               for (j = 0; j < level.initial_inventory_size[i]; j++)
10056               {
10057                 int element = level.initial_inventory_content[i][j];
10058                 int collect_count = element_info[element].collect_count_initial;
10059
10060                 if (!IS_CUSTOM_ELEMENT(element))
10061                   collect_count = 1;
10062
10063                 if (collect_count == 0)
10064                   player->inventory_infinite_element = element;
10065                 else
10066                   for (k = 0; k < collect_count; k++)
10067                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10068                       player->inventory_element[player->inventory_size++] =
10069                         element;
10070               }
10071             }
10072           }
10073         }
10074       }
10075
10076       break;
10077     }
10078
10079     /* ---------- CE actions  ---------------------------------------------- */
10080
10081     case CA_SET_CE_VALUE:
10082     {
10083       int last_ce_value = CustomValue[x][y];
10084
10085       CustomValue[x][y] = action_arg_number_new;
10086
10087       if (CustomValue[x][y] != last_ce_value)
10088       {
10089         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10090         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10091
10092         if (CustomValue[x][y] == 0)
10093         {
10094           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10095           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10096         }
10097       }
10098
10099       break;
10100     }
10101
10102     case CA_SET_CE_SCORE:
10103     {
10104       int last_ce_score = ei->collect_score;
10105
10106       ei->collect_score = action_arg_number_new;
10107
10108       if (ei->collect_score != last_ce_score)
10109       {
10110         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10111         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10112
10113         if (ei->collect_score == 0)
10114         {
10115           int xx, yy;
10116
10117           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10118           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10119
10120           /*
10121             This is a very special case that seems to be a mixture between
10122             CheckElementChange() and CheckTriggeredElementChange(): while
10123             the first one only affects single elements that are triggered
10124             directly, the second one affects multiple elements in the playfield
10125             that are triggered indirectly by another element. This is a third
10126             case: Changing the CE score always affects multiple identical CEs,
10127             so every affected CE must be checked, not only the single CE for
10128             which the CE score was changed in the first place (as every instance
10129             of that CE shares the same CE score, and therefore also can change)!
10130           */
10131           SCAN_PLAYFIELD(xx, yy)
10132           {
10133             if (Feld[xx][yy] == element)
10134               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10135                                  CE_SCORE_GETS_ZERO);
10136           }
10137         }
10138       }
10139
10140       break;
10141     }
10142
10143     case CA_SET_CE_ARTWORK:
10144     {
10145       int artwork_element = action_arg_element;
10146       boolean reset_frame = FALSE;
10147       int xx, yy;
10148
10149       if (action_arg == CA_ARG_ELEMENT_RESET)
10150         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10151                            element);
10152
10153       if (ei->gfx_element != artwork_element)
10154         reset_frame = TRUE;
10155
10156       ei->gfx_element = artwork_element;
10157
10158       SCAN_PLAYFIELD(xx, yy)
10159       {
10160         if (Feld[xx][yy] == element)
10161         {
10162           if (reset_frame)
10163           {
10164             ResetGfxAnimation(xx, yy);
10165             ResetRandomAnimationValue(xx, yy);
10166           }
10167
10168           TEST_DrawLevelField(xx, yy);
10169         }
10170       }
10171
10172       break;
10173     }
10174
10175     /* ---------- engine actions  ------------------------------------------ */
10176
10177     case CA_SET_ENGINE_SCAN_MODE:
10178     {
10179       InitPlayfieldScanMode(action_arg);
10180
10181       break;
10182     }
10183
10184     default:
10185       break;
10186   }
10187 }
10188
10189 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10190 {
10191   int old_element = Feld[x][y];
10192   int new_element = GetElementFromGroupElement(element);
10193   int previous_move_direction = MovDir[x][y];
10194   int last_ce_value = CustomValue[x][y];
10195   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10196   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10197   boolean add_player_onto_element = (new_element_is_player &&
10198                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10199                                      IS_WALKABLE(old_element));
10200
10201   if (!add_player_onto_element)
10202   {
10203     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10204       RemoveMovingField(x, y);
10205     else
10206       RemoveField(x, y);
10207
10208     Feld[x][y] = new_element;
10209
10210     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10211       MovDir[x][y] = previous_move_direction;
10212
10213     if (element_info[new_element].use_last_ce_value)
10214       CustomValue[x][y] = last_ce_value;
10215
10216     InitField_WithBug1(x, y, FALSE);
10217
10218     new_element = Feld[x][y];   /* element may have changed */
10219
10220     ResetGfxAnimation(x, y);
10221     ResetRandomAnimationValue(x, y);
10222
10223     TEST_DrawLevelField(x, y);
10224
10225     if (GFX_CRUMBLED(new_element))
10226       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10227   }
10228
10229   /* check if element under the player changes from accessible to unaccessible
10230      (needed for special case of dropping element which then changes) */
10231   /* (must be checked after creating new element for walkable group elements) */
10232   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10233       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10234   {
10235     Bang(x, y);
10236
10237     return;
10238   }
10239
10240   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10241   if (new_element_is_player)
10242     RelocatePlayer(x, y, new_element);
10243
10244   if (is_change)
10245     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10246
10247   TestIfBadThingTouchesPlayer(x, y);
10248   TestIfPlayerTouchesCustomElement(x, y);
10249   TestIfElementTouchesCustomElement(x, y);
10250 }
10251
10252 static void CreateField(int x, int y, int element)
10253 {
10254   CreateFieldExt(x, y, element, FALSE);
10255 }
10256
10257 static void CreateElementFromChange(int x, int y, int element)
10258 {
10259   element = GET_VALID_RUNTIME_ELEMENT(element);
10260
10261   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10262   {
10263     int old_element = Feld[x][y];
10264
10265     /* prevent changed element from moving in same engine frame
10266        unless both old and new element can either fall or move */
10267     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10268         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10269       Stop[x][y] = TRUE;
10270   }
10271
10272   CreateFieldExt(x, y, element, TRUE);
10273 }
10274
10275 static boolean ChangeElement(int x, int y, int element, int page)
10276 {
10277   struct ElementInfo *ei = &element_info[element];
10278   struct ElementChangeInfo *change = &ei->change_page[page];
10279   int ce_value = CustomValue[x][y];
10280   int ce_score = ei->collect_score;
10281   int target_element;
10282   int old_element = Feld[x][y];
10283
10284   /* always use default change event to prevent running into a loop */
10285   if (ChangeEvent[x][y] == -1)
10286     ChangeEvent[x][y] = CE_DELAY;
10287
10288   if (ChangeEvent[x][y] == CE_DELAY)
10289   {
10290     /* reset actual trigger element, trigger player and action element */
10291     change->actual_trigger_element = EL_EMPTY;
10292     change->actual_trigger_player = EL_EMPTY;
10293     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10294     change->actual_trigger_side = CH_SIDE_NONE;
10295     change->actual_trigger_ce_value = 0;
10296     change->actual_trigger_ce_score = 0;
10297   }
10298
10299   /* do not change elements more than a specified maximum number of changes */
10300   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10301     return FALSE;
10302
10303   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10304
10305   if (change->explode)
10306   {
10307     Bang(x, y);
10308
10309     return TRUE;
10310   }
10311
10312   if (change->use_target_content)
10313   {
10314     boolean complete_replace = TRUE;
10315     boolean can_replace[3][3];
10316     int xx, yy;
10317
10318     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10319     {
10320       boolean is_empty;
10321       boolean is_walkable;
10322       boolean is_diggable;
10323       boolean is_collectible;
10324       boolean is_removable;
10325       boolean is_destructible;
10326       int ex = x + xx - 1;
10327       int ey = y + yy - 1;
10328       int content_element = change->target_content.e[xx][yy];
10329       int e;
10330
10331       can_replace[xx][yy] = TRUE;
10332
10333       if (ex == x && ey == y)   /* do not check changing element itself */
10334         continue;
10335
10336       if (content_element == EL_EMPTY_SPACE)
10337       {
10338         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10339
10340         continue;
10341       }
10342
10343       if (!IN_LEV_FIELD(ex, ey))
10344       {
10345         can_replace[xx][yy] = FALSE;
10346         complete_replace = FALSE;
10347
10348         continue;
10349       }
10350
10351       e = Feld[ex][ey];
10352
10353       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10354         e = MovingOrBlocked2Element(ex, ey);
10355
10356       is_empty = (IS_FREE(ex, ey) ||
10357                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10358
10359       is_walkable     = (is_empty || IS_WALKABLE(e));
10360       is_diggable     = (is_empty || IS_DIGGABLE(e));
10361       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10362       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10363       is_removable    = (is_diggable || is_collectible);
10364
10365       can_replace[xx][yy] =
10366         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10367           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10368           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10369           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10370           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10371           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10372          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10373
10374       if (!can_replace[xx][yy])
10375         complete_replace = FALSE;
10376     }
10377
10378     if (!change->only_if_complete || complete_replace)
10379     {
10380       boolean something_has_changed = FALSE;
10381
10382       if (change->only_if_complete && change->use_random_replace &&
10383           RND(100) < change->random_percentage)
10384         return FALSE;
10385
10386       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10387       {
10388         int ex = x + xx - 1;
10389         int ey = y + yy - 1;
10390         int content_element;
10391
10392         if (can_replace[xx][yy] && (!change->use_random_replace ||
10393                                     RND(100) < change->random_percentage))
10394         {
10395           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10396             RemoveMovingField(ex, ey);
10397
10398           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10399
10400           content_element = change->target_content.e[xx][yy];
10401           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10402                                               ce_value, ce_score);
10403
10404           CreateElementFromChange(ex, ey, target_element);
10405
10406           something_has_changed = TRUE;
10407
10408           /* for symmetry reasons, freeze newly created border elements */
10409           if (ex != x || ey != y)
10410             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10411         }
10412       }
10413
10414       if (something_has_changed)
10415       {
10416         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10417         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10418       }
10419     }
10420   }
10421   else
10422   {
10423     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10424                                         ce_value, ce_score);
10425
10426     if (element == EL_DIAGONAL_GROWING ||
10427         element == EL_DIAGONAL_SHRINKING)
10428     {
10429       target_element = Store[x][y];
10430
10431       Store[x][y] = EL_EMPTY;
10432     }
10433
10434     CreateElementFromChange(x, y, target_element);
10435
10436     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10437     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10438   }
10439
10440   /* this uses direct change before indirect change */
10441   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10442
10443   return TRUE;
10444 }
10445
10446 static void HandleElementChange(int x, int y, int page)
10447 {
10448   int element = MovingOrBlocked2Element(x, y);
10449   struct ElementInfo *ei = &element_info[element];
10450   struct ElementChangeInfo *change = &ei->change_page[page];
10451   boolean handle_action_before_change = FALSE;
10452
10453 #ifdef DEBUG
10454   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10455       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10456   {
10457     printf("\n\n");
10458     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10459            x, y, element, element_info[element].token_name);
10460     printf("HandleElementChange(): This should never happen!\n");
10461     printf("\n\n");
10462   }
10463 #endif
10464
10465   /* this can happen with classic bombs on walkable, changing elements */
10466   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10467   {
10468     return;
10469   }
10470
10471   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10472   {
10473     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10474
10475     if (change->can_change)
10476     {
10477       /* !!! not clear why graphic animation should be reset at all here !!! */
10478       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10479       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10480
10481       /*
10482         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10483
10484         When using an animation frame delay of 1 (this only happens with
10485         "sp_zonk.moving.left/right" in the classic graphics), the default
10486         (non-moving) animation shows wrong animation frames (while the
10487         moving animation, like "sp_zonk.moving.left/right", is correct,
10488         so this graphical bug never shows up with the classic graphics).
10489         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10490         be drawn instead of the correct frames 0,1,2,3. This is caused by
10491         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10492         an element change: First when the change delay ("ChangeDelay[][]")
10493         counter has reached zero after decrementing, then a second time in
10494         the next frame (after "GfxFrame[][]" was already incremented) when
10495         "ChangeDelay[][]" is reset to the initial delay value again.
10496
10497         This causes frame 0 to be drawn twice, while the last frame won't
10498         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10499
10500         As some animations may already be cleverly designed around this bug
10501         (at least the "Snake Bite" snake tail animation does this), it cannot
10502         simply be fixed here without breaking such existing animations.
10503         Unfortunately, it cannot easily be detected if a graphics set was
10504         designed "before" or "after" the bug was fixed. As a workaround,
10505         a new graphics set option "game.graphics_engine_version" was added
10506         to be able to specify the game's major release version for which the
10507         graphics set was designed, which can then be used to decide if the
10508         bugfix should be used (version 4 and above) or not (version 3 or
10509         below, or if no version was specified at all, as with old sets).
10510
10511         (The wrong/fixed animation frames can be tested with the test level set
10512         "test_gfxframe" and level "000", which contains a specially prepared
10513         custom element at level position (x/y) == (11/9) which uses the zonk
10514         animation mentioned above. Using "game.graphics_engine_version: 4"
10515         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10516         This can also be seen from the debug output for this test element.)
10517       */
10518
10519       /* when a custom element is about to change (for example by change delay),
10520          do not reset graphic animation when the custom element is moving */
10521       if (game.graphics_engine_version < 4 &&
10522           !IS_MOVING(x, y))
10523       {
10524         ResetGfxAnimation(x, y);
10525         ResetRandomAnimationValue(x, y);
10526       }
10527
10528       if (change->pre_change_function)
10529         change->pre_change_function(x, y);
10530     }
10531   }
10532
10533   ChangeDelay[x][y]--;
10534
10535   if (ChangeDelay[x][y] != 0)           /* continue element change */
10536   {
10537     if (change->can_change)
10538     {
10539       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10540
10541       if (IS_ANIMATED(graphic))
10542         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10543
10544       if (change->change_function)
10545         change->change_function(x, y);
10546     }
10547   }
10548   else                                  /* finish element change */
10549   {
10550     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10551     {
10552       page = ChangePage[x][y];
10553       ChangePage[x][y] = -1;
10554
10555       change = &ei->change_page[page];
10556     }
10557
10558     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10559     {
10560       ChangeDelay[x][y] = 1;            /* try change after next move step */
10561       ChangePage[x][y] = page;          /* remember page to use for change */
10562
10563       return;
10564     }
10565
10566     /* special case: set new level random seed before changing element */
10567     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10568       handle_action_before_change = TRUE;
10569
10570     if (change->has_action && handle_action_before_change)
10571       ExecuteCustomElementAction(x, y, element, page);
10572
10573     if (change->can_change)
10574     {
10575       if (ChangeElement(x, y, element, page))
10576       {
10577         if (change->post_change_function)
10578           change->post_change_function(x, y);
10579       }
10580     }
10581
10582     if (change->has_action && !handle_action_before_change)
10583       ExecuteCustomElementAction(x, y, element, page);
10584   }
10585 }
10586
10587 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10588                                               int trigger_element,
10589                                               int trigger_event,
10590                                               int trigger_player,
10591                                               int trigger_side,
10592                                               int trigger_page)
10593 {
10594   boolean change_done_any = FALSE;
10595   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10596   int i;
10597
10598   if (!(trigger_events[trigger_element][trigger_event]))
10599     return FALSE;
10600
10601   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10602
10603   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10604   {
10605     int element = EL_CUSTOM_START + i;
10606     boolean change_done = FALSE;
10607     int p;
10608
10609     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10610         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10611       continue;
10612
10613     for (p = 0; p < element_info[element].num_change_pages; p++)
10614     {
10615       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10616
10617       if (change->can_change_or_has_action &&
10618           change->has_event[trigger_event] &&
10619           change->trigger_side & trigger_side &&
10620           change->trigger_player & trigger_player &&
10621           change->trigger_page & trigger_page_bits &&
10622           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10623       {
10624         change->actual_trigger_element = trigger_element;
10625         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10626         change->actual_trigger_player_bits = trigger_player;
10627         change->actual_trigger_side = trigger_side;
10628         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10629         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10630
10631         if ((change->can_change && !change_done) || change->has_action)
10632         {
10633           int x, y;
10634
10635           SCAN_PLAYFIELD(x, y)
10636           {
10637             if (Feld[x][y] == element)
10638             {
10639               if (change->can_change && !change_done)
10640               {
10641                 /* if element already changed in this frame, not only prevent
10642                    another element change (checked in ChangeElement()), but
10643                    also prevent additional element actions for this element */
10644
10645                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10646                     !level.use_action_after_change_bug)
10647                   continue;
10648
10649                 ChangeDelay[x][y] = 1;
10650                 ChangeEvent[x][y] = trigger_event;
10651
10652                 HandleElementChange(x, y, p);
10653               }
10654               else if (change->has_action)
10655               {
10656                 /* if element already changed in this frame, not only prevent
10657                    another element change (checked in ChangeElement()), but
10658                    also prevent additional element actions for this element */
10659
10660                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10661                     !level.use_action_after_change_bug)
10662                   continue;
10663
10664                 ExecuteCustomElementAction(x, y, element, p);
10665                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10666               }
10667             }
10668           }
10669
10670           if (change->can_change)
10671           {
10672             change_done = TRUE;
10673             change_done_any = TRUE;
10674           }
10675         }
10676       }
10677     }
10678   }
10679
10680   RECURSION_LOOP_DETECTION_END();
10681
10682   return change_done_any;
10683 }
10684
10685 static boolean CheckElementChangeExt(int x, int y,
10686                                      int element,
10687                                      int trigger_element,
10688                                      int trigger_event,
10689                                      int trigger_player,
10690                                      int trigger_side)
10691 {
10692   boolean change_done = FALSE;
10693   int p;
10694
10695   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10696       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10697     return FALSE;
10698
10699   if (Feld[x][y] == EL_BLOCKED)
10700   {
10701     Blocked2Moving(x, y, &x, &y);
10702     element = Feld[x][y];
10703   }
10704
10705   /* check if element has already changed or is about to change after moving */
10706   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10707        Feld[x][y] != element) ||
10708
10709       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10710        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10711         ChangePage[x][y] != -1)))
10712     return FALSE;
10713
10714   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10715
10716   for (p = 0; p < element_info[element].num_change_pages; p++)
10717   {
10718     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10719
10720     /* check trigger element for all events where the element that is checked
10721        for changing interacts with a directly adjacent element -- this is
10722        different to element changes that affect other elements to change on the
10723        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10724     boolean check_trigger_element =
10725       (trigger_event == CE_TOUCHING_X ||
10726        trigger_event == CE_HITTING_X ||
10727        trigger_event == CE_HIT_BY_X ||
10728        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10729
10730     if (change->can_change_or_has_action &&
10731         change->has_event[trigger_event] &&
10732         change->trigger_side & trigger_side &&
10733         change->trigger_player & trigger_player &&
10734         (!check_trigger_element ||
10735          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10736     {
10737       change->actual_trigger_element = trigger_element;
10738       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10739       change->actual_trigger_player_bits = trigger_player;
10740       change->actual_trigger_side = trigger_side;
10741       change->actual_trigger_ce_value = CustomValue[x][y];
10742       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10743
10744       /* special case: trigger element not at (x,y) position for some events */
10745       if (check_trigger_element)
10746       {
10747         static struct
10748         {
10749           int dx, dy;
10750         } move_xy[] =
10751           {
10752             {  0,  0 },
10753             { -1,  0 },
10754             { +1,  0 },
10755             {  0,  0 },
10756             {  0, -1 },
10757             {  0,  0 }, { 0, 0 }, { 0, 0 },
10758             {  0, +1 }
10759           };
10760
10761         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10762         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10763
10764         change->actual_trigger_ce_value = CustomValue[xx][yy];
10765         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10766       }
10767
10768       if (change->can_change && !change_done)
10769       {
10770         ChangeDelay[x][y] = 1;
10771         ChangeEvent[x][y] = trigger_event;
10772
10773         HandleElementChange(x, y, p);
10774
10775         change_done = TRUE;
10776       }
10777       else if (change->has_action)
10778       {
10779         ExecuteCustomElementAction(x, y, element, p);
10780         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10781       }
10782     }
10783   }
10784
10785   RECURSION_LOOP_DETECTION_END();
10786
10787   return change_done;
10788 }
10789
10790 static void PlayPlayerSound(struct PlayerInfo *player)
10791 {
10792   int jx = player->jx, jy = player->jy;
10793   int sound_element = player->artwork_element;
10794   int last_action = player->last_action_waiting;
10795   int action = player->action_waiting;
10796
10797   if (player->is_waiting)
10798   {
10799     if (action != last_action)
10800       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10801     else
10802       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10803   }
10804   else
10805   {
10806     if (action != last_action)
10807       StopSound(element_info[sound_element].sound[last_action]);
10808
10809     if (last_action == ACTION_SLEEPING)
10810       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10811   }
10812 }
10813
10814 static void PlayAllPlayersSound()
10815 {
10816   int i;
10817
10818   for (i = 0; i < MAX_PLAYERS; i++)
10819     if (stored_player[i].active)
10820       PlayPlayerSound(&stored_player[i]);
10821 }
10822
10823 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10824 {
10825   boolean last_waiting = player->is_waiting;
10826   int move_dir = player->MovDir;
10827
10828   player->dir_waiting = move_dir;
10829   player->last_action_waiting = player->action_waiting;
10830
10831   if (is_waiting)
10832   {
10833     if (!last_waiting)          /* not waiting -> waiting */
10834     {
10835       player->is_waiting = TRUE;
10836
10837       player->frame_counter_bored =
10838         FrameCounter +
10839         game.player_boring_delay_fixed +
10840         GetSimpleRandom(game.player_boring_delay_random);
10841       player->frame_counter_sleeping =
10842         FrameCounter +
10843         game.player_sleeping_delay_fixed +
10844         GetSimpleRandom(game.player_sleeping_delay_random);
10845
10846       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10847     }
10848
10849     if (game.player_sleeping_delay_fixed +
10850         game.player_sleeping_delay_random > 0 &&
10851         player->anim_delay_counter == 0 &&
10852         player->post_delay_counter == 0 &&
10853         FrameCounter >= player->frame_counter_sleeping)
10854       player->is_sleeping = TRUE;
10855     else if (game.player_boring_delay_fixed +
10856              game.player_boring_delay_random > 0 &&
10857              FrameCounter >= player->frame_counter_bored)
10858       player->is_bored = TRUE;
10859
10860     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10861                               player->is_bored ? ACTION_BORING :
10862                               ACTION_WAITING);
10863
10864     if (player->is_sleeping && player->use_murphy)
10865     {
10866       /* special case for sleeping Murphy when leaning against non-free tile */
10867
10868       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10869           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10870            !IS_MOVING(player->jx - 1, player->jy)))
10871         move_dir = MV_LEFT;
10872       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10873                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10874                 !IS_MOVING(player->jx + 1, player->jy)))
10875         move_dir = MV_RIGHT;
10876       else
10877         player->is_sleeping = FALSE;
10878
10879       player->dir_waiting = move_dir;
10880     }
10881
10882     if (player->is_sleeping)
10883     {
10884       if (player->num_special_action_sleeping > 0)
10885       {
10886         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10887         {
10888           int last_special_action = player->special_action_sleeping;
10889           int num_special_action = player->num_special_action_sleeping;
10890           int special_action =
10891             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10892              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10893              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10894              last_special_action + 1 : ACTION_SLEEPING);
10895           int special_graphic =
10896             el_act_dir2img(player->artwork_element, special_action, move_dir);
10897
10898           player->anim_delay_counter =
10899             graphic_info[special_graphic].anim_delay_fixed +
10900             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10901           player->post_delay_counter =
10902             graphic_info[special_graphic].post_delay_fixed +
10903             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10904
10905           player->special_action_sleeping = special_action;
10906         }
10907
10908         if (player->anim_delay_counter > 0)
10909         {
10910           player->action_waiting = player->special_action_sleeping;
10911           player->anim_delay_counter--;
10912         }
10913         else if (player->post_delay_counter > 0)
10914         {
10915           player->post_delay_counter--;
10916         }
10917       }
10918     }
10919     else if (player->is_bored)
10920     {
10921       if (player->num_special_action_bored > 0)
10922       {
10923         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10924         {
10925           int special_action =
10926             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10927           int special_graphic =
10928             el_act_dir2img(player->artwork_element, special_action, move_dir);
10929
10930           player->anim_delay_counter =
10931             graphic_info[special_graphic].anim_delay_fixed +
10932             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10933           player->post_delay_counter =
10934             graphic_info[special_graphic].post_delay_fixed +
10935             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10936
10937           player->special_action_bored = special_action;
10938         }
10939
10940         if (player->anim_delay_counter > 0)
10941         {
10942           player->action_waiting = player->special_action_bored;
10943           player->anim_delay_counter--;
10944         }
10945         else if (player->post_delay_counter > 0)
10946         {
10947           player->post_delay_counter--;
10948         }
10949       }
10950     }
10951   }
10952   else if (last_waiting)        /* waiting -> not waiting */
10953   {
10954     player->is_waiting = FALSE;
10955     player->is_bored = FALSE;
10956     player->is_sleeping = FALSE;
10957
10958     player->frame_counter_bored = -1;
10959     player->frame_counter_sleeping = -1;
10960
10961     player->anim_delay_counter = 0;
10962     player->post_delay_counter = 0;
10963
10964     player->dir_waiting = player->MovDir;
10965     player->action_waiting = ACTION_DEFAULT;
10966
10967     player->special_action_bored = ACTION_DEFAULT;
10968     player->special_action_sleeping = ACTION_DEFAULT;
10969   }
10970 }
10971
10972 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10973 {
10974   if ((!player->is_moving  && player->was_moving) ||
10975       (player->MovPos == 0 && player->was_moving) ||
10976       (player->is_snapping && !player->was_snapping) ||
10977       (player->is_dropping && !player->was_dropping))
10978   {
10979     if (!CheckSaveEngineSnapshotToList())
10980       return;
10981
10982     player->was_moving = FALSE;
10983     player->was_snapping = TRUE;
10984     player->was_dropping = TRUE;
10985   }
10986   else
10987   {
10988     if (player->is_moving)
10989       player->was_moving = TRUE;
10990
10991     if (!player->is_snapping)
10992       player->was_snapping = FALSE;
10993
10994     if (!player->is_dropping)
10995       player->was_dropping = FALSE;
10996   }
10997 }
10998
10999 static void CheckSingleStepMode(struct PlayerInfo *player)
11000 {
11001   if (tape.single_step && tape.recording && !tape.pausing)
11002   {
11003     /* as it is called "single step mode", just return to pause mode when the
11004        player stopped moving after one tile (or never starts moving at all) */
11005     if (!player->is_moving &&
11006         !player->is_pushing &&
11007         !player->is_dropping_pressed)
11008     {
11009       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11010       SnapField(player, 0, 0);                  /* stop snapping */
11011     }
11012   }
11013
11014   CheckSaveEngineSnapshot(player);
11015 }
11016
11017 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11018 {
11019   int left      = player_action & JOY_LEFT;
11020   int right     = player_action & JOY_RIGHT;
11021   int up        = player_action & JOY_UP;
11022   int down      = player_action & JOY_DOWN;
11023   int button1   = player_action & JOY_BUTTON_1;
11024   int button2   = player_action & JOY_BUTTON_2;
11025   int dx        = (left ? -1 : right ? 1 : 0);
11026   int dy        = (up   ? -1 : down  ? 1 : 0);
11027
11028   if (!player->active || tape.pausing)
11029     return 0;
11030
11031   if (player_action)
11032   {
11033     if (button1)
11034       SnapField(player, dx, dy);
11035     else
11036     {
11037       if (button2)
11038         DropElement(player);
11039
11040       MovePlayer(player, dx, dy);
11041     }
11042
11043     CheckSingleStepMode(player);
11044
11045     SetPlayerWaiting(player, FALSE);
11046
11047     return player_action;
11048   }
11049   else
11050   {
11051     /* no actions for this player (no input at player's configured device) */
11052
11053     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11054     SnapField(player, 0, 0);
11055     CheckGravityMovementWhenNotMoving(player);
11056
11057     if (player->MovPos == 0)
11058       SetPlayerWaiting(player, TRUE);
11059
11060     if (player->MovPos == 0)    /* needed for tape.playing */
11061       player->is_moving = FALSE;
11062
11063     player->is_dropping = FALSE;
11064     player->is_dropping_pressed = FALSE;
11065     player->drop_pressed_delay = 0;
11066
11067     CheckSingleStepMode(player);
11068
11069     return 0;
11070   }
11071 }
11072
11073 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11074                                          byte *tape_action)
11075 {
11076   if (!tape.use_mouse)
11077     return;
11078
11079   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11080   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11081   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11082 }
11083
11084 static void SetTapeActionFromMouseAction(byte *tape_action,
11085                                          struct MouseActionInfo *mouse_action)
11086 {
11087   if (!tape.use_mouse)
11088     return;
11089
11090   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11091   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11092   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11093 }
11094
11095 static void CheckLevelTime()
11096 {
11097   int i;
11098
11099   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11100   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11101   {
11102     if (level.native_em_level->lev->home == 0)  /* all players at home */
11103     {
11104       PlayerWins(local_player);
11105
11106       AllPlayersGone = TRUE;
11107
11108       level.native_em_level->lev->home = -1;
11109     }
11110
11111     if (level.native_em_level->ply[0]->alive == 0 &&
11112         level.native_em_level->ply[1]->alive == 0 &&
11113         level.native_em_level->ply[2]->alive == 0 &&
11114         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11115       AllPlayersGone = TRUE;
11116   }
11117   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11118   {
11119     if (game_sp.LevelSolved &&
11120         !game_sp.GameOver)                              /* game won */
11121     {
11122       PlayerWins(local_player);
11123
11124       game_sp.GameOver = TRUE;
11125
11126       AllPlayersGone = TRUE;
11127     }
11128
11129     if (game_sp.GameOver)                               /* game lost */
11130       AllPlayersGone = TRUE;
11131   }
11132   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11133   {
11134     if (game_mm.level_solved &&
11135         !game_mm.game_over)                             /* game won */
11136     {
11137       PlayerWins(local_player);
11138
11139       game_mm.game_over = TRUE;
11140
11141       AllPlayersGone = TRUE;
11142     }
11143
11144     if (game_mm.game_over)                              /* game lost */
11145       AllPlayersGone = TRUE;
11146   }
11147
11148   if (TimeFrames >= FRAMES_PER_SECOND)
11149   {
11150     TimeFrames = 0;
11151     TapeTime++;
11152
11153     for (i = 0; i < MAX_PLAYERS; i++)
11154     {
11155       struct PlayerInfo *player = &stored_player[i];
11156
11157       if (SHIELD_ON(player))
11158       {
11159         player->shield_normal_time_left--;
11160
11161         if (player->shield_deadly_time_left > 0)
11162           player->shield_deadly_time_left--;
11163       }
11164     }
11165
11166     if (!local_player->LevelSolved && !level.use_step_counter)
11167     {
11168       TimePlayed++;
11169
11170       if (TimeLeft > 0)
11171       {
11172         TimeLeft--;
11173
11174         if (TimeLeft <= 10 && setup.time_limit)
11175           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11176
11177         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11178            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11179
11180         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11181
11182         if (!TimeLeft && setup.time_limit)
11183         {
11184           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11185             level.native_em_level->lev->killed_out_of_time = TRUE;
11186           else
11187             for (i = 0; i < MAX_PLAYERS; i++)
11188               KillPlayer(&stored_player[i]);
11189         }
11190       }
11191       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11192       {
11193         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11194       }
11195
11196       level.native_em_level->lev->time =
11197         (game.no_time_limit ? TimePlayed : TimeLeft);
11198     }
11199
11200     if (tape.recording || tape.playing)
11201       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11202   }
11203
11204   if (tape.recording || tape.playing)
11205     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11206
11207   UpdateAndDisplayGameControlValues();
11208 }
11209
11210 void AdvanceFrameAndPlayerCounters(int player_nr)
11211 {
11212   int i;
11213
11214   /* advance frame counters (global frame counter and time frame counter) */
11215   FrameCounter++;
11216   TimeFrames++;
11217
11218   /* advance player counters (counters for move delay, move animation etc.) */
11219   for (i = 0; i < MAX_PLAYERS; i++)
11220   {
11221     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11222     int move_delay_value = stored_player[i].move_delay_value;
11223     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11224
11225     if (!advance_player_counters)       /* not all players may be affected */
11226       continue;
11227
11228     if (move_frames == 0)       /* less than one move per game frame */
11229     {
11230       int stepsize = TILEX / move_delay_value;
11231       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11232       int count = (stored_player[i].is_moving ?
11233                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11234
11235       if (count % delay == 0)
11236         move_frames = 1;
11237     }
11238
11239     stored_player[i].Frame += move_frames;
11240
11241     if (stored_player[i].MovPos != 0)
11242       stored_player[i].StepFrame += move_frames;
11243
11244     if (stored_player[i].move_delay > 0)
11245       stored_player[i].move_delay--;
11246
11247     /* due to bugs in previous versions, counter must count up, not down */
11248     if (stored_player[i].push_delay != -1)
11249       stored_player[i].push_delay++;
11250
11251     if (stored_player[i].drop_delay > 0)
11252       stored_player[i].drop_delay--;
11253
11254     if (stored_player[i].is_dropping_pressed)
11255       stored_player[i].drop_pressed_delay++;
11256   }
11257 }
11258
11259 void StartGameActions(boolean init_network_game, boolean record_tape,
11260                       int random_seed)
11261 {
11262   unsigned int new_random_seed = InitRND(random_seed);
11263
11264   if (record_tape)
11265     TapeStartRecording(new_random_seed);
11266
11267 #if defined(NETWORK_AVALIABLE)
11268   if (init_network_game)
11269   {
11270     SendToServer_StartPlaying();
11271
11272     return;
11273   }
11274 #endif
11275
11276   InitGame();
11277 }
11278
11279 void GameActionsExt()
11280 {
11281 #if 0
11282   static unsigned int game_frame_delay = 0;
11283 #endif
11284   unsigned int game_frame_delay_value;
11285   byte *recorded_player_action;
11286   byte summarized_player_action = 0;
11287   byte tape_action[MAX_PLAYERS];
11288   int i;
11289
11290   /* detect endless loops, caused by custom element programming */
11291   if (recursion_loop_detected && recursion_loop_depth == 0)
11292   {
11293     char *message = getStringCat3("Internal Error! Element ",
11294                                   EL_NAME(recursion_loop_element),
11295                                   " caused endless loop! Quit the game?");
11296
11297     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11298           EL_NAME(recursion_loop_element));
11299
11300     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11301
11302     recursion_loop_detected = FALSE;    /* if game should be continued */
11303
11304     free(message);
11305
11306     return;
11307   }
11308
11309   if (game.restart_level)
11310     StartGameActions(options.network, setup.autorecord, level.random_seed);
11311
11312   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11313   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11314   {
11315     if (level.native_em_level->lev->home == 0)  /* all players at home */
11316     {
11317       PlayerWins(local_player);
11318
11319       AllPlayersGone = TRUE;
11320
11321       level.native_em_level->lev->home = -1;
11322     }
11323
11324     if (level.native_em_level->ply[0]->alive == 0 &&
11325         level.native_em_level->ply[1]->alive == 0 &&
11326         level.native_em_level->ply[2]->alive == 0 &&
11327         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11328       AllPlayersGone = TRUE;
11329   }
11330   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11331   {
11332     if (game_sp.LevelSolved &&
11333         !game_sp.GameOver)                              /* game won */
11334     {
11335       PlayerWins(local_player);
11336
11337       game_sp.GameOver = TRUE;
11338
11339       AllPlayersGone = TRUE;
11340     }
11341
11342     if (game_sp.GameOver)                               /* game lost */
11343       AllPlayersGone = TRUE;
11344   }
11345   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11346   {
11347     if (game_mm.level_solved &&
11348         !game_mm.game_over)                             /* game won */
11349     {
11350       PlayerWins(local_player);
11351
11352       game_mm.game_over = TRUE;
11353
11354       AllPlayersGone = TRUE;
11355     }
11356
11357     if (game_mm.game_over)                              /* game lost */
11358       AllPlayersGone = TRUE;
11359   }
11360
11361   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11362     GameWon();
11363
11364   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11365     TapeStop();
11366
11367   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11368     return;
11369
11370   game_frame_delay_value =
11371     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11372
11373   if (tape.playing && tape.warp_forward && !tape.pausing)
11374     game_frame_delay_value = 0;
11375
11376   SetVideoFrameDelay(game_frame_delay_value);
11377
11378 #if 0
11379 #if 0
11380   /* ---------- main game synchronization point ---------- */
11381
11382   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11383
11384   printf("::: skip == %d\n", skip);
11385
11386 #else
11387   /* ---------- main game synchronization point ---------- */
11388
11389   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11390 #endif
11391 #endif
11392
11393   if (network_playing && !network_player_action_received)
11394   {
11395     /* try to get network player actions in time */
11396
11397 #if defined(NETWORK_AVALIABLE)
11398     /* last chance to get network player actions without main loop delay */
11399     HandleNetworking();
11400 #endif
11401
11402     /* game was quit by network peer */
11403     if (game_status != GAME_MODE_PLAYING)
11404       return;
11405
11406     if (!network_player_action_received)
11407       return;           /* failed to get network player actions in time */
11408
11409     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11410   }
11411
11412   if (tape.pausing)
11413     return;
11414
11415   /* at this point we know that we really continue executing the game */
11416
11417   network_player_action_received = FALSE;
11418
11419   /* when playing tape, read previously recorded player input from tape data */
11420   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11421
11422   local_player->effective_mouse_action = local_player->mouse_action;
11423
11424   if (recorded_player_action != NULL)
11425     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11426                                  recorded_player_action);
11427
11428   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11429   if (tape.pausing)
11430     return;
11431
11432   if (tape.set_centered_player)
11433   {
11434     game.centered_player_nr_next = tape.centered_player_nr_next;
11435     game.set_centered_player = TRUE;
11436   }
11437
11438   for (i = 0; i < MAX_PLAYERS; i++)
11439   {
11440     summarized_player_action |= stored_player[i].action;
11441
11442     if (!network_playing && (game.team_mode || tape.playing))
11443       stored_player[i].effective_action = stored_player[i].action;
11444   }
11445
11446 #if defined(NETWORK_AVALIABLE)
11447   if (network_playing)
11448     SendToServer_MovePlayer(summarized_player_action);
11449 #endif
11450
11451   // summarize all actions at local players mapped input device position
11452   // (this allows using different input devices in single player mode)
11453   if (!options.network && !game.team_mode)
11454     stored_player[map_player_action[local_player->index_nr]].effective_action =
11455       summarized_player_action;
11456
11457   if (tape.recording &&
11458       setup.team_mode &&
11459       setup.input_on_focus &&
11460       game.centered_player_nr != -1)
11461   {
11462     for (i = 0; i < MAX_PLAYERS; i++)
11463       stored_player[i].effective_action =
11464         (i == game.centered_player_nr ? summarized_player_action : 0);
11465   }
11466
11467   if (recorded_player_action != NULL)
11468     for (i = 0; i < MAX_PLAYERS; i++)
11469       stored_player[i].effective_action = recorded_player_action[i];
11470
11471   for (i = 0; i < MAX_PLAYERS; i++)
11472   {
11473     tape_action[i] = stored_player[i].effective_action;
11474
11475     /* (this may happen in the RND game engine if a player was not present on
11476        the playfield on level start, but appeared later from a custom element */
11477     if (setup.team_mode &&
11478         tape.recording &&
11479         tape_action[i] &&
11480         !tape.player_participates[i])
11481       tape.player_participates[i] = TRUE;
11482   }
11483
11484   SetTapeActionFromMouseAction(tape_action,
11485                                &local_player->effective_mouse_action);
11486
11487   /* only record actions from input devices, but not programmed actions */
11488   if (tape.recording)
11489     TapeRecordAction(tape_action);
11490
11491 #if USE_NEW_PLAYER_ASSIGNMENTS
11492   // !!! also map player actions in single player mode !!!
11493   // if (game.team_mode)
11494   if (1)
11495   {
11496     byte mapped_action[MAX_PLAYERS];
11497
11498 #if DEBUG_PLAYER_ACTIONS
11499     printf(":::");
11500     for (i = 0; i < MAX_PLAYERS; i++)
11501       printf(" %d, ", stored_player[i].effective_action);
11502 #endif
11503
11504     for (i = 0; i < MAX_PLAYERS; i++)
11505       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11506
11507     for (i = 0; i < MAX_PLAYERS; i++)
11508       stored_player[i].effective_action = mapped_action[i];
11509
11510 #if DEBUG_PLAYER_ACTIONS
11511     printf(" =>");
11512     for (i = 0; i < MAX_PLAYERS; i++)
11513       printf(" %d, ", stored_player[i].effective_action);
11514     printf("\n");
11515 #endif
11516   }
11517 #if DEBUG_PLAYER_ACTIONS
11518   else
11519   {
11520     printf(":::");
11521     for (i = 0; i < MAX_PLAYERS; i++)
11522       printf(" %d, ", stored_player[i].effective_action);
11523     printf("\n");
11524   }
11525 #endif
11526 #endif
11527
11528   for (i = 0; i < MAX_PLAYERS; i++)
11529   {
11530     // allow engine snapshot in case of changed movement attempt
11531     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11532         (stored_player[i].effective_action & KEY_MOTION))
11533       game.snapshot.changed_action = TRUE;
11534
11535     // allow engine snapshot in case of snapping/dropping attempt
11536     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11537         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11538       game.snapshot.changed_action = TRUE;
11539
11540     game.snapshot.last_action[i] = stored_player[i].effective_action;
11541   }
11542
11543   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11544   {
11545     GameActions_EM_Main();
11546   }
11547   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11548   {
11549     GameActions_SP_Main();
11550   }
11551   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11552   {
11553     GameActions_MM_Main();
11554   }
11555   else
11556   {
11557     GameActions_RND_Main();
11558   }
11559
11560   BlitScreenToBitmap(backbuffer);
11561
11562   CheckLevelTime();
11563
11564   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11565
11566   if (global.show_frames_per_second)
11567   {
11568     static unsigned int fps_counter = 0;
11569     static int fps_frames = 0;
11570     unsigned int fps_delay_ms = Counter() - fps_counter;
11571
11572     fps_frames++;
11573
11574     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11575     {
11576       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11577
11578       fps_frames = 0;
11579       fps_counter = Counter();
11580
11581       /* always draw FPS to screen after FPS value was updated */
11582       redraw_mask |= REDRAW_FPS;
11583     }
11584
11585     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11586     if (GetDrawDeactivationMask() == REDRAW_NONE)
11587       redraw_mask |= REDRAW_FPS;
11588   }
11589 }
11590
11591 static void GameActions_CheckSaveEngineSnapshot()
11592 {
11593   if (!game.snapshot.save_snapshot)
11594     return;
11595
11596   // clear flag for saving snapshot _before_ saving snapshot
11597   game.snapshot.save_snapshot = FALSE;
11598
11599   SaveEngineSnapshotToList();
11600 }
11601
11602 void GameActions()
11603 {
11604   GameActionsExt();
11605
11606   GameActions_CheckSaveEngineSnapshot();
11607 }
11608
11609 void GameActions_EM_Main()
11610 {
11611   byte effective_action[MAX_PLAYERS];
11612   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11613   int i;
11614
11615   for (i = 0; i < MAX_PLAYERS; i++)
11616     effective_action[i] = stored_player[i].effective_action;
11617
11618   GameActions_EM(effective_action, warp_mode);
11619 }
11620
11621 void GameActions_SP_Main()
11622 {
11623   byte effective_action[MAX_PLAYERS];
11624   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11625   int i;
11626
11627   for (i = 0; i < MAX_PLAYERS; i++)
11628     effective_action[i] = stored_player[i].effective_action;
11629
11630   GameActions_SP(effective_action, warp_mode);
11631
11632   for (i = 0; i < MAX_PLAYERS; i++)
11633   {
11634     if (stored_player[i].force_dropping)
11635       stored_player[i].action |= KEY_BUTTON_DROP;
11636
11637     stored_player[i].force_dropping = FALSE;
11638   }
11639 }
11640
11641 void GameActions_MM_Main()
11642 {
11643   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11644
11645   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11646 }
11647
11648 void GameActions_RND_Main()
11649 {
11650   GameActions_RND();
11651 }
11652
11653 void GameActions_RND()
11654 {
11655   int magic_wall_x = 0, magic_wall_y = 0;
11656   int i, x, y, element, graphic, last_gfx_frame;
11657
11658   InitPlayfieldScanModeVars();
11659
11660   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11661   {
11662     SCAN_PLAYFIELD(x, y)
11663     {
11664       ChangeCount[x][y] = 0;
11665       ChangeEvent[x][y] = -1;
11666     }
11667   }
11668
11669   if (game.set_centered_player)
11670   {
11671     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11672
11673     /* switching to "all players" only possible if all players fit to screen */
11674     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11675     {
11676       game.centered_player_nr_next = game.centered_player_nr;
11677       game.set_centered_player = FALSE;
11678     }
11679
11680     /* do not switch focus to non-existing (or non-active) player */
11681     if (game.centered_player_nr_next >= 0 &&
11682         !stored_player[game.centered_player_nr_next].active)
11683     {
11684       game.centered_player_nr_next = game.centered_player_nr;
11685       game.set_centered_player = FALSE;
11686     }
11687   }
11688
11689   if (game.set_centered_player &&
11690       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11691   {
11692     int sx, sy;
11693
11694     if (game.centered_player_nr_next == -1)
11695     {
11696       setScreenCenteredToAllPlayers(&sx, &sy);
11697     }
11698     else
11699     {
11700       sx = stored_player[game.centered_player_nr_next].jx;
11701       sy = stored_player[game.centered_player_nr_next].jy;
11702     }
11703
11704     game.centered_player_nr = game.centered_player_nr_next;
11705     game.set_centered_player = FALSE;
11706
11707     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11708     DrawGameDoorValues();
11709   }
11710
11711   for (i = 0; i < MAX_PLAYERS; i++)
11712   {
11713     int actual_player_action = stored_player[i].effective_action;
11714
11715 #if 1
11716     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11717        - rnd_equinox_tetrachloride 048
11718        - rnd_equinox_tetrachloride_ii 096
11719        - rnd_emanuel_schmieg 002
11720        - doctor_sloan_ww 001, 020
11721     */
11722     if (stored_player[i].MovPos == 0)
11723       CheckGravityMovement(&stored_player[i]);
11724 #endif
11725
11726     /* overwrite programmed action with tape action */
11727     if (stored_player[i].programmed_action)
11728       actual_player_action = stored_player[i].programmed_action;
11729
11730     PlayerActions(&stored_player[i], actual_player_action);
11731
11732     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11733   }
11734
11735   ScrollScreen(NULL, SCROLL_GO_ON);
11736
11737   /* for backwards compatibility, the following code emulates a fixed bug that
11738      occured when pushing elements (causing elements that just made their last
11739      pushing step to already (if possible) make their first falling step in the
11740      same game frame, which is bad); this code is also needed to use the famous
11741      "spring push bug" which is used in older levels and might be wanted to be
11742      used also in newer levels, but in this case the buggy pushing code is only
11743      affecting the "spring" element and no other elements */
11744
11745   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11746   {
11747     for (i = 0; i < MAX_PLAYERS; i++)
11748     {
11749       struct PlayerInfo *player = &stored_player[i];
11750       int x = player->jx;
11751       int y = player->jy;
11752
11753       if (player->active && player->is_pushing && player->is_moving &&
11754           IS_MOVING(x, y) &&
11755           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11756            Feld[x][y] == EL_SPRING))
11757       {
11758         ContinueMoving(x, y);
11759
11760         /* continue moving after pushing (this is actually a bug) */
11761         if (!IS_MOVING(x, y))
11762           Stop[x][y] = FALSE;
11763       }
11764     }
11765   }
11766
11767   SCAN_PLAYFIELD(x, y)
11768   {
11769     ChangeCount[x][y] = 0;
11770     ChangeEvent[x][y] = -1;
11771
11772     /* this must be handled before main playfield loop */
11773     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11774     {
11775       MovDelay[x][y]--;
11776       if (MovDelay[x][y] <= 0)
11777         RemoveField(x, y);
11778     }
11779
11780     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11781     {
11782       MovDelay[x][y]--;
11783       if (MovDelay[x][y] <= 0)
11784       {
11785         RemoveField(x, y);
11786         TEST_DrawLevelField(x, y);
11787
11788         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11789       }
11790     }
11791
11792 #if DEBUG
11793     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11794     {
11795       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11796       printf("GameActions(): This should never happen!\n");
11797
11798       ChangePage[x][y] = -1;
11799     }
11800 #endif
11801
11802     Stop[x][y] = FALSE;
11803     if (WasJustMoving[x][y] > 0)
11804       WasJustMoving[x][y]--;
11805     if (WasJustFalling[x][y] > 0)
11806       WasJustFalling[x][y]--;
11807     if (CheckCollision[x][y] > 0)
11808       CheckCollision[x][y]--;
11809     if (CheckImpact[x][y] > 0)
11810       CheckImpact[x][y]--;
11811
11812     GfxFrame[x][y]++;
11813
11814     /* reset finished pushing action (not done in ContinueMoving() to allow
11815        continuous pushing animation for elements with zero push delay) */
11816     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11817     {
11818       ResetGfxAnimation(x, y);
11819       TEST_DrawLevelField(x, y);
11820     }
11821
11822 #if DEBUG
11823     if (IS_BLOCKED(x, y))
11824     {
11825       int oldx, oldy;
11826
11827       Blocked2Moving(x, y, &oldx, &oldy);
11828       if (!IS_MOVING(oldx, oldy))
11829       {
11830         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11831         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11832         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11833         printf("GameActions(): This should never happen!\n");
11834       }
11835     }
11836 #endif
11837   }
11838
11839   SCAN_PLAYFIELD(x, y)
11840   {
11841     element = Feld[x][y];
11842     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11843     last_gfx_frame = GfxFrame[x][y];
11844
11845     ResetGfxFrame(x, y);
11846
11847     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11848       DrawLevelGraphicAnimation(x, y, graphic);
11849
11850     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11851         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11852       ResetRandomAnimationValue(x, y);
11853
11854     SetRandomAnimationValue(x, y);
11855
11856     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11857
11858     if (IS_INACTIVE(element))
11859     {
11860       if (IS_ANIMATED(graphic))
11861         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11862
11863       continue;
11864     }
11865
11866     /* this may take place after moving, so 'element' may have changed */
11867     if (IS_CHANGING(x, y) &&
11868         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11869     {
11870       int page = element_info[element].event_page_nr[CE_DELAY];
11871
11872       HandleElementChange(x, y, page);
11873
11874       element = Feld[x][y];
11875       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11876     }
11877
11878     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11879     {
11880       StartMoving(x, y);
11881
11882       element = Feld[x][y];
11883       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11884
11885       if (IS_ANIMATED(graphic) &&
11886           !IS_MOVING(x, y) &&
11887           !Stop[x][y])
11888         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11889
11890       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11891         TEST_DrawTwinkleOnField(x, y);
11892     }
11893     else if (element == EL_ACID)
11894     {
11895       if (!Stop[x][y])
11896         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11897     }
11898     else if ((element == EL_EXIT_OPEN ||
11899               element == EL_EM_EXIT_OPEN ||
11900               element == EL_SP_EXIT_OPEN ||
11901               element == EL_STEEL_EXIT_OPEN ||
11902               element == EL_EM_STEEL_EXIT_OPEN ||
11903               element == EL_SP_TERMINAL ||
11904               element == EL_SP_TERMINAL_ACTIVE ||
11905               element == EL_EXTRA_TIME ||
11906               element == EL_SHIELD_NORMAL ||
11907               element == EL_SHIELD_DEADLY) &&
11908              IS_ANIMATED(graphic))
11909       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11910     else if (IS_MOVING(x, y))
11911       ContinueMoving(x, y);
11912     else if (IS_ACTIVE_BOMB(element))
11913       CheckDynamite(x, y);
11914     else if (element == EL_AMOEBA_GROWING)
11915       AmoebeWaechst(x, y);
11916     else if (element == EL_AMOEBA_SHRINKING)
11917       AmoebaDisappearing(x, y);
11918
11919 #if !USE_NEW_AMOEBA_CODE
11920     else if (IS_AMOEBALIVE(element))
11921       AmoebeAbleger(x, y);
11922 #endif
11923
11924     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11925       Life(x, y);
11926     else if (element == EL_EXIT_CLOSED)
11927       CheckExit(x, y);
11928     else if (element == EL_EM_EXIT_CLOSED)
11929       CheckExitEM(x, y);
11930     else if (element == EL_STEEL_EXIT_CLOSED)
11931       CheckExitSteel(x, y);
11932     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11933       CheckExitSteelEM(x, y);
11934     else if (element == EL_SP_EXIT_CLOSED)
11935       CheckExitSP(x, y);
11936     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11937              element == EL_EXPANDABLE_STEELWALL_GROWING)
11938       MauerWaechst(x, y);
11939     else if (element == EL_EXPANDABLE_WALL ||
11940              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11941              element == EL_EXPANDABLE_WALL_VERTICAL ||
11942              element == EL_EXPANDABLE_WALL_ANY ||
11943              element == EL_BD_EXPANDABLE_WALL)
11944       MauerAbleger(x, y);
11945     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11946              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11947              element == EL_EXPANDABLE_STEELWALL_ANY)
11948       MauerAblegerStahl(x, y);
11949     else if (element == EL_FLAMES)
11950       CheckForDragon(x, y);
11951     else if (element == EL_EXPLOSION)
11952       ; /* drawing of correct explosion animation is handled separately */
11953     else if (element == EL_ELEMENT_SNAPPING ||
11954              element == EL_DIAGONAL_SHRINKING ||
11955              element == EL_DIAGONAL_GROWING)
11956     {
11957       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11958
11959       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11960     }
11961     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11962       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11963
11964     if (IS_BELT_ACTIVE(element))
11965       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11966
11967     if (game.magic_wall_active)
11968     {
11969       int jx = local_player->jx, jy = local_player->jy;
11970
11971       /* play the element sound at the position nearest to the player */
11972       if ((element == EL_MAGIC_WALL_FULL ||
11973            element == EL_MAGIC_WALL_ACTIVE ||
11974            element == EL_MAGIC_WALL_EMPTYING ||
11975            element == EL_BD_MAGIC_WALL_FULL ||
11976            element == EL_BD_MAGIC_WALL_ACTIVE ||
11977            element == EL_BD_MAGIC_WALL_EMPTYING ||
11978            element == EL_DC_MAGIC_WALL_FULL ||
11979            element == EL_DC_MAGIC_WALL_ACTIVE ||
11980            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11981           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11982       {
11983         magic_wall_x = x;
11984         magic_wall_y = y;
11985       }
11986     }
11987   }
11988
11989 #if USE_NEW_AMOEBA_CODE
11990   /* new experimental amoeba growth stuff */
11991   if (!(FrameCounter % 8))
11992   {
11993     static unsigned int random = 1684108901;
11994
11995     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11996     {
11997       x = RND(lev_fieldx);
11998       y = RND(lev_fieldy);
11999       element = Feld[x][y];
12000
12001       if (!IS_PLAYER(x,y) &&
12002           (element == EL_EMPTY ||
12003            CAN_GROW_INTO(element) ||
12004            element == EL_QUICKSAND_EMPTY ||
12005            element == EL_QUICKSAND_FAST_EMPTY ||
12006            element == EL_ACID_SPLASH_LEFT ||
12007            element == EL_ACID_SPLASH_RIGHT))
12008       {
12009         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12010             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12011             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12012             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12013           Feld[x][y] = EL_AMOEBA_DROP;
12014       }
12015
12016       random = random * 129 + 1;
12017     }
12018   }
12019 #endif
12020
12021   game.explosions_delayed = FALSE;
12022
12023   SCAN_PLAYFIELD(x, y)
12024   {
12025     element = Feld[x][y];
12026
12027     if (ExplodeField[x][y])
12028       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12029     else if (element == EL_EXPLOSION)
12030       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12031
12032     ExplodeField[x][y] = EX_TYPE_NONE;
12033   }
12034
12035   game.explosions_delayed = TRUE;
12036
12037   if (game.magic_wall_active)
12038   {
12039     if (!(game.magic_wall_time_left % 4))
12040     {
12041       int element = Feld[magic_wall_x][magic_wall_y];
12042
12043       if (element == EL_BD_MAGIC_WALL_FULL ||
12044           element == EL_BD_MAGIC_WALL_ACTIVE ||
12045           element == EL_BD_MAGIC_WALL_EMPTYING)
12046         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12047       else if (element == EL_DC_MAGIC_WALL_FULL ||
12048                element == EL_DC_MAGIC_WALL_ACTIVE ||
12049                element == EL_DC_MAGIC_WALL_EMPTYING)
12050         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12051       else
12052         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12053     }
12054
12055     if (game.magic_wall_time_left > 0)
12056     {
12057       game.magic_wall_time_left--;
12058
12059       if (!game.magic_wall_time_left)
12060       {
12061         SCAN_PLAYFIELD(x, y)
12062         {
12063           element = Feld[x][y];
12064
12065           if (element == EL_MAGIC_WALL_ACTIVE ||
12066               element == EL_MAGIC_WALL_FULL)
12067           {
12068             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12069             TEST_DrawLevelField(x, y);
12070           }
12071           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12072                    element == EL_BD_MAGIC_WALL_FULL)
12073           {
12074             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12075             TEST_DrawLevelField(x, y);
12076           }
12077           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12078                    element == EL_DC_MAGIC_WALL_FULL)
12079           {
12080             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12081             TEST_DrawLevelField(x, y);
12082           }
12083         }
12084
12085         game.magic_wall_active = FALSE;
12086       }
12087     }
12088   }
12089
12090   if (game.light_time_left > 0)
12091   {
12092     game.light_time_left--;
12093
12094     if (game.light_time_left == 0)
12095       RedrawAllLightSwitchesAndInvisibleElements();
12096   }
12097
12098   if (game.timegate_time_left > 0)
12099   {
12100     game.timegate_time_left--;
12101
12102     if (game.timegate_time_left == 0)
12103       CloseAllOpenTimegates();
12104   }
12105
12106   if (game.lenses_time_left > 0)
12107   {
12108     game.lenses_time_left--;
12109
12110     if (game.lenses_time_left == 0)
12111       RedrawAllInvisibleElementsForLenses();
12112   }
12113
12114   if (game.magnify_time_left > 0)
12115   {
12116     game.magnify_time_left--;
12117
12118     if (game.magnify_time_left == 0)
12119       RedrawAllInvisibleElementsForMagnifier();
12120   }
12121
12122   for (i = 0; i < MAX_PLAYERS; i++)
12123   {
12124     struct PlayerInfo *player = &stored_player[i];
12125
12126     if (SHIELD_ON(player))
12127     {
12128       if (player->shield_deadly_time_left)
12129         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12130       else if (player->shield_normal_time_left)
12131         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12132     }
12133   }
12134
12135 #if USE_DELAYED_GFX_REDRAW
12136   SCAN_PLAYFIELD(x, y)
12137   {
12138     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12139     {
12140       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12141          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12142
12143       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12144         DrawLevelField(x, y);
12145
12146       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12147         DrawLevelFieldCrumbled(x, y);
12148
12149       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12150         DrawLevelFieldCrumbledNeighbours(x, y);
12151
12152       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12153         DrawTwinkleOnField(x, y);
12154     }
12155
12156     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12157   }
12158 #endif
12159
12160   DrawAllPlayers();
12161   PlayAllPlayersSound();
12162
12163   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12164   {
12165     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12166
12167     local_player->show_envelope = 0;
12168   }
12169
12170   /* use random number generator in every frame to make it less predictable */
12171   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12172     RND(1);
12173 }
12174
12175 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12176 {
12177   int min_x = x, min_y = y, max_x = x, max_y = y;
12178   int i;
12179
12180   for (i = 0; i < MAX_PLAYERS; i++)
12181   {
12182     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12183
12184     if (!stored_player[i].active || &stored_player[i] == player)
12185       continue;
12186
12187     min_x = MIN(min_x, jx);
12188     min_y = MIN(min_y, jy);
12189     max_x = MAX(max_x, jx);
12190     max_y = MAX(max_y, jy);
12191   }
12192
12193   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12194 }
12195
12196 static boolean AllPlayersInVisibleScreen()
12197 {
12198   int i;
12199
12200   for (i = 0; i < MAX_PLAYERS; i++)
12201   {
12202     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12203
12204     if (!stored_player[i].active)
12205       continue;
12206
12207     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12208       return FALSE;
12209   }
12210
12211   return TRUE;
12212 }
12213
12214 void ScrollLevel(int dx, int dy)
12215 {
12216   int scroll_offset = 2 * TILEX_VAR;
12217   int x, y;
12218
12219   BlitBitmap(drawto_field, drawto_field,
12220              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12221              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12222              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12223              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12224              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12225              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12226
12227   if (dx != 0)
12228   {
12229     x = (dx == 1 ? BX1 : BX2);
12230     for (y = BY1; y <= BY2; y++)
12231       DrawScreenField(x, y);
12232   }
12233
12234   if (dy != 0)
12235   {
12236     y = (dy == 1 ? BY1 : BY2);
12237     for (x = BX1; x <= BX2; x++)
12238       DrawScreenField(x, y);
12239   }
12240
12241   redraw_mask |= REDRAW_FIELD;
12242 }
12243
12244 static boolean canFallDown(struct PlayerInfo *player)
12245 {
12246   int jx = player->jx, jy = player->jy;
12247
12248   return (IN_LEV_FIELD(jx, jy + 1) &&
12249           (IS_FREE(jx, jy + 1) ||
12250            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12251           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12252           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12253 }
12254
12255 static boolean canPassField(int x, int y, int move_dir)
12256 {
12257   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12258   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12259   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12260   int nextx = x + dx;
12261   int nexty = y + dy;
12262   int element = Feld[x][y];
12263
12264   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12265           !CAN_MOVE(element) &&
12266           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12267           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12268           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12269 }
12270
12271 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12272 {
12273   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12274   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12275   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12276   int newx = x + dx;
12277   int newy = y + dy;
12278
12279   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12280           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12281           (IS_DIGGABLE(Feld[newx][newy]) ||
12282            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12283            canPassField(newx, newy, move_dir)));
12284 }
12285
12286 static void CheckGravityMovement(struct PlayerInfo *player)
12287 {
12288   if (player->gravity && !player->programmed_action)
12289   {
12290     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12291     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12292     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12293     int jx = player->jx, jy = player->jy;
12294     boolean player_is_moving_to_valid_field =
12295       (!player_is_snapping &&
12296        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12297         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12298     boolean player_can_fall_down = canFallDown(player);
12299
12300     if (player_can_fall_down &&
12301         !player_is_moving_to_valid_field)
12302       player->programmed_action = MV_DOWN;
12303   }
12304 }
12305
12306 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12307 {
12308   return CheckGravityMovement(player);
12309
12310   if (player->gravity && !player->programmed_action)
12311   {
12312     int jx = player->jx, jy = player->jy;
12313     boolean field_under_player_is_free =
12314       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12315     boolean player_is_standing_on_valid_field =
12316       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12317        (IS_WALKABLE(Feld[jx][jy]) &&
12318         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12319
12320     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12321       player->programmed_action = MV_DOWN;
12322   }
12323 }
12324
12325 /*
12326   MovePlayerOneStep()
12327   -----------------------------------------------------------------------------
12328   dx, dy:               direction (non-diagonal) to try to move the player to
12329   real_dx, real_dy:     direction as read from input device (can be diagonal)
12330 */
12331
12332 boolean MovePlayerOneStep(struct PlayerInfo *player,
12333                           int dx, int dy, int real_dx, int real_dy)
12334 {
12335   int jx = player->jx, jy = player->jy;
12336   int new_jx = jx + dx, new_jy = jy + dy;
12337   int can_move;
12338   boolean player_can_move = !player->cannot_move;
12339
12340   if (!player->active || (!dx && !dy))
12341     return MP_NO_ACTION;
12342
12343   player->MovDir = (dx < 0 ? MV_LEFT :
12344                     dx > 0 ? MV_RIGHT :
12345                     dy < 0 ? MV_UP :
12346                     dy > 0 ? MV_DOWN :  MV_NONE);
12347
12348   if (!IN_LEV_FIELD(new_jx, new_jy))
12349     return MP_NO_ACTION;
12350
12351   if (!player_can_move)
12352   {
12353     if (player->MovPos == 0)
12354     {
12355       player->is_moving = FALSE;
12356       player->is_digging = FALSE;
12357       player->is_collecting = FALSE;
12358       player->is_snapping = FALSE;
12359       player->is_pushing = FALSE;
12360     }
12361   }
12362
12363   if (!options.network && game.centered_player_nr == -1 &&
12364       !AllPlayersInSight(player, new_jx, new_jy))
12365     return MP_NO_ACTION;
12366
12367   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12368   if (can_move != MP_MOVING)
12369     return can_move;
12370
12371   /* check if DigField() has caused relocation of the player */
12372   if (player->jx != jx || player->jy != jy)
12373     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12374
12375   StorePlayer[jx][jy] = 0;
12376   player->last_jx = jx;
12377   player->last_jy = jy;
12378   player->jx = new_jx;
12379   player->jy = new_jy;
12380   StorePlayer[new_jx][new_jy] = player->element_nr;
12381
12382   if (player->move_delay_value_next != -1)
12383   {
12384     player->move_delay_value = player->move_delay_value_next;
12385     player->move_delay_value_next = -1;
12386   }
12387
12388   player->MovPos =
12389     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12390
12391   player->step_counter++;
12392
12393   PlayerVisit[jx][jy] = FrameCounter;
12394
12395   player->is_moving = TRUE;
12396
12397 #if 1
12398   /* should better be called in MovePlayer(), but this breaks some tapes */
12399   ScrollPlayer(player, SCROLL_INIT);
12400 #endif
12401
12402   return MP_MOVING;
12403 }
12404
12405 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12406 {
12407   int jx = player->jx, jy = player->jy;
12408   int old_jx = jx, old_jy = jy;
12409   int moved = MP_NO_ACTION;
12410
12411   if (!player->active)
12412     return FALSE;
12413
12414   if (!dx && !dy)
12415   {
12416     if (player->MovPos == 0)
12417     {
12418       player->is_moving = FALSE;
12419       player->is_digging = FALSE;
12420       player->is_collecting = FALSE;
12421       player->is_snapping = FALSE;
12422       player->is_pushing = FALSE;
12423     }
12424
12425     return FALSE;
12426   }
12427
12428   if (player->move_delay > 0)
12429     return FALSE;
12430
12431   player->move_delay = -1;              /* set to "uninitialized" value */
12432
12433   /* store if player is automatically moved to next field */
12434   player->is_auto_moving = (player->programmed_action != MV_NONE);
12435
12436   /* remove the last programmed player action */
12437   player->programmed_action = 0;
12438
12439   if (player->MovPos)
12440   {
12441     /* should only happen if pre-1.2 tape recordings are played */
12442     /* this is only for backward compatibility */
12443
12444     int original_move_delay_value = player->move_delay_value;
12445
12446 #if DEBUG
12447     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12448            tape.counter);
12449 #endif
12450
12451     /* scroll remaining steps with finest movement resolution */
12452     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12453
12454     while (player->MovPos)
12455     {
12456       ScrollPlayer(player, SCROLL_GO_ON);
12457       ScrollScreen(NULL, SCROLL_GO_ON);
12458
12459       AdvanceFrameAndPlayerCounters(player->index_nr);
12460
12461       DrawAllPlayers();
12462       BackToFront_WithFrameDelay(0);
12463     }
12464
12465     player->move_delay_value = original_move_delay_value;
12466   }
12467
12468   player->is_active = FALSE;
12469
12470   if (player->last_move_dir & MV_HORIZONTAL)
12471   {
12472     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12473       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12474   }
12475   else
12476   {
12477     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12478       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12479   }
12480
12481   if (!moved && !player->is_active)
12482   {
12483     player->is_moving = FALSE;
12484     player->is_digging = FALSE;
12485     player->is_collecting = FALSE;
12486     player->is_snapping = FALSE;
12487     player->is_pushing = FALSE;
12488   }
12489
12490   jx = player->jx;
12491   jy = player->jy;
12492
12493   if (moved & MP_MOVING && !ScreenMovPos &&
12494       (player->index_nr == game.centered_player_nr ||
12495        game.centered_player_nr == -1))
12496   {
12497     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12498     int offset = game.scroll_delay_value;
12499
12500     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12501     {
12502       /* actual player has left the screen -- scroll in that direction */
12503       if (jx != old_jx)         /* player has moved horizontally */
12504         scroll_x += (jx - old_jx);
12505       else                      /* player has moved vertically */
12506         scroll_y += (jy - old_jy);
12507     }
12508     else
12509     {
12510       if (jx != old_jx)         /* player has moved horizontally */
12511       {
12512         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12513             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12514           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12515
12516         /* don't scroll over playfield boundaries */
12517         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12518           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12519
12520         /* don't scroll more than one field at a time */
12521         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12522
12523         /* don't scroll against the player's moving direction */
12524         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12525             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12526           scroll_x = old_scroll_x;
12527       }
12528       else                      /* player has moved vertically */
12529       {
12530         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12531             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12532           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12533
12534         /* don't scroll over playfield boundaries */
12535         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12536           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12537
12538         /* don't scroll more than one field at a time */
12539         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12540
12541         /* don't scroll against the player's moving direction */
12542         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12543             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12544           scroll_y = old_scroll_y;
12545       }
12546     }
12547
12548     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12549     {
12550       if (!options.network && game.centered_player_nr == -1 &&
12551           !AllPlayersInVisibleScreen())
12552       {
12553         scroll_x = old_scroll_x;
12554         scroll_y = old_scroll_y;
12555       }
12556       else
12557       {
12558         ScrollScreen(player, SCROLL_INIT);
12559         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12560       }
12561     }
12562   }
12563
12564   player->StepFrame = 0;
12565
12566   if (moved & MP_MOVING)
12567   {
12568     if (old_jx != jx && old_jy == jy)
12569       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12570     else if (old_jx == jx && old_jy != jy)
12571       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12572
12573     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12574
12575     player->last_move_dir = player->MovDir;
12576     player->is_moving = TRUE;
12577     player->is_snapping = FALSE;
12578     player->is_switching = FALSE;
12579     player->is_dropping = FALSE;
12580     player->is_dropping_pressed = FALSE;
12581     player->drop_pressed_delay = 0;
12582
12583 #if 0
12584     /* should better be called here than above, but this breaks some tapes */
12585     ScrollPlayer(player, SCROLL_INIT);
12586 #endif
12587   }
12588   else
12589   {
12590     CheckGravityMovementWhenNotMoving(player);
12591
12592     player->is_moving = FALSE;
12593
12594     /* at this point, the player is allowed to move, but cannot move right now
12595        (e.g. because of something blocking the way) -- ensure that the player
12596        is also allowed to move in the next frame (in old versions before 3.1.1,
12597        the player was forced to wait again for eight frames before next try) */
12598
12599     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12600       player->move_delay = 0;   /* allow direct movement in the next frame */
12601   }
12602
12603   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12604     player->move_delay = player->move_delay_value;
12605
12606   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12607   {
12608     TestIfPlayerTouchesBadThing(jx, jy);
12609     TestIfPlayerTouchesCustomElement(jx, jy);
12610   }
12611
12612   if (!player->active)
12613     RemovePlayer(player);
12614
12615   return moved;
12616 }
12617
12618 void ScrollPlayer(struct PlayerInfo *player, int mode)
12619 {
12620   int jx = player->jx, jy = player->jy;
12621   int last_jx = player->last_jx, last_jy = player->last_jy;
12622   int move_stepsize = TILEX / player->move_delay_value;
12623
12624   if (!player->active)
12625     return;
12626
12627   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12628     return;
12629
12630   if (mode == SCROLL_INIT)
12631   {
12632     player->actual_frame_counter = FrameCounter;
12633     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12634
12635     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12636         Feld[last_jx][last_jy] == EL_EMPTY)
12637     {
12638       int last_field_block_delay = 0;   /* start with no blocking at all */
12639       int block_delay_adjustment = player->block_delay_adjustment;
12640
12641       /* if player blocks last field, add delay for exactly one move */
12642       if (player->block_last_field)
12643       {
12644         last_field_block_delay += player->move_delay_value;
12645
12646         /* when blocking enabled, prevent moving up despite gravity */
12647         if (player->gravity && player->MovDir == MV_UP)
12648           block_delay_adjustment = -1;
12649       }
12650
12651       /* add block delay adjustment (also possible when not blocking) */
12652       last_field_block_delay += block_delay_adjustment;
12653
12654       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12655       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12656     }
12657
12658     if (player->MovPos != 0)    /* player has not yet reached destination */
12659       return;
12660   }
12661   else if (!FrameReached(&player->actual_frame_counter, 1))
12662     return;
12663
12664   if (player->MovPos != 0)
12665   {
12666     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12667     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12668
12669     /* before DrawPlayer() to draw correct player graphic for this case */
12670     if (player->MovPos == 0)
12671       CheckGravityMovement(player);
12672   }
12673
12674   if (player->MovPos == 0)      /* player reached destination field */
12675   {
12676     if (player->move_delay_reset_counter > 0)
12677     {
12678       player->move_delay_reset_counter--;
12679
12680       if (player->move_delay_reset_counter == 0)
12681       {
12682         /* continue with normal speed after quickly moving through gate */
12683         HALVE_PLAYER_SPEED(player);
12684
12685         /* be able to make the next move without delay */
12686         player->move_delay = 0;
12687       }
12688     }
12689
12690     player->last_jx = jx;
12691     player->last_jy = jy;
12692
12693     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12694         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12695         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12696         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12697         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12698         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12699         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12700         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12701     {
12702       DrawPlayer(player);       /* needed here only to cleanup last field */
12703       RemovePlayer(player);
12704
12705       if (local_player->friends_still_needed == 0 ||
12706           IS_SP_ELEMENT(Feld[jx][jy]))
12707         PlayerWins(player);
12708     }
12709
12710     /* this breaks one level: "machine", level 000 */
12711     {
12712       int move_direction = player->MovDir;
12713       int enter_side = MV_DIR_OPPOSITE(move_direction);
12714       int leave_side = move_direction;
12715       int old_jx = last_jx;
12716       int old_jy = last_jy;
12717       int old_element = Feld[old_jx][old_jy];
12718       int new_element = Feld[jx][jy];
12719
12720       if (IS_CUSTOM_ELEMENT(old_element))
12721         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12722                                    CE_LEFT_BY_PLAYER,
12723                                    player->index_bit, leave_side);
12724
12725       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12726                                           CE_PLAYER_LEAVES_X,
12727                                           player->index_bit, leave_side);
12728
12729       if (IS_CUSTOM_ELEMENT(new_element))
12730         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12731                                    player->index_bit, enter_side);
12732
12733       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12734                                           CE_PLAYER_ENTERS_X,
12735                                           player->index_bit, enter_side);
12736
12737       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12738                                         CE_MOVE_OF_X, move_direction);
12739     }
12740
12741     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12742     {
12743       TestIfPlayerTouchesBadThing(jx, jy);
12744       TestIfPlayerTouchesCustomElement(jx, jy);
12745
12746       /* needed because pushed element has not yet reached its destination,
12747          so it would trigger a change event at its previous field location */
12748       if (!player->is_pushing)
12749         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12750
12751       if (!player->active)
12752         RemovePlayer(player);
12753     }
12754
12755     if (!local_player->LevelSolved && level.use_step_counter)
12756     {
12757       int i;
12758
12759       TimePlayed++;
12760
12761       if (TimeLeft > 0)
12762       {
12763         TimeLeft--;
12764
12765         if (TimeLeft <= 10 && setup.time_limit)
12766           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12767
12768         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12769
12770         DisplayGameControlValues();
12771
12772         if (!TimeLeft && setup.time_limit)
12773           for (i = 0; i < MAX_PLAYERS; i++)
12774             KillPlayer(&stored_player[i]);
12775       }
12776       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12777       {
12778         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12779
12780         DisplayGameControlValues();
12781       }
12782     }
12783
12784     if (tape.single_step && tape.recording && !tape.pausing &&
12785         !player->programmed_action)
12786       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12787
12788     if (!player->programmed_action)
12789       CheckSaveEngineSnapshot(player);
12790   }
12791 }
12792
12793 void ScrollScreen(struct PlayerInfo *player, int mode)
12794 {
12795   static unsigned int screen_frame_counter = 0;
12796
12797   if (mode == SCROLL_INIT)
12798   {
12799     /* set scrolling step size according to actual player's moving speed */
12800     ScrollStepSize = TILEX / player->move_delay_value;
12801
12802     screen_frame_counter = FrameCounter;
12803     ScreenMovDir = player->MovDir;
12804     ScreenMovPos = player->MovPos;
12805     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12806     return;
12807   }
12808   else if (!FrameReached(&screen_frame_counter, 1))
12809     return;
12810
12811   if (ScreenMovPos)
12812   {
12813     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12814     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12815     redraw_mask |= REDRAW_FIELD;
12816   }
12817   else
12818     ScreenMovDir = MV_NONE;
12819 }
12820
12821 void TestIfPlayerTouchesCustomElement(int x, int y)
12822 {
12823   static int xy[4][2] =
12824   {
12825     { 0, -1 },
12826     { -1, 0 },
12827     { +1, 0 },
12828     { 0, +1 }
12829   };
12830   static int trigger_sides[4][2] =
12831   {
12832     /* center side       border side */
12833     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12834     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12835     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12836     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12837   };
12838   static int touch_dir[4] =
12839   {
12840     MV_LEFT | MV_RIGHT,
12841     MV_UP   | MV_DOWN,
12842     MV_UP   | MV_DOWN,
12843     MV_LEFT | MV_RIGHT
12844   };
12845   int center_element = Feld[x][y];      /* should always be non-moving! */
12846   int i;
12847
12848   for (i = 0; i < NUM_DIRECTIONS; i++)
12849   {
12850     int xx = x + xy[i][0];
12851     int yy = y + xy[i][1];
12852     int center_side = trigger_sides[i][0];
12853     int border_side = trigger_sides[i][1];
12854     int border_element;
12855
12856     if (!IN_LEV_FIELD(xx, yy))
12857       continue;
12858
12859     if (IS_PLAYER(x, y))                /* player found at center element */
12860     {
12861       struct PlayerInfo *player = PLAYERINFO(x, y);
12862
12863       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12864         border_element = Feld[xx][yy];          /* may be moving! */
12865       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12866         border_element = Feld[xx][yy];
12867       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12868         border_element = MovingOrBlocked2Element(xx, yy);
12869       else
12870         continue;               /* center and border element do not touch */
12871
12872       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12873                                  player->index_bit, border_side);
12874       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12875                                           CE_PLAYER_TOUCHES_X,
12876                                           player->index_bit, border_side);
12877
12878       {
12879         /* use player element that is initially defined in the level playfield,
12880            not the player element that corresponds to the runtime player number
12881            (example: a level that contains EL_PLAYER_3 as the only player would
12882            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12883         int player_element = PLAYERINFO(x, y)->initial_element;
12884
12885         CheckElementChangeBySide(xx, yy, border_element, player_element,
12886                                  CE_TOUCHING_X, border_side);
12887       }
12888     }
12889     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12890     {
12891       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12892
12893       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12894       {
12895         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12896           continue;             /* center and border element do not touch */
12897       }
12898
12899       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12900                                  player->index_bit, center_side);
12901       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12902                                           CE_PLAYER_TOUCHES_X,
12903                                           player->index_bit, center_side);
12904
12905       {
12906         /* use player element that is initially defined in the level playfield,
12907            not the player element that corresponds to the runtime player number
12908            (example: a level that contains EL_PLAYER_3 as the only player would
12909            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12910         int player_element = PLAYERINFO(xx, yy)->initial_element;
12911
12912         CheckElementChangeBySide(x, y, center_element, player_element,
12913                                  CE_TOUCHING_X, center_side);
12914       }
12915
12916       break;
12917     }
12918   }
12919 }
12920
12921 void TestIfElementTouchesCustomElement(int x, int y)
12922 {
12923   static int xy[4][2] =
12924   {
12925     { 0, -1 },
12926     { -1, 0 },
12927     { +1, 0 },
12928     { 0, +1 }
12929   };
12930   static int trigger_sides[4][2] =
12931   {
12932     /* center side      border side */
12933     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12934     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12935     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12936     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12937   };
12938   static int touch_dir[4] =
12939   {
12940     MV_LEFT | MV_RIGHT,
12941     MV_UP   | MV_DOWN,
12942     MV_UP   | MV_DOWN,
12943     MV_LEFT | MV_RIGHT
12944   };
12945   boolean change_center_element = FALSE;
12946   int center_element = Feld[x][y];      /* should always be non-moving! */
12947   int border_element_old[NUM_DIRECTIONS];
12948   int i;
12949
12950   for (i = 0; i < NUM_DIRECTIONS; i++)
12951   {
12952     int xx = x + xy[i][0];
12953     int yy = y + xy[i][1];
12954     int border_element;
12955
12956     border_element_old[i] = -1;
12957
12958     if (!IN_LEV_FIELD(xx, yy))
12959       continue;
12960
12961     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12962       border_element = Feld[xx][yy];    /* may be moving! */
12963     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12964       border_element = Feld[xx][yy];
12965     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12966       border_element = MovingOrBlocked2Element(xx, yy);
12967     else
12968       continue;                 /* center and border element do not touch */
12969
12970     border_element_old[i] = border_element;
12971   }
12972
12973   for (i = 0; i < NUM_DIRECTIONS; i++)
12974   {
12975     int xx = x + xy[i][0];
12976     int yy = y + xy[i][1];
12977     int center_side = trigger_sides[i][0];
12978     int border_element = border_element_old[i];
12979
12980     if (border_element == -1)
12981       continue;
12982
12983     /* check for change of border element */
12984     CheckElementChangeBySide(xx, yy, border_element, center_element,
12985                              CE_TOUCHING_X, center_side);
12986
12987     /* (center element cannot be player, so we dont have to check this here) */
12988   }
12989
12990   for (i = 0; i < NUM_DIRECTIONS; i++)
12991   {
12992     int xx = x + xy[i][0];
12993     int yy = y + xy[i][1];
12994     int border_side = trigger_sides[i][1];
12995     int border_element = border_element_old[i];
12996
12997     if (border_element == -1)
12998       continue;
12999
13000     /* check for change of center element (but change it only once) */
13001     if (!change_center_element)
13002       change_center_element =
13003         CheckElementChangeBySide(x, y, center_element, border_element,
13004                                  CE_TOUCHING_X, border_side);
13005
13006     if (IS_PLAYER(xx, yy))
13007     {
13008       /* use player element that is initially defined in the level playfield,
13009          not the player element that corresponds to the runtime player number
13010          (example: a level that contains EL_PLAYER_3 as the only player would
13011          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13012       int player_element = PLAYERINFO(xx, yy)->initial_element;
13013
13014       CheckElementChangeBySide(x, y, center_element, player_element,
13015                                CE_TOUCHING_X, border_side);
13016     }
13017   }
13018 }
13019
13020 void TestIfElementHitsCustomElement(int x, int y, int direction)
13021 {
13022   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13023   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13024   int hitx = x + dx, hity = y + dy;
13025   int hitting_element = Feld[x][y];
13026   int touched_element;
13027
13028   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13029     return;
13030
13031   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13032                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13033
13034   if (IN_LEV_FIELD(hitx, hity))
13035   {
13036     int opposite_direction = MV_DIR_OPPOSITE(direction);
13037     int hitting_side = direction;
13038     int touched_side = opposite_direction;
13039     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13040                           MovDir[hitx][hity] != direction ||
13041                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13042
13043     object_hit = TRUE;
13044
13045     if (object_hit)
13046     {
13047       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13048                                CE_HITTING_X, touched_side);
13049
13050       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13051                                CE_HIT_BY_X, hitting_side);
13052
13053       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13054                                CE_HIT_BY_SOMETHING, opposite_direction);
13055
13056       if (IS_PLAYER(hitx, hity))
13057       {
13058         /* use player element that is initially defined in the level playfield,
13059            not the player element that corresponds to the runtime player number
13060            (example: a level that contains EL_PLAYER_3 as the only player would
13061            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13062         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13063
13064         CheckElementChangeBySide(x, y, hitting_element, player_element,
13065                                  CE_HITTING_X, touched_side);
13066       }
13067     }
13068   }
13069
13070   /* "hitting something" is also true when hitting the playfield border */
13071   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13072                            CE_HITTING_SOMETHING, direction);
13073 }
13074
13075 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13076 {
13077   int i, kill_x = -1, kill_y = -1;
13078
13079   int bad_element = -1;
13080   static int test_xy[4][2] =
13081   {
13082     { 0, -1 },
13083     { -1, 0 },
13084     { +1, 0 },
13085     { 0, +1 }
13086   };
13087   static int test_dir[4] =
13088   {
13089     MV_UP,
13090     MV_LEFT,
13091     MV_RIGHT,
13092     MV_DOWN
13093   };
13094
13095   for (i = 0; i < NUM_DIRECTIONS; i++)
13096   {
13097     int test_x, test_y, test_move_dir, test_element;
13098
13099     test_x = good_x + test_xy[i][0];
13100     test_y = good_y + test_xy[i][1];
13101
13102     if (!IN_LEV_FIELD(test_x, test_y))
13103       continue;
13104
13105     test_move_dir =
13106       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13107
13108     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13109
13110     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13111        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13112     */
13113     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13114         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13115     {
13116       kill_x = test_x;
13117       kill_y = test_y;
13118       bad_element = test_element;
13119
13120       break;
13121     }
13122   }
13123
13124   if (kill_x != -1 || kill_y != -1)
13125   {
13126     if (IS_PLAYER(good_x, good_y))
13127     {
13128       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13129
13130       if (player->shield_deadly_time_left > 0 &&
13131           !IS_INDESTRUCTIBLE(bad_element))
13132         Bang(kill_x, kill_y);
13133       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13134         KillPlayer(player);
13135     }
13136     else
13137       Bang(good_x, good_y);
13138   }
13139 }
13140
13141 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13142 {
13143   int i, kill_x = -1, kill_y = -1;
13144   int bad_element = Feld[bad_x][bad_y];
13145   static int test_xy[4][2] =
13146   {
13147     { 0, -1 },
13148     { -1, 0 },
13149     { +1, 0 },
13150     { 0, +1 }
13151   };
13152   static int touch_dir[4] =
13153   {
13154     MV_LEFT | MV_RIGHT,
13155     MV_UP   | MV_DOWN,
13156     MV_UP   | MV_DOWN,
13157     MV_LEFT | MV_RIGHT
13158   };
13159   static int test_dir[4] =
13160   {
13161     MV_UP,
13162     MV_LEFT,
13163     MV_RIGHT,
13164     MV_DOWN
13165   };
13166
13167   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13168     return;
13169
13170   for (i = 0; i < NUM_DIRECTIONS; i++)
13171   {
13172     int test_x, test_y, test_move_dir, test_element;
13173
13174     test_x = bad_x + test_xy[i][0];
13175     test_y = bad_y + test_xy[i][1];
13176
13177     if (!IN_LEV_FIELD(test_x, test_y))
13178       continue;
13179
13180     test_move_dir =
13181       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13182
13183     test_element = Feld[test_x][test_y];
13184
13185     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13186        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13187     */
13188     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13189         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13190     {
13191       /* good thing is player or penguin that does not move away */
13192       if (IS_PLAYER(test_x, test_y))
13193       {
13194         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13195
13196         if (bad_element == EL_ROBOT && player->is_moving)
13197           continue;     /* robot does not kill player if he is moving */
13198
13199         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13200         {
13201           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13202             continue;           /* center and border element do not touch */
13203         }
13204
13205         kill_x = test_x;
13206         kill_y = test_y;
13207
13208         break;
13209       }
13210       else if (test_element == EL_PENGUIN)
13211       {
13212         kill_x = test_x;
13213         kill_y = test_y;
13214
13215         break;
13216       }
13217     }
13218   }
13219
13220   if (kill_x != -1 || kill_y != -1)
13221   {
13222     if (IS_PLAYER(kill_x, kill_y))
13223     {
13224       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13225
13226       if (player->shield_deadly_time_left > 0 &&
13227           !IS_INDESTRUCTIBLE(bad_element))
13228         Bang(bad_x, bad_y);
13229       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13230         KillPlayer(player);
13231     }
13232     else
13233       Bang(kill_x, kill_y);
13234   }
13235 }
13236
13237 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13238 {
13239   int bad_element = Feld[bad_x][bad_y];
13240   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13241   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13242   int test_x = bad_x + dx, test_y = bad_y + dy;
13243   int test_move_dir, test_element;
13244   int kill_x = -1, kill_y = -1;
13245
13246   if (!IN_LEV_FIELD(test_x, test_y))
13247     return;
13248
13249   test_move_dir =
13250     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13251
13252   test_element = Feld[test_x][test_y];
13253
13254   if (test_move_dir != bad_move_dir)
13255   {
13256     /* good thing can be player or penguin that does not move away */
13257     if (IS_PLAYER(test_x, test_y))
13258     {
13259       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13260
13261       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13262          player as being hit when he is moving towards the bad thing, because
13263          the "get hit by" condition would be lost after the player stops) */
13264       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13265         return;         /* player moves away from bad thing */
13266
13267       kill_x = test_x;
13268       kill_y = test_y;
13269     }
13270     else if (test_element == EL_PENGUIN)
13271     {
13272       kill_x = test_x;
13273       kill_y = test_y;
13274     }
13275   }
13276
13277   if (kill_x != -1 || kill_y != -1)
13278   {
13279     if (IS_PLAYER(kill_x, kill_y))
13280     {
13281       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13282
13283       if (player->shield_deadly_time_left > 0 &&
13284           !IS_INDESTRUCTIBLE(bad_element))
13285         Bang(bad_x, bad_y);
13286       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13287         KillPlayer(player);
13288     }
13289     else
13290       Bang(kill_x, kill_y);
13291   }
13292 }
13293
13294 void TestIfPlayerTouchesBadThing(int x, int y)
13295 {
13296   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13297 }
13298
13299 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13300 {
13301   TestIfGoodThingHitsBadThing(x, y, move_dir);
13302 }
13303
13304 void TestIfBadThingTouchesPlayer(int x, int y)
13305 {
13306   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13307 }
13308
13309 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13310 {
13311   TestIfBadThingHitsGoodThing(x, y, move_dir);
13312 }
13313
13314 void TestIfFriendTouchesBadThing(int x, int y)
13315 {
13316   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13317 }
13318
13319 void TestIfBadThingTouchesFriend(int x, int y)
13320 {
13321   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13322 }
13323
13324 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13325 {
13326   int i, kill_x = bad_x, kill_y = bad_y;
13327   static int xy[4][2] =
13328   {
13329     { 0, -1 },
13330     { -1, 0 },
13331     { +1, 0 },
13332     { 0, +1 }
13333   };
13334
13335   for (i = 0; i < NUM_DIRECTIONS; i++)
13336   {
13337     int x, y, element;
13338
13339     x = bad_x + xy[i][0];
13340     y = bad_y + xy[i][1];
13341     if (!IN_LEV_FIELD(x, y))
13342       continue;
13343
13344     element = Feld[x][y];
13345     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13346         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13347     {
13348       kill_x = x;
13349       kill_y = y;
13350       break;
13351     }
13352   }
13353
13354   if (kill_x != bad_x || kill_y != bad_y)
13355     Bang(bad_x, bad_y);
13356 }
13357
13358 void KillPlayer(struct PlayerInfo *player)
13359 {
13360   int jx = player->jx, jy = player->jy;
13361
13362   if (!player->active)
13363     return;
13364
13365 #if 0
13366   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13367          player->killed, player->active, player->reanimated);
13368 #endif
13369
13370   /* the following code was introduced to prevent an infinite loop when calling
13371      -> Bang()
13372      -> CheckTriggeredElementChangeExt()
13373      -> ExecuteCustomElementAction()
13374      -> KillPlayer()
13375      -> (infinitely repeating the above sequence of function calls)
13376      which occurs when killing the player while having a CE with the setting
13377      "kill player X when explosion of <player X>"; the solution using a new
13378      field "player->killed" was chosen for backwards compatibility, although
13379      clever use of the fields "player->active" etc. would probably also work */
13380 #if 1
13381   if (player->killed)
13382     return;
13383 #endif
13384
13385   player->killed = TRUE;
13386
13387   /* remove accessible field at the player's position */
13388   Feld[jx][jy] = EL_EMPTY;
13389
13390   /* deactivate shield (else Bang()/Explode() would not work right) */
13391   player->shield_normal_time_left = 0;
13392   player->shield_deadly_time_left = 0;
13393
13394 #if 0
13395   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13396          player->killed, player->active, player->reanimated);
13397 #endif
13398
13399   Bang(jx, jy);
13400
13401 #if 0
13402   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13403          player->killed, player->active, player->reanimated);
13404 #endif
13405
13406   if (player->reanimated)       /* killed player may have been reanimated */
13407     player->killed = player->reanimated = FALSE;
13408   else
13409     BuryPlayer(player);
13410 }
13411
13412 static void KillPlayerUnlessEnemyProtected(int x, int y)
13413 {
13414   if (!PLAYER_ENEMY_PROTECTED(x, y))
13415     KillPlayer(PLAYERINFO(x, y));
13416 }
13417
13418 static void KillPlayerUnlessExplosionProtected(int x, int y)
13419 {
13420   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13421     KillPlayer(PLAYERINFO(x, y));
13422 }
13423
13424 void BuryPlayer(struct PlayerInfo *player)
13425 {
13426   int jx = player->jx, jy = player->jy;
13427
13428   if (!player->active)
13429     return;
13430
13431   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13432   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13433
13434   player->GameOver = TRUE;
13435   RemovePlayer(player);
13436 }
13437
13438 void RemovePlayer(struct PlayerInfo *player)
13439 {
13440   int jx = player->jx, jy = player->jy;
13441   int i, found = FALSE;
13442
13443   player->present = FALSE;
13444   player->active = FALSE;
13445
13446   if (!ExplodeField[jx][jy])
13447     StorePlayer[jx][jy] = 0;
13448
13449   if (player->is_moving)
13450     TEST_DrawLevelField(player->last_jx, player->last_jy);
13451
13452   for (i = 0; i < MAX_PLAYERS; i++)
13453     if (stored_player[i].active)
13454       found = TRUE;
13455
13456   if (!found)
13457     AllPlayersGone = TRUE;
13458
13459   ExitX = ZX = jx;
13460   ExitY = ZY = jy;
13461 }
13462
13463 static void setFieldForSnapping(int x, int y, int element, int direction)
13464 {
13465   struct ElementInfo *ei = &element_info[element];
13466   int direction_bit = MV_DIR_TO_BIT(direction);
13467   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13468   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13469                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13470
13471   Feld[x][y] = EL_ELEMENT_SNAPPING;
13472   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13473
13474   ResetGfxAnimation(x, y);
13475
13476   GfxElement[x][y] = element;
13477   GfxAction[x][y] = action;
13478   GfxDir[x][y] = direction;
13479   GfxFrame[x][y] = -1;
13480 }
13481
13482 /*
13483   =============================================================================
13484   checkDiagonalPushing()
13485   -----------------------------------------------------------------------------
13486   check if diagonal input device direction results in pushing of object
13487   (by checking if the alternative direction is walkable, diggable, ...)
13488   =============================================================================
13489 */
13490
13491 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13492                                     int x, int y, int real_dx, int real_dy)
13493 {
13494   int jx, jy, dx, dy, xx, yy;
13495
13496   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13497     return TRUE;
13498
13499   /* diagonal direction: check alternative direction */
13500   jx = player->jx;
13501   jy = player->jy;
13502   dx = x - jx;
13503   dy = y - jy;
13504   xx = jx + (dx == 0 ? real_dx : 0);
13505   yy = jy + (dy == 0 ? real_dy : 0);
13506
13507   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13508 }
13509
13510 /*
13511   =============================================================================
13512   DigField()
13513   -----------------------------------------------------------------------------
13514   x, y:                 field next to player (non-diagonal) to try to dig to
13515   real_dx, real_dy:     direction as read from input device (can be diagonal)
13516   =============================================================================
13517 */
13518
13519 static int DigField(struct PlayerInfo *player,
13520                     int oldx, int oldy, int x, int y,
13521                     int real_dx, int real_dy, int mode)
13522 {
13523   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13524   boolean player_was_pushing = player->is_pushing;
13525   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13526   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13527   int jx = oldx, jy = oldy;
13528   int dx = x - jx, dy = y - jy;
13529   int nextx = x + dx, nexty = y + dy;
13530   int move_direction = (dx == -1 ? MV_LEFT  :
13531                         dx == +1 ? MV_RIGHT :
13532                         dy == -1 ? MV_UP    :
13533                         dy == +1 ? MV_DOWN  : MV_NONE);
13534   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13535   int dig_side = MV_DIR_OPPOSITE(move_direction);
13536   int old_element = Feld[jx][jy];
13537   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13538   int collect_count;
13539
13540   if (is_player)                /* function can also be called by EL_PENGUIN */
13541   {
13542     if (player->MovPos == 0)
13543     {
13544       player->is_digging = FALSE;
13545       player->is_collecting = FALSE;
13546     }
13547
13548     if (player->MovPos == 0)    /* last pushing move finished */
13549       player->is_pushing = FALSE;
13550
13551     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13552     {
13553       player->is_switching = FALSE;
13554       player->push_delay = -1;
13555
13556       return MP_NO_ACTION;
13557     }
13558   }
13559
13560   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13561     old_element = Back[jx][jy];
13562
13563   /* in case of element dropped at player position, check background */
13564   else if (Back[jx][jy] != EL_EMPTY &&
13565            game.engine_version >= VERSION_IDENT(2,2,0,0))
13566     old_element = Back[jx][jy];
13567
13568   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13569     return MP_NO_ACTION;        /* field has no opening in this direction */
13570
13571   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13572     return MP_NO_ACTION;        /* field has no opening in this direction */
13573
13574   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13575   {
13576     SplashAcid(x, y);
13577
13578     Feld[jx][jy] = player->artwork_element;
13579     InitMovingField(jx, jy, MV_DOWN);
13580     Store[jx][jy] = EL_ACID;
13581     ContinueMoving(jx, jy);
13582     BuryPlayer(player);
13583
13584     return MP_DONT_RUN_INTO;
13585   }
13586
13587   if (player_can_move && DONT_RUN_INTO(element))
13588   {
13589     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13590
13591     return MP_DONT_RUN_INTO;
13592   }
13593
13594   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13595     return MP_NO_ACTION;
13596
13597   collect_count = element_info[element].collect_count_initial;
13598
13599   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13600     return MP_NO_ACTION;
13601
13602   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13603     player_can_move = player_can_move_or_snap;
13604
13605   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13606       game.engine_version >= VERSION_IDENT(2,2,0,0))
13607   {
13608     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13609                                player->index_bit, dig_side);
13610     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13611                                         player->index_bit, dig_side);
13612
13613     if (element == EL_DC_LANDMINE)
13614       Bang(x, y);
13615
13616     if (Feld[x][y] != element)          /* field changed by snapping */
13617       return MP_ACTION;
13618
13619     return MP_NO_ACTION;
13620   }
13621
13622   if (player->gravity && is_player && !player->is_auto_moving &&
13623       canFallDown(player) && move_direction != MV_DOWN &&
13624       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13625     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13626
13627   if (player_can_move &&
13628       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13629   {
13630     int sound_element = SND_ELEMENT(element);
13631     int sound_action = ACTION_WALKING;
13632
13633     if (IS_RND_GATE(element))
13634     {
13635       if (!player->key[RND_GATE_NR(element)])
13636         return MP_NO_ACTION;
13637     }
13638     else if (IS_RND_GATE_GRAY(element))
13639     {
13640       if (!player->key[RND_GATE_GRAY_NR(element)])
13641         return MP_NO_ACTION;
13642     }
13643     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13644     {
13645       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13646         return MP_NO_ACTION;
13647     }
13648     else if (element == EL_EXIT_OPEN ||
13649              element == EL_EM_EXIT_OPEN ||
13650              element == EL_EM_EXIT_OPENING ||
13651              element == EL_STEEL_EXIT_OPEN ||
13652              element == EL_EM_STEEL_EXIT_OPEN ||
13653              element == EL_EM_STEEL_EXIT_OPENING ||
13654              element == EL_SP_EXIT_OPEN ||
13655              element == EL_SP_EXIT_OPENING)
13656     {
13657       sound_action = ACTION_PASSING;    /* player is passing exit */
13658     }
13659     else if (element == EL_EMPTY)
13660     {
13661       sound_action = ACTION_MOVING;             /* nothing to walk on */
13662     }
13663
13664     /* play sound from background or player, whatever is available */
13665     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13666       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13667     else
13668       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13669   }
13670   else if (player_can_move &&
13671            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13672   {
13673     if (!ACCESS_FROM(element, opposite_direction))
13674       return MP_NO_ACTION;      /* field not accessible from this direction */
13675
13676     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13677       return MP_NO_ACTION;
13678
13679     if (IS_EM_GATE(element))
13680     {
13681       if (!player->key[EM_GATE_NR(element)])
13682         return MP_NO_ACTION;
13683     }
13684     else if (IS_EM_GATE_GRAY(element))
13685     {
13686       if (!player->key[EM_GATE_GRAY_NR(element)])
13687         return MP_NO_ACTION;
13688     }
13689     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13690     {
13691       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13692         return MP_NO_ACTION;
13693     }
13694     else if (IS_EMC_GATE(element))
13695     {
13696       if (!player->key[EMC_GATE_NR(element)])
13697         return MP_NO_ACTION;
13698     }
13699     else if (IS_EMC_GATE_GRAY(element))
13700     {
13701       if (!player->key[EMC_GATE_GRAY_NR(element)])
13702         return MP_NO_ACTION;
13703     }
13704     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13705     {
13706       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13707         return MP_NO_ACTION;
13708     }
13709     else if (element == EL_DC_GATE_WHITE ||
13710              element == EL_DC_GATE_WHITE_GRAY ||
13711              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13712     {
13713       if (player->num_white_keys == 0)
13714         return MP_NO_ACTION;
13715
13716       player->num_white_keys--;
13717     }
13718     else if (IS_SP_PORT(element))
13719     {
13720       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13721           element == EL_SP_GRAVITY_PORT_RIGHT ||
13722           element == EL_SP_GRAVITY_PORT_UP ||
13723           element == EL_SP_GRAVITY_PORT_DOWN)
13724         player->gravity = !player->gravity;
13725       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13726                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13727                element == EL_SP_GRAVITY_ON_PORT_UP ||
13728                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13729         player->gravity = TRUE;
13730       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13731                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13732                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13733                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13734         player->gravity = FALSE;
13735     }
13736
13737     /* automatically move to the next field with double speed */
13738     player->programmed_action = move_direction;
13739
13740     if (player->move_delay_reset_counter == 0)
13741     {
13742       player->move_delay_reset_counter = 2;     /* two double speed steps */
13743
13744       DOUBLE_PLAYER_SPEED(player);
13745     }
13746
13747     PlayLevelSoundAction(x, y, ACTION_PASSING);
13748   }
13749   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13750   {
13751     RemoveField(x, y);
13752
13753     if (mode != DF_SNAP)
13754     {
13755       GfxElement[x][y] = GFX_ELEMENT(element);
13756       player->is_digging = TRUE;
13757     }
13758
13759     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13760
13761     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13762                                         player->index_bit, dig_side);
13763
13764     if (mode == DF_SNAP)
13765     {
13766       if (level.block_snap_field)
13767         setFieldForSnapping(x, y, element, move_direction);
13768       else
13769         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13770
13771       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13772                                           player->index_bit, dig_side);
13773     }
13774   }
13775   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13776   {
13777     RemoveField(x, y);
13778
13779     if (is_player && mode != DF_SNAP)
13780     {
13781       GfxElement[x][y] = element;
13782       player->is_collecting = TRUE;
13783     }
13784
13785     if (element == EL_SPEED_PILL)
13786     {
13787       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13788     }
13789     else if (element == EL_EXTRA_TIME && level.time > 0)
13790     {
13791       TimeLeft += level.extra_time;
13792
13793       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13794
13795       DisplayGameControlValues();
13796     }
13797     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13798     {
13799       player->shield_normal_time_left += level.shield_normal_time;
13800       if (element == EL_SHIELD_DEADLY)
13801         player->shield_deadly_time_left += level.shield_deadly_time;
13802     }
13803     else if (element == EL_DYNAMITE ||
13804              element == EL_EM_DYNAMITE ||
13805              element == EL_SP_DISK_RED)
13806     {
13807       if (player->inventory_size < MAX_INVENTORY_SIZE)
13808         player->inventory_element[player->inventory_size++] = element;
13809
13810       DrawGameDoorValues();
13811     }
13812     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13813     {
13814       player->dynabomb_count++;
13815       player->dynabombs_left++;
13816     }
13817     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13818     {
13819       player->dynabomb_size++;
13820     }
13821     else if (element == EL_DYNABOMB_INCREASE_POWER)
13822     {
13823       player->dynabomb_xl = TRUE;
13824     }
13825     else if (IS_KEY(element))
13826     {
13827       player->key[KEY_NR(element)] = TRUE;
13828
13829       DrawGameDoorValues();
13830     }
13831     else if (element == EL_DC_KEY_WHITE)
13832     {
13833       player->num_white_keys++;
13834
13835       /* display white keys? */
13836       /* DrawGameDoorValues(); */
13837     }
13838     else if (IS_ENVELOPE(element))
13839     {
13840       player->show_envelope = element;
13841     }
13842     else if (element == EL_EMC_LENSES)
13843     {
13844       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13845
13846       RedrawAllInvisibleElementsForLenses();
13847     }
13848     else if (element == EL_EMC_MAGNIFIER)
13849     {
13850       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13851
13852       RedrawAllInvisibleElementsForMagnifier();
13853     }
13854     else if (IS_DROPPABLE(element) ||
13855              IS_THROWABLE(element))     /* can be collected and dropped */
13856     {
13857       int i;
13858
13859       if (collect_count == 0)
13860         player->inventory_infinite_element = element;
13861       else
13862         for (i = 0; i < collect_count; i++)
13863           if (player->inventory_size < MAX_INVENTORY_SIZE)
13864             player->inventory_element[player->inventory_size++] = element;
13865
13866       DrawGameDoorValues();
13867     }
13868     else if (collect_count > 0)
13869     {
13870       local_player->gems_still_needed -= collect_count;
13871       if (local_player->gems_still_needed < 0)
13872         local_player->gems_still_needed = 0;
13873
13874       game.snapshot.collected_item = TRUE;
13875
13876       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13877
13878       DisplayGameControlValues();
13879     }
13880
13881     RaiseScoreElement(element);
13882     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13883
13884     if (is_player)
13885       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13886                                           player->index_bit, dig_side);
13887
13888     if (mode == DF_SNAP)
13889     {
13890       if (level.block_snap_field)
13891         setFieldForSnapping(x, y, element, move_direction);
13892       else
13893         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13894
13895       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13896                                           player->index_bit, dig_side);
13897     }
13898   }
13899   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13900   {
13901     if (mode == DF_SNAP && element != EL_BD_ROCK)
13902       return MP_NO_ACTION;
13903
13904     if (CAN_FALL(element) && dy)
13905       return MP_NO_ACTION;
13906
13907     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13908         !(element == EL_SPRING && level.use_spring_bug))
13909       return MP_NO_ACTION;
13910
13911     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13912         ((move_direction & MV_VERTICAL &&
13913           ((element_info[element].move_pattern & MV_LEFT &&
13914             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13915            (element_info[element].move_pattern & MV_RIGHT &&
13916             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13917          (move_direction & MV_HORIZONTAL &&
13918           ((element_info[element].move_pattern & MV_UP &&
13919             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13920            (element_info[element].move_pattern & MV_DOWN &&
13921             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13922       return MP_NO_ACTION;
13923
13924     /* do not push elements already moving away faster than player */
13925     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13926         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13927       return MP_NO_ACTION;
13928
13929     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13930     {
13931       if (player->push_delay_value == -1 || !player_was_pushing)
13932         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13933     }
13934     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13935     {
13936       if (player->push_delay_value == -1)
13937         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13938     }
13939     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13940     {
13941       if (!player->is_pushing)
13942         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13943     }
13944
13945     player->is_pushing = TRUE;
13946     player->is_active = TRUE;
13947
13948     if (!(IN_LEV_FIELD(nextx, nexty) &&
13949           (IS_FREE(nextx, nexty) ||
13950            (IS_SB_ELEMENT(element) &&
13951             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13952            (IS_CUSTOM_ELEMENT(element) &&
13953             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13954       return MP_NO_ACTION;
13955
13956     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13957       return MP_NO_ACTION;
13958
13959     if (player->push_delay == -1)       /* new pushing; restart delay */
13960       player->push_delay = 0;
13961
13962     if (player->push_delay < player->push_delay_value &&
13963         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13964         element != EL_SPRING && element != EL_BALLOON)
13965     {
13966       /* make sure that there is no move delay before next try to push */
13967       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13968         player->move_delay = 0;
13969
13970       return MP_NO_ACTION;
13971     }
13972
13973     if (IS_CUSTOM_ELEMENT(element) &&
13974         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13975     {
13976       if (!DigFieldByCE(nextx, nexty, element))
13977         return MP_NO_ACTION;
13978     }
13979
13980     if (IS_SB_ELEMENT(element))
13981     {
13982       if (element == EL_SOKOBAN_FIELD_FULL)
13983       {
13984         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13985         local_player->sokobanfields_still_needed++;
13986       }
13987
13988       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13989       {
13990         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13991         local_player->sokobanfields_still_needed--;
13992       }
13993
13994       Feld[x][y] = EL_SOKOBAN_OBJECT;
13995
13996       if (Back[x][y] == Back[nextx][nexty])
13997         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13998       else if (Back[x][y] != 0)
13999         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14000                                     ACTION_EMPTYING);
14001       else
14002         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14003                                     ACTION_FILLING);
14004
14005       if (local_player->sokobanfields_still_needed == 0 &&
14006           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14007       {
14008         PlayerWins(player);
14009
14010         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14011       }
14012     }
14013     else
14014       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14015
14016     InitMovingField(x, y, move_direction);
14017     GfxAction[x][y] = ACTION_PUSHING;
14018
14019     if (mode == DF_SNAP)
14020       ContinueMoving(x, y);
14021     else
14022       MovPos[x][y] = (dx != 0 ? dx : dy);
14023
14024     Pushed[x][y] = TRUE;
14025     Pushed[nextx][nexty] = TRUE;
14026
14027     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14028       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14029     else
14030       player->push_delay_value = -1;    /* get new value later */
14031
14032     /* check for element change _after_ element has been pushed */
14033     if (game.use_change_when_pushing_bug)
14034     {
14035       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14036                                  player->index_bit, dig_side);
14037       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14038                                           player->index_bit, dig_side);
14039     }
14040   }
14041   else if (IS_SWITCHABLE(element))
14042   {
14043     if (PLAYER_SWITCHING(player, x, y))
14044     {
14045       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14046                                           player->index_bit, dig_side);
14047
14048       return MP_ACTION;
14049     }
14050
14051     player->is_switching = TRUE;
14052     player->switch_x = x;
14053     player->switch_y = y;
14054
14055     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14056
14057     if (element == EL_ROBOT_WHEEL)
14058     {
14059       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14060       ZX = x;
14061       ZY = y;
14062
14063       game.robot_wheel_active = TRUE;
14064
14065       TEST_DrawLevelField(x, y);
14066     }
14067     else if (element == EL_SP_TERMINAL)
14068     {
14069       int xx, yy;
14070
14071       SCAN_PLAYFIELD(xx, yy)
14072       {
14073         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14074         {
14075           Bang(xx, yy);
14076         }
14077         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14078         {
14079           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14080
14081           ResetGfxAnimation(xx, yy);
14082           TEST_DrawLevelField(xx, yy);
14083         }
14084       }
14085     }
14086     else if (IS_BELT_SWITCH(element))
14087     {
14088       ToggleBeltSwitch(x, y);
14089     }
14090     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14091              element == EL_SWITCHGATE_SWITCH_DOWN ||
14092              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14093              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14094     {
14095       ToggleSwitchgateSwitch(x, y);
14096     }
14097     else if (element == EL_LIGHT_SWITCH ||
14098              element == EL_LIGHT_SWITCH_ACTIVE)
14099     {
14100       ToggleLightSwitch(x, y);
14101     }
14102     else if (element == EL_TIMEGATE_SWITCH ||
14103              element == EL_DC_TIMEGATE_SWITCH)
14104     {
14105       ActivateTimegateSwitch(x, y);
14106     }
14107     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14108              element == EL_BALLOON_SWITCH_RIGHT ||
14109              element == EL_BALLOON_SWITCH_UP    ||
14110              element == EL_BALLOON_SWITCH_DOWN  ||
14111              element == EL_BALLOON_SWITCH_NONE  ||
14112              element == EL_BALLOON_SWITCH_ANY)
14113     {
14114       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14115                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14116                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14117                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14118                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14119                              move_direction);
14120     }
14121     else if (element == EL_LAMP)
14122     {
14123       Feld[x][y] = EL_LAMP_ACTIVE;
14124       local_player->lights_still_needed--;
14125
14126       ResetGfxAnimation(x, y);
14127       TEST_DrawLevelField(x, y);
14128     }
14129     else if (element == EL_TIME_ORB_FULL)
14130     {
14131       Feld[x][y] = EL_TIME_ORB_EMPTY;
14132
14133       if (level.time > 0 || level.use_time_orb_bug)
14134       {
14135         TimeLeft += level.time_orb_time;
14136         game.no_time_limit = FALSE;
14137
14138         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14139
14140         DisplayGameControlValues();
14141       }
14142
14143       ResetGfxAnimation(x, y);
14144       TEST_DrawLevelField(x, y);
14145     }
14146     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14147              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14148     {
14149       int xx, yy;
14150
14151       game.ball_state = !game.ball_state;
14152
14153       SCAN_PLAYFIELD(xx, yy)
14154       {
14155         int e = Feld[xx][yy];
14156
14157         if (game.ball_state)
14158         {
14159           if (e == EL_EMC_MAGIC_BALL)
14160             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14161           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14162             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14163         }
14164         else
14165         {
14166           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14167             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14168           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14169             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14170         }
14171       }
14172     }
14173
14174     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14175                                         player->index_bit, dig_side);
14176
14177     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14178                                         player->index_bit, dig_side);
14179
14180     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14181                                         player->index_bit, dig_side);
14182
14183     return MP_ACTION;
14184   }
14185   else
14186   {
14187     if (!PLAYER_SWITCHING(player, x, y))
14188     {
14189       player->is_switching = TRUE;
14190       player->switch_x = x;
14191       player->switch_y = y;
14192
14193       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14194                                  player->index_bit, dig_side);
14195       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14196                                           player->index_bit, dig_side);
14197
14198       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14199                                  player->index_bit, dig_side);
14200       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14201                                           player->index_bit, dig_side);
14202     }
14203
14204     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14205                                player->index_bit, dig_side);
14206     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14207                                         player->index_bit, dig_side);
14208
14209     return MP_NO_ACTION;
14210   }
14211
14212   player->push_delay = -1;
14213
14214   if (is_player)                /* function can also be called by EL_PENGUIN */
14215   {
14216     if (Feld[x][y] != element)          /* really digged/collected something */
14217     {
14218       player->is_collecting = !player->is_digging;
14219       player->is_active = TRUE;
14220     }
14221   }
14222
14223   return MP_MOVING;
14224 }
14225
14226 static boolean DigFieldByCE(int x, int y, int digging_element)
14227 {
14228   int element = Feld[x][y];
14229
14230   if (!IS_FREE(x, y))
14231   {
14232     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14233                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14234                   ACTION_BREAKING);
14235
14236     /* no element can dig solid indestructible elements */
14237     if (IS_INDESTRUCTIBLE(element) &&
14238         !IS_DIGGABLE(element) &&
14239         !IS_COLLECTIBLE(element))
14240       return FALSE;
14241
14242     if (AmoebaNr[x][y] &&
14243         (element == EL_AMOEBA_FULL ||
14244          element == EL_BD_AMOEBA ||
14245          element == EL_AMOEBA_GROWING))
14246     {
14247       AmoebaCnt[AmoebaNr[x][y]]--;
14248       AmoebaCnt2[AmoebaNr[x][y]]--;
14249     }
14250
14251     if (IS_MOVING(x, y))
14252       RemoveMovingField(x, y);
14253     else
14254     {
14255       RemoveField(x, y);
14256       TEST_DrawLevelField(x, y);
14257     }
14258
14259     /* if digged element was about to explode, prevent the explosion */
14260     ExplodeField[x][y] = EX_TYPE_NONE;
14261
14262     PlayLevelSoundAction(x, y, action);
14263   }
14264
14265   Store[x][y] = EL_EMPTY;
14266
14267   /* this makes it possible to leave the removed element again */
14268   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14269     Store[x][y] = element;
14270
14271   return TRUE;
14272 }
14273
14274 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14275 {
14276   int jx = player->jx, jy = player->jy;
14277   int x = jx + dx, y = jy + dy;
14278   int snap_direction = (dx == -1 ? MV_LEFT  :
14279                         dx == +1 ? MV_RIGHT :
14280                         dy == -1 ? MV_UP    :
14281                         dy == +1 ? MV_DOWN  : MV_NONE);
14282   boolean can_continue_snapping = (level.continuous_snapping &&
14283                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14284
14285   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14286     return FALSE;
14287
14288   if (!player->active || !IN_LEV_FIELD(x, y))
14289     return FALSE;
14290
14291   if (dx && dy)
14292     return FALSE;
14293
14294   if (!dx && !dy)
14295   {
14296     if (player->MovPos == 0)
14297       player->is_pushing = FALSE;
14298
14299     player->is_snapping = FALSE;
14300
14301     if (player->MovPos == 0)
14302     {
14303       player->is_moving = FALSE;
14304       player->is_digging = FALSE;
14305       player->is_collecting = FALSE;
14306     }
14307
14308     return FALSE;
14309   }
14310
14311   /* prevent snapping with already pressed snap key when not allowed */
14312   if (player->is_snapping && !can_continue_snapping)
14313     return FALSE;
14314
14315   player->MovDir = snap_direction;
14316
14317   if (player->MovPos == 0)
14318   {
14319     player->is_moving = FALSE;
14320     player->is_digging = FALSE;
14321     player->is_collecting = FALSE;
14322   }
14323
14324   player->is_dropping = FALSE;
14325   player->is_dropping_pressed = FALSE;
14326   player->drop_pressed_delay = 0;
14327
14328   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14329     return FALSE;
14330
14331   player->is_snapping = TRUE;
14332   player->is_active = TRUE;
14333
14334   if (player->MovPos == 0)
14335   {
14336     player->is_moving = FALSE;
14337     player->is_digging = FALSE;
14338     player->is_collecting = FALSE;
14339   }
14340
14341   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14342     TEST_DrawLevelField(player->last_jx, player->last_jy);
14343
14344   TEST_DrawLevelField(x, y);
14345
14346   return TRUE;
14347 }
14348
14349 static boolean DropElement(struct PlayerInfo *player)
14350 {
14351   int old_element, new_element;
14352   int dropx = player->jx, dropy = player->jy;
14353   int drop_direction = player->MovDir;
14354   int drop_side = drop_direction;
14355   int drop_element = get_next_dropped_element(player);
14356
14357   /* do not drop an element on top of another element; when holding drop key
14358      pressed without moving, dropped element must move away before the next
14359      element can be dropped (this is especially important if the next element
14360      is dynamite, which can be placed on background for historical reasons) */
14361   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14362     return MP_ACTION;
14363
14364   if (IS_THROWABLE(drop_element))
14365   {
14366     dropx += GET_DX_FROM_DIR(drop_direction);
14367     dropy += GET_DY_FROM_DIR(drop_direction);
14368
14369     if (!IN_LEV_FIELD(dropx, dropy))
14370       return FALSE;
14371   }
14372
14373   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14374   new_element = drop_element;           /* default: no change when dropping */
14375
14376   /* check if player is active, not moving and ready to drop */
14377   if (!player->active || player->MovPos || player->drop_delay > 0)
14378     return FALSE;
14379
14380   /* check if player has anything that can be dropped */
14381   if (new_element == EL_UNDEFINED)
14382     return FALSE;
14383
14384   /* only set if player has anything that can be dropped */
14385   player->is_dropping_pressed = TRUE;
14386
14387   /* check if drop key was pressed long enough for EM style dynamite */
14388   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14389     return FALSE;
14390
14391   /* check if anything can be dropped at the current position */
14392   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14393     return FALSE;
14394
14395   /* collected custom elements can only be dropped on empty fields */
14396   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14397     return FALSE;
14398
14399   if (old_element != EL_EMPTY)
14400     Back[dropx][dropy] = old_element;   /* store old element on this field */
14401
14402   ResetGfxAnimation(dropx, dropy);
14403   ResetRandomAnimationValue(dropx, dropy);
14404
14405   if (player->inventory_size > 0 ||
14406       player->inventory_infinite_element != EL_UNDEFINED)
14407   {
14408     if (player->inventory_size > 0)
14409     {
14410       player->inventory_size--;
14411
14412       DrawGameDoorValues();
14413
14414       if (new_element == EL_DYNAMITE)
14415         new_element = EL_DYNAMITE_ACTIVE;
14416       else if (new_element == EL_EM_DYNAMITE)
14417         new_element = EL_EM_DYNAMITE_ACTIVE;
14418       else if (new_element == EL_SP_DISK_RED)
14419         new_element = EL_SP_DISK_RED_ACTIVE;
14420     }
14421
14422     Feld[dropx][dropy] = new_element;
14423
14424     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14425       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14426                           el2img(Feld[dropx][dropy]), 0);
14427
14428     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14429
14430     /* needed if previous element just changed to "empty" in the last frame */
14431     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14432
14433     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14434                                player->index_bit, drop_side);
14435     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14436                                         CE_PLAYER_DROPS_X,
14437                                         player->index_bit, drop_side);
14438
14439     TestIfElementTouchesCustomElement(dropx, dropy);
14440   }
14441   else          /* player is dropping a dyna bomb */
14442   {
14443     player->dynabombs_left--;
14444
14445     Feld[dropx][dropy] = new_element;
14446
14447     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14448       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14449                           el2img(Feld[dropx][dropy]), 0);
14450
14451     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14452   }
14453
14454   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14455     InitField_WithBug1(dropx, dropy, FALSE);
14456
14457   new_element = Feld[dropx][dropy];     /* element might have changed */
14458
14459   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14460       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14461   {
14462     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14463       MovDir[dropx][dropy] = drop_direction;
14464
14465     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14466
14467     /* do not cause impact style collision by dropping elements that can fall */
14468     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14469   }
14470
14471   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14472   player->is_dropping = TRUE;
14473
14474   player->drop_pressed_delay = 0;
14475   player->is_dropping_pressed = FALSE;
14476
14477   player->drop_x = dropx;
14478   player->drop_y = dropy;
14479
14480   return TRUE;
14481 }
14482
14483 /* ------------------------------------------------------------------------- */
14484 /* game sound playing functions                                              */
14485 /* ------------------------------------------------------------------------- */
14486
14487 static int *loop_sound_frame = NULL;
14488 static int *loop_sound_volume = NULL;
14489
14490 void InitPlayLevelSound()
14491 {
14492   int num_sounds = getSoundListSize();
14493
14494   checked_free(loop_sound_frame);
14495   checked_free(loop_sound_volume);
14496
14497   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14498   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14499 }
14500
14501 static void PlayLevelSound(int x, int y, int nr)
14502 {
14503   int sx = SCREENX(x), sy = SCREENY(y);
14504   int volume, stereo_position;
14505   int max_distance = 8;
14506   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14507
14508   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14509       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14510     return;
14511
14512   if (!IN_LEV_FIELD(x, y) ||
14513       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14514       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14515     return;
14516
14517   volume = SOUND_MAX_VOLUME;
14518
14519   if (!IN_SCR_FIELD(sx, sy))
14520   {
14521     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14522     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14523
14524     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14525   }
14526
14527   stereo_position = (SOUND_MAX_LEFT +
14528                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14529                      (SCR_FIELDX + 2 * max_distance));
14530
14531   if (IS_LOOP_SOUND(nr))
14532   {
14533     /* This assures that quieter loop sounds do not overwrite louder ones,
14534        while restarting sound volume comparison with each new game frame. */
14535
14536     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14537       return;
14538
14539     loop_sound_volume[nr] = volume;
14540     loop_sound_frame[nr] = FrameCounter;
14541   }
14542
14543   PlaySoundExt(nr, volume, stereo_position, type);
14544 }
14545
14546 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14547 {
14548   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14549                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14550                  y < LEVELY(BY1) ? LEVELY(BY1) :
14551                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14552                  sound_action);
14553 }
14554
14555 static void PlayLevelSoundAction(int x, int y, int action)
14556 {
14557   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14558 }
14559
14560 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14561 {
14562   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14563
14564   if (sound_effect != SND_UNDEFINED)
14565     PlayLevelSound(x, y, sound_effect);
14566 }
14567
14568 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14569                                               int action)
14570 {
14571   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14572
14573   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14574     PlayLevelSound(x, y, sound_effect);
14575 }
14576
14577 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14578 {
14579   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14580
14581   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14582     PlayLevelSound(x, y, sound_effect);
14583 }
14584
14585 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14586 {
14587   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14588
14589   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14590     StopSound(sound_effect);
14591 }
14592
14593 static int getLevelMusicNr()
14594 {
14595   if (levelset.music[level_nr] != MUS_UNDEFINED)
14596     return levelset.music[level_nr];            /* from config file */
14597   else
14598     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14599 }
14600
14601 static void FadeLevelSounds()
14602 {
14603   FadeSounds();
14604 }
14605
14606 static void FadeLevelMusic()
14607 {
14608   int music_nr = getLevelMusicNr();
14609   char *curr_music = getCurrentlyPlayingMusicFilename();
14610   char *next_music = getMusicInfoEntryFilename(music_nr);
14611
14612   if (!strEqual(curr_music, next_music))
14613     FadeMusic();
14614 }
14615
14616 void FadeLevelSoundsAndMusic()
14617 {
14618   FadeLevelSounds();
14619   FadeLevelMusic();
14620 }
14621
14622 static void PlayLevelMusic()
14623 {
14624   int music_nr = getLevelMusicNr();
14625   char *curr_music = getCurrentlyPlayingMusicFilename();
14626   char *next_music = getMusicInfoEntryFilename(music_nr);
14627
14628   if (!strEqual(curr_music, next_music))
14629     PlayMusic(music_nr);
14630 }
14631
14632 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14633 {
14634   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14635   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14636   int x = xx - 1 - offset;
14637   int y = yy - 1 - offset;
14638
14639   switch (sample)
14640   {
14641     case SAMPLE_blank:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14643       break;
14644
14645     case SAMPLE_roll:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14647       break;
14648
14649     case SAMPLE_stone:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14651       break;
14652
14653     case SAMPLE_nut:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14655       break;
14656
14657     case SAMPLE_crack:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14659       break;
14660
14661     case SAMPLE_bug:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14663       break;
14664
14665     case SAMPLE_tank:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14667       break;
14668
14669     case SAMPLE_android_clone:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14671       break;
14672
14673     case SAMPLE_android_move:
14674       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14675       break;
14676
14677     case SAMPLE_spring:
14678       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14679       break;
14680
14681     case SAMPLE_slurp:
14682       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14683       break;
14684
14685     case SAMPLE_eater:
14686       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14687       break;
14688
14689     case SAMPLE_eater_eat:
14690       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14691       break;
14692
14693     case SAMPLE_alien:
14694       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14695       break;
14696
14697     case SAMPLE_collect:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14699       break;
14700
14701     case SAMPLE_diamond:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14703       break;
14704
14705     case SAMPLE_squash:
14706       /* !!! CHECK THIS !!! */
14707 #if 1
14708       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14709 #else
14710       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14711 #endif
14712       break;
14713
14714     case SAMPLE_wonderfall:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14716       break;
14717
14718     case SAMPLE_drip:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14720       break;
14721
14722     case SAMPLE_push:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14724       break;
14725
14726     case SAMPLE_dirt:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14728       break;
14729
14730     case SAMPLE_acid:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14732       break;
14733
14734     case SAMPLE_ball:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14736       break;
14737
14738     case SAMPLE_grow:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14740       break;
14741
14742     case SAMPLE_wonder:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14744       break;
14745
14746     case SAMPLE_door:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14748       break;
14749
14750     case SAMPLE_exit_open:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14752       break;
14753
14754     case SAMPLE_exit_leave:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14756       break;
14757
14758     case SAMPLE_dynamite:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14760       break;
14761
14762     case SAMPLE_tick:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14764       break;
14765
14766     case SAMPLE_press:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14768       break;
14769
14770     case SAMPLE_wheel:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14772       break;
14773
14774     case SAMPLE_boom:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14776       break;
14777
14778     case SAMPLE_die:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14780       break;
14781
14782     case SAMPLE_time:
14783       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14784       break;
14785
14786     default:
14787       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14788       break;
14789   }
14790 }
14791
14792 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14793 {
14794   int element = map_element_SP_to_RND(element_sp);
14795   int action = map_action_SP_to_RND(action_sp);
14796   int offset = (setup.sp_show_border_elements ? 0 : 1);
14797   int x = xx - offset;
14798   int y = yy - offset;
14799
14800   PlayLevelSoundElementAction(x, y, element, action);
14801 }
14802
14803 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14804 {
14805   int element = map_element_MM_to_RND(element_mm);
14806   int action = map_action_MM_to_RND(action_mm);
14807   int offset = 0;
14808   int x = xx - offset;
14809   int y = yy - offset;
14810
14811   if (!IS_MM_ELEMENT(element))
14812     element = EL_MM_DEFAULT;
14813
14814   PlayLevelSoundElementAction(x, y, element, action);
14815 }
14816
14817 void PlaySound_MM(int sound_mm)
14818 {
14819   int sound = map_sound_MM_to_RND(sound_mm);
14820
14821   if (sound == SND_UNDEFINED)
14822     return;
14823
14824   PlaySound(sound);
14825 }
14826
14827 void PlaySoundLoop_MM(int sound_mm)
14828 {
14829   int sound = map_sound_MM_to_RND(sound_mm);
14830
14831   if (sound == SND_UNDEFINED)
14832     return;
14833
14834   PlaySoundLoop(sound);
14835 }
14836
14837 void StopSound_MM(int sound_mm)
14838 {
14839   int sound = map_sound_MM_to_RND(sound_mm);
14840
14841   if (sound == SND_UNDEFINED)
14842     return;
14843
14844   StopSound(sound);
14845 }
14846
14847 void RaiseScore(int value)
14848 {
14849   local_player->score += value;
14850
14851   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14852
14853   DisplayGameControlValues();
14854 }
14855
14856 void RaiseScoreElement(int element)
14857 {
14858   switch (element)
14859   {
14860     case EL_EMERALD:
14861     case EL_BD_DIAMOND:
14862     case EL_EMERALD_YELLOW:
14863     case EL_EMERALD_RED:
14864     case EL_EMERALD_PURPLE:
14865     case EL_SP_INFOTRON:
14866       RaiseScore(level.score[SC_EMERALD]);
14867       break;
14868     case EL_DIAMOND:
14869       RaiseScore(level.score[SC_DIAMOND]);
14870       break;
14871     case EL_CRYSTAL:
14872       RaiseScore(level.score[SC_CRYSTAL]);
14873       break;
14874     case EL_PEARL:
14875       RaiseScore(level.score[SC_PEARL]);
14876       break;
14877     case EL_BUG:
14878     case EL_BD_BUTTERFLY:
14879     case EL_SP_ELECTRON:
14880       RaiseScore(level.score[SC_BUG]);
14881       break;
14882     case EL_SPACESHIP:
14883     case EL_BD_FIREFLY:
14884     case EL_SP_SNIKSNAK:
14885       RaiseScore(level.score[SC_SPACESHIP]);
14886       break;
14887     case EL_YAMYAM:
14888     case EL_DARK_YAMYAM:
14889       RaiseScore(level.score[SC_YAMYAM]);
14890       break;
14891     case EL_ROBOT:
14892       RaiseScore(level.score[SC_ROBOT]);
14893       break;
14894     case EL_PACMAN:
14895       RaiseScore(level.score[SC_PACMAN]);
14896       break;
14897     case EL_NUT:
14898       RaiseScore(level.score[SC_NUT]);
14899       break;
14900     case EL_DYNAMITE:
14901     case EL_EM_DYNAMITE:
14902     case EL_SP_DISK_RED:
14903     case EL_DYNABOMB_INCREASE_NUMBER:
14904     case EL_DYNABOMB_INCREASE_SIZE:
14905     case EL_DYNABOMB_INCREASE_POWER:
14906       RaiseScore(level.score[SC_DYNAMITE]);
14907       break;
14908     case EL_SHIELD_NORMAL:
14909     case EL_SHIELD_DEADLY:
14910       RaiseScore(level.score[SC_SHIELD]);
14911       break;
14912     case EL_EXTRA_TIME:
14913       RaiseScore(level.extra_time_score);
14914       break;
14915     case EL_KEY_1:
14916     case EL_KEY_2:
14917     case EL_KEY_3:
14918     case EL_KEY_4:
14919     case EL_EM_KEY_1:
14920     case EL_EM_KEY_2:
14921     case EL_EM_KEY_3:
14922     case EL_EM_KEY_4:
14923     case EL_EMC_KEY_5:
14924     case EL_EMC_KEY_6:
14925     case EL_EMC_KEY_7:
14926     case EL_EMC_KEY_8:
14927     case EL_DC_KEY_WHITE:
14928       RaiseScore(level.score[SC_KEY]);
14929       break;
14930     default:
14931       RaiseScore(element_info[element].collect_score);
14932       break;
14933   }
14934 }
14935
14936 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14937 {
14938   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14939   {
14940     /* closing door required in case of envelope style request dialogs */
14941     if (!skip_request)
14942       CloseDoor(DOOR_CLOSE_1);
14943
14944 #if defined(NETWORK_AVALIABLE)
14945     if (options.network)
14946       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14947     else
14948 #endif
14949     {
14950       if (quick_quit)
14951         FadeSkipNextFadeIn();
14952
14953       SetGameStatus(GAME_MODE_MAIN);
14954
14955       DrawMainMenu();
14956     }
14957   }
14958   else          /* continue playing the game */
14959   {
14960     if (tape.playing && tape.deactivate_display)
14961       TapeDeactivateDisplayOff(TRUE);
14962
14963     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14964
14965     if (tape.playing && tape.deactivate_display)
14966       TapeDeactivateDisplayOn();
14967   }
14968 }
14969
14970 void RequestQuitGame(boolean ask_if_really_quit)
14971 {
14972   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14973   boolean skip_request = AllPlayersGone || quick_quit;
14974
14975   RequestQuitGameExt(skip_request, quick_quit,
14976                      "Do you really want to quit the game?");
14977 }
14978
14979 void RequestRestartGame(char *message)
14980 {
14981   game.restart_game_message = NULL;
14982
14983   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14984   {
14985     StartGameActions(options.network, setup.autorecord, level.random_seed);
14986   }
14987   else
14988   {
14989     SetGameStatus(GAME_MODE_MAIN);
14990
14991     DrawMainMenu();
14992   }
14993 }
14994
14995
14996 /* ------------------------------------------------------------------------- */
14997 /* random generator functions                                                */
14998 /* ------------------------------------------------------------------------- */
14999
15000 unsigned int InitEngineRandom_RND(int seed)
15001 {
15002   game.num_random_calls = 0;
15003
15004   return InitEngineRandom(seed);
15005 }
15006
15007 unsigned int RND(int max)
15008 {
15009   if (max > 0)
15010   {
15011     game.num_random_calls++;
15012
15013     return GetEngineRandom(max);
15014   }
15015
15016   return 0;
15017 }
15018
15019
15020 /* ------------------------------------------------------------------------- */
15021 /* game engine snapshot handling functions                                   */
15022 /* ------------------------------------------------------------------------- */
15023
15024 struct EngineSnapshotInfo
15025 {
15026   /* runtime values for custom element collect score */
15027   int collect_score[NUM_CUSTOM_ELEMENTS];
15028
15029   /* runtime values for group element choice position */
15030   int choice_pos[NUM_GROUP_ELEMENTS];
15031
15032   /* runtime values for belt position animations */
15033   int belt_graphic[4][NUM_BELT_PARTS];
15034   int belt_anim_mode[4][NUM_BELT_PARTS];
15035 };
15036
15037 static struct EngineSnapshotInfo engine_snapshot_rnd;
15038 static char *snapshot_level_identifier = NULL;
15039 static int snapshot_level_nr = -1;
15040
15041 static void SaveEngineSnapshotValues_RND()
15042 {
15043   static int belt_base_active_element[4] =
15044   {
15045     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15046     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15047     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15048     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15049   };
15050   int i, j;
15051
15052   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15053   {
15054     int element = EL_CUSTOM_START + i;
15055
15056     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15057   }
15058
15059   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15060   {
15061     int element = EL_GROUP_START + i;
15062
15063     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15064   }
15065
15066   for (i = 0; i < 4; i++)
15067   {
15068     for (j = 0; j < NUM_BELT_PARTS; j++)
15069     {
15070       int element = belt_base_active_element[i] + j;
15071       int graphic = el2img(element);
15072       int anim_mode = graphic_info[graphic].anim_mode;
15073
15074       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15075       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15076     }
15077   }
15078 }
15079
15080 static void LoadEngineSnapshotValues_RND()
15081 {
15082   unsigned int num_random_calls = game.num_random_calls;
15083   int i, j;
15084
15085   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15086   {
15087     int element = EL_CUSTOM_START + i;
15088
15089     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15090   }
15091
15092   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15093   {
15094     int element = EL_GROUP_START + i;
15095
15096     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15097   }
15098
15099   for (i = 0; i < 4; i++)
15100   {
15101     for (j = 0; j < NUM_BELT_PARTS; j++)
15102     {
15103       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15104       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15105
15106       graphic_info[graphic].anim_mode = anim_mode;
15107     }
15108   }
15109
15110   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15111   {
15112     InitRND(tape.random_seed);
15113     for (i = 0; i < num_random_calls; i++)
15114       RND(1);
15115   }
15116
15117   if (game.num_random_calls != num_random_calls)
15118   {
15119     Error(ERR_INFO, "number of random calls out of sync");
15120     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15121     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15122     Error(ERR_EXIT, "this should not happen -- please debug");
15123   }
15124 }
15125
15126 void FreeEngineSnapshotSingle()
15127 {
15128   FreeSnapshotSingle();
15129
15130   setString(&snapshot_level_identifier, NULL);
15131   snapshot_level_nr = -1;
15132 }
15133
15134 void FreeEngineSnapshotList()
15135 {
15136   FreeSnapshotList();
15137 }
15138
15139 ListNode *SaveEngineSnapshotBuffers()
15140 {
15141   ListNode *buffers = NULL;
15142
15143   /* copy some special values to a structure better suited for the snapshot */
15144
15145   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15146     SaveEngineSnapshotValues_RND();
15147   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15148     SaveEngineSnapshotValues_EM();
15149   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15150     SaveEngineSnapshotValues_SP(&buffers);
15151   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15152     SaveEngineSnapshotValues_MM(&buffers);
15153
15154   /* save values stored in special snapshot structure */
15155
15156   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15157     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15159     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15160   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15161     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15162   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15163     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15164
15165   /* save further RND engine values */
15166
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15170
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15175
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15181
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15185
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15187
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15189
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15192
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15211
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15214
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15216   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15218
15219   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15221
15222   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15224   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15225   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15226   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15227
15228   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15229   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15230
15231 #if 0
15232   ListNode *node = engine_snapshot_list_rnd;
15233   int num_bytes = 0;
15234
15235   while (node != NULL)
15236   {
15237     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15238
15239     node = node->next;
15240   }
15241
15242   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15243 #endif
15244
15245   return buffers;
15246 }
15247
15248 void SaveEngineSnapshotSingle()
15249 {
15250   ListNode *buffers = SaveEngineSnapshotBuffers();
15251
15252   /* finally save all snapshot buffers to single snapshot */
15253   SaveSnapshotSingle(buffers);
15254
15255   /* save level identification information */
15256   setString(&snapshot_level_identifier, leveldir_current->identifier);
15257   snapshot_level_nr = level_nr;
15258 }
15259
15260 boolean CheckSaveEngineSnapshotToList()
15261 {
15262   boolean save_snapshot =
15263     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15264      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15265       game.snapshot.changed_action) ||
15266      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15267       game.snapshot.collected_item));
15268
15269   game.snapshot.changed_action = FALSE;
15270   game.snapshot.collected_item = FALSE;
15271   game.snapshot.save_snapshot = save_snapshot;
15272
15273   return save_snapshot;
15274 }
15275
15276 void SaveEngineSnapshotToList()
15277 {
15278   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15279       tape.quick_resume)
15280     return;
15281
15282   ListNode *buffers = SaveEngineSnapshotBuffers();
15283
15284   /* finally save all snapshot buffers to snapshot list */
15285   SaveSnapshotToList(buffers);
15286 }
15287
15288 void SaveEngineSnapshotToListInitial()
15289 {
15290   FreeEngineSnapshotList();
15291
15292   SaveEngineSnapshotToList();
15293 }
15294
15295 void LoadEngineSnapshotValues()
15296 {
15297   /* restore special values from snapshot structure */
15298
15299   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15300     LoadEngineSnapshotValues_RND();
15301   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15302     LoadEngineSnapshotValues_EM();
15303   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15304     LoadEngineSnapshotValues_SP();
15305   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15306     LoadEngineSnapshotValues_MM();
15307 }
15308
15309 void LoadEngineSnapshotSingle()
15310 {
15311   LoadSnapshotSingle();
15312
15313   LoadEngineSnapshotValues();
15314 }
15315
15316 void LoadEngineSnapshot_Undo(int steps)
15317 {
15318   LoadSnapshotFromList_Older(steps);
15319
15320   LoadEngineSnapshotValues();
15321 }
15322
15323 void LoadEngineSnapshot_Redo(int steps)
15324 {
15325   LoadSnapshotFromList_Newer(steps);
15326
15327   LoadEngineSnapshotValues();
15328 }
15329
15330 boolean CheckEngineSnapshotSingle()
15331 {
15332   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15333           snapshot_level_nr == level_nr);
15334 }
15335
15336 boolean CheckEngineSnapshotList()
15337 {
15338   return CheckSnapshotList();
15339 }
15340
15341
15342 /* ---------- new game button stuff ---------------------------------------- */
15343
15344 static struct
15345 {
15346   int graphic;
15347   struct XY *pos;
15348   int gadget_id;
15349   boolean *setup_value;
15350   boolean allowed_on_tape;
15351   char *infotext;
15352 } gamebutton_info[NUM_GAME_BUTTONS] =
15353 {
15354   {
15355     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15356     GAME_CTRL_ID_STOP,                          NULL,
15357     TRUE,                                       "stop game"
15358   },
15359   {
15360     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15361     GAME_CTRL_ID_PAUSE,                         NULL,
15362     TRUE,                                       "pause game"
15363   },
15364   {
15365     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15366     GAME_CTRL_ID_PLAY,                          NULL,
15367     TRUE,                                       "play game"
15368   },
15369   {
15370     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15371     GAME_CTRL_ID_UNDO,                          NULL,
15372     TRUE,                                       "undo step"
15373   },
15374   {
15375     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15376     GAME_CTRL_ID_REDO,                          NULL,
15377     TRUE,                                       "redo step"
15378   },
15379   {
15380     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15381     GAME_CTRL_ID_SAVE,                          NULL,
15382     TRUE,                                       "save game"
15383   },
15384   {
15385     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15386     GAME_CTRL_ID_PAUSE2,                        NULL,
15387     TRUE,                                       "pause game"
15388   },
15389   {
15390     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15391     GAME_CTRL_ID_LOAD,                          NULL,
15392     TRUE,                                       "load game"
15393   },
15394   {
15395     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15396     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15397     FALSE,                                      "stop game"
15398   },
15399   {
15400     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15401     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15402     FALSE,                                      "pause game"
15403   },
15404   {
15405     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15406     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15407     FALSE,                                      "play game"
15408   },
15409   {
15410     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15411     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15412     TRUE,                                       "background music on/off"
15413   },
15414   {
15415     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15416     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15417     TRUE,                                       "sound loops on/off"
15418   },
15419   {
15420     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15421     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15422     TRUE,                                       "normal sounds on/off"
15423   },
15424   {
15425     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15426     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15427     FALSE,                                      "background music on/off"
15428   },
15429   {
15430     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15431     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15432     FALSE,                                      "sound loops on/off"
15433   },
15434   {
15435     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15436     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15437     FALSE,                                      "normal sounds on/off"
15438   }
15439 };
15440
15441 void CreateGameButtons()
15442 {
15443   int i;
15444
15445   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15446   {
15447     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15448     struct XY *pos = gamebutton_info[i].pos;
15449     struct GadgetInfo *gi;
15450     int button_type;
15451     boolean checked;
15452     unsigned int event_mask;
15453     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15454     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15455     int base_x = (on_tape ? VX : DX);
15456     int base_y = (on_tape ? VY : DY);
15457     int gd_x   = gfx->src_x;
15458     int gd_y   = gfx->src_y;
15459     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15460     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15461     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15462     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15463     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15464     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15465     int id = i;
15466
15467     if (gfx->bitmap == NULL)
15468     {
15469       game_gadget[id] = NULL;
15470
15471       continue;
15472     }
15473
15474     if (id == GAME_CTRL_ID_STOP ||
15475         id == GAME_CTRL_ID_PANEL_STOP ||
15476         id == GAME_CTRL_ID_PLAY ||
15477         id == GAME_CTRL_ID_PANEL_PLAY ||
15478         id == GAME_CTRL_ID_SAVE ||
15479         id == GAME_CTRL_ID_LOAD)
15480     {
15481       button_type = GD_TYPE_NORMAL_BUTTON;
15482       checked = FALSE;
15483       event_mask = GD_EVENT_RELEASED;
15484     }
15485     else if (id == GAME_CTRL_ID_UNDO ||
15486              id == GAME_CTRL_ID_REDO)
15487     {
15488       button_type = GD_TYPE_NORMAL_BUTTON;
15489       checked = FALSE;
15490       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15491     }
15492     else
15493     {
15494       button_type = GD_TYPE_CHECK_BUTTON;
15495       checked = (gamebutton_info[i].setup_value != NULL ?
15496                  *gamebutton_info[i].setup_value : FALSE);
15497       event_mask = GD_EVENT_PRESSED;
15498     }
15499
15500     gi = CreateGadget(GDI_CUSTOM_ID, id,
15501                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15502                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15503                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15504                       GDI_WIDTH, gfx->width,
15505                       GDI_HEIGHT, gfx->height,
15506                       GDI_TYPE, button_type,
15507                       GDI_STATE, GD_BUTTON_UNPRESSED,
15508                       GDI_CHECKED, checked,
15509                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15510                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15511                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15512                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15513                       GDI_DIRECT_DRAW, FALSE,
15514                       GDI_EVENT_MASK, event_mask,
15515                       GDI_CALLBACK_ACTION, HandleGameButtons,
15516                       GDI_END);
15517
15518     if (gi == NULL)
15519       Error(ERR_EXIT, "cannot create gadget");
15520
15521     game_gadget[id] = gi;
15522   }
15523 }
15524
15525 void FreeGameButtons()
15526 {
15527   int i;
15528
15529   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15530     FreeGadget(game_gadget[i]);
15531 }
15532
15533 static void UnmapGameButtonsAtSamePosition(int id)
15534 {
15535   int i;
15536
15537   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15538     if (i != id &&
15539         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15540         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15541       UnmapGadget(game_gadget[i]);
15542 }
15543
15544 static void UnmapGameButtonsAtSamePosition_All()
15545 {
15546   if (setup.show_snapshot_buttons)
15547   {
15548     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15549     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15550     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15551   }
15552   else
15553   {
15554     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15555     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15556     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15557
15558     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15559     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15560     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15561   }
15562 }
15563
15564 static void MapGameButtonsAtSamePosition(int id)
15565 {
15566   int i;
15567
15568   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15569     if (i != id &&
15570         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15571         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15572       MapGadget(game_gadget[i]);
15573
15574   UnmapGameButtonsAtSamePosition_All();
15575 }
15576
15577 void MapUndoRedoButtons()
15578 {
15579   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15580   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15581
15582   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15583   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15584
15585   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15586 }
15587
15588 void UnmapUndoRedoButtons()
15589 {
15590   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15591   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15592
15593   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15594   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15595
15596   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15597 }
15598
15599 void MapGameButtonsExt(boolean on_tape)
15600 {
15601   int i;
15602
15603   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15604     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15605         i != GAME_CTRL_ID_UNDO &&
15606         i != GAME_CTRL_ID_REDO)
15607       MapGadget(game_gadget[i]);
15608
15609   UnmapGameButtonsAtSamePosition_All();
15610
15611   RedrawGameButtons();
15612 }
15613
15614 void UnmapGameButtonsExt(boolean on_tape)
15615 {
15616   int i;
15617
15618   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15619     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15620       UnmapGadget(game_gadget[i]);
15621 }
15622
15623 void RedrawGameButtonsExt(boolean on_tape)
15624 {
15625   int i;
15626
15627   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15628     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15629       RedrawGadget(game_gadget[i]);
15630
15631   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15632   redraw_mask &= ~REDRAW_ALL;
15633 }
15634
15635 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15636 {
15637   if (gi == NULL)
15638     return;
15639
15640   gi->checked = state;
15641 }
15642
15643 void RedrawSoundButtonGadget(int id)
15644 {
15645   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15646              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15647              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15648              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15649              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15650              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15651              id);
15652
15653   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15654   RedrawGadget(game_gadget[id2]);
15655 }
15656
15657 void MapGameButtons()
15658 {
15659   MapGameButtonsExt(FALSE);
15660 }
15661
15662 void UnmapGameButtons()
15663 {
15664   UnmapGameButtonsExt(FALSE);
15665 }
15666
15667 void RedrawGameButtons()
15668 {
15669   RedrawGameButtonsExt(FALSE);
15670 }
15671
15672 void MapGameButtonsOnTape()
15673 {
15674   MapGameButtonsExt(TRUE);
15675 }
15676
15677 void UnmapGameButtonsOnTape()
15678 {
15679   UnmapGameButtonsExt(TRUE);
15680 }
15681
15682 void RedrawGameButtonsOnTape()
15683 {
15684   RedrawGameButtonsExt(TRUE);
15685 }
15686
15687 void GameUndoRedoExt()
15688 {
15689   ClearPlayerAction();
15690
15691   tape.pausing = TRUE;
15692
15693   RedrawPlayfield();
15694   UpdateAndDisplayGameControlValues();
15695
15696   DrawCompleteVideoDisplay();
15697   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15698   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15699   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15700
15701   BackToFront();
15702 }
15703
15704 void GameUndo(int steps)
15705 {
15706   if (!CheckEngineSnapshotList())
15707     return;
15708
15709   LoadEngineSnapshot_Undo(steps);
15710
15711   GameUndoRedoExt();
15712 }
15713
15714 void GameRedo(int steps)
15715 {
15716   if (!CheckEngineSnapshotList())
15717     return;
15718
15719   LoadEngineSnapshot_Redo(steps);
15720
15721   GameUndoRedoExt();
15722 }
15723
15724 static void HandleGameButtonsExt(int id, int button)
15725 {
15726   static boolean game_undo_executed = FALSE;
15727   int steps = BUTTON_STEPSIZE(button);
15728   boolean handle_game_buttons =
15729     (game_status == GAME_MODE_PLAYING ||
15730      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15731
15732   if (!handle_game_buttons)
15733     return;
15734
15735   switch (id)
15736   {
15737     case GAME_CTRL_ID_STOP:
15738     case GAME_CTRL_ID_PANEL_STOP:
15739       if (game_status == GAME_MODE_MAIN)
15740         break;
15741
15742       if (tape.playing)
15743         TapeStop();
15744       else
15745         RequestQuitGame(TRUE);
15746
15747       break;
15748
15749     case GAME_CTRL_ID_PAUSE:
15750     case GAME_CTRL_ID_PAUSE2:
15751     case GAME_CTRL_ID_PANEL_PAUSE:
15752       if (options.network && game_status == GAME_MODE_PLAYING)
15753       {
15754 #if defined(NETWORK_AVALIABLE)
15755         if (tape.pausing)
15756           SendToServer_ContinuePlaying();
15757         else
15758           SendToServer_PausePlaying();
15759 #endif
15760       }
15761       else
15762         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15763
15764       game_undo_executed = FALSE;
15765
15766       break;
15767
15768     case GAME_CTRL_ID_PLAY:
15769     case GAME_CTRL_ID_PANEL_PLAY:
15770       if (game_status == GAME_MODE_MAIN)
15771       {
15772         StartGameActions(options.network, setup.autorecord, level.random_seed);
15773       }
15774       else if (tape.pausing)
15775       {
15776 #if defined(NETWORK_AVALIABLE)
15777         if (options.network)
15778           SendToServer_ContinuePlaying();
15779         else
15780 #endif
15781           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15782       }
15783       break;
15784
15785     case GAME_CTRL_ID_UNDO:
15786       // Important: When using "save snapshot when collecting an item" mode,
15787       // load last (current) snapshot for first "undo" after pressing "pause"
15788       // (else the last-but-one snapshot would be loaded, because the snapshot
15789       // pointer already points to the last snapshot when pressing "pause",
15790       // which is fine for "every step/move" mode, but not for "every collect")
15791       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15792           !game_undo_executed)
15793         steps--;
15794
15795       game_undo_executed = TRUE;
15796
15797       GameUndo(steps);
15798       break;
15799
15800     case GAME_CTRL_ID_REDO:
15801       GameRedo(steps);
15802       break;
15803
15804     case GAME_CTRL_ID_SAVE:
15805       TapeQuickSave();
15806       break;
15807
15808     case GAME_CTRL_ID_LOAD:
15809       TapeQuickLoad();
15810       break;
15811
15812     case SOUND_CTRL_ID_MUSIC:
15813     case SOUND_CTRL_ID_PANEL_MUSIC:
15814       if (setup.sound_music)
15815       { 
15816         setup.sound_music = FALSE;
15817
15818         FadeMusic();
15819       }
15820       else if (audio.music_available)
15821       { 
15822         setup.sound = setup.sound_music = TRUE;
15823
15824         SetAudioMode(setup.sound);
15825
15826         if (game_status == GAME_MODE_PLAYING)
15827           PlayLevelMusic();
15828       }
15829
15830       RedrawSoundButtonGadget(id);
15831
15832       break;
15833
15834     case SOUND_CTRL_ID_LOOPS:
15835     case SOUND_CTRL_ID_PANEL_LOOPS:
15836       if (setup.sound_loops)
15837         setup.sound_loops = FALSE;
15838       else if (audio.loops_available)
15839       {
15840         setup.sound = setup.sound_loops = TRUE;
15841
15842         SetAudioMode(setup.sound);
15843       }
15844
15845       RedrawSoundButtonGadget(id);
15846
15847       break;
15848
15849     case SOUND_CTRL_ID_SIMPLE:
15850     case SOUND_CTRL_ID_PANEL_SIMPLE:
15851       if (setup.sound_simple)
15852         setup.sound_simple = FALSE;
15853       else if (audio.sound_available)
15854       {
15855         setup.sound = setup.sound_simple = TRUE;
15856
15857         SetAudioMode(setup.sound);
15858       }
15859
15860       RedrawSoundButtonGadget(id);
15861
15862       break;
15863
15864     default:
15865       break;
15866   }
15867 }
15868
15869 static void HandleGameButtons(struct GadgetInfo *gi)
15870 {
15871   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15872 }
15873
15874 void HandleSoundButtonKeys(Key key)
15875 {
15876   if (key == setup.shortcut.sound_simple)
15877     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15878   else if (key == setup.shortcut.sound_loops)
15879     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15880   else if (key == setup.shortcut.sound_music)
15881     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15882 }