renamed functions (after removing parameter in previous commit)
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     local_player->sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     local_player->sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (local_player->sokoban_fields_still_needed > 0)
1701     local_player->sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (local_player->sokoban_objects_still_needed > 0)
1707     local_player->sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       local_player->lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       local_player->friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                local_player->score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               local_player->gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      local_player->gems_still_needed > 0 ||
2243                      local_player->sokoban_fields_still_needed > 0 ||
2244                      local_player->sokoban_objects_still_needed > 0 ||
2245                      local_player->lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 local_player->health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     local_player->friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     local_player->sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     local_player->sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   FadeOut(fade_mask);
3369
3370   // needed if different viewport properties defined for playing
3371   ChangeViewportPropertiesIfNeeded();
3372
3373   ClearField();
3374
3375   DrawCompleteVideoDisplay();
3376
3377   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3378
3379   InitGameEngine();
3380   InitGameControlValues();
3381
3382   // don't play tapes over network
3383   network_playing = (network.enabled && !tape.playing);
3384
3385   for (i = 0; i < MAX_PLAYERS; i++)
3386   {
3387     struct PlayerInfo *player = &stored_player[i];
3388
3389     player->index_nr = i;
3390     player->index_bit = (1 << i);
3391     player->element_nr = EL_PLAYER_1 + i;
3392
3393     player->present = FALSE;
3394     player->active = FALSE;
3395     player->mapped = FALSE;
3396
3397     player->killed = FALSE;
3398     player->reanimated = FALSE;
3399
3400     player->action = 0;
3401     player->effective_action = 0;
3402     player->programmed_action = 0;
3403
3404     player->mouse_action.lx = 0;
3405     player->mouse_action.ly = 0;
3406     player->mouse_action.button = 0;
3407     player->mouse_action.button_hint = 0;
3408
3409     player->effective_mouse_action.lx = 0;
3410     player->effective_mouse_action.ly = 0;
3411     player->effective_mouse_action.button = 0;
3412     player->effective_mouse_action.button_hint = 0;
3413
3414     player->score = 0;
3415     player->score_final = 0;
3416
3417     player->health = MAX_HEALTH;
3418     player->health_final = MAX_HEALTH;
3419
3420     player->gems_still_needed = level.gems_needed;
3421     player->sokoban_fields_still_needed = 0;
3422     player->sokoban_objects_still_needed = 0;
3423     player->lights_still_needed = 0;
3424     player->players_still_needed = 0;
3425     player->friends_still_needed = 0;
3426
3427     for (j = 0; j < MAX_NUM_KEYS; j++)
3428       player->key[j] = FALSE;
3429
3430     player->num_white_keys = 0;
3431
3432     player->dynabomb_count = 0;
3433     player->dynabomb_size = 1;
3434     player->dynabombs_left = 0;
3435     player->dynabomb_xl = FALSE;
3436
3437     player->MovDir = initial_move_dir;
3438     player->MovPos = 0;
3439     player->GfxPos = 0;
3440     player->GfxDir = initial_move_dir;
3441     player->GfxAction = ACTION_DEFAULT;
3442     player->Frame = 0;
3443     player->StepFrame = 0;
3444
3445     player->initial_element = player->element_nr;
3446     player->artwork_element =
3447       (level.use_artwork_element[i] ? level.artwork_element[i] :
3448        player->element_nr);
3449     player->use_murphy = FALSE;
3450
3451     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3452     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3453
3454     player->gravity = level.initial_player_gravity[i];
3455
3456     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3457
3458     player->actual_frame_counter = 0;
3459
3460     player->step_counter = 0;
3461
3462     player->last_move_dir = initial_move_dir;
3463
3464     player->is_active = FALSE;
3465
3466     player->is_waiting = FALSE;
3467     player->is_moving = FALSE;
3468     player->is_auto_moving = FALSE;
3469     player->is_digging = FALSE;
3470     player->is_snapping = FALSE;
3471     player->is_collecting = FALSE;
3472     player->is_pushing = FALSE;
3473     player->is_switching = FALSE;
3474     player->is_dropping = FALSE;
3475     player->is_dropping_pressed = FALSE;
3476
3477     player->is_bored = FALSE;
3478     player->is_sleeping = FALSE;
3479
3480     player->was_waiting = TRUE;
3481     player->was_moving = FALSE;
3482     player->was_snapping = FALSE;
3483     player->was_dropping = FALSE;
3484
3485     player->force_dropping = FALSE;
3486
3487     player->frame_counter_bored = -1;
3488     player->frame_counter_sleeping = -1;
3489
3490     player->anim_delay_counter = 0;
3491     player->post_delay_counter = 0;
3492
3493     player->dir_waiting = initial_move_dir;
3494     player->action_waiting = ACTION_DEFAULT;
3495     player->last_action_waiting = ACTION_DEFAULT;
3496     player->special_action_bored = ACTION_DEFAULT;
3497     player->special_action_sleeping = ACTION_DEFAULT;
3498
3499     player->switch_x = -1;
3500     player->switch_y = -1;
3501
3502     player->drop_x = -1;
3503     player->drop_y = -1;
3504
3505     player->show_envelope = 0;
3506
3507     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3508
3509     player->push_delay       = -1;      // initialized when pushing starts
3510     player->push_delay_value = game.initial_push_delay_value;
3511
3512     player->drop_delay = 0;
3513     player->drop_pressed_delay = 0;
3514
3515     player->last_jx = -1;
3516     player->last_jy = -1;
3517     player->jx = -1;
3518     player->jy = -1;
3519
3520     player->shield_normal_time_left = 0;
3521     player->shield_deadly_time_left = 0;
3522
3523     player->inventory_infinite_element = EL_UNDEFINED;
3524     player->inventory_size = 0;
3525
3526     if (level.use_initial_inventory[i])
3527     {
3528       for (j = 0; j < level.initial_inventory_size[i]; j++)
3529       {
3530         int element = level.initial_inventory_content[i][j];
3531         int collect_count = element_info[element].collect_count_initial;
3532         int k;
3533
3534         if (!IS_CUSTOM_ELEMENT(element))
3535           collect_count = 1;
3536
3537         if (collect_count == 0)
3538           player->inventory_infinite_element = element;
3539         else
3540           for (k = 0; k < collect_count; k++)
3541             if (player->inventory_size < MAX_INVENTORY_SIZE)
3542               player->inventory_element[player->inventory_size++] = element;
3543       }
3544     }
3545
3546     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3547     SnapField(player, 0, 0);
3548
3549     player->GameOver = FALSE;
3550
3551     map_player_action[i] = i;
3552   }
3553
3554   network_player_action_received = FALSE;
3555
3556   // initial null action
3557   if (network_playing)
3558     SendToServer_MovePlayer(MV_NONE);
3559
3560   ZX = ZY = -1;
3561   ExitX = ExitY = -1;
3562
3563   FrameCounter = 0;
3564   TimeFrames = 0;
3565   TimePlayed = 0;
3566   TimeLeft = level.time;
3567   TapeTime = 0;
3568
3569   ScreenMovDir = MV_NONE;
3570   ScreenMovPos = 0;
3571   ScreenGfxPos = 0;
3572
3573   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3574
3575   AllPlayersGone = FALSE;
3576
3577   game.LevelSolved = FALSE;
3578
3579   game.LevelSolved_GameWon = FALSE;
3580   game.LevelSolved_GameEnd = FALSE;
3581   game.LevelSolved_SaveTape = FALSE;
3582   game.LevelSolved_SaveScore = FALSE;
3583
3584   game.LevelSolved_CountingTime = 0;
3585   game.LevelSolved_CountingScore = 0;
3586   game.LevelSolved_CountingHealth = 0;
3587
3588   game.panel.active = TRUE;
3589
3590   game.no_time_limit = (level.time == 0);
3591
3592   game.yamyam_content_nr = 0;
3593   game.robot_wheel_active = FALSE;
3594   game.magic_wall_active = FALSE;
3595   game.magic_wall_time_left = 0;
3596   game.light_time_left = 0;
3597   game.timegate_time_left = 0;
3598   game.switchgate_pos = 0;
3599   game.wind_direction = level.wind_direction_initial;
3600
3601   game.lenses_time_left = 0;
3602   game.magnify_time_left = 0;
3603
3604   game.ball_state = level.ball_state_initial;
3605   game.ball_content_nr = 0;
3606
3607   game.explosions_delayed = TRUE;
3608
3609   game.envelope_active = FALSE;
3610
3611   for (i = 0; i < NUM_BELTS; i++)
3612   {
3613     game.belt_dir[i] = MV_NONE;
3614     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3615   }
3616
3617   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3618     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3619
3620 #if DEBUG_INIT_PLAYER
3621   DebugPrintPlayerStatus("Player status at level initialization");
3622 #endif
3623
3624   SCAN_PLAYFIELD(x, y)
3625   {
3626     Feld[x][y] = Last[x][y] = level.field[x][y];
3627     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3628     ChangeDelay[x][y] = 0;
3629     ChangePage[x][y] = -1;
3630     CustomValue[x][y] = 0;              // initialized in InitField()
3631     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3632     AmoebaNr[x][y] = 0;
3633     WasJustMoving[x][y] = 0;
3634     WasJustFalling[x][y] = 0;
3635     CheckCollision[x][y] = 0;
3636     CheckImpact[x][y] = 0;
3637     Stop[x][y] = FALSE;
3638     Pushed[x][y] = FALSE;
3639
3640     ChangeCount[x][y] = 0;
3641     ChangeEvent[x][y] = -1;
3642
3643     ExplodePhase[x][y] = 0;
3644     ExplodeDelay[x][y] = 0;
3645     ExplodeField[x][y] = EX_TYPE_NONE;
3646
3647     RunnerVisit[x][y] = 0;
3648     PlayerVisit[x][y] = 0;
3649
3650     GfxFrame[x][y] = 0;
3651     GfxRandom[x][y] = INIT_GFX_RANDOM();
3652     GfxElement[x][y] = EL_UNDEFINED;
3653     GfxAction[x][y] = ACTION_DEFAULT;
3654     GfxDir[x][y] = MV_NONE;
3655     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3656   }
3657
3658   SCAN_PLAYFIELD(x, y)
3659   {
3660     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3661       emulate_bd = FALSE;
3662     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3663       emulate_sb = FALSE;
3664     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3665       emulate_sp = FALSE;
3666
3667     InitField(x, y, TRUE);
3668
3669     ResetGfxAnimation(x, y);
3670   }
3671
3672   InitBeltMovement();
3673
3674   for (i = 0; i < MAX_PLAYERS; i++)
3675   {
3676     struct PlayerInfo *player = &stored_player[i];
3677
3678     // set number of special actions for bored and sleeping animation
3679     player->num_special_action_bored =
3680       get_num_special_action(player->artwork_element,
3681                              ACTION_BORING_1, ACTION_BORING_LAST);
3682     player->num_special_action_sleeping =
3683       get_num_special_action(player->artwork_element,
3684                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3685   }
3686
3687   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3688                     emulate_sb ? EMU_SOKOBAN :
3689                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3690
3691   // initialize type of slippery elements
3692   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3693   {
3694     if (!IS_CUSTOM_ELEMENT(i))
3695     {
3696       // default: elements slip down either to the left or right randomly
3697       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3698
3699       // SP style elements prefer to slip down on the left side
3700       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3701         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3702
3703       // BD style elements prefer to slip down on the left side
3704       if (game.emulation == EMU_BOULDERDASH)
3705         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3706     }
3707   }
3708
3709   // initialize explosion and ignition delay
3710   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3711   {
3712     if (!IS_CUSTOM_ELEMENT(i))
3713     {
3714       int num_phase = 8;
3715       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3716                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3717                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3718       int last_phase = (num_phase + 1) * delay;
3719       int half_phase = (num_phase / 2) * delay;
3720
3721       element_info[i].explosion_delay = last_phase - 1;
3722       element_info[i].ignition_delay = half_phase;
3723
3724       if (i == EL_BLACK_ORB)
3725         element_info[i].ignition_delay = 1;
3726     }
3727   }
3728
3729   // correct non-moving belts to start moving left
3730   for (i = 0; i < NUM_BELTS; i++)
3731     if (game.belt_dir[i] == MV_NONE)
3732       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3733
3734 #if USE_NEW_PLAYER_ASSIGNMENTS
3735   for (i = 0; i < MAX_PLAYERS; i++)
3736   {
3737     stored_player[i].connected = FALSE;
3738
3739     // in network game mode, the local player might not be the first player
3740     if (stored_player[i].connected_locally)
3741       local_player = &stored_player[i];
3742   }
3743
3744   if (!network.enabled)
3745     local_player->connected = TRUE;
3746
3747   if (tape.playing)
3748   {
3749     for (i = 0; i < MAX_PLAYERS; i++)
3750       stored_player[i].connected = tape.player_participates[i];
3751   }
3752   else if (network.enabled)
3753   {
3754     // add team mode players connected over the network (needed for correct
3755     // assignment of player figures from level to locally playing players)
3756
3757     for (i = 0; i < MAX_PLAYERS; i++)
3758       if (stored_player[i].connected_network)
3759         stored_player[i].connected = TRUE;
3760   }
3761   else if (game.team_mode)
3762   {
3763     // try to guess locally connected team mode players (needed for correct
3764     // assignment of player figures from level to locally playing players)
3765
3766     for (i = 0; i < MAX_PLAYERS; i++)
3767       if (setup.input[i].use_joystick ||
3768           setup.input[i].key.left != KSYM_UNDEFINED)
3769         stored_player[i].connected = TRUE;
3770   }
3771
3772 #if DEBUG_INIT_PLAYER
3773   DebugPrintPlayerStatus("Player status after level initialization");
3774 #endif
3775
3776 #if DEBUG_INIT_PLAYER
3777   if (options.debug)
3778     printf("Reassigning players ...\n");
3779 #endif
3780
3781   // check if any connected player was not found in playfield
3782   for (i = 0; i < MAX_PLAYERS; i++)
3783   {
3784     struct PlayerInfo *player = &stored_player[i];
3785
3786     if (player->connected && !player->present)
3787     {
3788       struct PlayerInfo *field_player = NULL;
3789
3790 #if DEBUG_INIT_PLAYER
3791       if (options.debug)
3792         printf("- looking for field player for player %d ...\n", i + 1);
3793 #endif
3794
3795       // assign first free player found that is present in the playfield
3796
3797       // first try: look for unmapped playfield player that is not connected
3798       for (j = 0; j < MAX_PLAYERS; j++)
3799         if (field_player == NULL &&
3800             stored_player[j].present &&
3801             !stored_player[j].mapped &&
3802             !stored_player[j].connected)
3803           field_player = &stored_player[j];
3804
3805       // second try: look for *any* unmapped playfield player
3806       for (j = 0; j < MAX_PLAYERS; j++)
3807         if (field_player == NULL &&
3808             stored_player[j].present &&
3809             !stored_player[j].mapped)
3810           field_player = &stored_player[j];
3811
3812       if (field_player != NULL)
3813       {
3814         int jx = field_player->jx, jy = field_player->jy;
3815
3816 #if DEBUG_INIT_PLAYER
3817         if (options.debug)
3818           printf("- found player %d\n", field_player->index_nr + 1);
3819 #endif
3820
3821         player->present = FALSE;
3822         player->active = FALSE;
3823
3824         field_player->present = TRUE;
3825         field_player->active = TRUE;
3826
3827         /*
3828         player->initial_element = field_player->initial_element;
3829         player->artwork_element = field_player->artwork_element;
3830
3831         player->block_last_field       = field_player->block_last_field;
3832         player->block_delay_adjustment = field_player->block_delay_adjustment;
3833         */
3834
3835         StorePlayer[jx][jy] = field_player->element_nr;
3836
3837         field_player->jx = field_player->last_jx = jx;
3838         field_player->jy = field_player->last_jy = jy;
3839
3840         if (local_player == player)
3841           local_player = field_player;
3842
3843         map_player_action[field_player->index_nr] = i;
3844
3845         field_player->mapped = TRUE;
3846
3847 #if DEBUG_INIT_PLAYER
3848         if (options.debug)
3849           printf("- map_player_action[%d] == %d\n",
3850                  field_player->index_nr + 1, i + 1);
3851 #endif
3852       }
3853     }
3854
3855     if (player->connected && player->present)
3856       player->mapped = TRUE;
3857   }
3858
3859 #if DEBUG_INIT_PLAYER
3860   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3861 #endif
3862
3863 #else
3864
3865   // check if any connected player was not found in playfield
3866   for (i = 0; i < MAX_PLAYERS; i++)
3867   {
3868     struct PlayerInfo *player = &stored_player[i];
3869
3870     if (player->connected && !player->present)
3871     {
3872       for (j = 0; j < MAX_PLAYERS; j++)
3873       {
3874         struct PlayerInfo *field_player = &stored_player[j];
3875         int jx = field_player->jx, jy = field_player->jy;
3876
3877         // assign first free player found that is present in the playfield
3878         if (field_player->present && !field_player->connected)
3879         {
3880           player->present = TRUE;
3881           player->active = TRUE;
3882
3883           field_player->present = FALSE;
3884           field_player->active = FALSE;
3885
3886           player->initial_element = field_player->initial_element;
3887           player->artwork_element = field_player->artwork_element;
3888
3889           player->block_last_field       = field_player->block_last_field;
3890           player->block_delay_adjustment = field_player->block_delay_adjustment;
3891
3892           StorePlayer[jx][jy] = player->element_nr;
3893
3894           player->jx = player->last_jx = jx;
3895           player->jy = player->last_jy = jy;
3896
3897           break;
3898         }
3899       }
3900     }
3901   }
3902 #endif
3903
3904 #if 0
3905   printf("::: local_player->present == %d\n", local_player->present);
3906 #endif
3907
3908   // set focus to local player for network games, else to all players
3909   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3910   game.centered_player_nr_next = game.centered_player_nr;
3911   game.set_centered_player = FALSE;
3912
3913   if (network_playing && tape.recording)
3914   {
3915     // store client dependent player focus when recording network games
3916     tape.centered_player_nr_next = game.centered_player_nr_next;
3917     tape.set_centered_player = TRUE;
3918   }
3919
3920   if (tape.playing)
3921   {
3922     // when playing a tape, eliminate all players who do not participate
3923
3924 #if USE_NEW_PLAYER_ASSIGNMENTS
3925
3926     if (!game.team_mode)
3927     {
3928       for (i = 0; i < MAX_PLAYERS; i++)
3929       {
3930         if (stored_player[i].active &&
3931             !tape.player_participates[map_player_action[i]])
3932         {
3933           struct PlayerInfo *player = &stored_player[i];
3934           int jx = player->jx, jy = player->jy;
3935
3936 #if DEBUG_INIT_PLAYER
3937           if (options.debug)
3938             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3939 #endif
3940
3941           player->active = FALSE;
3942           StorePlayer[jx][jy] = 0;
3943           Feld[jx][jy] = EL_EMPTY;
3944         }
3945       }
3946     }
3947
3948 #else
3949
3950     for (i = 0; i < MAX_PLAYERS; i++)
3951     {
3952       if (stored_player[i].active &&
3953           !tape.player_participates[i])
3954       {
3955         struct PlayerInfo *player = &stored_player[i];
3956         int jx = player->jx, jy = player->jy;
3957
3958         player->active = FALSE;
3959         StorePlayer[jx][jy] = 0;
3960         Feld[jx][jy] = EL_EMPTY;
3961       }
3962     }
3963 #endif
3964   }
3965   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3966   {
3967     // when in single player mode, eliminate all but the local player
3968
3969     for (i = 0; i < MAX_PLAYERS; i++)
3970     {
3971       struct PlayerInfo *player = &stored_player[i];
3972
3973       if (player->active && player != local_player)
3974       {
3975         int jx = player->jx, jy = player->jy;
3976
3977         player->active = FALSE;
3978         player->present = FALSE;
3979
3980         StorePlayer[jx][jy] = 0;
3981         Feld[jx][jy] = EL_EMPTY;
3982       }
3983     }
3984   }
3985
3986   for (i = 0; i < MAX_PLAYERS; i++)
3987     if (stored_player[i].active)
3988       local_player->players_still_needed++;
3989
3990   if (level.solved_by_one_player)
3991     local_player->players_still_needed = 1;
3992
3993   // when recording the game, store which players take part in the game
3994   if (tape.recording)
3995   {
3996 #if USE_NEW_PLAYER_ASSIGNMENTS
3997     for (i = 0; i < MAX_PLAYERS; i++)
3998       if (stored_player[i].connected)
3999         tape.player_participates[i] = TRUE;
4000 #else
4001     for (i = 0; i < MAX_PLAYERS; i++)
4002       if (stored_player[i].active)
4003         tape.player_participates[i] = TRUE;
4004 #endif
4005   }
4006
4007 #if DEBUG_INIT_PLAYER
4008   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4009 #endif
4010
4011   if (BorderElement == EL_EMPTY)
4012   {
4013     SBX_Left = 0;
4014     SBX_Right = lev_fieldx - SCR_FIELDX;
4015     SBY_Upper = 0;
4016     SBY_Lower = lev_fieldy - SCR_FIELDY;
4017   }
4018   else
4019   {
4020     SBX_Left = -1;
4021     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4022     SBY_Upper = -1;
4023     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4024   }
4025
4026   if (full_lev_fieldx <= SCR_FIELDX)
4027     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4028   if (full_lev_fieldy <= SCR_FIELDY)
4029     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4030
4031   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4032     SBX_Left--;
4033   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4034     SBY_Upper--;
4035
4036   // if local player not found, look for custom element that might create
4037   // the player (make some assumptions about the right custom element)
4038   if (!local_player->present)
4039   {
4040     int start_x = 0, start_y = 0;
4041     int found_rating = 0;
4042     int found_element = EL_UNDEFINED;
4043     int player_nr = local_player->index_nr;
4044
4045     SCAN_PLAYFIELD(x, y)
4046     {
4047       int element = Feld[x][y];
4048       int content;
4049       int xx, yy;
4050       boolean is_player;
4051
4052       if (level.use_start_element[player_nr] &&
4053           level.start_element[player_nr] == element &&
4054           found_rating < 4)
4055       {
4056         start_x = x;
4057         start_y = y;
4058
4059         found_rating = 4;
4060         found_element = element;
4061       }
4062
4063       if (!IS_CUSTOM_ELEMENT(element))
4064         continue;
4065
4066       if (CAN_CHANGE(element))
4067       {
4068         for (i = 0; i < element_info[element].num_change_pages; i++)
4069         {
4070           // check for player created from custom element as single target
4071           content = element_info[element].change_page[i].target_element;
4072           is_player = ELEM_IS_PLAYER(content);
4073
4074           if (is_player && (found_rating < 3 ||
4075                             (found_rating == 3 && element < found_element)))
4076           {
4077             start_x = x;
4078             start_y = y;
4079
4080             found_rating = 3;
4081             found_element = element;
4082           }
4083         }
4084       }
4085
4086       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4087       {
4088         // check for player created from custom element as explosion content
4089         content = element_info[element].content.e[xx][yy];
4090         is_player = ELEM_IS_PLAYER(content);
4091
4092         if (is_player && (found_rating < 2 ||
4093                           (found_rating == 2 && element < found_element)))
4094         {
4095           start_x = x + xx - 1;
4096           start_y = y + yy - 1;
4097
4098           found_rating = 2;
4099           found_element = element;
4100         }
4101
4102         if (!CAN_CHANGE(element))
4103           continue;
4104
4105         for (i = 0; i < element_info[element].num_change_pages; i++)
4106         {
4107           // check for player created from custom element as extended target
4108           content =
4109             element_info[element].change_page[i].target_content.e[xx][yy];
4110
4111           is_player = ELEM_IS_PLAYER(content);
4112
4113           if (is_player && (found_rating < 1 ||
4114                             (found_rating == 1 && element < found_element)))
4115           {
4116             start_x = x + xx - 1;
4117             start_y = y + yy - 1;
4118
4119             found_rating = 1;
4120             found_element = element;
4121           }
4122         }
4123       }
4124     }
4125
4126     scroll_x = SCROLL_POSITION_X(start_x);
4127     scroll_y = SCROLL_POSITION_Y(start_y);
4128   }
4129   else
4130   {
4131     scroll_x = SCROLL_POSITION_X(local_player->jx);
4132     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4133   }
4134
4135   // !!! FIX THIS (START) !!!
4136   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4137   {
4138     InitGameEngine_EM();
4139   }
4140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4141   {
4142     InitGameEngine_SP();
4143   }
4144   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4145   {
4146     InitGameEngine_MM();
4147   }
4148   else
4149   {
4150     DrawLevel(REDRAW_FIELD);
4151     DrawAllPlayers();
4152
4153     // after drawing the level, correct some elements
4154     if (game.timegate_time_left == 0)
4155       CloseAllOpenTimegates();
4156   }
4157
4158   // blit playfield from scroll buffer to normal back buffer for fading in
4159   BlitScreenToBitmap(backbuffer);
4160   // !!! FIX THIS (END) !!!
4161
4162   DrawMaskedBorder(fade_mask);
4163
4164   FadeIn(fade_mask);
4165
4166 #if 1
4167   // full screen redraw is required at this point in the following cases:
4168   // - special editor door undrawn when game was started from level editor
4169   // - drawing area (playfield) was changed and has to be removed completely
4170   redraw_mask = REDRAW_ALL;
4171   BackToFront();
4172 #endif
4173
4174   if (!game.restart_level)
4175   {
4176     // copy default game door content to main double buffer
4177
4178     // !!! CHECK AGAIN !!!
4179     SetPanelBackground();
4180     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4181     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4182   }
4183
4184   SetPanelBackground();
4185   SetDrawBackgroundMask(REDRAW_DOOR_1);
4186
4187   UpdateAndDisplayGameControlValues();
4188
4189   if (!game.restart_level)
4190   {
4191     UnmapGameButtons();
4192     UnmapTapeButtons();
4193
4194     FreeGameButtons();
4195     CreateGameButtons();
4196
4197     MapGameButtons();
4198     MapTapeButtons();
4199
4200     // copy actual game door content to door double buffer for OpenDoor()
4201     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4202
4203     OpenDoor(DOOR_OPEN_ALL);
4204
4205     KeyboardAutoRepeatOffUnlessAutoplay();
4206
4207 #if DEBUG_INIT_PLAYER
4208     DebugPrintPlayerStatus("Player status (final)");
4209 #endif
4210   }
4211
4212   UnmapAllGadgets();
4213
4214   MapGameButtons();
4215   MapTapeButtons();
4216
4217   if (!game.restart_level && !tape.playing)
4218   {
4219     LevelStats_incPlayed(level_nr);
4220
4221     SaveLevelSetup_SeriesInfo();
4222   }
4223
4224   game.restart_level = FALSE;
4225   game.restart_game_message = NULL;
4226   game.request_active = FALSE;
4227
4228   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4229     InitGameActions_MM();
4230
4231   SaveEngineSnapshotToListInitial();
4232
4233   if (!game.restart_level)
4234   {
4235     PlaySound(SND_GAME_STARTING);
4236
4237     if (setup.sound_music)
4238       PlayLevelMusic();
4239   }
4240 }
4241
4242 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4243                         int actual_player_x, int actual_player_y)
4244 {
4245   // this is used for non-R'n'D game engines to update certain engine values
4246
4247   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4248   {
4249     actual_player_x = correctLevelPosX_EM(actual_player_x);
4250     actual_player_y = correctLevelPosY_EM(actual_player_y);
4251   }
4252
4253   // needed to determine if sounds are played within the visible screen area
4254   scroll_x = actual_scroll_x;
4255   scroll_y = actual_scroll_y;
4256
4257   // needed to get player position for "follow finger" playing input method
4258   local_player->jx = actual_player_x;
4259   local_player->jy = actual_player_y;
4260 }
4261
4262 void InitMovDir(int x, int y)
4263 {
4264   int i, element = Feld[x][y];
4265   static int xy[4][2] =
4266   {
4267     {  0, +1 },
4268     { +1,  0 },
4269     {  0, -1 },
4270     { -1,  0 }
4271   };
4272   static int direction[3][4] =
4273   {
4274     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4275     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4276     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4277   };
4278
4279   switch (element)
4280   {
4281     case EL_BUG_RIGHT:
4282     case EL_BUG_UP:
4283     case EL_BUG_LEFT:
4284     case EL_BUG_DOWN:
4285       Feld[x][y] = EL_BUG;
4286       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4287       break;
4288
4289     case EL_SPACESHIP_RIGHT:
4290     case EL_SPACESHIP_UP:
4291     case EL_SPACESHIP_LEFT:
4292     case EL_SPACESHIP_DOWN:
4293       Feld[x][y] = EL_SPACESHIP;
4294       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4295       break;
4296
4297     case EL_BD_BUTTERFLY_RIGHT:
4298     case EL_BD_BUTTERFLY_UP:
4299     case EL_BD_BUTTERFLY_LEFT:
4300     case EL_BD_BUTTERFLY_DOWN:
4301       Feld[x][y] = EL_BD_BUTTERFLY;
4302       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4303       break;
4304
4305     case EL_BD_FIREFLY_RIGHT:
4306     case EL_BD_FIREFLY_UP:
4307     case EL_BD_FIREFLY_LEFT:
4308     case EL_BD_FIREFLY_DOWN:
4309       Feld[x][y] = EL_BD_FIREFLY;
4310       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4311       break;
4312
4313     case EL_PACMAN_RIGHT:
4314     case EL_PACMAN_UP:
4315     case EL_PACMAN_LEFT:
4316     case EL_PACMAN_DOWN:
4317       Feld[x][y] = EL_PACMAN;
4318       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4319       break;
4320
4321     case EL_YAMYAM_LEFT:
4322     case EL_YAMYAM_RIGHT:
4323     case EL_YAMYAM_UP:
4324     case EL_YAMYAM_DOWN:
4325       Feld[x][y] = EL_YAMYAM;
4326       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4327       break;
4328
4329     case EL_SP_SNIKSNAK:
4330       MovDir[x][y] = MV_UP;
4331       break;
4332
4333     case EL_SP_ELECTRON:
4334       MovDir[x][y] = MV_LEFT;
4335       break;
4336
4337     case EL_MOLE_LEFT:
4338     case EL_MOLE_RIGHT:
4339     case EL_MOLE_UP:
4340     case EL_MOLE_DOWN:
4341       Feld[x][y] = EL_MOLE;
4342       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4343       break;
4344
4345     default:
4346       if (IS_CUSTOM_ELEMENT(element))
4347       {
4348         struct ElementInfo *ei = &element_info[element];
4349         int move_direction_initial = ei->move_direction_initial;
4350         int move_pattern = ei->move_pattern;
4351
4352         if (move_direction_initial == MV_START_PREVIOUS)
4353         {
4354           if (MovDir[x][y] != MV_NONE)
4355             return;
4356
4357           move_direction_initial = MV_START_AUTOMATIC;
4358         }
4359
4360         if (move_direction_initial == MV_START_RANDOM)
4361           MovDir[x][y] = 1 << RND(4);
4362         else if (move_direction_initial & MV_ANY_DIRECTION)
4363           MovDir[x][y] = move_direction_initial;
4364         else if (move_pattern == MV_ALL_DIRECTIONS ||
4365                  move_pattern == MV_TURNING_LEFT ||
4366                  move_pattern == MV_TURNING_RIGHT ||
4367                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4368                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4369                  move_pattern == MV_TURNING_RANDOM)
4370           MovDir[x][y] = 1 << RND(4);
4371         else if (move_pattern == MV_HORIZONTAL)
4372           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4373         else if (move_pattern == MV_VERTICAL)
4374           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4375         else if (move_pattern & MV_ANY_DIRECTION)
4376           MovDir[x][y] = element_info[element].move_pattern;
4377         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4378                  move_pattern == MV_ALONG_RIGHT_SIDE)
4379         {
4380           // use random direction as default start direction
4381           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4382             MovDir[x][y] = 1 << RND(4);
4383
4384           for (i = 0; i < NUM_DIRECTIONS; i++)
4385           {
4386             int x1 = x + xy[i][0];
4387             int y1 = y + xy[i][1];
4388
4389             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4390             {
4391               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4392                 MovDir[x][y] = direction[0][i];
4393               else
4394                 MovDir[x][y] = direction[1][i];
4395
4396               break;
4397             }
4398           }
4399         }                
4400       }
4401       else
4402       {
4403         MovDir[x][y] = 1 << RND(4);
4404
4405         if (element != EL_BUG &&
4406             element != EL_SPACESHIP &&
4407             element != EL_BD_BUTTERFLY &&
4408             element != EL_BD_FIREFLY)
4409           break;
4410
4411         for (i = 0; i < NUM_DIRECTIONS; i++)
4412         {
4413           int x1 = x + xy[i][0];
4414           int y1 = y + xy[i][1];
4415
4416           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4417           {
4418             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4419             {
4420               MovDir[x][y] = direction[0][i];
4421               break;
4422             }
4423             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4424                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4425             {
4426               MovDir[x][y] = direction[1][i];
4427               break;
4428             }
4429           }
4430         }
4431       }
4432       break;
4433   }
4434
4435   GfxDir[x][y] = MovDir[x][y];
4436 }
4437
4438 void InitAmoebaNr(int x, int y)
4439 {
4440   int i;
4441   int group_nr = AmoebeNachbarNr(x, y);
4442
4443   if (group_nr == 0)
4444   {
4445     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4446     {
4447       if (AmoebaCnt[i] == 0)
4448       {
4449         group_nr = i;
4450         break;
4451       }
4452     }
4453   }
4454
4455   AmoebaNr[x][y] = group_nr;
4456   AmoebaCnt[group_nr]++;
4457   AmoebaCnt2[group_nr]++;
4458 }
4459
4460 static void LevelSolved(void)
4461 {
4462   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4463       local_player->players_still_needed > 0)
4464     return;
4465
4466   game.LevelSolved = TRUE;
4467
4468   local_player->GameOver = TRUE;
4469
4470   local_player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4471                                level.native_em_level->lev->score :
4472                                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4473                                game_mm.score :
4474                                local_player->score);
4475   local_player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4476                                 MM_HEALTH(game_mm.laser_overload_value) :
4477                                 local_player->health);
4478
4479   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4480   game.LevelSolved_CountingScore = local_player->score_final;
4481   game.LevelSolved_CountingHealth = local_player->health_final;
4482 }
4483
4484 void GameWon(void)
4485 {
4486   static int time_count_steps;
4487   static int time, time_final;
4488   static int score, score_final;
4489   static int health, health_final;
4490   static int game_over_delay_1 = 0;
4491   static int game_over_delay_2 = 0;
4492   static int game_over_delay_3 = 0;
4493   int game_over_delay_value_1 = 50;
4494   int game_over_delay_value_2 = 25;
4495   int game_over_delay_value_3 = 50;
4496
4497   if (!game.LevelSolved_GameWon)
4498   {
4499     int i;
4500
4501     // do not start end game actions before the player stops moving (to exit)
4502     if (local_player->MovPos)
4503       return;
4504
4505     game.LevelSolved_GameWon = TRUE;
4506     game.LevelSolved_SaveTape = tape.recording;
4507     game.LevelSolved_SaveScore = !tape.playing;
4508
4509     if (!tape.playing)
4510     {
4511       LevelStats_incSolved(level_nr);
4512
4513       SaveLevelSetup_SeriesInfo();
4514     }
4515
4516     if (tape.auto_play)         // tape might already be stopped here
4517       tape.auto_play_level_solved = TRUE;
4518
4519     TapeStop();
4520
4521     game_over_delay_1 = 0;
4522     game_over_delay_2 = 0;
4523     game_over_delay_3 = game_over_delay_value_3;
4524
4525     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4526     score = score_final = local_player->score_final;
4527     health = health_final = local_player->health_final;
4528
4529     if (level.score[SC_TIME_BONUS] > 0)
4530     {
4531       if (TimeLeft > 0)
4532       {
4533         time_final = 0;
4534         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4535       }
4536       else if (game.no_time_limit && TimePlayed < 999)
4537       {
4538         time_final = 999;
4539         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4540       }
4541
4542       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4543
4544       game_over_delay_1 = game_over_delay_value_1;
4545
4546       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4547       {
4548         health_final = 0;
4549         score_final += health * level.score[SC_TIME_BONUS];
4550
4551         game_over_delay_2 = game_over_delay_value_2;
4552       }
4553
4554       local_player->score_final = score_final;
4555       local_player->health_final = health_final;
4556     }
4557
4558     if (level_editor_test_game)
4559     {
4560       time = time_final;
4561       score = score_final;
4562
4563       game.LevelSolved_CountingTime = time;
4564       game.LevelSolved_CountingScore = score;
4565
4566       game_panel_controls[GAME_PANEL_TIME].value = time;
4567       game_panel_controls[GAME_PANEL_SCORE].value = score;
4568
4569       DisplayGameControlValues();
4570     }
4571
4572     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4573     {
4574       if (ExitX >= 0 && ExitY >= 0)     // local player has left the level
4575       {
4576         // close exit door after last player
4577         if ((AllPlayersGone &&
4578              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4579               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4580               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4581             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4582             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4583         {
4584           int element = Feld[ExitX][ExitY];
4585
4586           Feld[ExitX][ExitY] =
4587             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4588              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4589              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4590              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4591              EL_EM_STEEL_EXIT_CLOSING);
4592
4593           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4594         }
4595
4596         // player disappears
4597         DrawLevelField(ExitX, ExitY);
4598       }
4599
4600       for (i = 0; i < MAX_PLAYERS; i++)
4601       {
4602         struct PlayerInfo *player = &stored_player[i];
4603
4604         if (player->present)
4605         {
4606           RemovePlayer(player);
4607
4608           // player disappears
4609           DrawLevelField(player->jx, player->jy);
4610         }
4611       }
4612     }
4613
4614     PlaySound(SND_GAME_WINNING);
4615   }
4616
4617   if (game_over_delay_1 > 0)
4618   {
4619     game_over_delay_1--;
4620
4621     return;
4622   }
4623
4624   if (time != time_final)
4625   {
4626     int time_to_go = ABS(time_final - time);
4627     int time_count_dir = (time < time_final ? +1 : -1);
4628
4629     if (time_to_go < time_count_steps)
4630       time_count_steps = 1;
4631
4632     time  += time_count_steps * time_count_dir;
4633     score += time_count_steps * level.score[SC_TIME_BONUS];
4634
4635     game.LevelSolved_CountingTime = time;
4636     game.LevelSolved_CountingScore = score;
4637
4638     game_panel_controls[GAME_PANEL_TIME].value = time;
4639     game_panel_controls[GAME_PANEL_SCORE].value = score;
4640
4641     DisplayGameControlValues();
4642
4643     if (time == time_final)
4644       StopSound(SND_GAME_LEVELTIME_BONUS);
4645     else if (setup.sound_loops)
4646       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4647     else
4648       PlaySound(SND_GAME_LEVELTIME_BONUS);
4649
4650     return;
4651   }
4652
4653   if (game_over_delay_2 > 0)
4654   {
4655     game_over_delay_2--;
4656
4657     return;
4658   }
4659
4660   if (health != health_final)
4661   {
4662     int health_count_dir = (health < health_final ? +1 : -1);
4663
4664     health += health_count_dir;
4665     score  += level.score[SC_TIME_BONUS];
4666
4667     game.LevelSolved_CountingHealth = health;
4668     game.LevelSolved_CountingScore = score;
4669
4670     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4671     game_panel_controls[GAME_PANEL_SCORE].value = score;
4672
4673     DisplayGameControlValues();
4674
4675     if (health == health_final)
4676       StopSound(SND_GAME_LEVELTIME_BONUS);
4677     else if (setup.sound_loops)
4678       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4679     else
4680       PlaySound(SND_GAME_LEVELTIME_BONUS);
4681
4682     return;
4683   }
4684
4685   game.panel.active = FALSE;
4686
4687   if (game_over_delay_3 > 0)
4688   {
4689     game_over_delay_3--;
4690
4691     return;
4692   }
4693
4694   GameEnd();
4695 }
4696
4697 void GameEnd(void)
4698 {
4699   // used instead of "level_nr" (needed for network games)
4700   int last_level_nr = levelset.level_nr;
4701   int hi_pos;
4702
4703   game.LevelSolved_GameEnd = TRUE;
4704
4705   if (game.LevelSolved_SaveTape)
4706   {
4707     // make sure that request dialog to save tape does not open door again
4708     if (!global.use_envelope_request)
4709       CloseDoor(DOOR_CLOSE_1);
4710
4711     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4712   }
4713
4714   // if no tape is to be saved, close both doors simultaneously
4715   CloseDoor(DOOR_CLOSE_ALL);
4716
4717   if (level_editor_test_game)
4718   {
4719     SetGameStatus(GAME_MODE_MAIN);
4720
4721     DrawMainMenu();
4722
4723     return;
4724   }
4725
4726   if (!game.LevelSolved_SaveScore)
4727   {
4728     SetGameStatus(GAME_MODE_MAIN);
4729
4730     DrawMainMenu();
4731
4732     return;
4733   }
4734
4735   if (level_nr == leveldir_current->handicap_level)
4736   {
4737     leveldir_current->handicap_level++;
4738
4739     SaveLevelSetup_SeriesInfo();
4740   }
4741
4742   if (setup.increment_levels &&
4743       level_nr < leveldir_current->last_level &&
4744       !network_playing)
4745   {
4746     level_nr++;         // advance to next level
4747     TapeErase();        // start with empty tape
4748
4749     if (setup.auto_play_next_level)
4750     {
4751       LoadLevel(level_nr);
4752
4753       SaveLevelSetup_SeriesInfo();
4754     }
4755   }
4756
4757   hi_pos = NewHiScore(last_level_nr);
4758
4759   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4760   {
4761     SetGameStatus(GAME_MODE_SCORES);
4762
4763     DrawHallOfFame(last_level_nr, hi_pos);
4764   }
4765   else if (setup.auto_play_next_level && setup.increment_levels &&
4766            last_level_nr < leveldir_current->last_level &&
4767            !network_playing)
4768   {
4769     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4770   }
4771   else
4772   {
4773     SetGameStatus(GAME_MODE_MAIN);
4774
4775     DrawMainMenu();
4776   }
4777 }
4778
4779 int NewHiScore(int level_nr)
4780 {
4781   int k, l;
4782   int position = -1;
4783   boolean one_score_entry_per_name = !program.many_scores_per_name;
4784
4785   LoadScore(level_nr);
4786
4787   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4788       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4789     return -1;
4790
4791   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4792   {
4793     if (local_player->score_final > highscore[k].Score)
4794     {
4795       // player has made it to the hall of fame
4796
4797       if (k < MAX_SCORE_ENTRIES - 1)
4798       {
4799         int m = MAX_SCORE_ENTRIES - 1;
4800
4801         if (one_score_entry_per_name)
4802         {
4803           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4804             if (strEqual(setup.player_name, highscore[l].Name))
4805               m = l;
4806
4807           if (m == k)   // player's new highscore overwrites his old one
4808             goto put_into_list;
4809         }
4810
4811         for (l = m; l > k; l--)
4812         {
4813           strcpy(highscore[l].Name, highscore[l - 1].Name);
4814           highscore[l].Score = highscore[l - 1].Score;
4815         }
4816       }
4817
4818       put_into_list:
4819
4820       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4821       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4822       highscore[k].Score = local_player->score_final; 
4823       position = k;
4824
4825       break;
4826     }
4827     else if (one_score_entry_per_name &&
4828              !strncmp(setup.player_name, highscore[k].Name,
4829                       MAX_PLAYER_NAME_LEN))
4830       break;    // player already there with a higher score
4831   }
4832
4833   if (position >= 0) 
4834     SaveScore(level_nr);
4835
4836   return position;
4837 }
4838
4839 static int getElementMoveStepsizeExt(int x, int y, int direction)
4840 {
4841   int element = Feld[x][y];
4842   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4843   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4844   int horiz_move = (dx != 0);
4845   int sign = (horiz_move ? dx : dy);
4846   int step = sign * element_info[element].move_stepsize;
4847
4848   // special values for move stepsize for spring and things on conveyor belt
4849   if (horiz_move)
4850   {
4851     if (CAN_FALL(element) &&
4852         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4853       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4854     else if (element == EL_SPRING)
4855       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4856   }
4857
4858   return step;
4859 }
4860
4861 static int getElementMoveStepsize(int x, int y)
4862 {
4863   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4864 }
4865
4866 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4867 {
4868   if (player->GfxAction != action || player->GfxDir != dir)
4869   {
4870     player->GfxAction = action;
4871     player->GfxDir = dir;
4872     player->Frame = 0;
4873     player->StepFrame = 0;
4874   }
4875 }
4876
4877 static void ResetGfxFrame(int x, int y)
4878 {
4879   // profiling showed that "autotest" spends 10~20% of its time in this function
4880   if (DrawingDeactivatedField())
4881     return;
4882
4883   int element = Feld[x][y];
4884   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4885
4886   if (graphic_info[graphic].anim_global_sync)
4887     GfxFrame[x][y] = FrameCounter;
4888   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4889     GfxFrame[x][y] = CustomValue[x][y];
4890   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4891     GfxFrame[x][y] = element_info[element].collect_score;
4892   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4893     GfxFrame[x][y] = ChangeDelay[x][y];
4894 }
4895
4896 static void ResetGfxAnimation(int x, int y)
4897 {
4898   GfxAction[x][y] = ACTION_DEFAULT;
4899   GfxDir[x][y] = MovDir[x][y];
4900   GfxFrame[x][y] = 0;
4901
4902   ResetGfxFrame(x, y);
4903 }
4904
4905 static void ResetRandomAnimationValue(int x, int y)
4906 {
4907   GfxRandom[x][y] = INIT_GFX_RANDOM();
4908 }
4909
4910 static void InitMovingField(int x, int y, int direction)
4911 {
4912   int element = Feld[x][y];
4913   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4914   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4915   int newx = x + dx;
4916   int newy = y + dy;
4917   boolean is_moving_before, is_moving_after;
4918
4919   // check if element was/is moving or being moved before/after mode change
4920   is_moving_before = (WasJustMoving[x][y] != 0);
4921   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4922
4923   // reset animation only for moving elements which change direction of moving
4924   // or which just started or stopped moving
4925   // (else CEs with property "can move" / "not moving" are reset each frame)
4926   if (is_moving_before != is_moving_after ||
4927       direction != MovDir[x][y])
4928     ResetGfxAnimation(x, y);
4929
4930   MovDir[x][y] = direction;
4931   GfxDir[x][y] = direction;
4932
4933   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4934                      direction == MV_DOWN && CAN_FALL(element) ?
4935                      ACTION_FALLING : ACTION_MOVING);
4936
4937   // this is needed for CEs with property "can move" / "not moving"
4938
4939   if (is_moving_after)
4940   {
4941     if (Feld[newx][newy] == EL_EMPTY)
4942       Feld[newx][newy] = EL_BLOCKED;
4943
4944     MovDir[newx][newy] = MovDir[x][y];
4945
4946     CustomValue[newx][newy] = CustomValue[x][y];
4947
4948     GfxFrame[newx][newy] = GfxFrame[x][y];
4949     GfxRandom[newx][newy] = GfxRandom[x][y];
4950     GfxAction[newx][newy] = GfxAction[x][y];
4951     GfxDir[newx][newy] = GfxDir[x][y];
4952   }
4953 }
4954
4955 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4956 {
4957   int direction = MovDir[x][y];
4958   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4959   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4960
4961   *goes_to_x = newx;
4962   *goes_to_y = newy;
4963 }
4964
4965 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4966 {
4967   int oldx = x, oldy = y;
4968   int direction = MovDir[x][y];
4969
4970   if (direction == MV_LEFT)
4971     oldx++;
4972   else if (direction == MV_RIGHT)
4973     oldx--;
4974   else if (direction == MV_UP)
4975     oldy++;
4976   else if (direction == MV_DOWN)
4977     oldy--;
4978
4979   *comes_from_x = oldx;
4980   *comes_from_y = oldy;
4981 }
4982
4983 static int MovingOrBlocked2Element(int x, int y)
4984 {
4985   int element = Feld[x][y];
4986
4987   if (element == EL_BLOCKED)
4988   {
4989     int oldx, oldy;
4990
4991     Blocked2Moving(x, y, &oldx, &oldy);
4992     return Feld[oldx][oldy];
4993   }
4994   else
4995     return element;
4996 }
4997
4998 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4999 {
5000   // like MovingOrBlocked2Element(), but if element is moving
5001   // and (x,y) is the field the moving element is just leaving,
5002   // return EL_BLOCKED instead of the element value
5003   int element = Feld[x][y];
5004
5005   if (IS_MOVING(x, y))
5006   {
5007     if (element == EL_BLOCKED)
5008     {
5009       int oldx, oldy;
5010
5011       Blocked2Moving(x, y, &oldx, &oldy);
5012       return Feld[oldx][oldy];
5013     }
5014     else
5015       return EL_BLOCKED;
5016   }
5017   else
5018     return element;
5019 }
5020
5021 static void RemoveField(int x, int y)
5022 {
5023   Feld[x][y] = EL_EMPTY;
5024
5025   MovPos[x][y] = 0;
5026   MovDir[x][y] = 0;
5027   MovDelay[x][y] = 0;
5028
5029   CustomValue[x][y] = 0;
5030
5031   AmoebaNr[x][y] = 0;
5032   ChangeDelay[x][y] = 0;
5033   ChangePage[x][y] = -1;
5034   Pushed[x][y] = FALSE;
5035
5036   GfxElement[x][y] = EL_UNDEFINED;
5037   GfxAction[x][y] = ACTION_DEFAULT;
5038   GfxDir[x][y] = MV_NONE;
5039 }
5040
5041 static void RemoveMovingField(int x, int y)
5042 {
5043   int oldx = x, oldy = y, newx = x, newy = y;
5044   int element = Feld[x][y];
5045   int next_element = EL_UNDEFINED;
5046
5047   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5048     return;
5049
5050   if (IS_MOVING(x, y))
5051   {
5052     Moving2Blocked(x, y, &newx, &newy);
5053
5054     if (Feld[newx][newy] != EL_BLOCKED)
5055     {
5056       // element is moving, but target field is not free (blocked), but
5057       // already occupied by something different (example: acid pool);
5058       // in this case, only remove the moving field, but not the target
5059
5060       RemoveField(oldx, oldy);
5061
5062       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5063
5064       TEST_DrawLevelField(oldx, oldy);
5065
5066       return;
5067     }
5068   }
5069   else if (element == EL_BLOCKED)
5070   {
5071     Blocked2Moving(x, y, &oldx, &oldy);
5072     if (!IS_MOVING(oldx, oldy))
5073       return;
5074   }
5075
5076   if (element == EL_BLOCKED &&
5077       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5078        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5079        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5080        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5081        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5082        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5083     next_element = get_next_element(Feld[oldx][oldy]);
5084
5085   RemoveField(oldx, oldy);
5086   RemoveField(newx, newy);
5087
5088   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5089
5090   if (next_element != EL_UNDEFINED)
5091     Feld[oldx][oldy] = next_element;
5092
5093   TEST_DrawLevelField(oldx, oldy);
5094   TEST_DrawLevelField(newx, newy);
5095 }
5096
5097 void DrawDynamite(int x, int y)
5098 {
5099   int sx = SCREENX(x), sy = SCREENY(y);
5100   int graphic = el2img(Feld[x][y]);
5101   int frame;
5102
5103   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5104     return;
5105
5106   if (IS_WALKABLE_INSIDE(Back[x][y]))
5107     return;
5108
5109   if (Back[x][y])
5110     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5111   else if (Store[x][y])
5112     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5113
5114   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5115
5116   if (Back[x][y] || Store[x][y])
5117     DrawGraphicThruMask(sx, sy, graphic, frame);
5118   else
5119     DrawGraphic(sx, sy, graphic, frame);
5120 }
5121
5122 static void CheckDynamite(int x, int y)
5123 {
5124   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5125   {
5126     MovDelay[x][y]--;
5127
5128     if (MovDelay[x][y] != 0)
5129     {
5130       DrawDynamite(x, y);
5131       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5132
5133       return;
5134     }
5135   }
5136
5137   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5138
5139   Bang(x, y);
5140 }
5141
5142 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5143 {
5144   boolean num_checked_players = 0;
5145   int i;
5146
5147   for (i = 0; i < MAX_PLAYERS; i++)
5148   {
5149     if (stored_player[i].active)
5150     {
5151       int sx = stored_player[i].jx;
5152       int sy = stored_player[i].jy;
5153
5154       if (num_checked_players == 0)
5155       {
5156         *sx1 = *sx2 = sx;
5157         *sy1 = *sy2 = sy;
5158       }
5159       else
5160       {
5161         *sx1 = MIN(*sx1, sx);
5162         *sy1 = MIN(*sy1, sy);
5163         *sx2 = MAX(*sx2, sx);
5164         *sy2 = MAX(*sy2, sy);
5165       }
5166
5167       num_checked_players++;
5168     }
5169   }
5170 }
5171
5172 static boolean checkIfAllPlayersFitToScreen_RND(void)
5173 {
5174   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5175
5176   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5177
5178   return (sx2 - sx1 < SCR_FIELDX &&
5179           sy2 - sy1 < SCR_FIELDY);
5180 }
5181
5182 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5183 {
5184   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5185
5186   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5187
5188   *sx = (sx1 + sx2) / 2;
5189   *sy = (sy1 + sy2) / 2;
5190 }
5191
5192 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5193                                boolean center_screen, boolean quick_relocation)
5194 {
5195   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5196   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5197   boolean no_delay = (tape.warp_forward);
5198   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5199   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5200   int new_scroll_x, new_scroll_y;
5201
5202   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5203   {
5204     // case 1: quick relocation inside visible screen (without scrolling)
5205
5206     RedrawPlayfield();
5207
5208     return;
5209   }
5210
5211   if (!level.shifted_relocation || center_screen)
5212   {
5213     // relocation _with_ centering of screen
5214
5215     new_scroll_x = SCROLL_POSITION_X(x);
5216     new_scroll_y = SCROLL_POSITION_Y(y);
5217   }
5218   else
5219   {
5220     // relocation _without_ centering of screen
5221
5222     int center_scroll_x = SCROLL_POSITION_X(old_x);
5223     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5224     int offset_x = x + (scroll_x - center_scroll_x);
5225     int offset_y = y + (scroll_y - center_scroll_y);
5226
5227     // for new screen position, apply previous offset to center position
5228     new_scroll_x = SCROLL_POSITION_X(offset_x);
5229     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5230   }
5231
5232   if (quick_relocation)
5233   {
5234     // case 2: quick relocation (redraw without visible scrolling)
5235
5236     scroll_x = new_scroll_x;
5237     scroll_y = new_scroll_y;
5238
5239     RedrawPlayfield();
5240
5241     return;
5242   }
5243
5244   // case 3: visible relocation (with scrolling to new position)
5245
5246   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5247
5248   SetVideoFrameDelay(wait_delay_value);
5249
5250   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5251   {
5252     int dx = 0, dy = 0;
5253     int fx = FX, fy = FY;
5254
5255     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5256     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5257
5258     if (dx == 0 && dy == 0)             // no scrolling needed at all
5259       break;
5260
5261     scroll_x -= dx;
5262     scroll_y -= dy;
5263
5264     fx += dx * TILEX / 2;
5265     fy += dy * TILEY / 2;
5266
5267     ScrollLevel(dx, dy);
5268     DrawAllPlayers();
5269
5270     // scroll in two steps of half tile size to make things smoother
5271     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5272
5273     // scroll second step to align at full tile size
5274     BlitScreenToBitmap(window);
5275   }
5276
5277   DrawAllPlayers();
5278   BackToFront();
5279
5280   SetVideoFrameDelay(frame_delay_value_old);
5281 }
5282
5283 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5284 {
5285   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5286   int player_nr = GET_PLAYER_NR(el_player);
5287   struct PlayerInfo *player = &stored_player[player_nr];
5288   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5289   boolean no_delay = (tape.warp_forward);
5290   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5291   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5292   int old_jx = player->jx;
5293   int old_jy = player->jy;
5294   int old_element = Feld[old_jx][old_jy];
5295   int element = Feld[jx][jy];
5296   boolean player_relocated = (old_jx != jx || old_jy != jy);
5297
5298   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5299   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5300   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5301   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5302   int leave_side_horiz = move_dir_horiz;
5303   int leave_side_vert  = move_dir_vert;
5304   int enter_side = enter_side_horiz | enter_side_vert;
5305   int leave_side = leave_side_horiz | leave_side_vert;
5306
5307   if (player->GameOver)         // do not reanimate dead player
5308     return;
5309
5310   if (!player_relocated)        // no need to relocate the player
5311     return;
5312
5313   if (IS_PLAYER(jx, jy))        // player already placed at new position
5314   {
5315     RemoveField(jx, jy);        // temporarily remove newly placed player
5316     DrawLevelField(jx, jy);
5317   }
5318
5319   if (player->present)
5320   {
5321     while (player->MovPos)
5322     {
5323       ScrollPlayer(player, SCROLL_GO_ON);
5324       ScrollScreen(NULL, SCROLL_GO_ON);
5325
5326       AdvanceFrameAndPlayerCounters(player->index_nr);
5327
5328       DrawPlayer(player);
5329
5330       BackToFront_WithFrameDelay(wait_delay_value);
5331     }
5332
5333     DrawPlayer(player);         // needed here only to cleanup last field
5334     DrawLevelField(player->jx, player->jy);     // remove player graphic
5335
5336     player->is_moving = FALSE;
5337   }
5338
5339   if (IS_CUSTOM_ELEMENT(old_element))
5340     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5341                                CE_LEFT_BY_PLAYER,
5342                                player->index_bit, leave_side);
5343
5344   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5345                                       CE_PLAYER_LEAVES_X,
5346                                       player->index_bit, leave_side);
5347
5348   Feld[jx][jy] = el_player;
5349   InitPlayerField(jx, jy, el_player, TRUE);
5350
5351   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5352      possible that the relocation target field did not contain a player element,
5353      but a walkable element, to which the new player was relocated -- in this
5354      case, restore that (already initialized!) element on the player field */
5355   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5356   {
5357     Feld[jx][jy] = element;     // restore previously existing element
5358   }
5359
5360   // only visually relocate centered player
5361   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5362                      FALSE, level.instant_relocation);
5363
5364   TestIfPlayerTouchesBadThing(jx, jy);
5365   TestIfPlayerTouchesCustomElement(jx, jy);
5366
5367   if (IS_CUSTOM_ELEMENT(element))
5368     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5369                                player->index_bit, enter_side);
5370
5371   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5372                                       player->index_bit, enter_side);
5373
5374   if (player->is_switching)
5375   {
5376     /* ensure that relocation while still switching an element does not cause
5377        a new element to be treated as also switched directly after relocation
5378        (this is important for teleporter switches that teleport the player to
5379        a place where another teleporter switch is in the same direction, which
5380        would then incorrectly be treated as immediately switched before the
5381        direction key that caused the switch was released) */
5382
5383     player->switch_x += jx - old_jx;
5384     player->switch_y += jy - old_jy;
5385   }
5386 }
5387
5388 static void Explode(int ex, int ey, int phase, int mode)
5389 {
5390   int x, y;
5391   int last_phase;
5392   int border_element;
5393
5394   // !!! eliminate this variable !!!
5395   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5396
5397   if (game.explosions_delayed)
5398   {
5399     ExplodeField[ex][ey] = mode;
5400     return;
5401   }
5402
5403   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5404   {
5405     int center_element = Feld[ex][ey];
5406     int artwork_element, explosion_element;     // set these values later
5407
5408     // remove things displayed in background while burning dynamite
5409     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5410       Back[ex][ey] = 0;
5411
5412     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5413     {
5414       // put moving element to center field (and let it explode there)
5415       center_element = MovingOrBlocked2Element(ex, ey);
5416       RemoveMovingField(ex, ey);
5417       Feld[ex][ey] = center_element;
5418     }
5419
5420     // now "center_element" is finally determined -- set related values now
5421     artwork_element = center_element;           // for custom player artwork
5422     explosion_element = center_element;         // for custom player artwork
5423
5424     if (IS_PLAYER(ex, ey))
5425     {
5426       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5427
5428       artwork_element = stored_player[player_nr].artwork_element;
5429
5430       if (level.use_explosion_element[player_nr])
5431       {
5432         explosion_element = level.explosion_element[player_nr];
5433         artwork_element = explosion_element;
5434       }
5435     }
5436
5437     if (mode == EX_TYPE_NORMAL ||
5438         mode == EX_TYPE_CENTER ||
5439         mode == EX_TYPE_CROSS)
5440       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5441
5442     last_phase = element_info[explosion_element].explosion_delay + 1;
5443
5444     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5445     {
5446       int xx = x - ex + 1;
5447       int yy = y - ey + 1;
5448       int element;
5449
5450       if (!IN_LEV_FIELD(x, y) ||
5451           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5452           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5453         continue;
5454
5455       element = Feld[x][y];
5456
5457       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5458       {
5459         element = MovingOrBlocked2Element(x, y);
5460
5461         if (!IS_EXPLOSION_PROOF(element))
5462           RemoveMovingField(x, y);
5463       }
5464
5465       // indestructible elements can only explode in center (but not flames)
5466       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5467                                            mode == EX_TYPE_BORDER)) ||
5468           element == EL_FLAMES)
5469         continue;
5470
5471       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5472          behaviour, for example when touching a yamyam that explodes to rocks
5473          with active deadly shield, a rock is created under the player !!! */
5474       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5475 #if 0
5476       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5477           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5478            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5479 #else
5480       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5481 #endif
5482       {
5483         if (IS_ACTIVE_BOMB(element))
5484         {
5485           // re-activate things under the bomb like gate or penguin
5486           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5487           Back[x][y] = 0;
5488         }
5489
5490         continue;
5491       }
5492
5493       // save walkable background elements while explosion on same tile
5494       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5495           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5496         Back[x][y] = element;
5497
5498       // ignite explodable elements reached by other explosion
5499       if (element == EL_EXPLOSION)
5500         element = Store2[x][y];
5501
5502       if (AmoebaNr[x][y] &&
5503           (element == EL_AMOEBA_FULL ||
5504            element == EL_BD_AMOEBA ||
5505            element == EL_AMOEBA_GROWING))
5506       {
5507         AmoebaCnt[AmoebaNr[x][y]]--;
5508         AmoebaCnt2[AmoebaNr[x][y]]--;
5509       }
5510
5511       RemoveField(x, y);
5512
5513       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5514       {
5515         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5516
5517         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5518
5519         if (PLAYERINFO(ex, ey)->use_murphy)
5520           Store[x][y] = EL_EMPTY;
5521       }
5522
5523       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5524       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5525       else if (ELEM_IS_PLAYER(center_element))
5526         Store[x][y] = EL_EMPTY;
5527       else if (center_element == EL_YAMYAM)
5528         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5529       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5530         Store[x][y] = element_info[center_element].content.e[xx][yy];
5531 #if 1
5532       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5533       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5534       // otherwise) -- FIX THIS !!!
5535       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5536         Store[x][y] = element_info[element].content.e[1][1];
5537 #else
5538       else if (!CAN_EXPLODE(element))
5539         Store[x][y] = element_info[element].content.e[1][1];
5540 #endif
5541       else
5542         Store[x][y] = EL_EMPTY;
5543
5544       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5545           center_element == EL_AMOEBA_TO_DIAMOND)
5546         Store2[x][y] = element;
5547
5548       Feld[x][y] = EL_EXPLOSION;
5549       GfxElement[x][y] = artwork_element;
5550
5551       ExplodePhase[x][y] = 1;
5552       ExplodeDelay[x][y] = last_phase;
5553
5554       Stop[x][y] = TRUE;
5555     }
5556
5557     if (center_element == EL_YAMYAM)
5558       game.yamyam_content_nr =
5559         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5560
5561     return;
5562   }
5563
5564   if (Stop[ex][ey])
5565     return;
5566
5567   x = ex;
5568   y = ey;
5569
5570   if (phase == 1)
5571     GfxFrame[x][y] = 0;         // restart explosion animation
5572
5573   last_phase = ExplodeDelay[x][y];
5574
5575   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5576
5577   // this can happen if the player leaves an explosion just in time
5578   if (GfxElement[x][y] == EL_UNDEFINED)
5579     GfxElement[x][y] = EL_EMPTY;
5580
5581   border_element = Store2[x][y];
5582   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5583     border_element = StorePlayer[x][y];
5584
5585   if (phase == element_info[border_element].ignition_delay ||
5586       phase == last_phase)
5587   {
5588     boolean border_explosion = FALSE;
5589
5590     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5591         !PLAYER_EXPLOSION_PROTECTED(x, y))
5592     {
5593       KillPlayerUnlessExplosionProtected(x, y);
5594       border_explosion = TRUE;
5595     }
5596     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5597     {
5598       Feld[x][y] = Store2[x][y];
5599       Store2[x][y] = 0;
5600       Bang(x, y);
5601       border_explosion = TRUE;
5602     }
5603     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5604     {
5605       AmoebeUmwandeln(x, y);
5606       Store2[x][y] = 0;
5607       border_explosion = TRUE;
5608     }
5609
5610     // if an element just explodes due to another explosion (chain-reaction),
5611     // do not immediately end the new explosion when it was the last frame of
5612     // the explosion (as it would be done in the following "if"-statement!)
5613     if (border_explosion && phase == last_phase)
5614       return;
5615   }
5616
5617   if (phase == last_phase)
5618   {
5619     int element;
5620
5621     element = Feld[x][y] = Store[x][y];
5622     Store[x][y] = Store2[x][y] = 0;
5623     GfxElement[x][y] = EL_UNDEFINED;
5624
5625     // player can escape from explosions and might therefore be still alive
5626     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5627         element <= EL_PLAYER_IS_EXPLODING_4)
5628     {
5629       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5630       int explosion_element = EL_PLAYER_1 + player_nr;
5631       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5632       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5633
5634       if (level.use_explosion_element[player_nr])
5635         explosion_element = level.explosion_element[player_nr];
5636
5637       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5638                     element_info[explosion_element].content.e[xx][yy]);
5639     }
5640
5641     // restore probably existing indestructible background element
5642     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5643       element = Feld[x][y] = Back[x][y];
5644     Back[x][y] = 0;
5645
5646     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5647     GfxDir[x][y] = MV_NONE;
5648     ChangeDelay[x][y] = 0;
5649     ChangePage[x][y] = -1;
5650
5651     CustomValue[x][y] = 0;
5652
5653     InitField_WithBug2(x, y, FALSE);
5654
5655     TEST_DrawLevelField(x, y);
5656
5657     TestIfElementTouchesCustomElement(x, y);
5658
5659     if (GFX_CRUMBLED(element))
5660       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5661
5662     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5663       StorePlayer[x][y] = 0;
5664
5665     if (ELEM_IS_PLAYER(element))
5666       RelocatePlayer(x, y, element);
5667   }
5668   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5669   {
5670     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5671     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5672
5673     if (phase == delay)
5674       TEST_DrawLevelFieldCrumbled(x, y);
5675
5676     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5677     {
5678       DrawLevelElement(x, y, Back[x][y]);
5679       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5680     }
5681     else if (IS_WALKABLE_UNDER(Back[x][y]))
5682     {
5683       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5684       DrawLevelElementThruMask(x, y, Back[x][y]);
5685     }
5686     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5687       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5688   }
5689 }
5690
5691 static void DynaExplode(int ex, int ey)
5692 {
5693   int i, j;
5694   int dynabomb_element = Feld[ex][ey];
5695   int dynabomb_size = 1;
5696   boolean dynabomb_xl = FALSE;
5697   struct PlayerInfo *player;
5698   static int xy[4][2] =
5699   {
5700     { 0, -1 },
5701     { -1, 0 },
5702     { +1, 0 },
5703     { 0, +1 }
5704   };
5705
5706   if (IS_ACTIVE_BOMB(dynabomb_element))
5707   {
5708     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5709     dynabomb_size = player->dynabomb_size;
5710     dynabomb_xl = player->dynabomb_xl;
5711     player->dynabombs_left++;
5712   }
5713
5714   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5715
5716   for (i = 0; i < NUM_DIRECTIONS; i++)
5717   {
5718     for (j = 1; j <= dynabomb_size; j++)
5719     {
5720       int x = ex + j * xy[i][0];
5721       int y = ey + j * xy[i][1];
5722       int element;
5723
5724       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5725         break;
5726
5727       element = Feld[x][y];
5728
5729       // do not restart explosions of fields with active bombs
5730       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5731         continue;
5732
5733       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5734
5735       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5736           !IS_DIGGABLE(element) && !dynabomb_xl)
5737         break;
5738     }
5739   }
5740 }
5741
5742 void Bang(int x, int y)
5743 {
5744   int element = MovingOrBlocked2Element(x, y);
5745   int explosion_type = EX_TYPE_NORMAL;
5746
5747   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5748   {
5749     struct PlayerInfo *player = PLAYERINFO(x, y);
5750
5751     element = Feld[x][y] = player->initial_element;
5752
5753     if (level.use_explosion_element[player->index_nr])
5754     {
5755       int explosion_element = level.explosion_element[player->index_nr];
5756
5757       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5758         explosion_type = EX_TYPE_CROSS;
5759       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5760         explosion_type = EX_TYPE_CENTER;
5761     }
5762   }
5763
5764   switch (element)
5765   {
5766     case EL_BUG:
5767     case EL_SPACESHIP:
5768     case EL_BD_BUTTERFLY:
5769     case EL_BD_FIREFLY:
5770     case EL_YAMYAM:
5771     case EL_DARK_YAMYAM:
5772     case EL_ROBOT:
5773     case EL_PACMAN:
5774     case EL_MOLE:
5775       RaiseScoreElement(element);
5776       break;
5777
5778     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5779     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5780     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5781     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5782     case EL_DYNABOMB_INCREASE_NUMBER:
5783     case EL_DYNABOMB_INCREASE_SIZE:
5784     case EL_DYNABOMB_INCREASE_POWER:
5785       explosion_type = EX_TYPE_DYNA;
5786       break;
5787
5788     case EL_DC_LANDMINE:
5789       explosion_type = EX_TYPE_CENTER;
5790       break;
5791
5792     case EL_PENGUIN:
5793     case EL_LAMP:
5794     case EL_LAMP_ACTIVE:
5795     case EL_AMOEBA_TO_DIAMOND:
5796       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5797         explosion_type = EX_TYPE_CENTER;
5798       break;
5799
5800     default:
5801       if (element_info[element].explosion_type == EXPLODES_CROSS)
5802         explosion_type = EX_TYPE_CROSS;
5803       else if (element_info[element].explosion_type == EXPLODES_1X1)
5804         explosion_type = EX_TYPE_CENTER;
5805       break;
5806   }
5807
5808   if (explosion_type == EX_TYPE_DYNA)
5809     DynaExplode(x, y);
5810   else
5811     Explode(x, y, EX_PHASE_START, explosion_type);
5812
5813   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5814 }
5815
5816 static void SplashAcid(int x, int y)
5817 {
5818   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5819       (!IN_LEV_FIELD(x - 1, y - 2) ||
5820        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5821     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5822
5823   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5824       (!IN_LEV_FIELD(x + 1, y - 2) ||
5825        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5826     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5827
5828   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5829 }
5830
5831 static void InitBeltMovement(void)
5832 {
5833   static int belt_base_element[4] =
5834   {
5835     EL_CONVEYOR_BELT_1_LEFT,
5836     EL_CONVEYOR_BELT_2_LEFT,
5837     EL_CONVEYOR_BELT_3_LEFT,
5838     EL_CONVEYOR_BELT_4_LEFT
5839   };
5840   static int belt_base_active_element[4] =
5841   {
5842     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5843     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5844     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5845     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5846   };
5847
5848   int x, y, i, j;
5849
5850   // set frame order for belt animation graphic according to belt direction
5851   for (i = 0; i < NUM_BELTS; i++)
5852   {
5853     int belt_nr = i;
5854
5855     for (j = 0; j < NUM_BELT_PARTS; j++)
5856     {
5857       int element = belt_base_active_element[belt_nr] + j;
5858       int graphic_1 = el2img(element);
5859       int graphic_2 = el2panelimg(element);
5860
5861       if (game.belt_dir[i] == MV_LEFT)
5862       {
5863         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5864         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5865       }
5866       else
5867       {
5868         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5869         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5870       }
5871     }
5872   }
5873
5874   SCAN_PLAYFIELD(x, y)
5875   {
5876     int element = Feld[x][y];
5877
5878     for (i = 0; i < NUM_BELTS; i++)
5879     {
5880       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5881       {
5882         int e_belt_nr = getBeltNrFromBeltElement(element);
5883         int belt_nr = i;
5884
5885         if (e_belt_nr == belt_nr)
5886         {
5887           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5888
5889           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5890         }
5891       }
5892     }
5893   }
5894 }
5895
5896 static void ToggleBeltSwitch(int x, int y)
5897 {
5898   static int belt_base_element[4] =
5899   {
5900     EL_CONVEYOR_BELT_1_LEFT,
5901     EL_CONVEYOR_BELT_2_LEFT,
5902     EL_CONVEYOR_BELT_3_LEFT,
5903     EL_CONVEYOR_BELT_4_LEFT
5904   };
5905   static int belt_base_active_element[4] =
5906   {
5907     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5908     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5909     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5910     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5911   };
5912   static int belt_base_switch_element[4] =
5913   {
5914     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5915     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5916     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5917     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5918   };
5919   static int belt_move_dir[4] =
5920   {
5921     MV_LEFT,
5922     MV_NONE,
5923     MV_RIGHT,
5924     MV_NONE,
5925   };
5926
5927   int element = Feld[x][y];
5928   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5929   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5930   int belt_dir = belt_move_dir[belt_dir_nr];
5931   int xx, yy, i;
5932
5933   if (!IS_BELT_SWITCH(element))
5934     return;
5935
5936   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5937   game.belt_dir[belt_nr] = belt_dir;
5938
5939   if (belt_dir_nr == 3)
5940     belt_dir_nr = 1;
5941
5942   // set frame order for belt animation graphic according to belt direction
5943   for (i = 0; i < NUM_BELT_PARTS; i++)
5944   {
5945     int element = belt_base_active_element[belt_nr] + i;
5946     int graphic_1 = el2img(element);
5947     int graphic_2 = el2panelimg(element);
5948
5949     if (belt_dir == MV_LEFT)
5950     {
5951       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5952       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5953     }
5954     else
5955     {
5956       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5957       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5958     }
5959   }
5960
5961   SCAN_PLAYFIELD(xx, yy)
5962   {
5963     int element = Feld[xx][yy];
5964
5965     if (IS_BELT_SWITCH(element))
5966     {
5967       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5968
5969       if (e_belt_nr == belt_nr)
5970       {
5971         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5972         TEST_DrawLevelField(xx, yy);
5973       }
5974     }
5975     else if (IS_BELT(element) && belt_dir != MV_NONE)
5976     {
5977       int e_belt_nr = getBeltNrFromBeltElement(element);
5978
5979       if (e_belt_nr == belt_nr)
5980       {
5981         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5982
5983         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5984         TEST_DrawLevelField(xx, yy);
5985       }
5986     }
5987     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5988     {
5989       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5990
5991       if (e_belt_nr == belt_nr)
5992       {
5993         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5994
5995         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5996         TEST_DrawLevelField(xx, yy);
5997       }
5998     }
5999   }
6000 }
6001
6002 static void ToggleSwitchgateSwitch(int x, int y)
6003 {
6004   int xx, yy;
6005
6006   game.switchgate_pos = !game.switchgate_pos;
6007
6008   SCAN_PLAYFIELD(xx, yy)
6009   {
6010     int element = Feld[xx][yy];
6011
6012     if (element == EL_SWITCHGATE_SWITCH_UP)
6013     {
6014       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6015       TEST_DrawLevelField(xx, yy);
6016     }
6017     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6018     {
6019       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6020       TEST_DrawLevelField(xx, yy);
6021     }
6022     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6023     {
6024       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6025       TEST_DrawLevelField(xx, yy);
6026     }
6027     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6028     {
6029       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6030       TEST_DrawLevelField(xx, yy);
6031     }
6032     else if (element == EL_SWITCHGATE_OPEN ||
6033              element == EL_SWITCHGATE_OPENING)
6034     {
6035       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6036
6037       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6038     }
6039     else if (element == EL_SWITCHGATE_CLOSED ||
6040              element == EL_SWITCHGATE_CLOSING)
6041     {
6042       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6043
6044       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6045     }
6046   }
6047 }
6048
6049 static int getInvisibleActiveFromInvisibleElement(int element)
6050 {
6051   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6052           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6053           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6054           element);
6055 }
6056
6057 static int getInvisibleFromInvisibleActiveElement(int element)
6058 {
6059   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6060           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6061           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6062           element);
6063 }
6064
6065 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6066 {
6067   int x, y;
6068
6069   SCAN_PLAYFIELD(x, y)
6070   {
6071     int element = Feld[x][y];
6072
6073     if (element == EL_LIGHT_SWITCH &&
6074         game.light_time_left > 0)
6075     {
6076       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6077       TEST_DrawLevelField(x, y);
6078     }
6079     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6080              game.light_time_left == 0)
6081     {
6082       Feld[x][y] = EL_LIGHT_SWITCH;
6083       TEST_DrawLevelField(x, y);
6084     }
6085     else if (element == EL_EMC_DRIPPER &&
6086              game.light_time_left > 0)
6087     {
6088       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6089       TEST_DrawLevelField(x, y);
6090     }
6091     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6092              game.light_time_left == 0)
6093     {
6094       Feld[x][y] = EL_EMC_DRIPPER;
6095       TEST_DrawLevelField(x, y);
6096     }
6097     else if (element == EL_INVISIBLE_STEELWALL ||
6098              element == EL_INVISIBLE_WALL ||
6099              element == EL_INVISIBLE_SAND)
6100     {
6101       if (game.light_time_left > 0)
6102         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6103
6104       TEST_DrawLevelField(x, y);
6105
6106       // uncrumble neighbour fields, if needed
6107       if (element == EL_INVISIBLE_SAND)
6108         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6109     }
6110     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6111              element == EL_INVISIBLE_WALL_ACTIVE ||
6112              element == EL_INVISIBLE_SAND_ACTIVE)
6113     {
6114       if (game.light_time_left == 0)
6115         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6116
6117       TEST_DrawLevelField(x, y);
6118
6119       // re-crumble neighbour fields, if needed
6120       if (element == EL_INVISIBLE_SAND)
6121         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6122     }
6123   }
6124 }
6125
6126 static void RedrawAllInvisibleElementsForLenses(void)
6127 {
6128   int x, y;
6129
6130   SCAN_PLAYFIELD(x, y)
6131   {
6132     int element = Feld[x][y];
6133
6134     if (element == EL_EMC_DRIPPER &&
6135         game.lenses_time_left > 0)
6136     {
6137       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6138       TEST_DrawLevelField(x, y);
6139     }
6140     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6141              game.lenses_time_left == 0)
6142     {
6143       Feld[x][y] = EL_EMC_DRIPPER;
6144       TEST_DrawLevelField(x, y);
6145     }
6146     else if (element == EL_INVISIBLE_STEELWALL ||
6147              element == EL_INVISIBLE_WALL ||
6148              element == EL_INVISIBLE_SAND)
6149     {
6150       if (game.lenses_time_left > 0)
6151         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6152
6153       TEST_DrawLevelField(x, y);
6154
6155       // uncrumble neighbour fields, if needed
6156       if (element == EL_INVISIBLE_SAND)
6157         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6158     }
6159     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6160              element == EL_INVISIBLE_WALL_ACTIVE ||
6161              element == EL_INVISIBLE_SAND_ACTIVE)
6162     {
6163       if (game.lenses_time_left == 0)
6164         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6165
6166       TEST_DrawLevelField(x, y);
6167
6168       // re-crumble neighbour fields, if needed
6169       if (element == EL_INVISIBLE_SAND)
6170         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6171     }
6172   }
6173 }
6174
6175 static void RedrawAllInvisibleElementsForMagnifier(void)
6176 {
6177   int x, y;
6178
6179   SCAN_PLAYFIELD(x, y)
6180   {
6181     int element = Feld[x][y];
6182
6183     if (element == EL_EMC_FAKE_GRASS &&
6184         game.magnify_time_left > 0)
6185     {
6186       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6187       TEST_DrawLevelField(x, y);
6188     }
6189     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6190              game.magnify_time_left == 0)
6191     {
6192       Feld[x][y] = EL_EMC_FAKE_GRASS;
6193       TEST_DrawLevelField(x, y);
6194     }
6195     else if (IS_GATE_GRAY(element) &&
6196              game.magnify_time_left > 0)
6197     {
6198       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6199                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6200                     IS_EM_GATE_GRAY(element) ?
6201                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6202                     IS_EMC_GATE_GRAY(element) ?
6203                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6204                     IS_DC_GATE_GRAY(element) ?
6205                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6206                     element);
6207       TEST_DrawLevelField(x, y);
6208     }
6209     else if (IS_GATE_GRAY_ACTIVE(element) &&
6210              game.magnify_time_left == 0)
6211     {
6212       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6213                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6214                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6215                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6216                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6217                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6218                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6219                     EL_DC_GATE_WHITE_GRAY :
6220                     element);
6221       TEST_DrawLevelField(x, y);
6222     }
6223   }
6224 }
6225
6226 static void ToggleLightSwitch(int x, int y)
6227 {
6228   int element = Feld[x][y];
6229
6230   game.light_time_left =
6231     (element == EL_LIGHT_SWITCH ?
6232      level.time_light * FRAMES_PER_SECOND : 0);
6233
6234   RedrawAllLightSwitchesAndInvisibleElements();
6235 }
6236
6237 static void ActivateTimegateSwitch(int x, int y)
6238 {
6239   int xx, yy;
6240
6241   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6242
6243   SCAN_PLAYFIELD(xx, yy)
6244   {
6245     int element = Feld[xx][yy];
6246
6247     if (element == EL_TIMEGATE_CLOSED ||
6248         element == EL_TIMEGATE_CLOSING)
6249     {
6250       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6251       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6252     }
6253
6254     /*
6255     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6256     {
6257       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6258       TEST_DrawLevelField(xx, yy);
6259     }
6260     */
6261
6262   }
6263
6264   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6265                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6266 }
6267
6268 static void Impact(int x, int y)
6269 {
6270   boolean last_line = (y == lev_fieldy - 1);
6271   boolean object_hit = FALSE;
6272   boolean impact = (last_line || object_hit);
6273   int element = Feld[x][y];
6274   int smashed = EL_STEELWALL;
6275
6276   if (!last_line)       // check if element below was hit
6277   {
6278     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6279       return;
6280
6281     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6282                                          MovDir[x][y + 1] != MV_DOWN ||
6283                                          MovPos[x][y + 1] <= TILEY / 2));
6284
6285     // do not smash moving elements that left the smashed field in time
6286     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6287         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6288       object_hit = FALSE;
6289
6290 #if USE_QUICKSAND_IMPACT_BUGFIX
6291     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6292     {
6293       RemoveMovingField(x, y + 1);
6294       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6295       Feld[x][y + 2] = EL_ROCK;
6296       TEST_DrawLevelField(x, y + 2);
6297
6298       object_hit = TRUE;
6299     }
6300
6301     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6302     {
6303       RemoveMovingField(x, y + 1);
6304       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6305       Feld[x][y + 2] = EL_ROCK;
6306       TEST_DrawLevelField(x, y + 2);
6307
6308       object_hit = TRUE;
6309     }
6310 #endif
6311
6312     if (object_hit)
6313       smashed = MovingOrBlocked2Element(x, y + 1);
6314
6315     impact = (last_line || object_hit);
6316   }
6317
6318   if (!last_line && smashed == EL_ACID) // element falls into acid
6319   {
6320     SplashAcid(x, y + 1);
6321     return;
6322   }
6323
6324   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6325   // only reset graphic animation if graphic really changes after impact
6326   if (impact &&
6327       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6328   {
6329     ResetGfxAnimation(x, y);
6330     TEST_DrawLevelField(x, y);
6331   }
6332
6333   if (impact && CAN_EXPLODE_IMPACT(element))
6334   {
6335     Bang(x, y);
6336     return;
6337   }
6338   else if (impact && element == EL_PEARL &&
6339            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6340   {
6341     ResetGfxAnimation(x, y);
6342
6343     Feld[x][y] = EL_PEARL_BREAKING;
6344     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6345     return;
6346   }
6347   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6348   {
6349     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6350
6351     return;
6352   }
6353
6354   if (impact && element == EL_AMOEBA_DROP)
6355   {
6356     if (object_hit && IS_PLAYER(x, y + 1))
6357       KillPlayerUnlessEnemyProtected(x, y + 1);
6358     else if (object_hit && smashed == EL_PENGUIN)
6359       Bang(x, y + 1);
6360     else
6361     {
6362       Feld[x][y] = EL_AMOEBA_GROWING;
6363       Store[x][y] = EL_AMOEBA_WET;
6364
6365       ResetRandomAnimationValue(x, y);
6366     }
6367     return;
6368   }
6369
6370   if (object_hit)               // check which object was hit
6371   {
6372     if ((CAN_PASS_MAGIC_WALL(element) && 
6373          (smashed == EL_MAGIC_WALL ||
6374           smashed == EL_BD_MAGIC_WALL)) ||
6375         (CAN_PASS_DC_MAGIC_WALL(element) &&
6376          smashed == EL_DC_MAGIC_WALL))
6377     {
6378       int xx, yy;
6379       int activated_magic_wall =
6380         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6381          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6382          EL_DC_MAGIC_WALL_ACTIVE);
6383
6384       // activate magic wall / mill
6385       SCAN_PLAYFIELD(xx, yy)
6386       {
6387         if (Feld[xx][yy] == smashed)
6388           Feld[xx][yy] = activated_magic_wall;
6389       }
6390
6391       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6392       game.magic_wall_active = TRUE;
6393
6394       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6395                             SND_MAGIC_WALL_ACTIVATING :
6396                             smashed == EL_BD_MAGIC_WALL ?
6397                             SND_BD_MAGIC_WALL_ACTIVATING :
6398                             SND_DC_MAGIC_WALL_ACTIVATING));
6399     }
6400
6401     if (IS_PLAYER(x, y + 1))
6402     {
6403       if (CAN_SMASH_PLAYER(element))
6404       {
6405         KillPlayerUnlessEnemyProtected(x, y + 1);
6406         return;
6407       }
6408     }
6409     else if (smashed == EL_PENGUIN)
6410     {
6411       if (CAN_SMASH_PLAYER(element))
6412       {
6413         Bang(x, y + 1);
6414         return;
6415       }
6416     }
6417     else if (element == EL_BD_DIAMOND)
6418     {
6419       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6420       {
6421         Bang(x, y + 1);
6422         return;
6423       }
6424     }
6425     else if (((element == EL_SP_INFOTRON ||
6426                element == EL_SP_ZONK) &&
6427               (smashed == EL_SP_SNIKSNAK ||
6428                smashed == EL_SP_ELECTRON ||
6429                smashed == EL_SP_DISK_ORANGE)) ||
6430              (element == EL_SP_INFOTRON &&
6431               smashed == EL_SP_DISK_YELLOW))
6432     {
6433       Bang(x, y + 1);
6434       return;
6435     }
6436     else if (CAN_SMASH_EVERYTHING(element))
6437     {
6438       if (IS_CLASSIC_ENEMY(smashed) ||
6439           CAN_EXPLODE_SMASHED(smashed))
6440       {
6441         Bang(x, y + 1);
6442         return;
6443       }
6444       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6445       {
6446         if (smashed == EL_LAMP ||
6447             smashed == EL_LAMP_ACTIVE)
6448         {
6449           Bang(x, y + 1);
6450           return;
6451         }
6452         else if (smashed == EL_NUT)
6453         {
6454           Feld[x][y + 1] = EL_NUT_BREAKING;
6455           PlayLevelSound(x, y, SND_NUT_BREAKING);
6456           RaiseScoreElement(EL_NUT);
6457           return;
6458         }
6459         else if (smashed == EL_PEARL)
6460         {
6461           ResetGfxAnimation(x, y);
6462
6463           Feld[x][y + 1] = EL_PEARL_BREAKING;
6464           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6465           return;
6466         }
6467         else if (smashed == EL_DIAMOND)
6468         {
6469           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6470           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6471           return;
6472         }
6473         else if (IS_BELT_SWITCH(smashed))
6474         {
6475           ToggleBeltSwitch(x, y + 1);
6476         }
6477         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6478                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6479                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6480                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6481         {
6482           ToggleSwitchgateSwitch(x, y + 1);
6483         }
6484         else if (smashed == EL_LIGHT_SWITCH ||
6485                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6486         {
6487           ToggleLightSwitch(x, y + 1);
6488         }
6489         else
6490         {
6491           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6492
6493           CheckElementChangeBySide(x, y + 1, smashed, element,
6494                                    CE_SWITCHED, CH_SIDE_TOP);
6495           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6496                                             CH_SIDE_TOP);
6497         }
6498       }
6499       else
6500       {
6501         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6502       }
6503     }
6504   }
6505
6506   // play sound of magic wall / mill
6507   if (!last_line &&
6508       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6509        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6510        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6511   {
6512     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6513       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6514     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6515       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6516     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6517       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6518
6519     return;
6520   }
6521
6522   // play sound of object that hits the ground
6523   if (last_line || object_hit)
6524     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6525 }
6526
6527 static void TurnRoundExt(int x, int y)
6528 {
6529   static struct
6530   {
6531     int dx, dy;
6532   } move_xy[] =
6533   {
6534     {  0,  0 },
6535     { -1,  0 },
6536     { +1,  0 },
6537     {  0,  0 },
6538     {  0, -1 },
6539     {  0,  0 }, { 0, 0 }, { 0, 0 },
6540     {  0, +1 }
6541   };
6542   static struct
6543   {
6544     int left, right, back;
6545   } turn[] =
6546   {
6547     { 0,        0,              0        },
6548     { MV_DOWN,  MV_UP,          MV_RIGHT },
6549     { MV_UP,    MV_DOWN,        MV_LEFT  },
6550     { 0,        0,              0        },
6551     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6552     { 0,        0,              0        },
6553     { 0,        0,              0        },
6554     { 0,        0,              0        },
6555     { MV_RIGHT, MV_LEFT,        MV_UP    }
6556   };
6557
6558   int element = Feld[x][y];
6559   int move_pattern = element_info[element].move_pattern;
6560
6561   int old_move_dir = MovDir[x][y];
6562   int left_dir  = turn[old_move_dir].left;
6563   int right_dir = turn[old_move_dir].right;
6564   int back_dir  = turn[old_move_dir].back;
6565
6566   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6567   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6568   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6569   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6570
6571   int left_x  = x + left_dx,  left_y  = y + left_dy;
6572   int right_x = x + right_dx, right_y = y + right_dy;
6573   int move_x  = x + move_dx,  move_y  = y + move_dy;
6574
6575   int xx, yy;
6576
6577   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6578   {
6579     TestIfBadThingTouchesOtherBadThing(x, y);
6580
6581     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6582       MovDir[x][y] = right_dir;
6583     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6584       MovDir[x][y] = left_dir;
6585
6586     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6587       MovDelay[x][y] = 9;
6588     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6589       MovDelay[x][y] = 1;
6590   }
6591   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6592   {
6593     TestIfBadThingTouchesOtherBadThing(x, y);
6594
6595     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6596       MovDir[x][y] = left_dir;
6597     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6598       MovDir[x][y] = right_dir;
6599
6600     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6601       MovDelay[x][y] = 9;
6602     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6603       MovDelay[x][y] = 1;
6604   }
6605   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6606   {
6607     TestIfBadThingTouchesOtherBadThing(x, y);
6608
6609     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6610       MovDir[x][y] = left_dir;
6611     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6612       MovDir[x][y] = right_dir;
6613
6614     if (MovDir[x][y] != old_move_dir)
6615       MovDelay[x][y] = 9;
6616   }
6617   else if (element == EL_YAMYAM)
6618   {
6619     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6620     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6621
6622     if (can_turn_left && can_turn_right)
6623       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6624     else if (can_turn_left)
6625       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6626     else if (can_turn_right)
6627       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6628     else
6629       MovDir[x][y] = back_dir;
6630
6631     MovDelay[x][y] = 16 + 16 * RND(3);
6632   }
6633   else if (element == EL_DARK_YAMYAM)
6634   {
6635     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6636                                                          left_x, left_y);
6637     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6638                                                          right_x, right_y);
6639
6640     if (can_turn_left && can_turn_right)
6641       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6642     else if (can_turn_left)
6643       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6644     else if (can_turn_right)
6645       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6646     else
6647       MovDir[x][y] = back_dir;
6648
6649     MovDelay[x][y] = 16 + 16 * RND(3);
6650   }
6651   else if (element == EL_PACMAN)
6652   {
6653     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6654     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6655
6656     if (can_turn_left && can_turn_right)
6657       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6658     else if (can_turn_left)
6659       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6660     else if (can_turn_right)
6661       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6662     else
6663       MovDir[x][y] = back_dir;
6664
6665     MovDelay[x][y] = 6 + RND(40);
6666   }
6667   else if (element == EL_PIG)
6668   {
6669     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6670     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6671     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6672     boolean should_turn_left, should_turn_right, should_move_on;
6673     int rnd_value = 24;
6674     int rnd = RND(rnd_value);
6675
6676     should_turn_left = (can_turn_left &&
6677                         (!can_move_on ||
6678                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6679                                                    y + back_dy + left_dy)));
6680     should_turn_right = (can_turn_right &&
6681                          (!can_move_on ||
6682                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6683                                                     y + back_dy + right_dy)));
6684     should_move_on = (can_move_on &&
6685                       (!can_turn_left ||
6686                        !can_turn_right ||
6687                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6688                                                  y + move_dy + left_dy) ||
6689                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6690                                                  y + move_dy + right_dy)));
6691
6692     if (should_turn_left || should_turn_right || should_move_on)
6693     {
6694       if (should_turn_left && should_turn_right && should_move_on)
6695         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6696                         rnd < 2 * rnd_value / 3 ? right_dir :
6697                         old_move_dir);
6698       else if (should_turn_left && should_turn_right)
6699         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6700       else if (should_turn_left && should_move_on)
6701         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6702       else if (should_turn_right && should_move_on)
6703         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6704       else if (should_turn_left)
6705         MovDir[x][y] = left_dir;
6706       else if (should_turn_right)
6707         MovDir[x][y] = right_dir;
6708       else if (should_move_on)
6709         MovDir[x][y] = old_move_dir;
6710     }
6711     else if (can_move_on && rnd > rnd_value / 8)
6712       MovDir[x][y] = old_move_dir;
6713     else if (can_turn_left && can_turn_right)
6714       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6715     else if (can_turn_left && rnd > rnd_value / 8)
6716       MovDir[x][y] = left_dir;
6717     else if (can_turn_right && rnd > rnd_value/8)
6718       MovDir[x][y] = right_dir;
6719     else
6720       MovDir[x][y] = back_dir;
6721
6722     xx = x + move_xy[MovDir[x][y]].dx;
6723     yy = y + move_xy[MovDir[x][y]].dy;
6724
6725     if (!IN_LEV_FIELD(xx, yy) ||
6726         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6727       MovDir[x][y] = old_move_dir;
6728
6729     MovDelay[x][y] = 0;
6730   }
6731   else if (element == EL_DRAGON)
6732   {
6733     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6734     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6735     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6736     int rnd_value = 24;
6737     int rnd = RND(rnd_value);
6738
6739     if (can_move_on && rnd > rnd_value / 8)
6740       MovDir[x][y] = old_move_dir;
6741     else if (can_turn_left && can_turn_right)
6742       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6743     else if (can_turn_left && rnd > rnd_value / 8)
6744       MovDir[x][y] = left_dir;
6745     else if (can_turn_right && rnd > rnd_value / 8)
6746       MovDir[x][y] = right_dir;
6747     else
6748       MovDir[x][y] = back_dir;
6749
6750     xx = x + move_xy[MovDir[x][y]].dx;
6751     yy = y + move_xy[MovDir[x][y]].dy;
6752
6753     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6754       MovDir[x][y] = old_move_dir;
6755
6756     MovDelay[x][y] = 0;
6757   }
6758   else if (element == EL_MOLE)
6759   {
6760     boolean can_move_on =
6761       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6762                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6763                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6764     if (!can_move_on)
6765     {
6766       boolean can_turn_left =
6767         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6768                               IS_AMOEBOID(Feld[left_x][left_y])));
6769
6770       boolean can_turn_right =
6771         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6772                               IS_AMOEBOID(Feld[right_x][right_y])));
6773
6774       if (can_turn_left && can_turn_right)
6775         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6776       else if (can_turn_left)
6777         MovDir[x][y] = left_dir;
6778       else
6779         MovDir[x][y] = right_dir;
6780     }
6781
6782     if (MovDir[x][y] != old_move_dir)
6783       MovDelay[x][y] = 9;
6784   }
6785   else if (element == EL_BALLOON)
6786   {
6787     MovDir[x][y] = game.wind_direction;
6788     MovDelay[x][y] = 0;
6789   }
6790   else if (element == EL_SPRING)
6791   {
6792     if (MovDir[x][y] & MV_HORIZONTAL)
6793     {
6794       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6795           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6796       {
6797         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6798         ResetGfxAnimation(move_x, move_y);
6799         TEST_DrawLevelField(move_x, move_y);
6800
6801         MovDir[x][y] = back_dir;
6802       }
6803       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6804                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6805         MovDir[x][y] = MV_NONE;
6806     }
6807
6808     MovDelay[x][y] = 0;
6809   }
6810   else if (element == EL_ROBOT ||
6811            element == EL_SATELLITE ||
6812            element == EL_PENGUIN ||
6813            element == EL_EMC_ANDROID)
6814   {
6815     int attr_x = -1, attr_y = -1;
6816
6817     if (AllPlayersGone)
6818     {
6819       attr_x = ExitX;
6820       attr_y = ExitY;
6821     }
6822     else
6823     {
6824       int i;
6825
6826       for (i = 0; i < MAX_PLAYERS; i++)
6827       {
6828         struct PlayerInfo *player = &stored_player[i];
6829         int jx = player->jx, jy = player->jy;
6830
6831         if (!player->active)
6832           continue;
6833
6834         if (attr_x == -1 ||
6835             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6836         {
6837           attr_x = jx;
6838           attr_y = jy;
6839         }
6840       }
6841     }
6842
6843     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6844         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6845          game.engine_version < VERSION_IDENT(3,1,0,0)))
6846     {
6847       attr_x = ZX;
6848       attr_y = ZY;
6849     }
6850
6851     if (element == EL_PENGUIN)
6852     {
6853       int i;
6854       static int xy[4][2] =
6855       {
6856         { 0, -1 },
6857         { -1, 0 },
6858         { +1, 0 },
6859         { 0, +1 }
6860       };
6861
6862       for (i = 0; i < NUM_DIRECTIONS; i++)
6863       {
6864         int ex = x + xy[i][0];
6865         int ey = y + xy[i][1];
6866
6867         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6868                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6869                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6870                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6871         {
6872           attr_x = ex;
6873           attr_y = ey;
6874           break;
6875         }
6876       }
6877     }
6878
6879     MovDir[x][y] = MV_NONE;
6880     if (attr_x < x)
6881       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6882     else if (attr_x > x)
6883       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6884     if (attr_y < y)
6885       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6886     else if (attr_y > y)
6887       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6888
6889     if (element == EL_ROBOT)
6890     {
6891       int newx, newy;
6892
6893       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6894         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6895       Moving2Blocked(x, y, &newx, &newy);
6896
6897       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6898         MovDelay[x][y] = 8 + 8 * !RND(3);
6899       else
6900         MovDelay[x][y] = 16;
6901     }
6902     else if (element == EL_PENGUIN)
6903     {
6904       int newx, newy;
6905
6906       MovDelay[x][y] = 1;
6907
6908       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6909       {
6910         boolean first_horiz = RND(2);
6911         int new_move_dir = MovDir[x][y];
6912
6913         MovDir[x][y] =
6914           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6915         Moving2Blocked(x, y, &newx, &newy);
6916
6917         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6918           return;
6919
6920         MovDir[x][y] =
6921           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6922         Moving2Blocked(x, y, &newx, &newy);
6923
6924         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6925           return;
6926
6927         MovDir[x][y] = old_move_dir;
6928         return;
6929       }
6930     }
6931     else if (element == EL_SATELLITE)
6932     {
6933       int newx, newy;
6934
6935       MovDelay[x][y] = 1;
6936
6937       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6938       {
6939         boolean first_horiz = RND(2);
6940         int new_move_dir = MovDir[x][y];
6941
6942         MovDir[x][y] =
6943           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6944         Moving2Blocked(x, y, &newx, &newy);
6945
6946         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6947           return;
6948
6949         MovDir[x][y] =
6950           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6951         Moving2Blocked(x, y, &newx, &newy);
6952
6953         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6954           return;
6955
6956         MovDir[x][y] = old_move_dir;
6957         return;
6958       }
6959     }
6960     else if (element == EL_EMC_ANDROID)
6961     {
6962       static int check_pos[16] =
6963       {
6964         -1,             //  0 => (invalid)
6965         7,              //  1 => MV_LEFT
6966         3,              //  2 => MV_RIGHT
6967         -1,             //  3 => (invalid)
6968         1,              //  4 =>            MV_UP
6969         0,              //  5 => MV_LEFT  | MV_UP
6970         2,              //  6 => MV_RIGHT | MV_UP
6971         -1,             //  7 => (invalid)
6972         5,              //  8 =>            MV_DOWN
6973         6,              //  9 => MV_LEFT  | MV_DOWN
6974         4,              // 10 => MV_RIGHT | MV_DOWN
6975         -1,             // 11 => (invalid)
6976         -1,             // 12 => (invalid)
6977         -1,             // 13 => (invalid)
6978         -1,             // 14 => (invalid)
6979         -1,             // 15 => (invalid)
6980       };
6981       static struct
6982       {
6983         int dx, dy;
6984         int dir;
6985       } check_xy[8] =
6986       {
6987         { -1, -1,       MV_LEFT  | MV_UP   },
6988         {  0, -1,                  MV_UP   },
6989         { +1, -1,       MV_RIGHT | MV_UP   },
6990         { +1,  0,       MV_RIGHT           },
6991         { +1, +1,       MV_RIGHT | MV_DOWN },
6992         {  0, +1,                  MV_DOWN },
6993         { -1, +1,       MV_LEFT  | MV_DOWN },
6994         { -1,  0,       MV_LEFT            },
6995       };
6996       int start_pos, check_order;
6997       boolean can_clone = FALSE;
6998       int i;
6999
7000       // check if there is any free field around current position
7001       for (i = 0; i < 8; i++)
7002       {
7003         int newx = x + check_xy[i].dx;
7004         int newy = y + check_xy[i].dy;
7005
7006         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7007         {
7008           can_clone = TRUE;
7009
7010           break;
7011         }
7012       }
7013
7014       if (can_clone)            // randomly find an element to clone
7015       {
7016         can_clone = FALSE;
7017
7018         start_pos = check_pos[RND(8)];
7019         check_order = (RND(2) ? -1 : +1);
7020
7021         for (i = 0; i < 8; i++)
7022         {
7023           int pos_raw = start_pos + i * check_order;
7024           int pos = (pos_raw + 8) % 8;
7025           int newx = x + check_xy[pos].dx;
7026           int newy = y + check_xy[pos].dy;
7027
7028           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7029           {
7030             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7031             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7032
7033             Store[x][y] = Feld[newx][newy];
7034
7035             can_clone = TRUE;
7036
7037             break;
7038           }
7039         }
7040       }
7041
7042       if (can_clone)            // randomly find a direction to move
7043       {
7044         can_clone = FALSE;
7045
7046         start_pos = check_pos[RND(8)];
7047         check_order = (RND(2) ? -1 : +1);
7048
7049         for (i = 0; i < 8; i++)
7050         {
7051           int pos_raw = start_pos + i * check_order;
7052           int pos = (pos_raw + 8) % 8;
7053           int newx = x + check_xy[pos].dx;
7054           int newy = y + check_xy[pos].dy;
7055           int new_move_dir = check_xy[pos].dir;
7056
7057           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7058           {
7059             MovDir[x][y] = new_move_dir;
7060             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7061
7062             can_clone = TRUE;
7063
7064             break;
7065           }
7066         }
7067       }
7068
7069       if (can_clone)            // cloning and moving successful
7070         return;
7071
7072       // cannot clone -- try to move towards player
7073
7074       start_pos = check_pos[MovDir[x][y] & 0x0f];
7075       check_order = (RND(2) ? -1 : +1);
7076
7077       for (i = 0; i < 3; i++)
7078       {
7079         // first check start_pos, then previous/next or (next/previous) pos
7080         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7081         int pos = (pos_raw + 8) % 8;
7082         int newx = x + check_xy[pos].dx;
7083         int newy = y + check_xy[pos].dy;
7084         int new_move_dir = check_xy[pos].dir;
7085
7086         if (IS_PLAYER(newx, newy))
7087           break;
7088
7089         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7090         {
7091           MovDir[x][y] = new_move_dir;
7092           MovDelay[x][y] = level.android_move_time * 8 + 1;
7093
7094           break;
7095         }
7096       }
7097     }
7098   }
7099   else if (move_pattern == MV_TURNING_LEFT ||
7100            move_pattern == MV_TURNING_RIGHT ||
7101            move_pattern == MV_TURNING_LEFT_RIGHT ||
7102            move_pattern == MV_TURNING_RIGHT_LEFT ||
7103            move_pattern == MV_TURNING_RANDOM ||
7104            move_pattern == MV_ALL_DIRECTIONS)
7105   {
7106     boolean can_turn_left =
7107       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7108     boolean can_turn_right =
7109       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7110
7111     if (element_info[element].move_stepsize == 0)       // "not moving"
7112       return;
7113
7114     if (move_pattern == MV_TURNING_LEFT)
7115       MovDir[x][y] = left_dir;
7116     else if (move_pattern == MV_TURNING_RIGHT)
7117       MovDir[x][y] = right_dir;
7118     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7119       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7120     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7121       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7122     else if (move_pattern == MV_TURNING_RANDOM)
7123       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7124                       can_turn_right && !can_turn_left ? right_dir :
7125                       RND(2) ? left_dir : right_dir);
7126     else if (can_turn_left && can_turn_right)
7127       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7128     else if (can_turn_left)
7129       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7130     else if (can_turn_right)
7131       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7132     else
7133       MovDir[x][y] = back_dir;
7134
7135     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7136   }
7137   else if (move_pattern == MV_HORIZONTAL ||
7138            move_pattern == MV_VERTICAL)
7139   {
7140     if (move_pattern & old_move_dir)
7141       MovDir[x][y] = back_dir;
7142     else if (move_pattern == MV_HORIZONTAL)
7143       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7144     else if (move_pattern == MV_VERTICAL)
7145       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7146
7147     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7148   }
7149   else if (move_pattern & MV_ANY_DIRECTION)
7150   {
7151     MovDir[x][y] = move_pattern;
7152     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7153   }
7154   else if (move_pattern & MV_WIND_DIRECTION)
7155   {
7156     MovDir[x][y] = game.wind_direction;
7157     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7158   }
7159   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7160   {
7161     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7162       MovDir[x][y] = left_dir;
7163     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7164       MovDir[x][y] = right_dir;
7165
7166     if (MovDir[x][y] != old_move_dir)
7167       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7168   }
7169   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7170   {
7171     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7172       MovDir[x][y] = right_dir;
7173     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7174       MovDir[x][y] = left_dir;
7175
7176     if (MovDir[x][y] != old_move_dir)
7177       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7178   }
7179   else if (move_pattern == MV_TOWARDS_PLAYER ||
7180            move_pattern == MV_AWAY_FROM_PLAYER)
7181   {
7182     int attr_x = -1, attr_y = -1;
7183     int newx, newy;
7184     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7185
7186     if (AllPlayersGone)
7187     {
7188       attr_x = ExitX;
7189       attr_y = ExitY;
7190     }
7191     else
7192     {
7193       int i;
7194
7195       for (i = 0; i < MAX_PLAYERS; i++)
7196       {
7197         struct PlayerInfo *player = &stored_player[i];
7198         int jx = player->jx, jy = player->jy;
7199
7200         if (!player->active)
7201           continue;
7202
7203         if (attr_x == -1 ||
7204             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7205         {
7206           attr_x = jx;
7207           attr_y = jy;
7208         }
7209       }
7210     }
7211
7212     MovDir[x][y] = MV_NONE;
7213     if (attr_x < x)
7214       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7215     else if (attr_x > x)
7216       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7217     if (attr_y < y)
7218       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7219     else if (attr_y > y)
7220       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7221
7222     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7223
7224     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7225     {
7226       boolean first_horiz = RND(2);
7227       int new_move_dir = MovDir[x][y];
7228
7229       if (element_info[element].move_stepsize == 0)     // "not moving"
7230       {
7231         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7232         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7233
7234         return;
7235       }
7236
7237       MovDir[x][y] =
7238         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7239       Moving2Blocked(x, y, &newx, &newy);
7240
7241       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7242         return;
7243
7244       MovDir[x][y] =
7245         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7246       Moving2Blocked(x, y, &newx, &newy);
7247
7248       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7249         return;
7250
7251       MovDir[x][y] = old_move_dir;
7252     }
7253   }
7254   else if (move_pattern == MV_WHEN_PUSHED ||
7255            move_pattern == MV_WHEN_DROPPED)
7256   {
7257     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7258       MovDir[x][y] = MV_NONE;
7259
7260     MovDelay[x][y] = 0;
7261   }
7262   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7263   {
7264     static int test_xy[7][2] =
7265     {
7266       { 0, -1 },
7267       { -1, 0 },
7268       { +1, 0 },
7269       { 0, +1 },
7270       { 0, -1 },
7271       { -1, 0 },
7272       { +1, 0 },
7273     };
7274     static int test_dir[7] =
7275     {
7276       MV_UP,
7277       MV_LEFT,
7278       MV_RIGHT,
7279       MV_DOWN,
7280       MV_UP,
7281       MV_LEFT,
7282       MV_RIGHT,
7283     };
7284     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7285     int move_preference = -1000000;     // start with very low preference
7286     int new_move_dir = MV_NONE;
7287     int start_test = RND(4);
7288     int i;
7289
7290     for (i = 0; i < NUM_DIRECTIONS; i++)
7291     {
7292       int move_dir = test_dir[start_test + i];
7293       int move_dir_preference;
7294
7295       xx = x + test_xy[start_test + i][0];
7296       yy = y + test_xy[start_test + i][1];
7297
7298       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7299           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7300       {
7301         new_move_dir = move_dir;
7302
7303         break;
7304       }
7305
7306       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7307         continue;
7308
7309       move_dir_preference = -1 * RunnerVisit[xx][yy];
7310       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7311         move_dir_preference = PlayerVisit[xx][yy];
7312
7313       if (move_dir_preference > move_preference)
7314       {
7315         // prefer field that has not been visited for the longest time
7316         move_preference = move_dir_preference;
7317         new_move_dir = move_dir;
7318       }
7319       else if (move_dir_preference == move_preference &&
7320                move_dir == old_move_dir)
7321       {
7322         // prefer last direction when all directions are preferred equally
7323         move_preference = move_dir_preference;
7324         new_move_dir = move_dir;
7325       }
7326     }
7327
7328     MovDir[x][y] = new_move_dir;
7329     if (old_move_dir != new_move_dir)
7330       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7331   }
7332 }
7333
7334 static void TurnRound(int x, int y)
7335 {
7336   int direction = MovDir[x][y];
7337
7338   TurnRoundExt(x, y);
7339
7340   GfxDir[x][y] = MovDir[x][y];
7341
7342   if (direction != MovDir[x][y])
7343     GfxFrame[x][y] = 0;
7344
7345   if (MovDelay[x][y])
7346     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7347
7348   ResetGfxFrame(x, y);
7349 }
7350
7351 static boolean JustBeingPushed(int x, int y)
7352 {
7353   int i;
7354
7355   for (i = 0; i < MAX_PLAYERS; i++)
7356   {
7357     struct PlayerInfo *player = &stored_player[i];
7358
7359     if (player->active && player->is_pushing && player->MovPos)
7360     {
7361       int next_jx = player->jx + (player->jx - player->last_jx);
7362       int next_jy = player->jy + (player->jy - player->last_jy);
7363
7364       if (x == next_jx && y == next_jy)
7365         return TRUE;
7366     }
7367   }
7368
7369   return FALSE;
7370 }
7371
7372 static void StartMoving(int x, int y)
7373 {
7374   boolean started_moving = FALSE;       // some elements can fall _and_ move
7375   int element = Feld[x][y];
7376
7377   if (Stop[x][y])
7378     return;
7379
7380   if (MovDelay[x][y] == 0)
7381     GfxAction[x][y] = ACTION_DEFAULT;
7382
7383   if (CAN_FALL(element) && y < lev_fieldy - 1)
7384   {
7385     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7386         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7387       if (JustBeingPushed(x, y))
7388         return;
7389
7390     if (element == EL_QUICKSAND_FULL)
7391     {
7392       if (IS_FREE(x, y + 1))
7393       {
7394         InitMovingField(x, y, MV_DOWN);
7395         started_moving = TRUE;
7396
7397         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7398 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7399         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7400           Store[x][y] = EL_ROCK;
7401 #else
7402         Store[x][y] = EL_ROCK;
7403 #endif
7404
7405         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7406       }
7407       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7408       {
7409         if (!MovDelay[x][y])
7410         {
7411           MovDelay[x][y] = TILEY + 1;
7412
7413           ResetGfxAnimation(x, y);
7414           ResetGfxAnimation(x, y + 1);
7415         }
7416
7417         if (MovDelay[x][y])
7418         {
7419           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7420           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7421
7422           MovDelay[x][y]--;
7423           if (MovDelay[x][y])
7424             return;
7425         }
7426
7427         Feld[x][y] = EL_QUICKSAND_EMPTY;
7428         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7429         Store[x][y + 1] = Store[x][y];
7430         Store[x][y] = 0;
7431
7432         PlayLevelSoundAction(x, y, ACTION_FILLING);
7433       }
7434       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7435       {
7436         if (!MovDelay[x][y])
7437         {
7438           MovDelay[x][y] = TILEY + 1;
7439
7440           ResetGfxAnimation(x, y);
7441           ResetGfxAnimation(x, y + 1);
7442         }
7443
7444         if (MovDelay[x][y])
7445         {
7446           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7447           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7448
7449           MovDelay[x][y]--;
7450           if (MovDelay[x][y])
7451             return;
7452         }
7453
7454         Feld[x][y] = EL_QUICKSAND_EMPTY;
7455         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7456         Store[x][y + 1] = Store[x][y];
7457         Store[x][y] = 0;
7458
7459         PlayLevelSoundAction(x, y, ACTION_FILLING);
7460       }
7461     }
7462     else if (element == EL_QUICKSAND_FAST_FULL)
7463     {
7464       if (IS_FREE(x, y + 1))
7465       {
7466         InitMovingField(x, y, MV_DOWN);
7467         started_moving = TRUE;
7468
7469         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7470 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7471         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7472           Store[x][y] = EL_ROCK;
7473 #else
7474         Store[x][y] = EL_ROCK;
7475 #endif
7476
7477         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7478       }
7479       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7480       {
7481         if (!MovDelay[x][y])
7482         {
7483           MovDelay[x][y] = TILEY + 1;
7484
7485           ResetGfxAnimation(x, y);
7486           ResetGfxAnimation(x, y + 1);
7487         }
7488
7489         if (MovDelay[x][y])
7490         {
7491           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7492           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7493
7494           MovDelay[x][y]--;
7495           if (MovDelay[x][y])
7496             return;
7497         }
7498
7499         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7500         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7501         Store[x][y + 1] = Store[x][y];
7502         Store[x][y] = 0;
7503
7504         PlayLevelSoundAction(x, y, ACTION_FILLING);
7505       }
7506       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7507       {
7508         if (!MovDelay[x][y])
7509         {
7510           MovDelay[x][y] = TILEY + 1;
7511
7512           ResetGfxAnimation(x, y);
7513           ResetGfxAnimation(x, y + 1);
7514         }
7515
7516         if (MovDelay[x][y])
7517         {
7518           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7519           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7520
7521           MovDelay[x][y]--;
7522           if (MovDelay[x][y])
7523             return;
7524         }
7525
7526         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7527         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7528         Store[x][y + 1] = Store[x][y];
7529         Store[x][y] = 0;
7530
7531         PlayLevelSoundAction(x, y, ACTION_FILLING);
7532       }
7533     }
7534     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7535              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7536     {
7537       InitMovingField(x, y, MV_DOWN);
7538       started_moving = TRUE;
7539
7540       Feld[x][y] = EL_QUICKSAND_FILLING;
7541       Store[x][y] = element;
7542
7543       PlayLevelSoundAction(x, y, ACTION_FILLING);
7544     }
7545     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7546              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7547     {
7548       InitMovingField(x, y, MV_DOWN);
7549       started_moving = TRUE;
7550
7551       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7552       Store[x][y] = element;
7553
7554       PlayLevelSoundAction(x, y, ACTION_FILLING);
7555     }
7556     else if (element == EL_MAGIC_WALL_FULL)
7557     {
7558       if (IS_FREE(x, y + 1))
7559       {
7560         InitMovingField(x, y, MV_DOWN);
7561         started_moving = TRUE;
7562
7563         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7564         Store[x][y] = EL_CHANGED(Store[x][y]);
7565       }
7566       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7567       {
7568         if (!MovDelay[x][y])
7569           MovDelay[x][y] = TILEY / 4 + 1;
7570
7571         if (MovDelay[x][y])
7572         {
7573           MovDelay[x][y]--;
7574           if (MovDelay[x][y])
7575             return;
7576         }
7577
7578         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7579         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7580         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7581         Store[x][y] = 0;
7582       }
7583     }
7584     else if (element == EL_BD_MAGIC_WALL_FULL)
7585     {
7586       if (IS_FREE(x, y + 1))
7587       {
7588         InitMovingField(x, y, MV_DOWN);
7589         started_moving = TRUE;
7590
7591         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7592         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7593       }
7594       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7595       {
7596         if (!MovDelay[x][y])
7597           MovDelay[x][y] = TILEY / 4 + 1;
7598
7599         if (MovDelay[x][y])
7600         {
7601           MovDelay[x][y]--;
7602           if (MovDelay[x][y])
7603             return;
7604         }
7605
7606         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7607         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7608         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7609         Store[x][y] = 0;
7610       }
7611     }
7612     else if (element == EL_DC_MAGIC_WALL_FULL)
7613     {
7614       if (IS_FREE(x, y + 1))
7615       {
7616         InitMovingField(x, y, MV_DOWN);
7617         started_moving = TRUE;
7618
7619         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7620         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7621       }
7622       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7623       {
7624         if (!MovDelay[x][y])
7625           MovDelay[x][y] = TILEY / 4 + 1;
7626
7627         if (MovDelay[x][y])
7628         {
7629           MovDelay[x][y]--;
7630           if (MovDelay[x][y])
7631             return;
7632         }
7633
7634         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7635         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7636         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7637         Store[x][y] = 0;
7638       }
7639     }
7640     else if ((CAN_PASS_MAGIC_WALL(element) &&
7641               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7642                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7643              (CAN_PASS_DC_MAGIC_WALL(element) &&
7644               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7645
7646     {
7647       InitMovingField(x, y, MV_DOWN);
7648       started_moving = TRUE;
7649
7650       Feld[x][y] =
7651         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7652          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7653          EL_DC_MAGIC_WALL_FILLING);
7654       Store[x][y] = element;
7655     }
7656     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7657     {
7658       SplashAcid(x, y + 1);
7659
7660       InitMovingField(x, y, MV_DOWN);
7661       started_moving = TRUE;
7662
7663       Store[x][y] = EL_ACID;
7664     }
7665     else if (
7666              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7667               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7668              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7669               CAN_FALL(element) && WasJustFalling[x][y] &&
7670               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7671
7672              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7673               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7674               (Feld[x][y + 1] == EL_BLOCKED)))
7675     {
7676       /* this is needed for a special case not covered by calling "Impact()"
7677          from "ContinueMoving()": if an element moves to a tile directly below
7678          another element which was just falling on that tile (which was empty
7679          in the previous frame), the falling element above would just stop
7680          instead of smashing the element below (in previous version, the above
7681          element was just checked for "moving" instead of "falling", resulting
7682          in incorrect smashes caused by horizontal movement of the above
7683          element; also, the case of the player being the element to smash was
7684          simply not covered here... :-/ ) */
7685
7686       CheckCollision[x][y] = 0;
7687       CheckImpact[x][y] = 0;
7688
7689       Impact(x, y);
7690     }
7691     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7692     {
7693       if (MovDir[x][y] == MV_NONE)
7694       {
7695         InitMovingField(x, y, MV_DOWN);
7696         started_moving = TRUE;
7697       }
7698     }
7699     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7700     {
7701       if (WasJustFalling[x][y]) // prevent animation from being restarted
7702         MovDir[x][y] = MV_DOWN;
7703
7704       InitMovingField(x, y, MV_DOWN);
7705       started_moving = TRUE;
7706     }
7707     else if (element == EL_AMOEBA_DROP)
7708     {
7709       Feld[x][y] = EL_AMOEBA_GROWING;
7710       Store[x][y] = EL_AMOEBA_WET;
7711     }
7712     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7713               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7714              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7715              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7716     {
7717       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7718                                 (IS_FREE(x - 1, y + 1) ||
7719                                  Feld[x - 1][y + 1] == EL_ACID));
7720       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7721                                 (IS_FREE(x + 1, y + 1) ||
7722                                  Feld[x + 1][y + 1] == EL_ACID));
7723       boolean can_fall_any  = (can_fall_left || can_fall_right);
7724       boolean can_fall_both = (can_fall_left && can_fall_right);
7725       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7726
7727       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7728       {
7729         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7730           can_fall_right = FALSE;
7731         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7732           can_fall_left = FALSE;
7733         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7734           can_fall_right = FALSE;
7735         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7736           can_fall_left = FALSE;
7737
7738         can_fall_any  = (can_fall_left || can_fall_right);
7739         can_fall_both = FALSE;
7740       }
7741
7742       if (can_fall_both)
7743       {
7744         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7745           can_fall_right = FALSE;       // slip down on left side
7746         else
7747           can_fall_left = !(can_fall_right = RND(2));
7748
7749         can_fall_both = FALSE;
7750       }
7751
7752       if (can_fall_any)
7753       {
7754         // if not determined otherwise, prefer left side for slipping down
7755         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7756         started_moving = TRUE;
7757       }
7758     }
7759     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7760     {
7761       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7762       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7763       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7764       int belt_dir = game.belt_dir[belt_nr];
7765
7766       if ((belt_dir == MV_LEFT  && left_is_free) ||
7767           (belt_dir == MV_RIGHT && right_is_free))
7768       {
7769         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7770
7771         InitMovingField(x, y, belt_dir);
7772         started_moving = TRUE;
7773
7774         Pushed[x][y] = TRUE;
7775         Pushed[nextx][y] = TRUE;
7776
7777         GfxAction[x][y] = ACTION_DEFAULT;
7778       }
7779       else
7780       {
7781         MovDir[x][y] = 0;       // if element was moving, stop it
7782       }
7783     }
7784   }
7785
7786   // not "else if" because of elements that can fall and move (EL_SPRING)
7787   if (CAN_MOVE(element) && !started_moving)
7788   {
7789     int move_pattern = element_info[element].move_pattern;
7790     int newx, newy;
7791
7792     Moving2Blocked(x, y, &newx, &newy);
7793
7794     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7795       return;
7796
7797     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7798         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7799     {
7800       WasJustMoving[x][y] = 0;
7801       CheckCollision[x][y] = 0;
7802
7803       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7804
7805       if (Feld[x][y] != element)        // element has changed
7806         return;
7807     }
7808
7809     if (!MovDelay[x][y])        // start new movement phase
7810     {
7811       // all objects that can change their move direction after each step
7812       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7813
7814       if (element != EL_YAMYAM &&
7815           element != EL_DARK_YAMYAM &&
7816           element != EL_PACMAN &&
7817           !(move_pattern & MV_ANY_DIRECTION) &&
7818           move_pattern != MV_TURNING_LEFT &&
7819           move_pattern != MV_TURNING_RIGHT &&
7820           move_pattern != MV_TURNING_LEFT_RIGHT &&
7821           move_pattern != MV_TURNING_RIGHT_LEFT &&
7822           move_pattern != MV_TURNING_RANDOM)
7823       {
7824         TurnRound(x, y);
7825
7826         if (MovDelay[x][y] && (element == EL_BUG ||
7827                                element == EL_SPACESHIP ||
7828                                element == EL_SP_SNIKSNAK ||
7829                                element == EL_SP_ELECTRON ||
7830                                element == EL_MOLE))
7831           TEST_DrawLevelField(x, y);
7832       }
7833     }
7834
7835     if (MovDelay[x][y])         // wait some time before next movement
7836     {
7837       MovDelay[x][y]--;
7838
7839       if (element == EL_ROBOT ||
7840           element == EL_YAMYAM ||
7841           element == EL_DARK_YAMYAM)
7842       {
7843         DrawLevelElementAnimationIfNeeded(x, y, element);
7844         PlayLevelSoundAction(x, y, ACTION_WAITING);
7845       }
7846       else if (element == EL_SP_ELECTRON)
7847         DrawLevelElementAnimationIfNeeded(x, y, element);
7848       else if (element == EL_DRAGON)
7849       {
7850         int i;
7851         int dir = MovDir[x][y];
7852         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7853         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7854         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7855                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7856                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7857                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7858         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7859
7860         GfxAction[x][y] = ACTION_ATTACKING;
7861
7862         if (IS_PLAYER(x, y))
7863           DrawPlayerField(x, y);
7864         else
7865           TEST_DrawLevelField(x, y);
7866
7867         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7868
7869         for (i = 1; i <= 3; i++)
7870         {
7871           int xx = x + i * dx;
7872           int yy = y + i * dy;
7873           int sx = SCREENX(xx);
7874           int sy = SCREENY(yy);
7875           int flame_graphic = graphic + (i - 1);
7876
7877           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7878             break;
7879
7880           if (MovDelay[x][y])
7881           {
7882             int flamed = MovingOrBlocked2Element(xx, yy);
7883
7884             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7885               Bang(xx, yy);
7886             else
7887               RemoveMovingField(xx, yy);
7888
7889             ChangeDelay[xx][yy] = 0;
7890
7891             Feld[xx][yy] = EL_FLAMES;
7892
7893             if (IN_SCR_FIELD(sx, sy))
7894             {
7895               TEST_DrawLevelFieldCrumbled(xx, yy);
7896               DrawGraphic(sx, sy, flame_graphic, frame);
7897             }
7898           }
7899           else
7900           {
7901             if (Feld[xx][yy] == EL_FLAMES)
7902               Feld[xx][yy] = EL_EMPTY;
7903             TEST_DrawLevelField(xx, yy);
7904           }
7905         }
7906       }
7907
7908       if (MovDelay[x][y])       // element still has to wait some time
7909       {
7910         PlayLevelSoundAction(x, y, ACTION_WAITING);
7911
7912         return;
7913       }
7914     }
7915
7916     // now make next step
7917
7918     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7919
7920     if (DONT_COLLIDE_WITH(element) &&
7921         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7922         !PLAYER_ENEMY_PROTECTED(newx, newy))
7923     {
7924       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7925
7926       return;
7927     }
7928
7929     else if (CAN_MOVE_INTO_ACID(element) &&
7930              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7931              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7932              (MovDir[x][y] == MV_DOWN ||
7933               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7934     {
7935       SplashAcid(newx, newy);
7936       Store[x][y] = EL_ACID;
7937     }
7938     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7939     {
7940       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7941           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7942           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7943           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7944       {
7945         RemoveField(x, y);
7946         TEST_DrawLevelField(x, y);
7947
7948         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7949         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7950           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7951
7952         local_player->friends_still_needed--;
7953         if (!local_player->friends_still_needed &&
7954             !local_player->GameOver && AllPlayersGone)
7955           LevelSolved();
7956
7957         return;
7958       }
7959       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7960       {
7961         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7962           TEST_DrawLevelField(newx, newy);
7963         else
7964           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7965       }
7966       else if (!IS_FREE(newx, newy))
7967       {
7968         GfxAction[x][y] = ACTION_WAITING;
7969
7970         if (IS_PLAYER(x, y))
7971           DrawPlayerField(x, y);
7972         else
7973           TEST_DrawLevelField(x, y);
7974
7975         return;
7976       }
7977     }
7978     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7979     {
7980       if (IS_FOOD_PIG(Feld[newx][newy]))
7981       {
7982         if (IS_MOVING(newx, newy))
7983           RemoveMovingField(newx, newy);
7984         else
7985         {
7986           Feld[newx][newy] = EL_EMPTY;
7987           TEST_DrawLevelField(newx, newy);
7988         }
7989
7990         PlayLevelSound(x, y, SND_PIG_DIGGING);
7991       }
7992       else if (!IS_FREE(newx, newy))
7993       {
7994         if (IS_PLAYER(x, y))
7995           DrawPlayerField(x, y);
7996         else
7997           TEST_DrawLevelField(x, y);
7998
7999         return;
8000       }
8001     }
8002     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8003     {
8004       if (Store[x][y] != EL_EMPTY)
8005       {
8006         boolean can_clone = FALSE;
8007         int xx, yy;
8008
8009         // check if element to clone is still there
8010         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8011         {
8012           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8013           {
8014             can_clone = TRUE;
8015
8016             break;
8017           }
8018         }
8019
8020         // cannot clone or target field not free anymore -- do not clone
8021         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8022           Store[x][y] = EL_EMPTY;
8023       }
8024
8025       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8026       {
8027         if (IS_MV_DIAGONAL(MovDir[x][y]))
8028         {
8029           int diagonal_move_dir = MovDir[x][y];
8030           int stored = Store[x][y];
8031           int change_delay = 8;
8032           int graphic;
8033
8034           // android is moving diagonally
8035
8036           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8037
8038           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8039           GfxElement[x][y] = EL_EMC_ANDROID;
8040           GfxAction[x][y] = ACTION_SHRINKING;
8041           GfxDir[x][y] = diagonal_move_dir;
8042           ChangeDelay[x][y] = change_delay;
8043
8044           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8045                                    GfxDir[x][y]);
8046
8047           DrawLevelGraphicAnimation(x, y, graphic);
8048           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8049
8050           if (Feld[newx][newy] == EL_ACID)
8051           {
8052             SplashAcid(newx, newy);
8053
8054             return;
8055           }
8056
8057           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8058
8059           Store[newx][newy] = EL_EMC_ANDROID;
8060           GfxElement[newx][newy] = EL_EMC_ANDROID;
8061           GfxAction[newx][newy] = ACTION_GROWING;
8062           GfxDir[newx][newy] = diagonal_move_dir;
8063           ChangeDelay[newx][newy] = change_delay;
8064
8065           graphic = el_act_dir2img(GfxElement[newx][newy],
8066                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8067
8068           DrawLevelGraphicAnimation(newx, newy, graphic);
8069           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8070
8071           return;
8072         }
8073         else
8074         {
8075           Feld[newx][newy] = EL_EMPTY;
8076           TEST_DrawLevelField(newx, newy);
8077
8078           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8079         }
8080       }
8081       else if (!IS_FREE(newx, newy))
8082       {
8083         return;
8084       }
8085     }
8086     else if (IS_CUSTOM_ELEMENT(element) &&
8087              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8088     {
8089       if (!DigFieldByCE(newx, newy, element))
8090         return;
8091
8092       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8093       {
8094         RunnerVisit[x][y] = FrameCounter;
8095         PlayerVisit[x][y] /= 8;         // expire player visit path
8096       }
8097     }
8098     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8099     {
8100       if (!IS_FREE(newx, newy))
8101       {
8102         if (IS_PLAYER(x, y))
8103           DrawPlayerField(x, y);
8104         else
8105           TEST_DrawLevelField(x, y);
8106
8107         return;
8108       }
8109       else
8110       {
8111         boolean wanna_flame = !RND(10);
8112         int dx = newx - x, dy = newy - y;
8113         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8114         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8115         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8116                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8117         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8118                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8119
8120         if ((wanna_flame ||
8121              IS_CLASSIC_ENEMY(element1) ||
8122              IS_CLASSIC_ENEMY(element2)) &&
8123             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8124             element1 != EL_FLAMES && element2 != EL_FLAMES)
8125         {
8126           ResetGfxAnimation(x, y);
8127           GfxAction[x][y] = ACTION_ATTACKING;
8128
8129           if (IS_PLAYER(x, y))
8130             DrawPlayerField(x, y);
8131           else
8132             TEST_DrawLevelField(x, y);
8133
8134           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8135
8136           MovDelay[x][y] = 50;
8137
8138           Feld[newx][newy] = EL_FLAMES;
8139           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8140             Feld[newx1][newy1] = EL_FLAMES;
8141           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8142             Feld[newx2][newy2] = EL_FLAMES;
8143
8144           return;
8145         }
8146       }
8147     }
8148     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8149              Feld[newx][newy] == EL_DIAMOND)
8150     {
8151       if (IS_MOVING(newx, newy))
8152         RemoveMovingField(newx, newy);
8153       else
8154       {
8155         Feld[newx][newy] = EL_EMPTY;
8156         TEST_DrawLevelField(newx, newy);
8157       }
8158
8159       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8160     }
8161     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8162              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8163     {
8164       if (AmoebaNr[newx][newy])
8165       {
8166         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8167         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8168             Feld[newx][newy] == EL_BD_AMOEBA)
8169           AmoebaCnt[AmoebaNr[newx][newy]]--;
8170       }
8171
8172       if (IS_MOVING(newx, newy))
8173       {
8174         RemoveMovingField(newx, newy);
8175       }
8176       else
8177       {
8178         Feld[newx][newy] = EL_EMPTY;
8179         TEST_DrawLevelField(newx, newy);
8180       }
8181
8182       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8183     }
8184     else if ((element == EL_PACMAN || element == EL_MOLE)
8185              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8186     {
8187       if (AmoebaNr[newx][newy])
8188       {
8189         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8190         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8191             Feld[newx][newy] == EL_BD_AMOEBA)
8192           AmoebaCnt[AmoebaNr[newx][newy]]--;
8193       }
8194
8195       if (element == EL_MOLE)
8196       {
8197         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8198         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8199
8200         ResetGfxAnimation(x, y);
8201         GfxAction[x][y] = ACTION_DIGGING;
8202         TEST_DrawLevelField(x, y);
8203
8204         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8205
8206         return;                         // wait for shrinking amoeba
8207       }
8208       else      // element == EL_PACMAN
8209       {
8210         Feld[newx][newy] = EL_EMPTY;
8211         TEST_DrawLevelField(newx, newy);
8212         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8213       }
8214     }
8215     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8216              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8217               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8218     {
8219       // wait for shrinking amoeba to completely disappear
8220       return;
8221     }
8222     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8223     {
8224       // object was running against a wall
8225
8226       TurnRound(x, y);
8227
8228       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8229         DrawLevelElementAnimation(x, y, element);
8230
8231       if (DONT_TOUCH(element))
8232         TestIfBadThingTouchesPlayer(x, y);
8233
8234       return;
8235     }
8236
8237     InitMovingField(x, y, MovDir[x][y]);
8238
8239     PlayLevelSoundAction(x, y, ACTION_MOVING);
8240   }
8241
8242   if (MovDir[x][y])
8243     ContinueMoving(x, y);
8244 }
8245
8246 void ContinueMoving(int x, int y)
8247 {
8248   int element = Feld[x][y];
8249   struct ElementInfo *ei = &element_info[element];
8250   int direction = MovDir[x][y];
8251   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8252   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8253   int newx = x + dx, newy = y + dy;
8254   int stored = Store[x][y];
8255   int stored_new = Store[newx][newy];
8256   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8257   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8258   boolean last_line = (newy == lev_fieldy - 1);
8259
8260   MovPos[x][y] += getElementMoveStepsize(x, y);
8261
8262   if (pushed_by_player) // special case: moving object pushed by player
8263     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8264
8265   if (ABS(MovPos[x][y]) < TILEX)
8266   {
8267     TEST_DrawLevelField(x, y);
8268
8269     return;     // element is still moving
8270   }
8271
8272   // element reached destination field
8273
8274   Feld[x][y] = EL_EMPTY;
8275   Feld[newx][newy] = element;
8276   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8277
8278   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8279   {
8280     element = Feld[newx][newy] = EL_ACID;
8281   }
8282   else if (element == EL_MOLE)
8283   {
8284     Feld[x][y] = EL_SAND;
8285
8286     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8287   }
8288   else if (element == EL_QUICKSAND_FILLING)
8289   {
8290     element = Feld[newx][newy] = get_next_element(element);
8291     Store[newx][newy] = Store[x][y];
8292   }
8293   else if (element == EL_QUICKSAND_EMPTYING)
8294   {
8295     Feld[x][y] = get_next_element(element);
8296     element = Feld[newx][newy] = Store[x][y];
8297   }
8298   else if (element == EL_QUICKSAND_FAST_FILLING)
8299   {
8300     element = Feld[newx][newy] = get_next_element(element);
8301     Store[newx][newy] = Store[x][y];
8302   }
8303   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8304   {
8305     Feld[x][y] = get_next_element(element);
8306     element = Feld[newx][newy] = Store[x][y];
8307   }
8308   else if (element == EL_MAGIC_WALL_FILLING)
8309   {
8310     element = Feld[newx][newy] = get_next_element(element);
8311     if (!game.magic_wall_active)
8312       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8313     Store[newx][newy] = Store[x][y];
8314   }
8315   else if (element == EL_MAGIC_WALL_EMPTYING)
8316   {
8317     Feld[x][y] = get_next_element(element);
8318     if (!game.magic_wall_active)
8319       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8320     element = Feld[newx][newy] = Store[x][y];
8321
8322     InitField(newx, newy, FALSE);
8323   }
8324   else if (element == EL_BD_MAGIC_WALL_FILLING)
8325   {
8326     element = Feld[newx][newy] = get_next_element(element);
8327     if (!game.magic_wall_active)
8328       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8329     Store[newx][newy] = Store[x][y];
8330   }
8331   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8332   {
8333     Feld[x][y] = get_next_element(element);
8334     if (!game.magic_wall_active)
8335       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8336     element = Feld[newx][newy] = Store[x][y];
8337
8338     InitField(newx, newy, FALSE);
8339   }
8340   else if (element == EL_DC_MAGIC_WALL_FILLING)
8341   {
8342     element = Feld[newx][newy] = get_next_element(element);
8343     if (!game.magic_wall_active)
8344       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8345     Store[newx][newy] = Store[x][y];
8346   }
8347   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8348   {
8349     Feld[x][y] = get_next_element(element);
8350     if (!game.magic_wall_active)
8351       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8352     element = Feld[newx][newy] = Store[x][y];
8353
8354     InitField(newx, newy, FALSE);
8355   }
8356   else if (element == EL_AMOEBA_DROPPING)
8357   {
8358     Feld[x][y] = get_next_element(element);
8359     element = Feld[newx][newy] = Store[x][y];
8360   }
8361   else if (element == EL_SOKOBAN_OBJECT)
8362   {
8363     if (Back[x][y])
8364       Feld[x][y] = Back[x][y];
8365
8366     if (Back[newx][newy])
8367       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8368
8369     Back[x][y] = Back[newx][newy] = 0;
8370   }
8371
8372   Store[x][y] = EL_EMPTY;
8373   MovPos[x][y] = 0;
8374   MovDir[x][y] = 0;
8375   MovDelay[x][y] = 0;
8376
8377   MovDelay[newx][newy] = 0;
8378
8379   if (CAN_CHANGE_OR_HAS_ACTION(element))
8380   {
8381     // copy element change control values to new field
8382     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8383     ChangePage[newx][newy]  = ChangePage[x][y];
8384     ChangeCount[newx][newy] = ChangeCount[x][y];
8385     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8386   }
8387
8388   CustomValue[newx][newy] = CustomValue[x][y];
8389
8390   ChangeDelay[x][y] = 0;
8391   ChangePage[x][y] = -1;
8392   ChangeCount[x][y] = 0;
8393   ChangeEvent[x][y] = -1;
8394
8395   CustomValue[x][y] = 0;
8396
8397   // copy animation control values to new field
8398   GfxFrame[newx][newy]  = GfxFrame[x][y];
8399   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8400   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8401   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8402
8403   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8404
8405   // some elements can leave other elements behind after moving
8406   if (ei->move_leave_element != EL_EMPTY &&
8407       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8408       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8409   {
8410     int move_leave_element = ei->move_leave_element;
8411
8412     // this makes it possible to leave the removed element again
8413     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8414       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8415
8416     Feld[x][y] = move_leave_element;
8417
8418     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8419       MovDir[x][y] = direction;
8420
8421     InitField(x, y, FALSE);
8422
8423     if (GFX_CRUMBLED(Feld[x][y]))
8424       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8425
8426     if (ELEM_IS_PLAYER(move_leave_element))
8427       RelocatePlayer(x, y, move_leave_element);
8428   }
8429
8430   // do this after checking for left-behind element
8431   ResetGfxAnimation(x, y);      // reset animation values for old field
8432
8433   if (!CAN_MOVE(element) ||
8434       (CAN_FALL(element) && direction == MV_DOWN &&
8435        (element == EL_SPRING ||
8436         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8437         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8438     GfxDir[x][y] = MovDir[newx][newy] = 0;
8439
8440   TEST_DrawLevelField(x, y);
8441   TEST_DrawLevelField(newx, newy);
8442
8443   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8444
8445   // prevent pushed element from moving on in pushed direction
8446   if (pushed_by_player && CAN_MOVE(element) &&
8447       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8448       !(element_info[element].move_pattern & direction))
8449     TurnRound(newx, newy);
8450
8451   // prevent elements on conveyor belt from moving on in last direction
8452   if (pushed_by_conveyor && CAN_FALL(element) &&
8453       direction & MV_HORIZONTAL)
8454     MovDir[newx][newy] = 0;
8455
8456   if (!pushed_by_player)
8457   {
8458     int nextx = newx + dx, nexty = newy + dy;
8459     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8460
8461     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8462
8463     if (CAN_FALL(element) && direction == MV_DOWN)
8464       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8465
8466     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8467       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8468
8469     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8470       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8471   }
8472
8473   if (DONT_TOUCH(element))      // object may be nasty to player or others
8474   {
8475     TestIfBadThingTouchesPlayer(newx, newy);
8476     TestIfBadThingTouchesFriend(newx, newy);
8477
8478     if (!IS_CUSTOM_ELEMENT(element))
8479       TestIfBadThingTouchesOtherBadThing(newx, newy);
8480   }
8481   else if (element == EL_PENGUIN)
8482     TestIfFriendTouchesBadThing(newx, newy);
8483
8484   if (DONT_GET_HIT_BY(element))
8485   {
8486     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8487   }
8488
8489   // give the player one last chance (one more frame) to move away
8490   if (CAN_FALL(element) && direction == MV_DOWN &&
8491       (last_line || (!IS_FREE(x, newy + 1) &&
8492                      (!IS_PLAYER(x, newy + 1) ||
8493                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8494     Impact(x, newy);
8495
8496   if (pushed_by_player && !game.use_change_when_pushing_bug)
8497   {
8498     int push_side = MV_DIR_OPPOSITE(direction);
8499     struct PlayerInfo *player = PLAYERINFO(x, y);
8500
8501     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8502                                player->index_bit, push_side);
8503     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8504                                         player->index_bit, push_side);
8505   }
8506
8507   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8508     MovDelay[newx][newy] = 1;
8509
8510   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8511
8512   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8513   TestIfElementHitsCustomElement(newx, newy, direction);
8514   TestIfPlayerTouchesCustomElement(newx, newy);
8515   TestIfElementTouchesCustomElement(newx, newy);
8516
8517   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8518       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8519     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8520                              MV_DIR_OPPOSITE(direction));
8521 }
8522
8523 int AmoebeNachbarNr(int ax, int ay)
8524 {
8525   int i;
8526   int element = Feld[ax][ay];
8527   int group_nr = 0;
8528   static int xy[4][2] =
8529   {
8530     { 0, -1 },
8531     { -1, 0 },
8532     { +1, 0 },
8533     { 0, +1 }
8534   };
8535
8536   for (i = 0; i < NUM_DIRECTIONS; i++)
8537   {
8538     int x = ax + xy[i][0];
8539     int y = ay + xy[i][1];
8540
8541     if (!IN_LEV_FIELD(x, y))
8542       continue;
8543
8544     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8545       group_nr = AmoebaNr[x][y];
8546   }
8547
8548   return group_nr;
8549 }
8550
8551 static void AmoebenVereinigen(int ax, int ay)
8552 {
8553   int i, x, y, xx, yy;
8554   int new_group_nr = AmoebaNr[ax][ay];
8555   static int xy[4][2] =
8556   {
8557     { 0, -1 },
8558     { -1, 0 },
8559     { +1, 0 },
8560     { 0, +1 }
8561   };
8562
8563   if (new_group_nr == 0)
8564     return;
8565
8566   for (i = 0; i < NUM_DIRECTIONS; i++)
8567   {
8568     x = ax + xy[i][0];
8569     y = ay + xy[i][1];
8570
8571     if (!IN_LEV_FIELD(x, y))
8572       continue;
8573
8574     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8575          Feld[x][y] == EL_BD_AMOEBA ||
8576          Feld[x][y] == EL_AMOEBA_DEAD) &&
8577         AmoebaNr[x][y] != new_group_nr)
8578     {
8579       int old_group_nr = AmoebaNr[x][y];
8580
8581       if (old_group_nr == 0)
8582         return;
8583
8584       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8585       AmoebaCnt[old_group_nr] = 0;
8586       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8587       AmoebaCnt2[old_group_nr] = 0;
8588
8589       SCAN_PLAYFIELD(xx, yy)
8590       {
8591         if (AmoebaNr[xx][yy] == old_group_nr)
8592           AmoebaNr[xx][yy] = new_group_nr;
8593       }
8594     }
8595   }
8596 }
8597
8598 void AmoebeUmwandeln(int ax, int ay)
8599 {
8600   int i, x, y;
8601
8602   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8603   {
8604     int group_nr = AmoebaNr[ax][ay];
8605
8606 #ifdef DEBUG
8607     if (group_nr == 0)
8608     {
8609       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8610       printf("AmoebeUmwandeln(): This should never happen!\n");
8611       return;
8612     }
8613 #endif
8614
8615     SCAN_PLAYFIELD(x, y)
8616     {
8617       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8618       {
8619         AmoebaNr[x][y] = 0;
8620         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8621       }
8622     }
8623
8624     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8625                             SND_AMOEBA_TURNING_TO_GEM :
8626                             SND_AMOEBA_TURNING_TO_ROCK));
8627     Bang(ax, ay);
8628   }
8629   else
8630   {
8631     static int xy[4][2] =
8632     {
8633       { 0, -1 },
8634       { -1, 0 },
8635       { +1, 0 },
8636       { 0, +1 }
8637     };
8638
8639     for (i = 0; i < NUM_DIRECTIONS; i++)
8640     {
8641       x = ax + xy[i][0];
8642       y = ay + xy[i][1];
8643
8644       if (!IN_LEV_FIELD(x, y))
8645         continue;
8646
8647       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8648       {
8649         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8650                               SND_AMOEBA_TURNING_TO_GEM :
8651                               SND_AMOEBA_TURNING_TO_ROCK));
8652         Bang(x, y);
8653       }
8654     }
8655   }
8656 }
8657
8658 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8659 {
8660   int x, y;
8661   int group_nr = AmoebaNr[ax][ay];
8662   boolean done = FALSE;
8663
8664 #ifdef DEBUG
8665   if (group_nr == 0)
8666   {
8667     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8668     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8669     return;
8670   }
8671 #endif
8672
8673   SCAN_PLAYFIELD(x, y)
8674   {
8675     if (AmoebaNr[x][y] == group_nr &&
8676         (Feld[x][y] == EL_AMOEBA_DEAD ||
8677          Feld[x][y] == EL_BD_AMOEBA ||
8678          Feld[x][y] == EL_AMOEBA_GROWING))
8679     {
8680       AmoebaNr[x][y] = 0;
8681       Feld[x][y] = new_element;
8682       InitField(x, y, FALSE);
8683       TEST_DrawLevelField(x, y);
8684       done = TRUE;
8685     }
8686   }
8687
8688   if (done)
8689     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8690                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8691                             SND_BD_AMOEBA_TURNING_TO_GEM));
8692 }
8693
8694 static void AmoebeWaechst(int x, int y)
8695 {
8696   static unsigned int sound_delay = 0;
8697   static unsigned int sound_delay_value = 0;
8698
8699   if (!MovDelay[x][y])          // start new growing cycle
8700   {
8701     MovDelay[x][y] = 7;
8702
8703     if (DelayReached(&sound_delay, sound_delay_value))
8704     {
8705       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8706       sound_delay_value = 30;
8707     }
8708   }
8709
8710   if (MovDelay[x][y])           // wait some time before growing bigger
8711   {
8712     MovDelay[x][y]--;
8713     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8714     {
8715       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8716                                            6 - MovDelay[x][y]);
8717
8718       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8719     }
8720
8721     if (!MovDelay[x][y])
8722     {
8723       Feld[x][y] = Store[x][y];
8724       Store[x][y] = 0;
8725       TEST_DrawLevelField(x, y);
8726     }
8727   }
8728 }
8729
8730 static void AmoebaDisappearing(int x, int y)
8731 {
8732   static unsigned int sound_delay = 0;
8733   static unsigned int sound_delay_value = 0;
8734
8735   if (!MovDelay[x][y])          // start new shrinking cycle
8736   {
8737     MovDelay[x][y] = 7;
8738
8739     if (DelayReached(&sound_delay, sound_delay_value))
8740       sound_delay_value = 30;
8741   }
8742
8743   if (MovDelay[x][y])           // wait some time before shrinking
8744   {
8745     MovDelay[x][y]--;
8746     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8747     {
8748       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8749                                            6 - MovDelay[x][y]);
8750
8751       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8752     }
8753
8754     if (!MovDelay[x][y])
8755     {
8756       Feld[x][y] = EL_EMPTY;
8757       TEST_DrawLevelField(x, y);
8758
8759       // don't let mole enter this field in this cycle;
8760       // (give priority to objects falling to this field from above)
8761       Stop[x][y] = TRUE;
8762     }
8763   }
8764 }
8765
8766 static void AmoebeAbleger(int ax, int ay)
8767 {
8768   int i;
8769   int element = Feld[ax][ay];
8770   int graphic = el2img(element);
8771   int newax = ax, neway = ay;
8772   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8773   static int xy[4][2] =
8774   {
8775     { 0, -1 },
8776     { -1, 0 },
8777     { +1, 0 },
8778     { 0, +1 }
8779   };
8780
8781   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8782   {
8783     Feld[ax][ay] = EL_AMOEBA_DEAD;
8784     TEST_DrawLevelField(ax, ay);
8785     return;
8786   }
8787
8788   if (IS_ANIMATED(graphic))
8789     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8790
8791   if (!MovDelay[ax][ay])        // start making new amoeba field
8792     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8793
8794   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8795   {
8796     MovDelay[ax][ay]--;
8797     if (MovDelay[ax][ay])
8798       return;
8799   }
8800
8801   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8802   {
8803     int start = RND(4);
8804     int x = ax + xy[start][0];
8805     int y = ay + xy[start][1];
8806
8807     if (!IN_LEV_FIELD(x, y))
8808       return;
8809
8810     if (IS_FREE(x, y) ||
8811         CAN_GROW_INTO(Feld[x][y]) ||
8812         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8813         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8814     {
8815       newax = x;
8816       neway = y;
8817     }
8818
8819     if (newax == ax && neway == ay)
8820       return;
8821   }
8822   else                          // normal or "filled" (BD style) amoeba
8823   {
8824     int start = RND(4);
8825     boolean waiting_for_player = FALSE;
8826
8827     for (i = 0; i < NUM_DIRECTIONS; i++)
8828     {
8829       int j = (start + i) % 4;
8830       int x = ax + xy[j][0];
8831       int y = ay + xy[j][1];
8832
8833       if (!IN_LEV_FIELD(x, y))
8834         continue;
8835
8836       if (IS_FREE(x, y) ||
8837           CAN_GROW_INTO(Feld[x][y]) ||
8838           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8839           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8840       {
8841         newax = x;
8842         neway = y;
8843         break;
8844       }
8845       else if (IS_PLAYER(x, y))
8846         waiting_for_player = TRUE;
8847     }
8848
8849     if (newax == ax && neway == ay)             // amoeba cannot grow
8850     {
8851       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8852       {
8853         Feld[ax][ay] = EL_AMOEBA_DEAD;
8854         TEST_DrawLevelField(ax, ay);
8855         AmoebaCnt[AmoebaNr[ax][ay]]--;
8856
8857         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8858         {
8859           if (element == EL_AMOEBA_FULL)
8860             AmoebeUmwandeln(ax, ay);
8861           else if (element == EL_BD_AMOEBA)
8862             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8863         }
8864       }
8865       return;
8866     }
8867     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8868     {
8869       // amoeba gets larger by growing in some direction
8870
8871       int new_group_nr = AmoebaNr[ax][ay];
8872
8873 #ifdef DEBUG
8874   if (new_group_nr == 0)
8875   {
8876     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8877     printf("AmoebeAbleger(): This should never happen!\n");
8878     return;
8879   }
8880 #endif
8881
8882       AmoebaNr[newax][neway] = new_group_nr;
8883       AmoebaCnt[new_group_nr]++;
8884       AmoebaCnt2[new_group_nr]++;
8885
8886       // if amoeba touches other amoeba(s) after growing, unify them
8887       AmoebenVereinigen(newax, neway);
8888
8889       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8890       {
8891         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8892         return;
8893       }
8894     }
8895   }
8896
8897   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8898       (neway == lev_fieldy - 1 && newax != ax))
8899   {
8900     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8901     Store[newax][neway] = element;
8902   }
8903   else if (neway == ay || element == EL_EMC_DRIPPER)
8904   {
8905     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8906
8907     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8908   }
8909   else
8910   {
8911     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8912     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8913     Store[ax][ay] = EL_AMOEBA_DROP;
8914     ContinueMoving(ax, ay);
8915     return;
8916   }
8917
8918   TEST_DrawLevelField(newax, neway);
8919 }
8920
8921 static void Life(int ax, int ay)
8922 {
8923   int x1, y1, x2, y2;
8924   int life_time = 40;
8925   int element = Feld[ax][ay];
8926   int graphic = el2img(element);
8927   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8928                          level.biomaze);
8929   boolean changed = FALSE;
8930
8931   if (IS_ANIMATED(graphic))
8932     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8933
8934   if (Stop[ax][ay])
8935     return;
8936
8937   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8938     MovDelay[ax][ay] = life_time;
8939
8940   if (MovDelay[ax][ay])         // wait some time before next cycle
8941   {
8942     MovDelay[ax][ay]--;
8943     if (MovDelay[ax][ay])
8944       return;
8945   }
8946
8947   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8948   {
8949     int xx = ax+x1, yy = ay+y1;
8950     int old_element = Feld[xx][yy];
8951     int num_neighbours = 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       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8964       boolean is_neighbour = FALSE;
8965
8966       if (level.use_life_bugs)
8967         is_neighbour =
8968           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8969            (IS_FREE(x, y)                             &&  Stop[x][y]));
8970       else
8971         is_neighbour =
8972           (Last[x][y] == element || is_player_cell);
8973
8974       if (is_neighbour)
8975         num_neighbours++;
8976     }
8977
8978     boolean is_free = FALSE;
8979
8980     if (level.use_life_bugs)
8981       is_free = (IS_FREE(xx, yy));
8982     else
8983       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8984
8985     if (xx == ax && yy == ay)           // field in the middle
8986     {
8987       if (num_neighbours < life_parameter[0] ||
8988           num_neighbours > life_parameter[1])
8989       {
8990         Feld[xx][yy] = EL_EMPTY;
8991         if (Feld[xx][yy] != old_element)
8992           TEST_DrawLevelField(xx, yy);
8993         Stop[xx][yy] = TRUE;
8994         changed = TRUE;
8995       }
8996     }
8997     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8998     {                                   // free border field
8999       if (num_neighbours >= life_parameter[2] &&
9000           num_neighbours <= life_parameter[3])
9001       {
9002         Feld[xx][yy] = element;
9003         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9004         if (Feld[xx][yy] != old_element)
9005           TEST_DrawLevelField(xx, yy);
9006         Stop[xx][yy] = TRUE;
9007         changed = TRUE;
9008       }
9009     }
9010   }
9011
9012   if (changed)
9013     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9014                    SND_GAME_OF_LIFE_GROWING);
9015 }
9016
9017 static void InitRobotWheel(int x, int y)
9018 {
9019   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9020 }
9021
9022 static void RunRobotWheel(int x, int y)
9023 {
9024   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9025 }
9026
9027 static void StopRobotWheel(int x, int y)
9028 {
9029   if (ZX == x && ZY == y)
9030   {
9031     ZX = ZY = -1;
9032
9033     game.robot_wheel_active = FALSE;
9034   }
9035 }
9036
9037 static void InitTimegateWheel(int x, int y)
9038 {
9039   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9040 }
9041
9042 static void RunTimegateWheel(int x, int y)
9043 {
9044   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9045 }
9046
9047 static void InitMagicBallDelay(int x, int y)
9048 {
9049   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9050 }
9051
9052 static void ActivateMagicBall(int bx, int by)
9053 {
9054   int x, y;
9055
9056   if (level.ball_random)
9057   {
9058     int pos_border = RND(8);    // select one of the eight border elements
9059     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9060     int xx = pos_content % 3;
9061     int yy = pos_content / 3;
9062
9063     x = bx - 1 + xx;
9064     y = by - 1 + yy;
9065
9066     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9067       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9068   }
9069   else
9070   {
9071     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9072     {
9073       int xx = x - bx + 1;
9074       int yy = y - by + 1;
9075
9076       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9077         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9078     }
9079   }
9080
9081   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9082 }
9083
9084 static void CheckExit(int x, int y)
9085 {
9086   if (local_player->gems_still_needed > 0 ||
9087       local_player->sokoban_fields_still_needed > 0 ||
9088       local_player->sokoban_objects_still_needed > 0 ||
9089       local_player->lights_still_needed > 0)
9090   {
9091     int element = Feld[x][y];
9092     int graphic = el2img(element);
9093
9094     if (IS_ANIMATED(graphic))
9095       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9096
9097     return;
9098   }
9099
9100   if (AllPlayersGone)   // do not re-open exit door closed after last player
9101     return;
9102
9103   Feld[x][y] = EL_EXIT_OPENING;
9104
9105   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9106 }
9107
9108 static void CheckExitEM(int x, int y)
9109 {
9110   if (local_player->gems_still_needed > 0 ||
9111       local_player->sokoban_fields_still_needed > 0 ||
9112       local_player->sokoban_objects_still_needed > 0 ||
9113       local_player->lights_still_needed > 0)
9114   {
9115     int element = Feld[x][y];
9116     int graphic = el2img(element);
9117
9118     if (IS_ANIMATED(graphic))
9119       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9120
9121     return;
9122   }
9123
9124   if (AllPlayersGone)   // do not re-open exit door closed after last player
9125     return;
9126
9127   Feld[x][y] = EL_EM_EXIT_OPENING;
9128
9129   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9130 }
9131
9132 static void CheckExitSteel(int x, int y)
9133 {
9134   if (local_player->gems_still_needed > 0 ||
9135       local_player->sokoban_fields_still_needed > 0 ||
9136       local_player->sokoban_objects_still_needed > 0 ||
9137       local_player->lights_still_needed > 0)
9138   {
9139     int element = Feld[x][y];
9140     int graphic = el2img(element);
9141
9142     if (IS_ANIMATED(graphic))
9143       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9144
9145     return;
9146   }
9147
9148   if (AllPlayersGone)   // do not re-open exit door closed after last player
9149     return;
9150
9151   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9152
9153   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9154 }
9155
9156 static void CheckExitSteelEM(int x, int y)
9157 {
9158   if (local_player->gems_still_needed > 0 ||
9159       local_player->sokoban_fields_still_needed > 0 ||
9160       local_player->sokoban_objects_still_needed > 0 ||
9161       local_player->lights_still_needed > 0)
9162   {
9163     int element = Feld[x][y];
9164     int graphic = el2img(element);
9165
9166     if (IS_ANIMATED(graphic))
9167       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9168
9169     return;
9170   }
9171
9172   if (AllPlayersGone)   // do not re-open exit door closed after last player
9173     return;
9174
9175   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9176
9177   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9178 }
9179
9180 static void CheckExitSP(int x, int y)
9181 {
9182   if (local_player->gems_still_needed > 0)
9183   {
9184     int element = Feld[x][y];
9185     int graphic = el2img(element);
9186
9187     if (IS_ANIMATED(graphic))
9188       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9189
9190     return;
9191   }
9192
9193   if (AllPlayersGone)   // do not re-open exit door closed after last player
9194     return;
9195
9196   Feld[x][y] = EL_SP_EXIT_OPENING;
9197
9198   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9199 }
9200
9201 static void CloseAllOpenTimegates(void)
9202 {
9203   int x, y;
9204
9205   SCAN_PLAYFIELD(x, y)
9206   {
9207     int element = Feld[x][y];
9208
9209     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9210     {
9211       Feld[x][y] = EL_TIMEGATE_CLOSING;
9212
9213       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9214     }
9215   }
9216 }
9217
9218 static void DrawTwinkleOnField(int x, int y)
9219 {
9220   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9221     return;
9222
9223   if (Feld[x][y] == EL_BD_DIAMOND)
9224     return;
9225
9226   if (MovDelay[x][y] == 0)      // next animation frame
9227     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9228
9229   if (MovDelay[x][y] != 0)      // wait some time before next frame
9230   {
9231     MovDelay[x][y]--;
9232
9233     DrawLevelElementAnimation(x, y, Feld[x][y]);
9234
9235     if (MovDelay[x][y] != 0)
9236     {
9237       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9238                                            10 - MovDelay[x][y]);
9239
9240       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9241     }
9242   }
9243 }
9244
9245 static void MauerWaechst(int x, int y)
9246 {
9247   int delay = 6;
9248
9249   if (!MovDelay[x][y])          // next animation frame
9250     MovDelay[x][y] = 3 * delay;
9251
9252   if (MovDelay[x][y])           // wait some time before next frame
9253   {
9254     MovDelay[x][y]--;
9255
9256     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9257     {
9258       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9259       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9260
9261       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9262     }
9263
9264     if (!MovDelay[x][y])
9265     {
9266       if (MovDir[x][y] == MV_LEFT)
9267       {
9268         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9269           TEST_DrawLevelField(x - 1, y);
9270       }
9271       else if (MovDir[x][y] == MV_RIGHT)
9272       {
9273         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9274           TEST_DrawLevelField(x + 1, y);
9275       }
9276       else if (MovDir[x][y] == MV_UP)
9277       {
9278         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9279           TEST_DrawLevelField(x, y - 1);
9280       }
9281       else
9282       {
9283         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9284           TEST_DrawLevelField(x, y + 1);
9285       }
9286
9287       Feld[x][y] = Store[x][y];
9288       Store[x][y] = 0;
9289       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9290       TEST_DrawLevelField(x, y);
9291     }
9292   }
9293 }
9294
9295 static void MauerAbleger(int ax, int ay)
9296 {
9297   int element = Feld[ax][ay];
9298   int graphic = el2img(element);
9299   boolean oben_frei = FALSE, unten_frei = FALSE;
9300   boolean links_frei = FALSE, rechts_frei = FALSE;
9301   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9302   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9303   boolean new_wall = FALSE;
9304
9305   if (IS_ANIMATED(graphic))
9306     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9307
9308   if (!MovDelay[ax][ay])        // start building new wall
9309     MovDelay[ax][ay] = 6;
9310
9311   if (MovDelay[ax][ay])         // wait some time before building new wall
9312   {
9313     MovDelay[ax][ay]--;
9314     if (MovDelay[ax][ay])
9315       return;
9316   }
9317
9318   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9319     oben_frei = TRUE;
9320   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9321     unten_frei = TRUE;
9322   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9323     links_frei = TRUE;
9324   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9325     rechts_frei = TRUE;
9326
9327   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9328       element == EL_EXPANDABLE_WALL_ANY)
9329   {
9330     if (oben_frei)
9331     {
9332       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9333       Store[ax][ay-1] = element;
9334       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9335       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9336         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9337                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9338       new_wall = TRUE;
9339     }
9340     if (unten_frei)
9341     {
9342       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9343       Store[ax][ay+1] = element;
9344       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9345       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9346         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9347                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9348       new_wall = TRUE;
9349     }
9350   }
9351
9352   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9353       element == EL_EXPANDABLE_WALL_ANY ||
9354       element == EL_EXPANDABLE_WALL ||
9355       element == EL_BD_EXPANDABLE_WALL)
9356   {
9357     if (links_frei)
9358     {
9359       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9360       Store[ax-1][ay] = element;
9361       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9362       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9363         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9364                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9365       new_wall = TRUE;
9366     }
9367
9368     if (rechts_frei)
9369     {
9370       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9371       Store[ax+1][ay] = element;
9372       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9373       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9374         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9375                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9376       new_wall = TRUE;
9377     }
9378   }
9379
9380   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9381     TEST_DrawLevelField(ax, ay);
9382
9383   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9384     oben_massiv = TRUE;
9385   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9386     unten_massiv = TRUE;
9387   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9388     links_massiv = TRUE;
9389   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9390     rechts_massiv = TRUE;
9391
9392   if (((oben_massiv && unten_massiv) ||
9393        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9394        element == EL_EXPANDABLE_WALL) &&
9395       ((links_massiv && rechts_massiv) ||
9396        element == EL_EXPANDABLE_WALL_VERTICAL))
9397     Feld[ax][ay] = EL_WALL;
9398
9399   if (new_wall)
9400     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9401 }
9402
9403 static void MauerAblegerStahl(int ax, int ay)
9404 {
9405   int element = Feld[ax][ay];
9406   int graphic = el2img(element);
9407   boolean oben_frei = FALSE, unten_frei = FALSE;
9408   boolean links_frei = FALSE, rechts_frei = FALSE;
9409   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9410   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9411   boolean new_wall = FALSE;
9412
9413   if (IS_ANIMATED(graphic))
9414     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9415
9416   if (!MovDelay[ax][ay])        // start building new wall
9417     MovDelay[ax][ay] = 6;
9418
9419   if (MovDelay[ax][ay])         // wait some time before building new wall
9420   {
9421     MovDelay[ax][ay]--;
9422     if (MovDelay[ax][ay])
9423       return;
9424   }
9425
9426   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9427     oben_frei = TRUE;
9428   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9429     unten_frei = TRUE;
9430   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9431     links_frei = TRUE;
9432   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9433     rechts_frei = TRUE;
9434
9435   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9436       element == EL_EXPANDABLE_STEELWALL_ANY)
9437   {
9438     if (oben_frei)
9439     {
9440       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9441       Store[ax][ay-1] = element;
9442       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9443       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9444         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9445                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9446       new_wall = TRUE;
9447     }
9448     if (unten_frei)
9449     {
9450       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9451       Store[ax][ay+1] = element;
9452       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9453       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9454         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9455                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9456       new_wall = TRUE;
9457     }
9458   }
9459
9460   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9461       element == EL_EXPANDABLE_STEELWALL_ANY)
9462   {
9463     if (links_frei)
9464     {
9465       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9466       Store[ax-1][ay] = element;
9467       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9468       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9469         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9470                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9471       new_wall = TRUE;
9472     }
9473
9474     if (rechts_frei)
9475     {
9476       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9477       Store[ax+1][ay] = element;
9478       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9479       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9480         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9481                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9482       new_wall = TRUE;
9483     }
9484   }
9485
9486   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9487     oben_massiv = TRUE;
9488   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9489     unten_massiv = TRUE;
9490   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9491     links_massiv = TRUE;
9492   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9493     rechts_massiv = TRUE;
9494
9495   if (((oben_massiv && unten_massiv) ||
9496        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9497       ((links_massiv && rechts_massiv) ||
9498        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9499     Feld[ax][ay] = EL_STEELWALL;
9500
9501   if (new_wall)
9502     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9503 }
9504
9505 static void CheckForDragon(int x, int y)
9506 {
9507   int i, j;
9508   boolean dragon_found = FALSE;
9509   static int xy[4][2] =
9510   {
9511     { 0, -1 },
9512     { -1, 0 },
9513     { +1, 0 },
9514     { 0, +1 }
9515   };
9516
9517   for (i = 0; i < NUM_DIRECTIONS; i++)
9518   {
9519     for (j = 0; j < 4; j++)
9520     {
9521       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9522
9523       if (IN_LEV_FIELD(xx, yy) &&
9524           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9525       {
9526         if (Feld[xx][yy] == EL_DRAGON)
9527           dragon_found = TRUE;
9528       }
9529       else
9530         break;
9531     }
9532   }
9533
9534   if (!dragon_found)
9535   {
9536     for (i = 0; i < NUM_DIRECTIONS; i++)
9537     {
9538       for (j = 0; j < 3; j++)
9539       {
9540         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9541   
9542         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9543         {
9544           Feld[xx][yy] = EL_EMPTY;
9545           TEST_DrawLevelField(xx, yy);
9546         }
9547         else
9548           break;
9549       }
9550     }
9551   }
9552 }
9553
9554 static void InitBuggyBase(int x, int y)
9555 {
9556   int element = Feld[x][y];
9557   int activating_delay = FRAMES_PER_SECOND / 4;
9558
9559   ChangeDelay[x][y] =
9560     (element == EL_SP_BUGGY_BASE ?
9561      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9562      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9563      activating_delay :
9564      element == EL_SP_BUGGY_BASE_ACTIVE ?
9565      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9566 }
9567
9568 static void WarnBuggyBase(int x, int y)
9569 {
9570   int i;
9571   static int xy[4][2] =
9572   {
9573     { 0, -1 },
9574     { -1, 0 },
9575     { +1, 0 },
9576     { 0, +1 }
9577   };
9578
9579   for (i = 0; i < NUM_DIRECTIONS; i++)
9580   {
9581     int xx = x + xy[i][0];
9582     int yy = y + xy[i][1];
9583
9584     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9585     {
9586       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9587
9588       break;
9589     }
9590   }
9591 }
9592
9593 static void InitTrap(int x, int y)
9594 {
9595   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9596 }
9597
9598 static void ActivateTrap(int x, int y)
9599 {
9600   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9601 }
9602
9603 static void ChangeActiveTrap(int x, int y)
9604 {
9605   int graphic = IMG_TRAP_ACTIVE;
9606
9607   // if new animation frame was drawn, correct crumbled sand border
9608   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9609     TEST_DrawLevelFieldCrumbled(x, y);
9610 }
9611
9612 static int getSpecialActionElement(int element, int number, int base_element)
9613 {
9614   return (element != EL_EMPTY ? element :
9615           number != -1 ? base_element + number - 1 :
9616           EL_EMPTY);
9617 }
9618
9619 static int getModifiedActionNumber(int value_old, int operator, int operand,
9620                                    int value_min, int value_max)
9621 {
9622   int value_new = (operator == CA_MODE_SET      ? operand :
9623                    operator == CA_MODE_ADD      ? value_old + operand :
9624                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9625                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9626                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9627                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9628                    value_old);
9629
9630   return (value_new < value_min ? value_min :
9631           value_new > value_max ? value_max :
9632           value_new);
9633 }
9634
9635 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9636 {
9637   struct ElementInfo *ei = &element_info[element];
9638   struct ElementChangeInfo *change = &ei->change_page[page];
9639   int target_element = change->target_element;
9640   int action_type = change->action_type;
9641   int action_mode = change->action_mode;
9642   int action_arg = change->action_arg;
9643   int action_element = change->action_element;
9644   int i;
9645
9646   if (!change->has_action)
9647     return;
9648
9649   // ---------- determine action paramater values -----------------------------
9650
9651   int level_time_value =
9652     (level.time > 0 ? TimeLeft :
9653      TimePlayed);
9654
9655   int action_arg_element_raw =
9656     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9657      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9658      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9659      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9660      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9661      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9662      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9663      EL_EMPTY);
9664   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9665
9666   int action_arg_direction =
9667     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9668      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9669      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9670      change->actual_trigger_side :
9671      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9672      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9673      MV_NONE);
9674
9675   int action_arg_number_min =
9676     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9677      CA_ARG_MIN);
9678
9679   int action_arg_number_max =
9680     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9681      action_type == CA_SET_LEVEL_GEMS ? 999 :
9682      action_type == CA_SET_LEVEL_TIME ? 9999 :
9683      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9684      action_type == CA_SET_CE_VALUE ? 9999 :
9685      action_type == CA_SET_CE_SCORE ? 9999 :
9686      CA_ARG_MAX);
9687
9688   int action_arg_number_reset =
9689     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9690      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9691      action_type == CA_SET_LEVEL_TIME ? level.time :
9692      action_type == CA_SET_LEVEL_SCORE ? 0 :
9693      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9694      action_type == CA_SET_CE_SCORE ? 0 :
9695      0);
9696
9697   int action_arg_number =
9698     (action_arg <= CA_ARG_MAX ? action_arg :
9699      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9700      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9701      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9702      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9703      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9704      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9705      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9706      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9707      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9708      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9709      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9710      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9711      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9712      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9713      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9714      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9715      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9716      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9717      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9718      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9719      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9720      -1);
9721
9722   int action_arg_number_old =
9723     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9724      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9725      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9726      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9727      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9728      0);
9729
9730   int action_arg_number_new =
9731     getModifiedActionNumber(action_arg_number_old,
9732                             action_mode, action_arg_number,
9733                             action_arg_number_min, action_arg_number_max);
9734
9735   int trigger_player_bits =
9736     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9737      change->actual_trigger_player_bits : change->trigger_player);
9738
9739   int action_arg_player_bits =
9740     (action_arg >= CA_ARG_PLAYER_1 &&
9741      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9742      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9743      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9744      PLAYER_BITS_ANY);
9745
9746   // ---------- execute action  -----------------------------------------------
9747
9748   switch (action_type)
9749   {
9750     case CA_NO_ACTION:
9751     {
9752       return;
9753     }
9754
9755     // ---------- level actions  ----------------------------------------------
9756
9757     case CA_RESTART_LEVEL:
9758     {
9759       game.restart_level = TRUE;
9760
9761       break;
9762     }
9763
9764     case CA_SHOW_ENVELOPE:
9765     {
9766       int element = getSpecialActionElement(action_arg_element,
9767                                             action_arg_number, EL_ENVELOPE_1);
9768
9769       if (IS_ENVELOPE(element))
9770         local_player->show_envelope = element;
9771
9772       break;
9773     }
9774
9775     case CA_SET_LEVEL_TIME:
9776     {
9777       if (level.time > 0)       // only modify limited time value
9778       {
9779         TimeLeft = action_arg_number_new;
9780
9781         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9782
9783         DisplayGameControlValues();
9784
9785         if (!TimeLeft && setup.time_limit)
9786           for (i = 0; i < MAX_PLAYERS; i++)
9787             KillPlayer(&stored_player[i]);
9788       }
9789
9790       break;
9791     }
9792
9793     case CA_SET_LEVEL_SCORE:
9794     {
9795       local_player->score = action_arg_number_new;
9796
9797       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9798
9799       DisplayGameControlValues();
9800
9801       break;
9802     }
9803
9804     case CA_SET_LEVEL_GEMS:
9805     {
9806       local_player->gems_still_needed = action_arg_number_new;
9807
9808       game.snapshot.collected_item = TRUE;
9809
9810       game_panel_controls[GAME_PANEL_GEMS].value =
9811         local_player->gems_still_needed;
9812
9813       DisplayGameControlValues();
9814
9815       break;
9816     }
9817
9818     case CA_SET_LEVEL_WIND:
9819     {
9820       game.wind_direction = action_arg_direction;
9821
9822       break;
9823     }
9824
9825     case CA_SET_LEVEL_RANDOM_SEED:
9826     {
9827       // ensure that setting a new random seed while playing is predictable
9828       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9829
9830       break;
9831     }
9832
9833     // ---------- player actions  ---------------------------------------------
9834
9835     case CA_MOVE_PLAYER:
9836     {
9837       // automatically move to the next field in specified direction
9838       for (i = 0; i < MAX_PLAYERS; i++)
9839         if (trigger_player_bits & (1 << i))
9840           stored_player[i].programmed_action = action_arg_direction;
9841
9842       break;
9843     }
9844
9845     case CA_EXIT_PLAYER:
9846     {
9847       for (i = 0; i < MAX_PLAYERS; i++)
9848         if (action_arg_player_bits & (1 << i))
9849           ExitPlayer(&stored_player[i]);
9850
9851       if (AllPlayersGone)
9852         LevelSolved();
9853
9854       break;
9855     }
9856
9857     case CA_KILL_PLAYER:
9858     {
9859       for (i = 0; i < MAX_PLAYERS; i++)
9860         if (action_arg_player_bits & (1 << i))
9861           KillPlayer(&stored_player[i]);
9862
9863       break;
9864     }
9865
9866     case CA_SET_PLAYER_KEYS:
9867     {
9868       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9869       int element = getSpecialActionElement(action_arg_element,
9870                                             action_arg_number, EL_KEY_1);
9871
9872       if (IS_KEY(element))
9873       {
9874         for (i = 0; i < MAX_PLAYERS; i++)
9875         {
9876           if (trigger_player_bits & (1 << i))
9877           {
9878             stored_player[i].key[KEY_NR(element)] = key_state;
9879
9880             DrawGameDoorValues();
9881           }
9882         }
9883       }
9884
9885       break;
9886     }
9887
9888     case CA_SET_PLAYER_SPEED:
9889     {
9890       for (i = 0; i < MAX_PLAYERS; i++)
9891       {
9892         if (trigger_player_bits & (1 << i))
9893         {
9894           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9895
9896           if (action_arg == CA_ARG_SPEED_FASTER &&
9897               stored_player[i].cannot_move)
9898           {
9899             action_arg_number = STEPSIZE_VERY_SLOW;
9900           }
9901           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9902                    action_arg == CA_ARG_SPEED_FASTER)
9903           {
9904             action_arg_number = 2;
9905             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9906                            CA_MODE_MULTIPLY);
9907           }
9908           else if (action_arg == CA_ARG_NUMBER_RESET)
9909           {
9910             action_arg_number = level.initial_player_stepsize[i];
9911           }
9912
9913           move_stepsize =
9914             getModifiedActionNumber(move_stepsize,
9915                                     action_mode,
9916                                     action_arg_number,
9917                                     action_arg_number_min,
9918                                     action_arg_number_max);
9919
9920           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9921         }
9922       }
9923
9924       break;
9925     }
9926
9927     case CA_SET_PLAYER_SHIELD:
9928     {
9929       for (i = 0; i < MAX_PLAYERS; i++)
9930       {
9931         if (trigger_player_bits & (1 << i))
9932         {
9933           if (action_arg == CA_ARG_SHIELD_OFF)
9934           {
9935             stored_player[i].shield_normal_time_left = 0;
9936             stored_player[i].shield_deadly_time_left = 0;
9937           }
9938           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9939           {
9940             stored_player[i].shield_normal_time_left = 999999;
9941           }
9942           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9943           {
9944             stored_player[i].shield_normal_time_left = 999999;
9945             stored_player[i].shield_deadly_time_left = 999999;
9946           }
9947         }
9948       }
9949
9950       break;
9951     }
9952
9953     case CA_SET_PLAYER_GRAVITY:
9954     {
9955       for (i = 0; i < MAX_PLAYERS; i++)
9956       {
9957         if (trigger_player_bits & (1 << i))
9958         {
9959           stored_player[i].gravity =
9960             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9961              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9962              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9963              stored_player[i].gravity);
9964         }
9965       }
9966
9967       break;
9968     }
9969
9970     case CA_SET_PLAYER_ARTWORK:
9971     {
9972       for (i = 0; i < MAX_PLAYERS; i++)
9973       {
9974         if (trigger_player_bits & (1 << i))
9975         {
9976           int artwork_element = action_arg_element;
9977
9978           if (action_arg == CA_ARG_ELEMENT_RESET)
9979             artwork_element =
9980               (level.use_artwork_element[i] ? level.artwork_element[i] :
9981                stored_player[i].element_nr);
9982
9983           if (stored_player[i].artwork_element != artwork_element)
9984             stored_player[i].Frame = 0;
9985
9986           stored_player[i].artwork_element = artwork_element;
9987
9988           SetPlayerWaiting(&stored_player[i], FALSE);
9989
9990           // set number of special actions for bored and sleeping animation
9991           stored_player[i].num_special_action_bored =
9992             get_num_special_action(artwork_element,
9993                                    ACTION_BORING_1, ACTION_BORING_LAST);
9994           stored_player[i].num_special_action_sleeping =
9995             get_num_special_action(artwork_element,
9996                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9997         }
9998       }
9999
10000       break;
10001     }
10002
10003     case CA_SET_PLAYER_INVENTORY:
10004     {
10005       for (i = 0; i < MAX_PLAYERS; i++)
10006       {
10007         struct PlayerInfo *player = &stored_player[i];
10008         int j, k;
10009
10010         if (trigger_player_bits & (1 << i))
10011         {
10012           int inventory_element = action_arg_element;
10013
10014           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10015               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10016               action_arg == CA_ARG_ELEMENT_ACTION)
10017           {
10018             int element = inventory_element;
10019             int collect_count = element_info[element].collect_count_initial;
10020
10021             if (!IS_CUSTOM_ELEMENT(element))
10022               collect_count = 1;
10023
10024             if (collect_count == 0)
10025               player->inventory_infinite_element = element;
10026             else
10027               for (k = 0; k < collect_count; k++)
10028                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10029                   player->inventory_element[player->inventory_size++] =
10030                     element;
10031           }
10032           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10033                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10034                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10035           {
10036             if (player->inventory_infinite_element != EL_UNDEFINED &&
10037                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10038                                      action_arg_element_raw))
10039               player->inventory_infinite_element = EL_UNDEFINED;
10040
10041             for (k = 0, j = 0; j < player->inventory_size; j++)
10042             {
10043               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10044                                         action_arg_element_raw))
10045                 player->inventory_element[k++] = player->inventory_element[j];
10046             }
10047
10048             player->inventory_size = k;
10049           }
10050           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10051           {
10052             if (player->inventory_size > 0)
10053             {
10054               for (j = 0; j < player->inventory_size - 1; j++)
10055                 player->inventory_element[j] = player->inventory_element[j + 1];
10056
10057               player->inventory_size--;
10058             }
10059           }
10060           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10061           {
10062             if (player->inventory_size > 0)
10063               player->inventory_size--;
10064           }
10065           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10066           {
10067             player->inventory_infinite_element = EL_UNDEFINED;
10068             player->inventory_size = 0;
10069           }
10070           else if (action_arg == CA_ARG_INVENTORY_RESET)
10071           {
10072             player->inventory_infinite_element = EL_UNDEFINED;
10073             player->inventory_size = 0;
10074
10075             if (level.use_initial_inventory[i])
10076             {
10077               for (j = 0; j < level.initial_inventory_size[i]; j++)
10078               {
10079                 int element = level.initial_inventory_content[i][j];
10080                 int collect_count = element_info[element].collect_count_initial;
10081
10082                 if (!IS_CUSTOM_ELEMENT(element))
10083                   collect_count = 1;
10084
10085                 if (collect_count == 0)
10086                   player->inventory_infinite_element = element;
10087                 else
10088                   for (k = 0; k < collect_count; k++)
10089                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10090                       player->inventory_element[player->inventory_size++] =
10091                         element;
10092               }
10093             }
10094           }
10095         }
10096       }
10097
10098       break;
10099     }
10100
10101     // ---------- CE actions  -------------------------------------------------
10102
10103     case CA_SET_CE_VALUE:
10104     {
10105       int last_ce_value = CustomValue[x][y];
10106
10107       CustomValue[x][y] = action_arg_number_new;
10108
10109       if (CustomValue[x][y] != last_ce_value)
10110       {
10111         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10112         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10113
10114         if (CustomValue[x][y] == 0)
10115         {
10116           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10117           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10118         }
10119       }
10120
10121       break;
10122     }
10123
10124     case CA_SET_CE_SCORE:
10125     {
10126       int last_ce_score = ei->collect_score;
10127
10128       ei->collect_score = action_arg_number_new;
10129
10130       if (ei->collect_score != last_ce_score)
10131       {
10132         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10133         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10134
10135         if (ei->collect_score == 0)
10136         {
10137           int xx, yy;
10138
10139           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10140           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10141
10142           /*
10143             This is a very special case that seems to be a mixture between
10144             CheckElementChange() and CheckTriggeredElementChange(): while
10145             the first one only affects single elements that are triggered
10146             directly, the second one affects multiple elements in the playfield
10147             that are triggered indirectly by another element. This is a third
10148             case: Changing the CE score always affects multiple identical CEs,
10149             so every affected CE must be checked, not only the single CE for
10150             which the CE score was changed in the first place (as every instance
10151             of that CE shares the same CE score, and therefore also can change)!
10152           */
10153           SCAN_PLAYFIELD(xx, yy)
10154           {
10155             if (Feld[xx][yy] == element)
10156               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10157                                  CE_SCORE_GETS_ZERO);
10158           }
10159         }
10160       }
10161
10162       break;
10163     }
10164
10165     case CA_SET_CE_ARTWORK:
10166     {
10167       int artwork_element = action_arg_element;
10168       boolean reset_frame = FALSE;
10169       int xx, yy;
10170
10171       if (action_arg == CA_ARG_ELEMENT_RESET)
10172         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10173                            element);
10174
10175       if (ei->gfx_element != artwork_element)
10176         reset_frame = TRUE;
10177
10178       ei->gfx_element = artwork_element;
10179
10180       SCAN_PLAYFIELD(xx, yy)
10181       {
10182         if (Feld[xx][yy] == element)
10183         {
10184           if (reset_frame)
10185           {
10186             ResetGfxAnimation(xx, yy);
10187             ResetRandomAnimationValue(xx, yy);
10188           }
10189
10190           TEST_DrawLevelField(xx, yy);
10191         }
10192       }
10193
10194       break;
10195     }
10196
10197     // ---------- engine actions  ---------------------------------------------
10198
10199     case CA_SET_ENGINE_SCAN_MODE:
10200     {
10201       InitPlayfieldScanMode(action_arg);
10202
10203       break;
10204     }
10205
10206     default:
10207       break;
10208   }
10209 }
10210
10211 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10212 {
10213   int old_element = Feld[x][y];
10214   int new_element = GetElementFromGroupElement(element);
10215   int previous_move_direction = MovDir[x][y];
10216   int last_ce_value = CustomValue[x][y];
10217   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10218   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10219   boolean add_player_onto_element = (new_element_is_player &&
10220                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10221                                      IS_WALKABLE(old_element));
10222
10223   if (!add_player_onto_element)
10224   {
10225     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10226       RemoveMovingField(x, y);
10227     else
10228       RemoveField(x, y);
10229
10230     Feld[x][y] = new_element;
10231
10232     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10233       MovDir[x][y] = previous_move_direction;
10234
10235     if (element_info[new_element].use_last_ce_value)
10236       CustomValue[x][y] = last_ce_value;
10237
10238     InitField_WithBug1(x, y, FALSE);
10239
10240     new_element = Feld[x][y];   // element may have changed
10241
10242     ResetGfxAnimation(x, y);
10243     ResetRandomAnimationValue(x, y);
10244
10245     TEST_DrawLevelField(x, y);
10246
10247     if (GFX_CRUMBLED(new_element))
10248       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10249   }
10250
10251   // check if element under the player changes from accessible to unaccessible
10252   // (needed for special case of dropping element which then changes)
10253   // (must be checked after creating new element for walkable group elements)
10254   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10255       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10256   {
10257     Bang(x, y);
10258
10259     return;
10260   }
10261
10262   // "ChangeCount" not set yet to allow "entered by player" change one time
10263   if (new_element_is_player)
10264     RelocatePlayer(x, y, new_element);
10265
10266   if (is_change)
10267     ChangeCount[x][y]++;        // count number of changes in the same frame
10268
10269   TestIfBadThingTouchesPlayer(x, y);
10270   TestIfPlayerTouchesCustomElement(x, y);
10271   TestIfElementTouchesCustomElement(x, y);
10272 }
10273
10274 static void CreateField(int x, int y, int element)
10275 {
10276   CreateFieldExt(x, y, element, FALSE);
10277 }
10278
10279 static void CreateElementFromChange(int x, int y, int element)
10280 {
10281   element = GET_VALID_RUNTIME_ELEMENT(element);
10282
10283   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10284   {
10285     int old_element = Feld[x][y];
10286
10287     // prevent changed element from moving in same engine frame
10288     // unless both old and new element can either fall or move
10289     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10290         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10291       Stop[x][y] = TRUE;
10292   }
10293
10294   CreateFieldExt(x, y, element, TRUE);
10295 }
10296
10297 static boolean ChangeElement(int x, int y, int element, int page)
10298 {
10299   struct ElementInfo *ei = &element_info[element];
10300   struct ElementChangeInfo *change = &ei->change_page[page];
10301   int ce_value = CustomValue[x][y];
10302   int ce_score = ei->collect_score;
10303   int target_element;
10304   int old_element = Feld[x][y];
10305
10306   // always use default change event to prevent running into a loop
10307   if (ChangeEvent[x][y] == -1)
10308     ChangeEvent[x][y] = CE_DELAY;
10309
10310   if (ChangeEvent[x][y] == CE_DELAY)
10311   {
10312     // reset actual trigger element, trigger player and action element
10313     change->actual_trigger_element = EL_EMPTY;
10314     change->actual_trigger_player = EL_EMPTY;
10315     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10316     change->actual_trigger_side = CH_SIDE_NONE;
10317     change->actual_trigger_ce_value = 0;
10318     change->actual_trigger_ce_score = 0;
10319   }
10320
10321   // do not change elements more than a specified maximum number of changes
10322   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10323     return FALSE;
10324
10325   ChangeCount[x][y]++;          // count number of changes in the same frame
10326
10327   if (change->explode)
10328   {
10329     Bang(x, y);
10330
10331     return TRUE;
10332   }
10333
10334   if (change->use_target_content)
10335   {
10336     boolean complete_replace = TRUE;
10337     boolean can_replace[3][3];
10338     int xx, yy;
10339
10340     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10341     {
10342       boolean is_empty;
10343       boolean is_walkable;
10344       boolean is_diggable;
10345       boolean is_collectible;
10346       boolean is_removable;
10347       boolean is_destructible;
10348       int ex = x + xx - 1;
10349       int ey = y + yy - 1;
10350       int content_element = change->target_content.e[xx][yy];
10351       int e;
10352
10353       can_replace[xx][yy] = TRUE;
10354
10355       if (ex == x && ey == y)   // do not check changing element itself
10356         continue;
10357
10358       if (content_element == EL_EMPTY_SPACE)
10359       {
10360         can_replace[xx][yy] = FALSE;    // do not replace border with space
10361
10362         continue;
10363       }
10364
10365       if (!IN_LEV_FIELD(ex, ey))
10366       {
10367         can_replace[xx][yy] = FALSE;
10368         complete_replace = FALSE;
10369
10370         continue;
10371       }
10372
10373       e = Feld[ex][ey];
10374
10375       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10376         e = MovingOrBlocked2Element(ex, ey);
10377
10378       is_empty = (IS_FREE(ex, ey) ||
10379                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10380
10381       is_walkable     = (is_empty || IS_WALKABLE(e));
10382       is_diggable     = (is_empty || IS_DIGGABLE(e));
10383       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10384       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10385       is_removable    = (is_diggable || is_collectible);
10386
10387       can_replace[xx][yy] =
10388         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10389           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10390           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10391           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10392           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10393           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10394          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10395
10396       if (!can_replace[xx][yy])
10397         complete_replace = FALSE;
10398     }
10399
10400     if (!change->only_if_complete || complete_replace)
10401     {
10402       boolean something_has_changed = FALSE;
10403
10404       if (change->only_if_complete && change->use_random_replace &&
10405           RND(100) < change->random_percentage)
10406         return FALSE;
10407
10408       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10409       {
10410         int ex = x + xx - 1;
10411         int ey = y + yy - 1;
10412         int content_element;
10413
10414         if (can_replace[xx][yy] && (!change->use_random_replace ||
10415                                     RND(100) < change->random_percentage))
10416         {
10417           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10418             RemoveMovingField(ex, ey);
10419
10420           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10421
10422           content_element = change->target_content.e[xx][yy];
10423           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10424                                               ce_value, ce_score);
10425
10426           CreateElementFromChange(ex, ey, target_element);
10427
10428           something_has_changed = TRUE;
10429
10430           // for symmetry reasons, freeze newly created border elements
10431           if (ex != x || ey != y)
10432             Stop[ex][ey] = TRUE;        // no more moving in this frame
10433         }
10434       }
10435
10436       if (something_has_changed)
10437       {
10438         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10439         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10440       }
10441     }
10442   }
10443   else
10444   {
10445     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10446                                         ce_value, ce_score);
10447
10448     if (element == EL_DIAGONAL_GROWING ||
10449         element == EL_DIAGONAL_SHRINKING)
10450     {
10451       target_element = Store[x][y];
10452
10453       Store[x][y] = EL_EMPTY;
10454     }
10455
10456     CreateElementFromChange(x, y, target_element);
10457
10458     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10459     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10460   }
10461
10462   // this uses direct change before indirect change
10463   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10464
10465   return TRUE;
10466 }
10467
10468 static void HandleElementChange(int x, int y, int page)
10469 {
10470   int element = MovingOrBlocked2Element(x, y);
10471   struct ElementInfo *ei = &element_info[element];
10472   struct ElementChangeInfo *change = &ei->change_page[page];
10473   boolean handle_action_before_change = FALSE;
10474
10475 #ifdef DEBUG
10476   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10477       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10478   {
10479     printf("\n\n");
10480     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10481            x, y, element, element_info[element].token_name);
10482     printf("HandleElementChange(): This should never happen!\n");
10483     printf("\n\n");
10484   }
10485 #endif
10486
10487   // this can happen with classic bombs on walkable, changing elements
10488   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10489   {
10490     return;
10491   }
10492
10493   if (ChangeDelay[x][y] == 0)           // initialize element change
10494   {
10495     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10496
10497     if (change->can_change)
10498     {
10499       // !!! not clear why graphic animation should be reset at all here !!!
10500       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10501       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10502
10503       /*
10504         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10505
10506         When using an animation frame delay of 1 (this only happens with
10507         "sp_zonk.moving.left/right" in the classic graphics), the default
10508         (non-moving) animation shows wrong animation frames (while the
10509         moving animation, like "sp_zonk.moving.left/right", is correct,
10510         so this graphical bug never shows up with the classic graphics).
10511         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10512         be drawn instead of the correct frames 0,1,2,3. This is caused by
10513         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10514         an element change: First when the change delay ("ChangeDelay[][]")
10515         counter has reached zero after decrementing, then a second time in
10516         the next frame (after "GfxFrame[][]" was already incremented) when
10517         "ChangeDelay[][]" is reset to the initial delay value again.
10518
10519         This causes frame 0 to be drawn twice, while the last frame won't
10520         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10521
10522         As some animations may already be cleverly designed around this bug
10523         (at least the "Snake Bite" snake tail animation does this), it cannot
10524         simply be fixed here without breaking such existing animations.
10525         Unfortunately, it cannot easily be detected if a graphics set was
10526         designed "before" or "after" the bug was fixed. As a workaround,
10527         a new graphics set option "game.graphics_engine_version" was added
10528         to be able to specify the game's major release version for which the
10529         graphics set was designed, which can then be used to decide if the
10530         bugfix should be used (version 4 and above) or not (version 3 or
10531         below, or if no version was specified at all, as with old sets).
10532
10533         (The wrong/fixed animation frames can be tested with the test level set
10534         "test_gfxframe" and level "000", which contains a specially prepared
10535         custom element at level position (x/y) == (11/9) which uses the zonk
10536         animation mentioned above. Using "game.graphics_engine_version: 4"
10537         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10538         This can also be seen from the debug output for this test element.)
10539       */
10540
10541       // when a custom element is about to change (for example by change delay),
10542       // do not reset graphic animation when the custom element is moving
10543       if (game.graphics_engine_version < 4 &&
10544           !IS_MOVING(x, y))
10545       {
10546         ResetGfxAnimation(x, y);
10547         ResetRandomAnimationValue(x, y);
10548       }
10549
10550       if (change->pre_change_function)
10551         change->pre_change_function(x, y);
10552     }
10553   }
10554
10555   ChangeDelay[x][y]--;
10556
10557   if (ChangeDelay[x][y] != 0)           // continue element change
10558   {
10559     if (change->can_change)
10560     {
10561       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10562
10563       if (IS_ANIMATED(graphic))
10564         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10565
10566       if (change->change_function)
10567         change->change_function(x, y);
10568     }
10569   }
10570   else                                  // finish element change
10571   {
10572     if (ChangePage[x][y] != -1)         // remember page from delayed change
10573     {
10574       page = ChangePage[x][y];
10575       ChangePage[x][y] = -1;
10576
10577       change = &ei->change_page[page];
10578     }
10579
10580     if (IS_MOVING(x, y))                // never change a running system ;-)
10581     {
10582       ChangeDelay[x][y] = 1;            // try change after next move step
10583       ChangePage[x][y] = page;          // remember page to use for change
10584
10585       return;
10586     }
10587
10588     // special case: set new level random seed before changing element
10589     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10590       handle_action_before_change = TRUE;
10591
10592     if (change->has_action && handle_action_before_change)
10593       ExecuteCustomElementAction(x, y, element, page);
10594
10595     if (change->can_change)
10596     {
10597       if (ChangeElement(x, y, element, page))
10598       {
10599         if (change->post_change_function)
10600           change->post_change_function(x, y);
10601       }
10602     }
10603
10604     if (change->has_action && !handle_action_before_change)
10605       ExecuteCustomElementAction(x, y, element, page);
10606   }
10607 }
10608
10609 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10610                                               int trigger_element,
10611                                               int trigger_event,
10612                                               int trigger_player,
10613                                               int trigger_side,
10614                                               int trigger_page)
10615 {
10616   boolean change_done_any = FALSE;
10617   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10618   int i;
10619
10620   if (!(trigger_events[trigger_element][trigger_event]))
10621     return FALSE;
10622
10623   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10624
10625   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10626   {
10627     int element = EL_CUSTOM_START + i;
10628     boolean change_done = FALSE;
10629     int p;
10630
10631     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10632         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10633       continue;
10634
10635     for (p = 0; p < element_info[element].num_change_pages; p++)
10636     {
10637       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10638
10639       if (change->can_change_or_has_action &&
10640           change->has_event[trigger_event] &&
10641           change->trigger_side & trigger_side &&
10642           change->trigger_player & trigger_player &&
10643           change->trigger_page & trigger_page_bits &&
10644           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10645       {
10646         change->actual_trigger_element = trigger_element;
10647         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10648         change->actual_trigger_player_bits = trigger_player;
10649         change->actual_trigger_side = trigger_side;
10650         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10651         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10652
10653         if ((change->can_change && !change_done) || change->has_action)
10654         {
10655           int x, y;
10656
10657           SCAN_PLAYFIELD(x, y)
10658           {
10659             if (Feld[x][y] == element)
10660             {
10661               if (change->can_change && !change_done)
10662               {
10663                 // if element already changed in this frame, not only prevent
10664                 // another element change (checked in ChangeElement()), but
10665                 // also prevent additional element actions for this element
10666
10667                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10668                     !level.use_action_after_change_bug)
10669                   continue;
10670
10671                 ChangeDelay[x][y] = 1;
10672                 ChangeEvent[x][y] = trigger_event;
10673
10674                 HandleElementChange(x, y, p);
10675               }
10676               else if (change->has_action)
10677               {
10678                 // if element already changed in this frame, not only prevent
10679                 // another element change (checked in ChangeElement()), but
10680                 // also prevent additional element actions for this element
10681
10682                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10683                     !level.use_action_after_change_bug)
10684                   continue;
10685
10686                 ExecuteCustomElementAction(x, y, element, p);
10687                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10688               }
10689             }
10690           }
10691
10692           if (change->can_change)
10693           {
10694             change_done = TRUE;
10695             change_done_any = TRUE;
10696           }
10697         }
10698       }
10699     }
10700   }
10701
10702   RECURSION_LOOP_DETECTION_END();
10703
10704   return change_done_any;
10705 }
10706
10707 static boolean CheckElementChangeExt(int x, int y,
10708                                      int element,
10709                                      int trigger_element,
10710                                      int trigger_event,
10711                                      int trigger_player,
10712                                      int trigger_side)
10713 {
10714   boolean change_done = FALSE;
10715   int p;
10716
10717   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10718       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10719     return FALSE;
10720
10721   if (Feld[x][y] == EL_BLOCKED)
10722   {
10723     Blocked2Moving(x, y, &x, &y);
10724     element = Feld[x][y];
10725   }
10726
10727   // check if element has already changed or is about to change after moving
10728   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10729        Feld[x][y] != element) ||
10730
10731       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10732        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10733         ChangePage[x][y] != -1)))
10734     return FALSE;
10735
10736   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10737
10738   for (p = 0; p < element_info[element].num_change_pages; p++)
10739   {
10740     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10741
10742     /* check trigger element for all events where the element that is checked
10743        for changing interacts with a directly adjacent element -- this is
10744        different to element changes that affect other elements to change on the
10745        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10746     boolean check_trigger_element =
10747       (trigger_event == CE_TOUCHING_X ||
10748        trigger_event == CE_HITTING_X ||
10749        trigger_event == CE_HIT_BY_X ||
10750        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10751
10752     if (change->can_change_or_has_action &&
10753         change->has_event[trigger_event] &&
10754         change->trigger_side & trigger_side &&
10755         change->trigger_player & trigger_player &&
10756         (!check_trigger_element ||
10757          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10758     {
10759       change->actual_trigger_element = trigger_element;
10760       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10761       change->actual_trigger_player_bits = trigger_player;
10762       change->actual_trigger_side = trigger_side;
10763       change->actual_trigger_ce_value = CustomValue[x][y];
10764       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10765
10766       // special case: trigger element not at (x,y) position for some events
10767       if (check_trigger_element)
10768       {
10769         static struct
10770         {
10771           int dx, dy;
10772         } move_xy[] =
10773           {
10774             {  0,  0 },
10775             { -1,  0 },
10776             { +1,  0 },
10777             {  0,  0 },
10778             {  0, -1 },
10779             {  0,  0 }, { 0, 0 }, { 0, 0 },
10780             {  0, +1 }
10781           };
10782
10783         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10784         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10785
10786         change->actual_trigger_ce_value = CustomValue[xx][yy];
10787         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10788       }
10789
10790       if (change->can_change && !change_done)
10791       {
10792         ChangeDelay[x][y] = 1;
10793         ChangeEvent[x][y] = trigger_event;
10794
10795         HandleElementChange(x, y, p);
10796
10797         change_done = TRUE;
10798       }
10799       else if (change->has_action)
10800       {
10801         ExecuteCustomElementAction(x, y, element, p);
10802         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10803       }
10804     }
10805   }
10806
10807   RECURSION_LOOP_DETECTION_END();
10808
10809   return change_done;
10810 }
10811
10812 static void PlayPlayerSound(struct PlayerInfo *player)
10813 {
10814   int jx = player->jx, jy = player->jy;
10815   int sound_element = player->artwork_element;
10816   int last_action = player->last_action_waiting;
10817   int action = player->action_waiting;
10818
10819   if (player->is_waiting)
10820   {
10821     if (action != last_action)
10822       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10823     else
10824       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10825   }
10826   else
10827   {
10828     if (action != last_action)
10829       StopSound(element_info[sound_element].sound[last_action]);
10830
10831     if (last_action == ACTION_SLEEPING)
10832       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10833   }
10834 }
10835
10836 static void PlayAllPlayersSound(void)
10837 {
10838   int i;
10839
10840   for (i = 0; i < MAX_PLAYERS; i++)
10841     if (stored_player[i].active)
10842       PlayPlayerSound(&stored_player[i]);
10843 }
10844
10845 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10846 {
10847   boolean last_waiting = player->is_waiting;
10848   int move_dir = player->MovDir;
10849
10850   player->dir_waiting = move_dir;
10851   player->last_action_waiting = player->action_waiting;
10852
10853   if (is_waiting)
10854   {
10855     if (!last_waiting)          // not waiting -> waiting
10856     {
10857       player->is_waiting = TRUE;
10858
10859       player->frame_counter_bored =
10860         FrameCounter +
10861         game.player_boring_delay_fixed +
10862         GetSimpleRandom(game.player_boring_delay_random);
10863       player->frame_counter_sleeping =
10864         FrameCounter +
10865         game.player_sleeping_delay_fixed +
10866         GetSimpleRandom(game.player_sleeping_delay_random);
10867
10868       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10869     }
10870
10871     if (game.player_sleeping_delay_fixed +
10872         game.player_sleeping_delay_random > 0 &&
10873         player->anim_delay_counter == 0 &&
10874         player->post_delay_counter == 0 &&
10875         FrameCounter >= player->frame_counter_sleeping)
10876       player->is_sleeping = TRUE;
10877     else if (game.player_boring_delay_fixed +
10878              game.player_boring_delay_random > 0 &&
10879              FrameCounter >= player->frame_counter_bored)
10880       player->is_bored = TRUE;
10881
10882     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10883                               player->is_bored ? ACTION_BORING :
10884                               ACTION_WAITING);
10885
10886     if (player->is_sleeping && player->use_murphy)
10887     {
10888       // special case for sleeping Murphy when leaning against non-free tile
10889
10890       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10891           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10892            !IS_MOVING(player->jx - 1, player->jy)))
10893         move_dir = MV_LEFT;
10894       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10895                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10896                 !IS_MOVING(player->jx + 1, player->jy)))
10897         move_dir = MV_RIGHT;
10898       else
10899         player->is_sleeping = FALSE;
10900
10901       player->dir_waiting = move_dir;
10902     }
10903
10904     if (player->is_sleeping)
10905     {
10906       if (player->num_special_action_sleeping > 0)
10907       {
10908         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10909         {
10910           int last_special_action = player->special_action_sleeping;
10911           int num_special_action = player->num_special_action_sleeping;
10912           int special_action =
10913             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10914              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10915              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10916              last_special_action + 1 : ACTION_SLEEPING);
10917           int special_graphic =
10918             el_act_dir2img(player->artwork_element, special_action, move_dir);
10919
10920           player->anim_delay_counter =
10921             graphic_info[special_graphic].anim_delay_fixed +
10922             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10923           player->post_delay_counter =
10924             graphic_info[special_graphic].post_delay_fixed +
10925             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10926
10927           player->special_action_sleeping = special_action;
10928         }
10929
10930         if (player->anim_delay_counter > 0)
10931         {
10932           player->action_waiting = player->special_action_sleeping;
10933           player->anim_delay_counter--;
10934         }
10935         else if (player->post_delay_counter > 0)
10936         {
10937           player->post_delay_counter--;
10938         }
10939       }
10940     }
10941     else if (player->is_bored)
10942     {
10943       if (player->num_special_action_bored > 0)
10944       {
10945         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10946         {
10947           int special_action =
10948             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10949           int special_graphic =
10950             el_act_dir2img(player->artwork_element, special_action, move_dir);
10951
10952           player->anim_delay_counter =
10953             graphic_info[special_graphic].anim_delay_fixed +
10954             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10955           player->post_delay_counter =
10956             graphic_info[special_graphic].post_delay_fixed +
10957             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10958
10959           player->special_action_bored = special_action;
10960         }
10961
10962         if (player->anim_delay_counter > 0)
10963         {
10964           player->action_waiting = player->special_action_bored;
10965           player->anim_delay_counter--;
10966         }
10967         else if (player->post_delay_counter > 0)
10968         {
10969           player->post_delay_counter--;
10970         }
10971       }
10972     }
10973   }
10974   else if (last_waiting)        // waiting -> not waiting
10975   {
10976     player->is_waiting = FALSE;
10977     player->is_bored = FALSE;
10978     player->is_sleeping = FALSE;
10979
10980     player->frame_counter_bored = -1;
10981     player->frame_counter_sleeping = -1;
10982
10983     player->anim_delay_counter = 0;
10984     player->post_delay_counter = 0;
10985
10986     player->dir_waiting = player->MovDir;
10987     player->action_waiting = ACTION_DEFAULT;
10988
10989     player->special_action_bored = ACTION_DEFAULT;
10990     player->special_action_sleeping = ACTION_DEFAULT;
10991   }
10992 }
10993
10994 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10995 {
10996   if ((!player->is_moving  && player->was_moving) ||
10997       (player->MovPos == 0 && player->was_moving) ||
10998       (player->is_snapping && !player->was_snapping) ||
10999       (player->is_dropping && !player->was_dropping))
11000   {
11001     if (!CheckSaveEngineSnapshotToList())
11002       return;
11003
11004     player->was_moving = FALSE;
11005     player->was_snapping = TRUE;
11006     player->was_dropping = TRUE;
11007   }
11008   else
11009   {
11010     if (player->is_moving)
11011       player->was_moving = TRUE;
11012
11013     if (!player->is_snapping)
11014       player->was_snapping = FALSE;
11015
11016     if (!player->is_dropping)
11017       player->was_dropping = FALSE;
11018   }
11019 }
11020
11021 static void CheckSingleStepMode(struct PlayerInfo *player)
11022 {
11023   if (tape.single_step && tape.recording && !tape.pausing)
11024   {
11025     /* as it is called "single step mode", just return to pause mode when the
11026        player stopped moving after one tile (or never starts moving at all) */
11027     if (!player->is_moving &&
11028         !player->is_pushing &&
11029         !player->is_dropping_pressed)
11030     {
11031       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11032       SnapField(player, 0, 0);                  // stop snapping
11033     }
11034   }
11035
11036   CheckSaveEngineSnapshot(player);
11037 }
11038
11039 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11040 {
11041   int left      = player_action & JOY_LEFT;
11042   int right     = player_action & JOY_RIGHT;
11043   int up        = player_action & JOY_UP;
11044   int down      = player_action & JOY_DOWN;
11045   int button1   = player_action & JOY_BUTTON_1;
11046   int button2   = player_action & JOY_BUTTON_2;
11047   int dx        = (left ? -1 : right ? 1 : 0);
11048   int dy        = (up   ? -1 : down  ? 1 : 0);
11049
11050   if (!player->active || tape.pausing)
11051     return 0;
11052
11053   if (player_action)
11054   {
11055     if (button1)
11056       SnapField(player, dx, dy);
11057     else
11058     {
11059       if (button2)
11060         DropElement(player);
11061
11062       MovePlayer(player, dx, dy);
11063     }
11064
11065     CheckSingleStepMode(player);
11066
11067     SetPlayerWaiting(player, FALSE);
11068
11069     return player_action;
11070   }
11071   else
11072   {
11073     // no actions for this player (no input at player's configured device)
11074
11075     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11076     SnapField(player, 0, 0);
11077     CheckGravityMovementWhenNotMoving(player);
11078
11079     if (player->MovPos == 0)
11080       SetPlayerWaiting(player, TRUE);
11081
11082     if (player->MovPos == 0)    // needed for tape.playing
11083       player->is_moving = FALSE;
11084
11085     player->is_dropping = FALSE;
11086     player->is_dropping_pressed = FALSE;
11087     player->drop_pressed_delay = 0;
11088
11089     CheckSingleStepMode(player);
11090
11091     return 0;
11092   }
11093 }
11094
11095 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11096                                          byte *tape_action)
11097 {
11098   if (!tape.use_mouse)
11099     return;
11100
11101   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11102   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11103   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11104 }
11105
11106 static void SetTapeActionFromMouseAction(byte *tape_action,
11107                                          struct MouseActionInfo *mouse_action)
11108 {
11109   if (!tape.use_mouse)
11110     return;
11111
11112   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11113   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11114   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11115 }
11116
11117 static void CheckLevelSolved(void)
11118 {
11119   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11120   {
11121     if (game_em.level_solved &&
11122         !game_em.game_over)                             // game won
11123     {
11124       LevelSolved();
11125
11126       game_em.game_over = TRUE;
11127
11128       AllPlayersGone = TRUE;
11129     }
11130
11131     if (game_em.game_over)                              // game lost
11132       AllPlayersGone = TRUE;
11133   }
11134   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11135   {
11136     if (game_sp.level_solved &&
11137         !game_sp.game_over)                             // game won
11138     {
11139       LevelSolved();
11140
11141       game_sp.game_over = TRUE;
11142
11143       AllPlayersGone = TRUE;
11144     }
11145
11146     if (game_sp.game_over)                              // game lost
11147       AllPlayersGone = TRUE;
11148   }
11149   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11150   {
11151     if (game_mm.level_solved &&
11152         !game_mm.game_over)                             // game won
11153     {
11154       LevelSolved();
11155
11156       game_mm.game_over = TRUE;
11157
11158       AllPlayersGone = TRUE;
11159     }
11160
11161     if (game_mm.game_over)                              // game lost
11162       AllPlayersGone = TRUE;
11163   }
11164 }
11165
11166 static void CheckLevelTime(void)
11167 {
11168   int i;
11169
11170   if (TimeFrames >= FRAMES_PER_SECOND)
11171   {
11172     TimeFrames = 0;
11173     TapeTime++;
11174
11175     for (i = 0; i < MAX_PLAYERS; i++)
11176     {
11177       struct PlayerInfo *player = &stored_player[i];
11178
11179       if (SHIELD_ON(player))
11180       {
11181         player->shield_normal_time_left--;
11182
11183         if (player->shield_deadly_time_left > 0)
11184           player->shield_deadly_time_left--;
11185       }
11186     }
11187
11188     if (!game.LevelSolved && !level.use_step_counter)
11189     {
11190       TimePlayed++;
11191
11192       if (TimeLeft > 0)
11193       {
11194         TimeLeft--;
11195
11196         if (TimeLeft <= 10 && setup.time_limit)
11197           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11198
11199         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11200            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11201
11202         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11203
11204         if (!TimeLeft && setup.time_limit)
11205         {
11206           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11207             level.native_em_level->lev->killed_out_of_time = TRUE;
11208           else
11209             for (i = 0; i < MAX_PLAYERS; i++)
11210               KillPlayer(&stored_player[i]);
11211         }
11212       }
11213       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
11214       {
11215         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11216       }
11217
11218       level.native_em_level->lev->time =
11219         (game.no_time_limit ? TimePlayed : TimeLeft);
11220     }
11221
11222     if (tape.recording || tape.playing)
11223       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11224   }
11225
11226   if (tape.recording || tape.playing)
11227     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11228
11229   UpdateAndDisplayGameControlValues();
11230 }
11231
11232 void AdvanceFrameAndPlayerCounters(int player_nr)
11233 {
11234   int i;
11235
11236   // advance frame counters (global frame counter and time frame counter)
11237   FrameCounter++;
11238   TimeFrames++;
11239
11240   // advance player counters (counters for move delay, move animation etc.)
11241   for (i = 0; i < MAX_PLAYERS; i++)
11242   {
11243     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11244     int move_delay_value = stored_player[i].move_delay_value;
11245     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11246
11247     if (!advance_player_counters)       // not all players may be affected
11248       continue;
11249
11250     if (move_frames == 0)       // less than one move per game frame
11251     {
11252       int stepsize = TILEX / move_delay_value;
11253       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11254       int count = (stored_player[i].is_moving ?
11255                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11256
11257       if (count % delay == 0)
11258         move_frames = 1;
11259     }
11260
11261     stored_player[i].Frame += move_frames;
11262
11263     if (stored_player[i].MovPos != 0)
11264       stored_player[i].StepFrame += move_frames;
11265
11266     if (stored_player[i].move_delay > 0)
11267       stored_player[i].move_delay--;
11268
11269     // due to bugs in previous versions, counter must count up, not down
11270     if (stored_player[i].push_delay != -1)
11271       stored_player[i].push_delay++;
11272
11273     if (stored_player[i].drop_delay > 0)
11274       stored_player[i].drop_delay--;
11275
11276     if (stored_player[i].is_dropping_pressed)
11277       stored_player[i].drop_pressed_delay++;
11278   }
11279 }
11280
11281 void StartGameActions(boolean init_network_game, boolean record_tape,
11282                       int random_seed)
11283 {
11284   unsigned int new_random_seed = InitRND(random_seed);
11285
11286   if (record_tape)
11287     TapeStartRecording(new_random_seed);
11288
11289   if (init_network_game)
11290   {
11291     SendToServer_LevelFile();
11292     SendToServer_StartPlaying();
11293
11294     return;
11295   }
11296
11297   InitGame();
11298 }
11299
11300 static void GameActionsExt(void)
11301 {
11302 #if 0
11303   static unsigned int game_frame_delay = 0;
11304 #endif
11305   unsigned int game_frame_delay_value;
11306   byte *recorded_player_action;
11307   byte summarized_player_action = 0;
11308   byte tape_action[MAX_PLAYERS];
11309   int i;
11310
11311   // detect endless loops, caused by custom element programming
11312   if (recursion_loop_detected && recursion_loop_depth == 0)
11313   {
11314     char *message = getStringCat3("Internal Error! Element ",
11315                                   EL_NAME(recursion_loop_element),
11316                                   " caused endless loop! Quit the game?");
11317
11318     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11319           EL_NAME(recursion_loop_element));
11320
11321     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11322
11323     recursion_loop_detected = FALSE;    // if game should be continued
11324
11325     free(message);
11326
11327     return;
11328   }
11329
11330   if (game.restart_level)
11331     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11332
11333   CheckLevelSolved();
11334
11335   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11336     GameWon();
11337
11338   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11339     TapeStop();
11340
11341   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11342     return;
11343
11344   game_frame_delay_value =
11345     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11346
11347   if (tape.playing && tape.warp_forward && !tape.pausing)
11348     game_frame_delay_value = 0;
11349
11350   SetVideoFrameDelay(game_frame_delay_value);
11351
11352 #if 0
11353 #if 0
11354   // ---------- main game synchronization point ----------
11355
11356   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11357
11358   printf("::: skip == %d\n", skip);
11359
11360 #else
11361   // ---------- main game synchronization point ----------
11362
11363   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11364 #endif
11365 #endif
11366
11367   if (network_playing && !network_player_action_received)
11368   {
11369     // try to get network player actions in time
11370
11371     // last chance to get network player actions without main loop delay
11372     HandleNetworking();
11373
11374     // game was quit by network peer
11375     if (game_status != GAME_MODE_PLAYING)
11376       return;
11377
11378     // check if network player actions still missing and game still running
11379     if (!network_player_action_received && !checkGameEnded())
11380       return;           // failed to get network player actions in time
11381
11382     // do not yet reset "network_player_action_received" (for tape.pausing)
11383   }
11384
11385   if (tape.pausing)
11386     return;
11387
11388   // at this point we know that we really continue executing the game
11389
11390   network_player_action_received = FALSE;
11391
11392   // when playing tape, read previously recorded player input from tape data
11393   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11394
11395   local_player->effective_mouse_action = local_player->mouse_action;
11396
11397   if (recorded_player_action != NULL)
11398     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11399                                  recorded_player_action);
11400
11401   // TapePlayAction() may return NULL when toggling to "pause before death"
11402   if (tape.pausing)
11403     return;
11404
11405   if (tape.set_centered_player)
11406   {
11407     game.centered_player_nr_next = tape.centered_player_nr_next;
11408     game.set_centered_player = TRUE;
11409   }
11410
11411   for (i = 0; i < MAX_PLAYERS; i++)
11412   {
11413     summarized_player_action |= stored_player[i].action;
11414
11415     if (!network_playing && (game.team_mode || tape.playing))
11416       stored_player[i].effective_action = stored_player[i].action;
11417   }
11418
11419   if (network_playing && !checkGameEnded())
11420     SendToServer_MovePlayer(summarized_player_action);
11421
11422   // summarize all actions at local players mapped input device position
11423   // (this allows using different input devices in single player mode)
11424   if (!network.enabled && !game.team_mode)
11425     stored_player[map_player_action[local_player->index_nr]].effective_action =
11426       summarized_player_action;
11427
11428   if (tape.recording &&
11429       setup.team_mode &&
11430       setup.input_on_focus &&
11431       game.centered_player_nr != -1)
11432   {
11433     for (i = 0; i < MAX_PLAYERS; i++)
11434       stored_player[i].effective_action =
11435         (i == game.centered_player_nr ? summarized_player_action : 0);
11436   }
11437
11438   if (recorded_player_action != NULL)
11439     for (i = 0; i < MAX_PLAYERS; i++)
11440       stored_player[i].effective_action = recorded_player_action[i];
11441
11442   for (i = 0; i < MAX_PLAYERS; i++)
11443   {
11444     tape_action[i] = stored_player[i].effective_action;
11445
11446     /* (this may happen in the RND game engine if a player was not present on
11447        the playfield on level start, but appeared later from a custom element */
11448     if (setup.team_mode &&
11449         tape.recording &&
11450         tape_action[i] &&
11451         !tape.player_participates[i])
11452       tape.player_participates[i] = TRUE;
11453   }
11454
11455   SetTapeActionFromMouseAction(tape_action,
11456                                &local_player->effective_mouse_action);
11457
11458   // only record actions from input devices, but not programmed actions
11459   if (tape.recording)
11460     TapeRecordAction(tape_action);
11461
11462 #if USE_NEW_PLAYER_ASSIGNMENTS
11463   // !!! also map player actions in single player mode !!!
11464   // if (game.team_mode)
11465   if (1)
11466   {
11467     byte mapped_action[MAX_PLAYERS];
11468
11469 #if DEBUG_PLAYER_ACTIONS
11470     printf(":::");
11471     for (i = 0; i < MAX_PLAYERS; i++)
11472       printf(" %d, ", stored_player[i].effective_action);
11473 #endif
11474
11475     for (i = 0; i < MAX_PLAYERS; i++)
11476       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11477
11478     for (i = 0; i < MAX_PLAYERS; i++)
11479       stored_player[i].effective_action = mapped_action[i];
11480
11481 #if DEBUG_PLAYER_ACTIONS
11482     printf(" =>");
11483     for (i = 0; i < MAX_PLAYERS; i++)
11484       printf(" %d, ", stored_player[i].effective_action);
11485     printf("\n");
11486 #endif
11487   }
11488 #if DEBUG_PLAYER_ACTIONS
11489   else
11490   {
11491     printf(":::");
11492     for (i = 0; i < MAX_PLAYERS; i++)
11493       printf(" %d, ", stored_player[i].effective_action);
11494     printf("\n");
11495   }
11496 #endif
11497 #endif
11498
11499   for (i = 0; i < MAX_PLAYERS; i++)
11500   {
11501     // allow engine snapshot in case of changed movement attempt
11502     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11503         (stored_player[i].effective_action & KEY_MOTION))
11504       game.snapshot.changed_action = TRUE;
11505
11506     // allow engine snapshot in case of snapping/dropping attempt
11507     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11508         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11509       game.snapshot.changed_action = TRUE;
11510
11511     game.snapshot.last_action[i] = stored_player[i].effective_action;
11512   }
11513
11514   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11515   {
11516     GameActions_EM_Main();
11517   }
11518   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11519   {
11520     GameActions_SP_Main();
11521   }
11522   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11523   {
11524     GameActions_MM_Main();
11525   }
11526   else
11527   {
11528     GameActions_RND_Main();
11529   }
11530
11531   BlitScreenToBitmap(backbuffer);
11532
11533   CheckLevelSolved();
11534   CheckLevelTime();
11535
11536   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11537
11538   if (global.show_frames_per_second)
11539   {
11540     static unsigned int fps_counter = 0;
11541     static int fps_frames = 0;
11542     unsigned int fps_delay_ms = Counter() - fps_counter;
11543
11544     fps_frames++;
11545
11546     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11547     {
11548       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11549
11550       fps_frames = 0;
11551       fps_counter = Counter();
11552
11553       // always draw FPS to screen after FPS value was updated
11554       redraw_mask |= REDRAW_FPS;
11555     }
11556
11557     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11558     if (GetDrawDeactivationMask() == REDRAW_NONE)
11559       redraw_mask |= REDRAW_FPS;
11560   }
11561 }
11562
11563 static void GameActions_CheckSaveEngineSnapshot(void)
11564 {
11565   if (!game.snapshot.save_snapshot)
11566     return;
11567
11568   // clear flag for saving snapshot _before_ saving snapshot
11569   game.snapshot.save_snapshot = FALSE;
11570
11571   SaveEngineSnapshotToList();
11572 }
11573
11574 void GameActions(void)
11575 {
11576   GameActionsExt();
11577
11578   GameActions_CheckSaveEngineSnapshot();
11579 }
11580
11581 void GameActions_EM_Main(void)
11582 {
11583   byte effective_action[MAX_PLAYERS];
11584   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11585   int i;
11586
11587   for (i = 0; i < MAX_PLAYERS; i++)
11588     effective_action[i] = stored_player[i].effective_action;
11589
11590   GameActions_EM(effective_action, warp_mode);
11591 }
11592
11593 void GameActions_SP_Main(void)
11594 {
11595   byte effective_action[MAX_PLAYERS];
11596   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11597   int i;
11598
11599   for (i = 0; i < MAX_PLAYERS; i++)
11600     effective_action[i] = stored_player[i].effective_action;
11601
11602   GameActions_SP(effective_action, warp_mode);
11603
11604   for (i = 0; i < MAX_PLAYERS; i++)
11605   {
11606     if (stored_player[i].force_dropping)
11607       stored_player[i].action |= KEY_BUTTON_DROP;
11608
11609     stored_player[i].force_dropping = FALSE;
11610   }
11611 }
11612
11613 void GameActions_MM_Main(void)
11614 {
11615   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11616
11617   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11618 }
11619
11620 void GameActions_RND_Main(void)
11621 {
11622   GameActions_RND();
11623 }
11624
11625 void GameActions_RND(void)
11626 {
11627   int magic_wall_x = 0, magic_wall_y = 0;
11628   int i, x, y, element, graphic, last_gfx_frame;
11629
11630   InitPlayfieldScanModeVars();
11631
11632   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11633   {
11634     SCAN_PLAYFIELD(x, y)
11635     {
11636       ChangeCount[x][y] = 0;
11637       ChangeEvent[x][y] = -1;
11638     }
11639   }
11640
11641   if (game.set_centered_player)
11642   {
11643     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11644
11645     // switching to "all players" only possible if all players fit to screen
11646     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11647     {
11648       game.centered_player_nr_next = game.centered_player_nr;
11649       game.set_centered_player = FALSE;
11650     }
11651
11652     // do not switch focus to non-existing (or non-active) player
11653     if (game.centered_player_nr_next >= 0 &&
11654         !stored_player[game.centered_player_nr_next].active)
11655     {
11656       game.centered_player_nr_next = game.centered_player_nr;
11657       game.set_centered_player = FALSE;
11658     }
11659   }
11660
11661   if (game.set_centered_player &&
11662       ScreenMovPos == 0)        // screen currently aligned at tile position
11663   {
11664     int sx, sy;
11665
11666     if (game.centered_player_nr_next == -1)
11667     {
11668       setScreenCenteredToAllPlayers(&sx, &sy);
11669     }
11670     else
11671     {
11672       sx = stored_player[game.centered_player_nr_next].jx;
11673       sy = stored_player[game.centered_player_nr_next].jy;
11674     }
11675
11676     game.centered_player_nr = game.centered_player_nr_next;
11677     game.set_centered_player = FALSE;
11678
11679     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11680     DrawGameDoorValues();
11681   }
11682
11683   for (i = 0; i < MAX_PLAYERS; i++)
11684   {
11685     int actual_player_action = stored_player[i].effective_action;
11686
11687 #if 1
11688     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11689        - rnd_equinox_tetrachloride 048
11690        - rnd_equinox_tetrachloride_ii 096
11691        - rnd_emanuel_schmieg 002
11692        - doctor_sloan_ww 001, 020
11693     */
11694     if (stored_player[i].MovPos == 0)
11695       CheckGravityMovement(&stored_player[i]);
11696 #endif
11697
11698     // overwrite programmed action with tape action
11699     if (stored_player[i].programmed_action)
11700       actual_player_action = stored_player[i].programmed_action;
11701
11702     PlayerActions(&stored_player[i], actual_player_action);
11703
11704     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11705   }
11706
11707   ScrollScreen(NULL, SCROLL_GO_ON);
11708
11709   /* for backwards compatibility, the following code emulates a fixed bug that
11710      occured when pushing elements (causing elements that just made their last
11711      pushing step to already (if possible) make their first falling step in the
11712      same game frame, which is bad); this code is also needed to use the famous
11713      "spring push bug" which is used in older levels and might be wanted to be
11714      used also in newer levels, but in this case the buggy pushing code is only
11715      affecting the "spring" element and no other elements */
11716
11717   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11718   {
11719     for (i = 0; i < MAX_PLAYERS; i++)
11720     {
11721       struct PlayerInfo *player = &stored_player[i];
11722       int x = player->jx;
11723       int y = player->jy;
11724
11725       if (player->active && player->is_pushing && player->is_moving &&
11726           IS_MOVING(x, y) &&
11727           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11728            Feld[x][y] == EL_SPRING))
11729       {
11730         ContinueMoving(x, y);
11731
11732         // continue moving after pushing (this is actually a bug)
11733         if (!IS_MOVING(x, y))
11734           Stop[x][y] = FALSE;
11735       }
11736     }
11737   }
11738
11739   SCAN_PLAYFIELD(x, y)
11740   {
11741     Last[x][y] = Feld[x][y];
11742
11743     ChangeCount[x][y] = 0;
11744     ChangeEvent[x][y] = -1;
11745
11746     // this must be handled before main playfield loop
11747     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11748     {
11749       MovDelay[x][y]--;
11750       if (MovDelay[x][y] <= 0)
11751         RemoveField(x, y);
11752     }
11753
11754     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11755     {
11756       MovDelay[x][y]--;
11757       if (MovDelay[x][y] <= 0)
11758       {
11759         RemoveField(x, y);
11760         TEST_DrawLevelField(x, y);
11761
11762         TestIfElementTouchesCustomElement(x, y);        // for empty space
11763       }
11764     }
11765
11766 #if DEBUG
11767     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11768     {
11769       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11770       printf("GameActions(): This should never happen!\n");
11771
11772       ChangePage[x][y] = -1;
11773     }
11774 #endif
11775
11776     Stop[x][y] = FALSE;
11777     if (WasJustMoving[x][y] > 0)
11778       WasJustMoving[x][y]--;
11779     if (WasJustFalling[x][y] > 0)
11780       WasJustFalling[x][y]--;
11781     if (CheckCollision[x][y] > 0)
11782       CheckCollision[x][y]--;
11783     if (CheckImpact[x][y] > 0)
11784       CheckImpact[x][y]--;
11785
11786     GfxFrame[x][y]++;
11787
11788     /* reset finished pushing action (not done in ContinueMoving() to allow
11789        continuous pushing animation for elements with zero push delay) */
11790     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11791     {
11792       ResetGfxAnimation(x, y);
11793       TEST_DrawLevelField(x, y);
11794     }
11795
11796 #if DEBUG
11797     if (IS_BLOCKED(x, y))
11798     {
11799       int oldx, oldy;
11800
11801       Blocked2Moving(x, y, &oldx, &oldy);
11802       if (!IS_MOVING(oldx, oldy))
11803       {
11804         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11805         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11806         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11807         printf("GameActions(): This should never happen!\n");
11808       }
11809     }
11810 #endif
11811   }
11812
11813   SCAN_PLAYFIELD(x, y)
11814   {
11815     element = Feld[x][y];
11816     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11817     last_gfx_frame = GfxFrame[x][y];
11818
11819     ResetGfxFrame(x, y);
11820
11821     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11822       DrawLevelGraphicAnimation(x, y, graphic);
11823
11824     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11825         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11826       ResetRandomAnimationValue(x, y);
11827
11828     SetRandomAnimationValue(x, y);
11829
11830     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11831
11832     if (IS_INACTIVE(element))
11833     {
11834       if (IS_ANIMATED(graphic))
11835         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11836
11837       continue;
11838     }
11839
11840     // this may take place after moving, so 'element' may have changed
11841     if (IS_CHANGING(x, y) &&
11842         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11843     {
11844       int page = element_info[element].event_page_nr[CE_DELAY];
11845
11846       HandleElementChange(x, y, page);
11847
11848       element = Feld[x][y];
11849       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11850     }
11851
11852     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11853     {
11854       StartMoving(x, y);
11855
11856       element = Feld[x][y];
11857       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11858
11859       if (IS_ANIMATED(graphic) &&
11860           !IS_MOVING(x, y) &&
11861           !Stop[x][y])
11862         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11863
11864       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11865         TEST_DrawTwinkleOnField(x, y);
11866     }
11867     else if (element == EL_ACID)
11868     {
11869       if (!Stop[x][y])
11870         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11871     }
11872     else if ((element == EL_EXIT_OPEN ||
11873               element == EL_EM_EXIT_OPEN ||
11874               element == EL_SP_EXIT_OPEN ||
11875               element == EL_STEEL_EXIT_OPEN ||
11876               element == EL_EM_STEEL_EXIT_OPEN ||
11877               element == EL_SP_TERMINAL ||
11878               element == EL_SP_TERMINAL_ACTIVE ||
11879               element == EL_EXTRA_TIME ||
11880               element == EL_SHIELD_NORMAL ||
11881               element == EL_SHIELD_DEADLY) &&
11882              IS_ANIMATED(graphic))
11883       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11884     else if (IS_MOVING(x, y))
11885       ContinueMoving(x, y);
11886     else if (IS_ACTIVE_BOMB(element))
11887       CheckDynamite(x, y);
11888     else if (element == EL_AMOEBA_GROWING)
11889       AmoebeWaechst(x, y);
11890     else if (element == EL_AMOEBA_SHRINKING)
11891       AmoebaDisappearing(x, y);
11892
11893 #if !USE_NEW_AMOEBA_CODE
11894     else if (IS_AMOEBALIVE(element))
11895       AmoebeAbleger(x, y);
11896 #endif
11897
11898     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11899       Life(x, y);
11900     else if (element == EL_EXIT_CLOSED)
11901       CheckExit(x, y);
11902     else if (element == EL_EM_EXIT_CLOSED)
11903       CheckExitEM(x, y);
11904     else if (element == EL_STEEL_EXIT_CLOSED)
11905       CheckExitSteel(x, y);
11906     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11907       CheckExitSteelEM(x, y);
11908     else if (element == EL_SP_EXIT_CLOSED)
11909       CheckExitSP(x, y);
11910     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11911              element == EL_EXPANDABLE_STEELWALL_GROWING)
11912       MauerWaechst(x, y);
11913     else if (element == EL_EXPANDABLE_WALL ||
11914              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11915              element == EL_EXPANDABLE_WALL_VERTICAL ||
11916              element == EL_EXPANDABLE_WALL_ANY ||
11917              element == EL_BD_EXPANDABLE_WALL)
11918       MauerAbleger(x, y);
11919     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11920              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11921              element == EL_EXPANDABLE_STEELWALL_ANY)
11922       MauerAblegerStahl(x, y);
11923     else if (element == EL_FLAMES)
11924       CheckForDragon(x, y);
11925     else if (element == EL_EXPLOSION)
11926       ; // drawing of correct explosion animation is handled separately
11927     else if (element == EL_ELEMENT_SNAPPING ||
11928              element == EL_DIAGONAL_SHRINKING ||
11929              element == EL_DIAGONAL_GROWING)
11930     {
11931       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11932
11933       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11934     }
11935     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11936       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11937
11938     if (IS_BELT_ACTIVE(element))
11939       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11940
11941     if (game.magic_wall_active)
11942     {
11943       int jx = local_player->jx, jy = local_player->jy;
11944
11945       // play the element sound at the position nearest to the player
11946       if ((element == EL_MAGIC_WALL_FULL ||
11947            element == EL_MAGIC_WALL_ACTIVE ||
11948            element == EL_MAGIC_WALL_EMPTYING ||
11949            element == EL_BD_MAGIC_WALL_FULL ||
11950            element == EL_BD_MAGIC_WALL_ACTIVE ||
11951            element == EL_BD_MAGIC_WALL_EMPTYING ||
11952            element == EL_DC_MAGIC_WALL_FULL ||
11953            element == EL_DC_MAGIC_WALL_ACTIVE ||
11954            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11955           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11956       {
11957         magic_wall_x = x;
11958         magic_wall_y = y;
11959       }
11960     }
11961   }
11962
11963 #if USE_NEW_AMOEBA_CODE
11964   // new experimental amoeba growth stuff
11965   if (!(FrameCounter % 8))
11966   {
11967     static unsigned int random = 1684108901;
11968
11969     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11970     {
11971       x = RND(lev_fieldx);
11972       y = RND(lev_fieldy);
11973       element = Feld[x][y];
11974
11975       if (!IS_PLAYER(x,y) &&
11976           (element == EL_EMPTY ||
11977            CAN_GROW_INTO(element) ||
11978            element == EL_QUICKSAND_EMPTY ||
11979            element == EL_QUICKSAND_FAST_EMPTY ||
11980            element == EL_ACID_SPLASH_LEFT ||
11981            element == EL_ACID_SPLASH_RIGHT))
11982       {
11983         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11984             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11985             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11986             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11987           Feld[x][y] = EL_AMOEBA_DROP;
11988       }
11989
11990       random = random * 129 + 1;
11991     }
11992   }
11993 #endif
11994
11995   game.explosions_delayed = FALSE;
11996
11997   SCAN_PLAYFIELD(x, y)
11998   {
11999     element = Feld[x][y];
12000
12001     if (ExplodeField[x][y])
12002       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12003     else if (element == EL_EXPLOSION)
12004       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12005
12006     ExplodeField[x][y] = EX_TYPE_NONE;
12007   }
12008
12009   game.explosions_delayed = TRUE;
12010
12011   if (game.magic_wall_active)
12012   {
12013     if (!(game.magic_wall_time_left % 4))
12014     {
12015       int element = Feld[magic_wall_x][magic_wall_y];
12016
12017       if (element == EL_BD_MAGIC_WALL_FULL ||
12018           element == EL_BD_MAGIC_WALL_ACTIVE ||
12019           element == EL_BD_MAGIC_WALL_EMPTYING)
12020         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12021       else if (element == EL_DC_MAGIC_WALL_FULL ||
12022                element == EL_DC_MAGIC_WALL_ACTIVE ||
12023                element == EL_DC_MAGIC_WALL_EMPTYING)
12024         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12025       else
12026         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12027     }
12028
12029     if (game.magic_wall_time_left > 0)
12030     {
12031       game.magic_wall_time_left--;
12032
12033       if (!game.magic_wall_time_left)
12034       {
12035         SCAN_PLAYFIELD(x, y)
12036         {
12037           element = Feld[x][y];
12038
12039           if (element == EL_MAGIC_WALL_ACTIVE ||
12040               element == EL_MAGIC_WALL_FULL)
12041           {
12042             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12043             TEST_DrawLevelField(x, y);
12044           }
12045           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12046                    element == EL_BD_MAGIC_WALL_FULL)
12047           {
12048             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12049             TEST_DrawLevelField(x, y);
12050           }
12051           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12052                    element == EL_DC_MAGIC_WALL_FULL)
12053           {
12054             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12055             TEST_DrawLevelField(x, y);
12056           }
12057         }
12058
12059         game.magic_wall_active = FALSE;
12060       }
12061     }
12062   }
12063
12064   if (game.light_time_left > 0)
12065   {
12066     game.light_time_left--;
12067
12068     if (game.light_time_left == 0)
12069       RedrawAllLightSwitchesAndInvisibleElements();
12070   }
12071
12072   if (game.timegate_time_left > 0)
12073   {
12074     game.timegate_time_left--;
12075
12076     if (game.timegate_time_left == 0)
12077       CloseAllOpenTimegates();
12078   }
12079
12080   if (game.lenses_time_left > 0)
12081   {
12082     game.lenses_time_left--;
12083
12084     if (game.lenses_time_left == 0)
12085       RedrawAllInvisibleElementsForLenses();
12086   }
12087
12088   if (game.magnify_time_left > 0)
12089   {
12090     game.magnify_time_left--;
12091
12092     if (game.magnify_time_left == 0)
12093       RedrawAllInvisibleElementsForMagnifier();
12094   }
12095
12096   for (i = 0; i < MAX_PLAYERS; i++)
12097   {
12098     struct PlayerInfo *player = &stored_player[i];
12099
12100     if (SHIELD_ON(player))
12101     {
12102       if (player->shield_deadly_time_left)
12103         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12104       else if (player->shield_normal_time_left)
12105         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12106     }
12107   }
12108
12109 #if USE_DELAYED_GFX_REDRAW
12110   SCAN_PLAYFIELD(x, y)
12111   {
12112     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12113     {
12114       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12115          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12116
12117       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12118         DrawLevelField(x, y);
12119
12120       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12121         DrawLevelFieldCrumbled(x, y);
12122
12123       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12124         DrawLevelFieldCrumbledNeighbours(x, y);
12125
12126       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12127         DrawTwinkleOnField(x, y);
12128     }
12129
12130     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12131   }
12132 #endif
12133
12134   DrawAllPlayers();
12135   PlayAllPlayersSound();
12136
12137   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12138   {
12139     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12140
12141     local_player->show_envelope = 0;
12142   }
12143
12144   // use random number generator in every frame to make it less predictable
12145   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12146     RND(1);
12147 }
12148
12149 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12150 {
12151   int min_x = x, min_y = y, max_x = x, max_y = y;
12152   int i;
12153
12154   for (i = 0; i < MAX_PLAYERS; i++)
12155   {
12156     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12157
12158     if (!stored_player[i].active || &stored_player[i] == player)
12159       continue;
12160
12161     min_x = MIN(min_x, jx);
12162     min_y = MIN(min_y, jy);
12163     max_x = MAX(max_x, jx);
12164     max_y = MAX(max_y, jy);
12165   }
12166
12167   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12168 }
12169
12170 static boolean AllPlayersInVisibleScreen(void)
12171 {
12172   int i;
12173
12174   for (i = 0; i < MAX_PLAYERS; i++)
12175   {
12176     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12177
12178     if (!stored_player[i].active)
12179       continue;
12180
12181     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12182       return FALSE;
12183   }
12184
12185   return TRUE;
12186 }
12187
12188 void ScrollLevel(int dx, int dy)
12189 {
12190   int scroll_offset = 2 * TILEX_VAR;
12191   int x, y;
12192
12193   BlitBitmap(drawto_field, drawto_field,
12194              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12195              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12196              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12197              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12198              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12199              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12200
12201   if (dx != 0)
12202   {
12203     x = (dx == 1 ? BX1 : BX2);
12204     for (y = BY1; y <= BY2; y++)
12205       DrawScreenField(x, y);
12206   }
12207
12208   if (dy != 0)
12209   {
12210     y = (dy == 1 ? BY1 : BY2);
12211     for (x = BX1; x <= BX2; x++)
12212       DrawScreenField(x, y);
12213   }
12214
12215   redraw_mask |= REDRAW_FIELD;
12216 }
12217
12218 static boolean canFallDown(struct PlayerInfo *player)
12219 {
12220   int jx = player->jx, jy = player->jy;
12221
12222   return (IN_LEV_FIELD(jx, jy + 1) &&
12223           (IS_FREE(jx, jy + 1) ||
12224            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12225           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12226           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12227 }
12228
12229 static boolean canPassField(int x, int y, int move_dir)
12230 {
12231   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12232   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12233   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12234   int nextx = x + dx;
12235   int nexty = y + dy;
12236   int element = Feld[x][y];
12237
12238   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12239           !CAN_MOVE(element) &&
12240           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12241           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12242           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12243 }
12244
12245 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12246 {
12247   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12248   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12249   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12250   int newx = x + dx;
12251   int newy = y + dy;
12252
12253   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12254           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12255           (IS_DIGGABLE(Feld[newx][newy]) ||
12256            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12257            canPassField(newx, newy, move_dir)));
12258 }
12259
12260 static void CheckGravityMovement(struct PlayerInfo *player)
12261 {
12262   if (player->gravity && !player->programmed_action)
12263   {
12264     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12265     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12266     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12267     int jx = player->jx, jy = player->jy;
12268     boolean player_is_moving_to_valid_field =
12269       (!player_is_snapping &&
12270        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12271         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12272     boolean player_can_fall_down = canFallDown(player);
12273
12274     if (player_can_fall_down &&
12275         !player_is_moving_to_valid_field)
12276       player->programmed_action = MV_DOWN;
12277   }
12278 }
12279
12280 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12281 {
12282   return CheckGravityMovement(player);
12283
12284   if (player->gravity && !player->programmed_action)
12285   {
12286     int jx = player->jx, jy = player->jy;
12287     boolean field_under_player_is_free =
12288       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12289     boolean player_is_standing_on_valid_field =
12290       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12291        (IS_WALKABLE(Feld[jx][jy]) &&
12292         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12293
12294     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12295       player->programmed_action = MV_DOWN;
12296   }
12297 }
12298
12299 /*
12300   MovePlayerOneStep()
12301   -----------------------------------------------------------------------------
12302   dx, dy:               direction (non-diagonal) to try to move the player to
12303   real_dx, real_dy:     direction as read from input device (can be diagonal)
12304 */
12305
12306 boolean MovePlayerOneStep(struct PlayerInfo *player,
12307                           int dx, int dy, int real_dx, int real_dy)
12308 {
12309   int jx = player->jx, jy = player->jy;
12310   int new_jx = jx + dx, new_jy = jy + dy;
12311   int can_move;
12312   boolean player_can_move = !player->cannot_move;
12313
12314   if (!player->active || (!dx && !dy))
12315     return MP_NO_ACTION;
12316
12317   player->MovDir = (dx < 0 ? MV_LEFT :
12318                     dx > 0 ? MV_RIGHT :
12319                     dy < 0 ? MV_UP :
12320                     dy > 0 ? MV_DOWN :  MV_NONE);
12321
12322   if (!IN_LEV_FIELD(new_jx, new_jy))
12323     return MP_NO_ACTION;
12324
12325   if (!player_can_move)
12326   {
12327     if (player->MovPos == 0)
12328     {
12329       player->is_moving = FALSE;
12330       player->is_digging = FALSE;
12331       player->is_collecting = FALSE;
12332       player->is_snapping = FALSE;
12333       player->is_pushing = FALSE;
12334     }
12335   }
12336
12337   if (!network.enabled && game.centered_player_nr == -1 &&
12338       !AllPlayersInSight(player, new_jx, new_jy))
12339     return MP_NO_ACTION;
12340
12341   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12342   if (can_move != MP_MOVING)
12343     return can_move;
12344
12345   // check if DigField() has caused relocation of the player
12346   if (player->jx != jx || player->jy != jy)
12347     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12348
12349   StorePlayer[jx][jy] = 0;
12350   player->last_jx = jx;
12351   player->last_jy = jy;
12352   player->jx = new_jx;
12353   player->jy = new_jy;
12354   StorePlayer[new_jx][new_jy] = player->element_nr;
12355
12356   if (player->move_delay_value_next != -1)
12357   {
12358     player->move_delay_value = player->move_delay_value_next;
12359     player->move_delay_value_next = -1;
12360   }
12361
12362   player->MovPos =
12363     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12364
12365   player->step_counter++;
12366
12367   PlayerVisit[jx][jy] = FrameCounter;
12368
12369   player->is_moving = TRUE;
12370
12371 #if 1
12372   // should better be called in MovePlayer(), but this breaks some tapes
12373   ScrollPlayer(player, SCROLL_INIT);
12374 #endif
12375
12376   return MP_MOVING;
12377 }
12378
12379 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12380 {
12381   int jx = player->jx, jy = player->jy;
12382   int old_jx = jx, old_jy = jy;
12383   int moved = MP_NO_ACTION;
12384
12385   if (!player->active)
12386     return FALSE;
12387
12388   if (!dx && !dy)
12389   {
12390     if (player->MovPos == 0)
12391     {
12392       player->is_moving = FALSE;
12393       player->is_digging = FALSE;
12394       player->is_collecting = FALSE;
12395       player->is_snapping = FALSE;
12396       player->is_pushing = FALSE;
12397     }
12398
12399     return FALSE;
12400   }
12401
12402   if (player->move_delay > 0)
12403     return FALSE;
12404
12405   player->move_delay = -1;              // set to "uninitialized" value
12406
12407   // store if player is automatically moved to next field
12408   player->is_auto_moving = (player->programmed_action != MV_NONE);
12409
12410   // remove the last programmed player action
12411   player->programmed_action = 0;
12412
12413   if (player->MovPos)
12414   {
12415     // should only happen if pre-1.2 tape recordings are played
12416     // this is only for backward compatibility
12417
12418     int original_move_delay_value = player->move_delay_value;
12419
12420 #if DEBUG
12421     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12422            tape.counter);
12423 #endif
12424
12425     // scroll remaining steps with finest movement resolution
12426     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12427
12428     while (player->MovPos)
12429     {
12430       ScrollPlayer(player, SCROLL_GO_ON);
12431       ScrollScreen(NULL, SCROLL_GO_ON);
12432
12433       AdvanceFrameAndPlayerCounters(player->index_nr);
12434
12435       DrawAllPlayers();
12436       BackToFront_WithFrameDelay(0);
12437     }
12438
12439     player->move_delay_value = original_move_delay_value;
12440   }
12441
12442   player->is_active = FALSE;
12443
12444   if (player->last_move_dir & MV_HORIZONTAL)
12445   {
12446     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12447       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12448   }
12449   else
12450   {
12451     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12452       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12453   }
12454
12455   if (!moved && !player->is_active)
12456   {
12457     player->is_moving = FALSE;
12458     player->is_digging = FALSE;
12459     player->is_collecting = FALSE;
12460     player->is_snapping = FALSE;
12461     player->is_pushing = FALSE;
12462   }
12463
12464   jx = player->jx;
12465   jy = player->jy;
12466
12467   if (moved & MP_MOVING && !ScreenMovPos &&
12468       (player->index_nr == game.centered_player_nr ||
12469        game.centered_player_nr == -1))
12470   {
12471     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12472     int offset = game.scroll_delay_value;
12473
12474     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12475     {
12476       // actual player has left the screen -- scroll in that direction
12477       if (jx != old_jx)         // player has moved horizontally
12478         scroll_x += (jx - old_jx);
12479       else                      // player has moved vertically
12480         scroll_y += (jy - old_jy);
12481     }
12482     else
12483     {
12484       if (jx != old_jx)         // player has moved horizontally
12485       {
12486         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12487             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12488           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12489
12490         // don't scroll over playfield boundaries
12491         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12492           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12493
12494         // don't scroll more than one field at a time
12495         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12496
12497         // don't scroll against the player's moving direction
12498         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12499             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12500           scroll_x = old_scroll_x;
12501       }
12502       else                      // player has moved vertically
12503       {
12504         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12505             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12506           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12507
12508         // don't scroll over playfield boundaries
12509         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12510           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12511
12512         // don't scroll more than one field at a time
12513         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12514
12515         // don't scroll against the player's moving direction
12516         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12517             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12518           scroll_y = old_scroll_y;
12519       }
12520     }
12521
12522     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12523     {
12524       if (!network.enabled && game.centered_player_nr == -1 &&
12525           !AllPlayersInVisibleScreen())
12526       {
12527         scroll_x = old_scroll_x;
12528         scroll_y = old_scroll_y;
12529       }
12530       else
12531       {
12532         ScrollScreen(player, SCROLL_INIT);
12533         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12534       }
12535     }
12536   }
12537
12538   player->StepFrame = 0;
12539
12540   if (moved & MP_MOVING)
12541   {
12542     if (old_jx != jx && old_jy == jy)
12543       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12544     else if (old_jx == jx && old_jy != jy)
12545       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12546
12547     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12548
12549     player->last_move_dir = player->MovDir;
12550     player->is_moving = TRUE;
12551     player->is_snapping = FALSE;
12552     player->is_switching = FALSE;
12553     player->is_dropping = FALSE;
12554     player->is_dropping_pressed = FALSE;
12555     player->drop_pressed_delay = 0;
12556
12557 #if 0
12558     // should better be called here than above, but this breaks some tapes
12559     ScrollPlayer(player, SCROLL_INIT);
12560 #endif
12561   }
12562   else
12563   {
12564     CheckGravityMovementWhenNotMoving(player);
12565
12566     player->is_moving = FALSE;
12567
12568     /* at this point, the player is allowed to move, but cannot move right now
12569        (e.g. because of something blocking the way) -- ensure that the player
12570        is also allowed to move in the next frame (in old versions before 3.1.1,
12571        the player was forced to wait again for eight frames before next try) */
12572
12573     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12574       player->move_delay = 0;   // allow direct movement in the next frame
12575   }
12576
12577   if (player->move_delay == -1)         // not yet initialized by DigField()
12578     player->move_delay = player->move_delay_value;
12579
12580   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12581   {
12582     TestIfPlayerTouchesBadThing(jx, jy);
12583     TestIfPlayerTouchesCustomElement(jx, jy);
12584   }
12585
12586   if (!player->active)
12587     RemovePlayer(player);
12588
12589   return moved;
12590 }
12591
12592 void ScrollPlayer(struct PlayerInfo *player, int mode)
12593 {
12594   int jx = player->jx, jy = player->jy;
12595   int last_jx = player->last_jx, last_jy = player->last_jy;
12596   int move_stepsize = TILEX / player->move_delay_value;
12597
12598   if (!player->active)
12599     return;
12600
12601   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12602     return;
12603
12604   if (mode == SCROLL_INIT)
12605   {
12606     player->actual_frame_counter = FrameCounter;
12607     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12608
12609     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12610         Feld[last_jx][last_jy] == EL_EMPTY)
12611     {
12612       int last_field_block_delay = 0;   // start with no blocking at all
12613       int block_delay_adjustment = player->block_delay_adjustment;
12614
12615       // if player blocks last field, add delay for exactly one move
12616       if (player->block_last_field)
12617       {
12618         last_field_block_delay += player->move_delay_value;
12619
12620         // when blocking enabled, prevent moving up despite gravity
12621         if (player->gravity && player->MovDir == MV_UP)
12622           block_delay_adjustment = -1;
12623       }
12624
12625       // add block delay adjustment (also possible when not blocking)
12626       last_field_block_delay += block_delay_adjustment;
12627
12628       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12629       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12630     }
12631
12632     if (player->MovPos != 0)    // player has not yet reached destination
12633       return;
12634   }
12635   else if (!FrameReached(&player->actual_frame_counter, 1))
12636     return;
12637
12638   if (player->MovPos != 0)
12639   {
12640     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12641     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12642
12643     // before DrawPlayer() to draw correct player graphic for this case
12644     if (player->MovPos == 0)
12645       CheckGravityMovement(player);
12646   }
12647
12648   if (player->MovPos == 0)      // player reached destination field
12649   {
12650     if (player->move_delay_reset_counter > 0)
12651     {
12652       player->move_delay_reset_counter--;
12653
12654       if (player->move_delay_reset_counter == 0)
12655       {
12656         // continue with normal speed after quickly moving through gate
12657         HALVE_PLAYER_SPEED(player);
12658
12659         // be able to make the next move without delay
12660         player->move_delay = 0;
12661       }
12662     }
12663
12664     player->last_jx = jx;
12665     player->last_jy = jy;
12666
12667     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12668         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12669         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12670         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12671         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12672         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12673         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12674         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12675     {
12676       ExitPlayer(player);
12677
12678       if ((local_player->friends_still_needed == 0 ||
12679            IS_SP_ELEMENT(Feld[jx][jy])) &&
12680           AllPlayersGone)
12681         LevelSolved();
12682     }
12683
12684     // this breaks one level: "machine", level 000
12685     {
12686       int move_direction = player->MovDir;
12687       int enter_side = MV_DIR_OPPOSITE(move_direction);
12688       int leave_side = move_direction;
12689       int old_jx = last_jx;
12690       int old_jy = last_jy;
12691       int old_element = Feld[old_jx][old_jy];
12692       int new_element = Feld[jx][jy];
12693
12694       if (IS_CUSTOM_ELEMENT(old_element))
12695         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12696                                    CE_LEFT_BY_PLAYER,
12697                                    player->index_bit, leave_side);
12698
12699       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12700                                           CE_PLAYER_LEAVES_X,
12701                                           player->index_bit, leave_side);
12702
12703       if (IS_CUSTOM_ELEMENT(new_element))
12704         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12705                                    player->index_bit, enter_side);
12706
12707       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12708                                           CE_PLAYER_ENTERS_X,
12709                                           player->index_bit, enter_side);
12710
12711       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12712                                         CE_MOVE_OF_X, move_direction);
12713     }
12714
12715     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12716     {
12717       TestIfPlayerTouchesBadThing(jx, jy);
12718       TestIfPlayerTouchesCustomElement(jx, jy);
12719
12720       /* needed because pushed element has not yet reached its destination,
12721          so it would trigger a change event at its previous field location */
12722       if (!player->is_pushing)
12723         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12724
12725       if (!player->active)
12726         RemovePlayer(player);
12727     }
12728
12729     if (!game.LevelSolved && level.use_step_counter)
12730     {
12731       int i;
12732
12733       TimePlayed++;
12734
12735       if (TimeLeft > 0)
12736       {
12737         TimeLeft--;
12738
12739         if (TimeLeft <= 10 && setup.time_limit)
12740           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12741
12742         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12743
12744         DisplayGameControlValues();
12745
12746         if (!TimeLeft && setup.time_limit)
12747           for (i = 0; i < MAX_PLAYERS; i++)
12748             KillPlayer(&stored_player[i]);
12749       }
12750       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
12751       {
12752         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12753
12754         DisplayGameControlValues();
12755       }
12756     }
12757
12758     if (tape.single_step && tape.recording && !tape.pausing &&
12759         !player->programmed_action)
12760       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12761
12762     if (!player->programmed_action)
12763       CheckSaveEngineSnapshot(player);
12764   }
12765 }
12766
12767 void ScrollScreen(struct PlayerInfo *player, int mode)
12768 {
12769   static unsigned int screen_frame_counter = 0;
12770
12771   if (mode == SCROLL_INIT)
12772   {
12773     // set scrolling step size according to actual player's moving speed
12774     ScrollStepSize = TILEX / player->move_delay_value;
12775
12776     screen_frame_counter = FrameCounter;
12777     ScreenMovDir = player->MovDir;
12778     ScreenMovPos = player->MovPos;
12779     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12780     return;
12781   }
12782   else if (!FrameReached(&screen_frame_counter, 1))
12783     return;
12784
12785   if (ScreenMovPos)
12786   {
12787     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12788     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12789     redraw_mask |= REDRAW_FIELD;
12790   }
12791   else
12792     ScreenMovDir = MV_NONE;
12793 }
12794
12795 void TestIfPlayerTouchesCustomElement(int x, int y)
12796 {
12797   static int xy[4][2] =
12798   {
12799     { 0, -1 },
12800     { -1, 0 },
12801     { +1, 0 },
12802     { 0, +1 }
12803   };
12804   static int trigger_sides[4][2] =
12805   {
12806     // center side       border side
12807     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12808     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12809     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12810     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12811   };
12812   static int touch_dir[4] =
12813   {
12814     MV_LEFT | MV_RIGHT,
12815     MV_UP   | MV_DOWN,
12816     MV_UP   | MV_DOWN,
12817     MV_LEFT | MV_RIGHT
12818   };
12819   int center_element = Feld[x][y];      // should always be non-moving!
12820   int i;
12821
12822   for (i = 0; i < NUM_DIRECTIONS; i++)
12823   {
12824     int xx = x + xy[i][0];
12825     int yy = y + xy[i][1];
12826     int center_side = trigger_sides[i][0];
12827     int border_side = trigger_sides[i][1];
12828     int border_element;
12829
12830     if (!IN_LEV_FIELD(xx, yy))
12831       continue;
12832
12833     if (IS_PLAYER(x, y))                // player found at center element
12834     {
12835       struct PlayerInfo *player = PLAYERINFO(x, y);
12836
12837       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12838         border_element = Feld[xx][yy];          // may be moving!
12839       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12840         border_element = Feld[xx][yy];
12841       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12842         border_element = MovingOrBlocked2Element(xx, yy);
12843       else
12844         continue;               // center and border element do not touch
12845
12846       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12847                                  player->index_bit, border_side);
12848       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12849                                           CE_PLAYER_TOUCHES_X,
12850                                           player->index_bit, border_side);
12851
12852       {
12853         /* use player element that is initially defined in the level playfield,
12854            not the player element that corresponds to the runtime player number
12855            (example: a level that contains EL_PLAYER_3 as the only player would
12856            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12857         int player_element = PLAYERINFO(x, y)->initial_element;
12858
12859         CheckElementChangeBySide(xx, yy, border_element, player_element,
12860                                  CE_TOUCHING_X, border_side);
12861       }
12862     }
12863     else if (IS_PLAYER(xx, yy))         // player found at border element
12864     {
12865       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12866
12867       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12868       {
12869         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12870           continue;             // center and border element do not touch
12871       }
12872
12873       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12874                                  player->index_bit, center_side);
12875       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12876                                           CE_PLAYER_TOUCHES_X,
12877                                           player->index_bit, center_side);
12878
12879       {
12880         /* use player element that is initially defined in the level playfield,
12881            not the player element that corresponds to the runtime player number
12882            (example: a level that contains EL_PLAYER_3 as the only player would
12883            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12884         int player_element = PLAYERINFO(xx, yy)->initial_element;
12885
12886         CheckElementChangeBySide(x, y, center_element, player_element,
12887                                  CE_TOUCHING_X, center_side);
12888       }
12889
12890       break;
12891     }
12892   }
12893 }
12894
12895 void TestIfElementTouchesCustomElement(int x, int y)
12896 {
12897   static int xy[4][2] =
12898   {
12899     { 0, -1 },
12900     { -1, 0 },
12901     { +1, 0 },
12902     { 0, +1 }
12903   };
12904   static int trigger_sides[4][2] =
12905   {
12906     // center side      border side
12907     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12908     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12909     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12910     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12911   };
12912   static int touch_dir[4] =
12913   {
12914     MV_LEFT | MV_RIGHT,
12915     MV_UP   | MV_DOWN,
12916     MV_UP   | MV_DOWN,
12917     MV_LEFT | MV_RIGHT
12918   };
12919   boolean change_center_element = FALSE;
12920   int center_element = Feld[x][y];      // should always be non-moving!
12921   int border_element_old[NUM_DIRECTIONS];
12922   int i;
12923
12924   for (i = 0; i < NUM_DIRECTIONS; i++)
12925   {
12926     int xx = x + xy[i][0];
12927     int yy = y + xy[i][1];
12928     int border_element;
12929
12930     border_element_old[i] = -1;
12931
12932     if (!IN_LEV_FIELD(xx, yy))
12933       continue;
12934
12935     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12936       border_element = Feld[xx][yy];    // may be moving!
12937     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12938       border_element = Feld[xx][yy];
12939     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12940       border_element = MovingOrBlocked2Element(xx, yy);
12941     else
12942       continue;                 // center and border element do not touch
12943
12944     border_element_old[i] = border_element;
12945   }
12946
12947   for (i = 0; i < NUM_DIRECTIONS; i++)
12948   {
12949     int xx = x + xy[i][0];
12950     int yy = y + xy[i][1];
12951     int center_side = trigger_sides[i][0];
12952     int border_element = border_element_old[i];
12953
12954     if (border_element == -1)
12955       continue;
12956
12957     // check for change of border element
12958     CheckElementChangeBySide(xx, yy, border_element, center_element,
12959                              CE_TOUCHING_X, center_side);
12960
12961     // (center element cannot be player, so we dont have to check this here)
12962   }
12963
12964   for (i = 0; i < NUM_DIRECTIONS; i++)
12965   {
12966     int xx = x + xy[i][0];
12967     int yy = y + xy[i][1];
12968     int border_side = trigger_sides[i][1];
12969     int border_element = border_element_old[i];
12970
12971     if (border_element == -1)
12972       continue;
12973
12974     // check for change of center element (but change it only once)
12975     if (!change_center_element)
12976       change_center_element =
12977         CheckElementChangeBySide(x, y, center_element, border_element,
12978                                  CE_TOUCHING_X, border_side);
12979
12980     if (IS_PLAYER(xx, yy))
12981     {
12982       /* use player element that is initially defined in the level playfield,
12983          not the player element that corresponds to the runtime player number
12984          (example: a level that contains EL_PLAYER_3 as the only player would
12985          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12986       int player_element = PLAYERINFO(xx, yy)->initial_element;
12987
12988       CheckElementChangeBySide(x, y, center_element, player_element,
12989                                CE_TOUCHING_X, border_side);
12990     }
12991   }
12992 }
12993
12994 void TestIfElementHitsCustomElement(int x, int y, int direction)
12995 {
12996   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12997   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12998   int hitx = x + dx, hity = y + dy;
12999   int hitting_element = Feld[x][y];
13000   int touched_element;
13001
13002   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13003     return;
13004
13005   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13006                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13007
13008   if (IN_LEV_FIELD(hitx, hity))
13009   {
13010     int opposite_direction = MV_DIR_OPPOSITE(direction);
13011     int hitting_side = direction;
13012     int touched_side = opposite_direction;
13013     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13014                           MovDir[hitx][hity] != direction ||
13015                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13016
13017     object_hit = TRUE;
13018
13019     if (object_hit)
13020     {
13021       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13022                                CE_HITTING_X, touched_side);
13023
13024       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13025                                CE_HIT_BY_X, hitting_side);
13026
13027       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13028                                CE_HIT_BY_SOMETHING, opposite_direction);
13029
13030       if (IS_PLAYER(hitx, hity))
13031       {
13032         /* use player element that is initially defined in the level playfield,
13033            not the player element that corresponds to the runtime player number
13034            (example: a level that contains EL_PLAYER_3 as the only player would
13035            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13036         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13037
13038         CheckElementChangeBySide(x, y, hitting_element, player_element,
13039                                  CE_HITTING_X, touched_side);
13040       }
13041     }
13042   }
13043
13044   // "hitting something" is also true when hitting the playfield border
13045   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13046                            CE_HITTING_SOMETHING, direction);
13047 }
13048
13049 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13050 {
13051   int i, kill_x = -1, kill_y = -1;
13052
13053   int bad_element = -1;
13054   static int test_xy[4][2] =
13055   {
13056     { 0, -1 },
13057     { -1, 0 },
13058     { +1, 0 },
13059     { 0, +1 }
13060   };
13061   static int test_dir[4] =
13062   {
13063     MV_UP,
13064     MV_LEFT,
13065     MV_RIGHT,
13066     MV_DOWN
13067   };
13068
13069   for (i = 0; i < NUM_DIRECTIONS; i++)
13070   {
13071     int test_x, test_y, test_move_dir, test_element;
13072
13073     test_x = good_x + test_xy[i][0];
13074     test_y = good_y + test_xy[i][1];
13075
13076     if (!IN_LEV_FIELD(test_x, test_y))
13077       continue;
13078
13079     test_move_dir =
13080       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13081
13082     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13083
13084     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13085        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13086     */
13087     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13088         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13089     {
13090       kill_x = test_x;
13091       kill_y = test_y;
13092       bad_element = test_element;
13093
13094       break;
13095     }
13096   }
13097
13098   if (kill_x != -1 || kill_y != -1)
13099   {
13100     if (IS_PLAYER(good_x, good_y))
13101     {
13102       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13103
13104       if (player->shield_deadly_time_left > 0 &&
13105           !IS_INDESTRUCTIBLE(bad_element))
13106         Bang(kill_x, kill_y);
13107       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13108         KillPlayer(player);
13109     }
13110     else
13111       Bang(good_x, good_y);
13112   }
13113 }
13114
13115 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13116 {
13117   int i, kill_x = -1, kill_y = -1;
13118   int bad_element = Feld[bad_x][bad_y];
13119   static int test_xy[4][2] =
13120   {
13121     { 0, -1 },
13122     { -1, 0 },
13123     { +1, 0 },
13124     { 0, +1 }
13125   };
13126   static int touch_dir[4] =
13127   {
13128     MV_LEFT | MV_RIGHT,
13129     MV_UP   | MV_DOWN,
13130     MV_UP   | MV_DOWN,
13131     MV_LEFT | MV_RIGHT
13132   };
13133   static int test_dir[4] =
13134   {
13135     MV_UP,
13136     MV_LEFT,
13137     MV_RIGHT,
13138     MV_DOWN
13139   };
13140
13141   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13142     return;
13143
13144   for (i = 0; i < NUM_DIRECTIONS; i++)
13145   {
13146     int test_x, test_y, test_move_dir, test_element;
13147
13148     test_x = bad_x + test_xy[i][0];
13149     test_y = bad_y + test_xy[i][1];
13150
13151     if (!IN_LEV_FIELD(test_x, test_y))
13152       continue;
13153
13154     test_move_dir =
13155       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13156
13157     test_element = Feld[test_x][test_y];
13158
13159     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13160        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13161     */
13162     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13163         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13164     {
13165       // good thing is player or penguin that does not move away
13166       if (IS_PLAYER(test_x, test_y))
13167       {
13168         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13169
13170         if (bad_element == EL_ROBOT && player->is_moving)
13171           continue;     // robot does not kill player if he is moving
13172
13173         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13174         {
13175           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13176             continue;           // center and border element do not touch
13177         }
13178
13179         kill_x = test_x;
13180         kill_y = test_y;
13181
13182         break;
13183       }
13184       else if (test_element == EL_PENGUIN)
13185       {
13186         kill_x = test_x;
13187         kill_y = test_y;
13188
13189         break;
13190       }
13191     }
13192   }
13193
13194   if (kill_x != -1 || kill_y != -1)
13195   {
13196     if (IS_PLAYER(kill_x, kill_y))
13197     {
13198       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13199
13200       if (player->shield_deadly_time_left > 0 &&
13201           !IS_INDESTRUCTIBLE(bad_element))
13202         Bang(bad_x, bad_y);
13203       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13204         KillPlayer(player);
13205     }
13206     else
13207       Bang(kill_x, kill_y);
13208   }
13209 }
13210
13211 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13212 {
13213   int bad_element = Feld[bad_x][bad_y];
13214   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13215   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13216   int test_x = bad_x + dx, test_y = bad_y + dy;
13217   int test_move_dir, test_element;
13218   int kill_x = -1, kill_y = -1;
13219
13220   if (!IN_LEV_FIELD(test_x, test_y))
13221     return;
13222
13223   test_move_dir =
13224     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13225
13226   test_element = Feld[test_x][test_y];
13227
13228   if (test_move_dir != bad_move_dir)
13229   {
13230     // good thing can be player or penguin that does not move away
13231     if (IS_PLAYER(test_x, test_y))
13232     {
13233       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13234
13235       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13236          player as being hit when he is moving towards the bad thing, because
13237          the "get hit by" condition would be lost after the player stops) */
13238       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13239         return;         // player moves away from bad thing
13240
13241       kill_x = test_x;
13242       kill_y = test_y;
13243     }
13244     else if (test_element == EL_PENGUIN)
13245     {
13246       kill_x = test_x;
13247       kill_y = test_y;
13248     }
13249   }
13250
13251   if (kill_x != -1 || kill_y != -1)
13252   {
13253     if (IS_PLAYER(kill_x, kill_y))
13254     {
13255       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13256
13257       if (player->shield_deadly_time_left > 0 &&
13258           !IS_INDESTRUCTIBLE(bad_element))
13259         Bang(bad_x, bad_y);
13260       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13261         KillPlayer(player);
13262     }
13263     else
13264       Bang(kill_x, kill_y);
13265   }
13266 }
13267
13268 void TestIfPlayerTouchesBadThing(int x, int y)
13269 {
13270   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13271 }
13272
13273 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13274 {
13275   TestIfGoodThingHitsBadThing(x, y, move_dir);
13276 }
13277
13278 void TestIfBadThingTouchesPlayer(int x, int y)
13279 {
13280   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13281 }
13282
13283 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13284 {
13285   TestIfBadThingHitsGoodThing(x, y, move_dir);
13286 }
13287
13288 void TestIfFriendTouchesBadThing(int x, int y)
13289 {
13290   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13291 }
13292
13293 void TestIfBadThingTouchesFriend(int x, int y)
13294 {
13295   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13296 }
13297
13298 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13299 {
13300   int i, kill_x = bad_x, kill_y = bad_y;
13301   static int xy[4][2] =
13302   {
13303     { 0, -1 },
13304     { -1, 0 },
13305     { +1, 0 },
13306     { 0, +1 }
13307   };
13308
13309   for (i = 0; i < NUM_DIRECTIONS; i++)
13310   {
13311     int x, y, element;
13312
13313     x = bad_x + xy[i][0];
13314     y = bad_y + xy[i][1];
13315     if (!IN_LEV_FIELD(x, y))
13316       continue;
13317
13318     element = Feld[x][y];
13319     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13320         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13321     {
13322       kill_x = x;
13323       kill_y = y;
13324       break;
13325     }
13326   }
13327
13328   if (kill_x != bad_x || kill_y != bad_y)
13329     Bang(bad_x, bad_y);
13330 }
13331
13332 void KillPlayer(struct PlayerInfo *player)
13333 {
13334   int jx = player->jx, jy = player->jy;
13335
13336   if (!player->active)
13337     return;
13338
13339 #if 0
13340   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13341          player->killed, player->active, player->reanimated);
13342 #endif
13343
13344   /* the following code was introduced to prevent an infinite loop when calling
13345      -> Bang()
13346      -> CheckTriggeredElementChangeExt()
13347      -> ExecuteCustomElementAction()
13348      -> KillPlayer()
13349      -> (infinitely repeating the above sequence of function calls)
13350      which occurs when killing the player while having a CE with the setting
13351      "kill player X when explosion of <player X>"; the solution using a new
13352      field "player->killed" was chosen for backwards compatibility, although
13353      clever use of the fields "player->active" etc. would probably also work */
13354 #if 1
13355   if (player->killed)
13356     return;
13357 #endif
13358
13359   player->killed = TRUE;
13360
13361   // remove accessible field at the player's position
13362   Feld[jx][jy] = EL_EMPTY;
13363
13364   // deactivate shield (else Bang()/Explode() would not work right)
13365   player->shield_normal_time_left = 0;
13366   player->shield_deadly_time_left = 0;
13367
13368 #if 0
13369   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13370          player->killed, player->active, player->reanimated);
13371 #endif
13372
13373   Bang(jx, jy);
13374
13375 #if 0
13376   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13377          player->killed, player->active, player->reanimated);
13378 #endif
13379
13380   if (player->reanimated)       // killed player may have been reanimated
13381     player->killed = player->reanimated = FALSE;
13382   else
13383     BuryPlayer(player);
13384 }
13385
13386 static void KillPlayerUnlessEnemyProtected(int x, int y)
13387 {
13388   if (!PLAYER_ENEMY_PROTECTED(x, y))
13389     KillPlayer(PLAYERINFO(x, y));
13390 }
13391
13392 static void KillPlayerUnlessExplosionProtected(int x, int y)
13393 {
13394   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13395     KillPlayer(PLAYERINFO(x, y));
13396 }
13397
13398 void BuryPlayer(struct PlayerInfo *player)
13399 {
13400   int jx = player->jx, jy = player->jy;
13401
13402   if (!player->active)
13403     return;
13404
13405   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13406   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13407
13408   player->GameOver = TRUE;
13409   RemovePlayer(player);
13410 }
13411
13412 void RemovePlayer(struct PlayerInfo *player)
13413 {
13414   int jx = player->jx, jy = player->jy;
13415   int i, found = FALSE;
13416
13417   player->present = FALSE;
13418   player->active = FALSE;
13419
13420   if (!ExplodeField[jx][jy])
13421     StorePlayer[jx][jy] = 0;
13422
13423   if (player->is_moving)
13424     TEST_DrawLevelField(player->last_jx, player->last_jy);
13425
13426   for (i = 0; i < MAX_PLAYERS; i++)
13427     if (stored_player[i].active)
13428       found = TRUE;
13429
13430   if (!found)
13431     AllPlayersGone = TRUE;
13432
13433   ExitX = ZX = jx;
13434   ExitY = ZY = jy;
13435 }
13436
13437 void ExitPlayer(struct PlayerInfo *player)
13438 {
13439   DrawPlayer(player);   // needed here only to cleanup last field
13440   RemovePlayer(player);
13441
13442   if (local_player->players_still_needed > 0)
13443     local_player->players_still_needed--;
13444
13445   // also set if some players not yet gone, but not needed to solve level
13446   if (local_player->players_still_needed == 0)
13447     AllPlayersGone = TRUE;
13448 }
13449
13450 static void setFieldForSnapping(int x, int y, int element, int direction)
13451 {
13452   struct ElementInfo *ei = &element_info[element];
13453   int direction_bit = MV_DIR_TO_BIT(direction);
13454   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13455   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13456                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13457
13458   Feld[x][y] = EL_ELEMENT_SNAPPING;
13459   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13460
13461   ResetGfxAnimation(x, y);
13462
13463   GfxElement[x][y] = element;
13464   GfxAction[x][y] = action;
13465   GfxDir[x][y] = direction;
13466   GfxFrame[x][y] = -1;
13467 }
13468
13469 /*
13470   =============================================================================
13471   checkDiagonalPushing()
13472   -----------------------------------------------------------------------------
13473   check if diagonal input device direction results in pushing of object
13474   (by checking if the alternative direction is walkable, diggable, ...)
13475   =============================================================================
13476 */
13477
13478 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13479                                     int x, int y, int real_dx, int real_dy)
13480 {
13481   int jx, jy, dx, dy, xx, yy;
13482
13483   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13484     return TRUE;
13485
13486   // diagonal direction: check alternative direction
13487   jx = player->jx;
13488   jy = player->jy;
13489   dx = x - jx;
13490   dy = y - jy;
13491   xx = jx + (dx == 0 ? real_dx : 0);
13492   yy = jy + (dy == 0 ? real_dy : 0);
13493
13494   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13495 }
13496
13497 /*
13498   =============================================================================
13499   DigField()
13500   -----------------------------------------------------------------------------
13501   x, y:                 field next to player (non-diagonal) to try to dig to
13502   real_dx, real_dy:     direction as read from input device (can be diagonal)
13503   =============================================================================
13504 */
13505
13506 static int DigField(struct PlayerInfo *player,
13507                     int oldx, int oldy, int x, int y,
13508                     int real_dx, int real_dy, int mode)
13509 {
13510   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13511   boolean player_was_pushing = player->is_pushing;
13512   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13513   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13514   int jx = oldx, jy = oldy;
13515   int dx = x - jx, dy = y - jy;
13516   int nextx = x + dx, nexty = y + dy;
13517   int move_direction = (dx == -1 ? MV_LEFT  :
13518                         dx == +1 ? MV_RIGHT :
13519                         dy == -1 ? MV_UP    :
13520                         dy == +1 ? MV_DOWN  : MV_NONE);
13521   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13522   int dig_side = MV_DIR_OPPOSITE(move_direction);
13523   int old_element = Feld[jx][jy];
13524   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13525   int collect_count;
13526
13527   if (is_player)                // function can also be called by EL_PENGUIN
13528   {
13529     if (player->MovPos == 0)
13530     {
13531       player->is_digging = FALSE;
13532       player->is_collecting = FALSE;
13533     }
13534
13535     if (player->MovPos == 0)    // last pushing move finished
13536       player->is_pushing = FALSE;
13537
13538     if (mode == DF_NO_PUSH)     // player just stopped pushing
13539     {
13540       player->is_switching = FALSE;
13541       player->push_delay = -1;
13542
13543       return MP_NO_ACTION;
13544     }
13545   }
13546
13547   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13548     old_element = Back[jx][jy];
13549
13550   // in case of element dropped at player position, check background
13551   else if (Back[jx][jy] != EL_EMPTY &&
13552            game.engine_version >= VERSION_IDENT(2,2,0,0))
13553     old_element = Back[jx][jy];
13554
13555   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13556     return MP_NO_ACTION;        // field has no opening in this direction
13557
13558   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13559     return MP_NO_ACTION;        // field has no opening in this direction
13560
13561   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13562   {
13563     SplashAcid(x, y);
13564
13565     Feld[jx][jy] = player->artwork_element;
13566     InitMovingField(jx, jy, MV_DOWN);
13567     Store[jx][jy] = EL_ACID;
13568     ContinueMoving(jx, jy);
13569     BuryPlayer(player);
13570
13571     return MP_DONT_RUN_INTO;
13572   }
13573
13574   if (player_can_move && DONT_RUN_INTO(element))
13575   {
13576     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13577
13578     return MP_DONT_RUN_INTO;
13579   }
13580
13581   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13582     return MP_NO_ACTION;
13583
13584   collect_count = element_info[element].collect_count_initial;
13585
13586   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13587     return MP_NO_ACTION;
13588
13589   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13590     player_can_move = player_can_move_or_snap;
13591
13592   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13593       game.engine_version >= VERSION_IDENT(2,2,0,0))
13594   {
13595     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13596                                player->index_bit, dig_side);
13597     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13598                                         player->index_bit, dig_side);
13599
13600     if (element == EL_DC_LANDMINE)
13601       Bang(x, y);
13602
13603     if (Feld[x][y] != element)          // field changed by snapping
13604       return MP_ACTION;
13605
13606     return MP_NO_ACTION;
13607   }
13608
13609   if (player->gravity && is_player && !player->is_auto_moving &&
13610       canFallDown(player) && move_direction != MV_DOWN &&
13611       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13612     return MP_NO_ACTION;        // player cannot walk here due to gravity
13613
13614   if (player_can_move &&
13615       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13616   {
13617     int sound_element = SND_ELEMENT(element);
13618     int sound_action = ACTION_WALKING;
13619
13620     if (IS_RND_GATE(element))
13621     {
13622       if (!player->key[RND_GATE_NR(element)])
13623         return MP_NO_ACTION;
13624     }
13625     else if (IS_RND_GATE_GRAY(element))
13626     {
13627       if (!player->key[RND_GATE_GRAY_NR(element)])
13628         return MP_NO_ACTION;
13629     }
13630     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13631     {
13632       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13633         return MP_NO_ACTION;
13634     }
13635     else if (element == EL_EXIT_OPEN ||
13636              element == EL_EM_EXIT_OPEN ||
13637              element == EL_EM_EXIT_OPENING ||
13638              element == EL_STEEL_EXIT_OPEN ||
13639              element == EL_EM_STEEL_EXIT_OPEN ||
13640              element == EL_EM_STEEL_EXIT_OPENING ||
13641              element == EL_SP_EXIT_OPEN ||
13642              element == EL_SP_EXIT_OPENING)
13643     {
13644       sound_action = ACTION_PASSING;    // player is passing exit
13645     }
13646     else if (element == EL_EMPTY)
13647     {
13648       sound_action = ACTION_MOVING;             // nothing to walk on
13649     }
13650
13651     // play sound from background or player, whatever is available
13652     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13653       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13654     else
13655       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13656   }
13657   else if (player_can_move &&
13658            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13659   {
13660     if (!ACCESS_FROM(element, opposite_direction))
13661       return MP_NO_ACTION;      // field not accessible from this direction
13662
13663     if (CAN_MOVE(element))      // only fixed elements can be passed!
13664       return MP_NO_ACTION;
13665
13666     if (IS_EM_GATE(element))
13667     {
13668       if (!player->key[EM_GATE_NR(element)])
13669         return MP_NO_ACTION;
13670     }
13671     else if (IS_EM_GATE_GRAY(element))
13672     {
13673       if (!player->key[EM_GATE_GRAY_NR(element)])
13674         return MP_NO_ACTION;
13675     }
13676     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13677     {
13678       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13679         return MP_NO_ACTION;
13680     }
13681     else if (IS_EMC_GATE(element))
13682     {
13683       if (!player->key[EMC_GATE_NR(element)])
13684         return MP_NO_ACTION;
13685     }
13686     else if (IS_EMC_GATE_GRAY(element))
13687     {
13688       if (!player->key[EMC_GATE_GRAY_NR(element)])
13689         return MP_NO_ACTION;
13690     }
13691     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13692     {
13693       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13694         return MP_NO_ACTION;
13695     }
13696     else if (element == EL_DC_GATE_WHITE ||
13697              element == EL_DC_GATE_WHITE_GRAY ||
13698              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13699     {
13700       if (player->num_white_keys == 0)
13701         return MP_NO_ACTION;
13702
13703       player->num_white_keys--;
13704     }
13705     else if (IS_SP_PORT(element))
13706     {
13707       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13708           element == EL_SP_GRAVITY_PORT_RIGHT ||
13709           element == EL_SP_GRAVITY_PORT_UP ||
13710           element == EL_SP_GRAVITY_PORT_DOWN)
13711         player->gravity = !player->gravity;
13712       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13713                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13714                element == EL_SP_GRAVITY_ON_PORT_UP ||
13715                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13716         player->gravity = TRUE;
13717       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13718                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13719                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13720                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13721         player->gravity = FALSE;
13722     }
13723
13724     // automatically move to the next field with double speed
13725     player->programmed_action = move_direction;
13726
13727     if (player->move_delay_reset_counter == 0)
13728     {
13729       player->move_delay_reset_counter = 2;     // two double speed steps
13730
13731       DOUBLE_PLAYER_SPEED(player);
13732     }
13733
13734     PlayLevelSoundAction(x, y, ACTION_PASSING);
13735   }
13736   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13737   {
13738     RemoveField(x, y);
13739
13740     if (mode != DF_SNAP)
13741     {
13742       GfxElement[x][y] = GFX_ELEMENT(element);
13743       player->is_digging = TRUE;
13744     }
13745
13746     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13747
13748     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13749                                         player->index_bit, dig_side);
13750
13751     if (mode == DF_SNAP)
13752     {
13753       if (level.block_snap_field)
13754         setFieldForSnapping(x, y, element, move_direction);
13755       else
13756         TestIfElementTouchesCustomElement(x, y);        // for empty space
13757
13758       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13759                                           player->index_bit, dig_side);
13760     }
13761   }
13762   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13763   {
13764     RemoveField(x, y);
13765
13766     if (is_player && mode != DF_SNAP)
13767     {
13768       GfxElement[x][y] = element;
13769       player->is_collecting = TRUE;
13770     }
13771
13772     if (element == EL_SPEED_PILL)
13773     {
13774       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13775     }
13776     else if (element == EL_EXTRA_TIME && level.time > 0)
13777     {
13778       TimeLeft += level.extra_time;
13779
13780       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13781
13782       DisplayGameControlValues();
13783     }
13784     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13785     {
13786       player->shield_normal_time_left += level.shield_normal_time;
13787       if (element == EL_SHIELD_DEADLY)
13788         player->shield_deadly_time_left += level.shield_deadly_time;
13789     }
13790     else if (element == EL_DYNAMITE ||
13791              element == EL_EM_DYNAMITE ||
13792              element == EL_SP_DISK_RED)
13793     {
13794       if (player->inventory_size < MAX_INVENTORY_SIZE)
13795         player->inventory_element[player->inventory_size++] = element;
13796
13797       DrawGameDoorValues();
13798     }
13799     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13800     {
13801       player->dynabomb_count++;
13802       player->dynabombs_left++;
13803     }
13804     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13805     {
13806       player->dynabomb_size++;
13807     }
13808     else if (element == EL_DYNABOMB_INCREASE_POWER)
13809     {
13810       player->dynabomb_xl = TRUE;
13811     }
13812     else if (IS_KEY(element))
13813     {
13814       player->key[KEY_NR(element)] = TRUE;
13815
13816       DrawGameDoorValues();
13817     }
13818     else if (element == EL_DC_KEY_WHITE)
13819     {
13820       player->num_white_keys++;
13821
13822       // display white keys?
13823       // DrawGameDoorValues();
13824     }
13825     else if (IS_ENVELOPE(element))
13826     {
13827       player->show_envelope = element;
13828     }
13829     else if (element == EL_EMC_LENSES)
13830     {
13831       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13832
13833       RedrawAllInvisibleElementsForLenses();
13834     }
13835     else if (element == EL_EMC_MAGNIFIER)
13836     {
13837       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13838
13839       RedrawAllInvisibleElementsForMagnifier();
13840     }
13841     else if (IS_DROPPABLE(element) ||
13842              IS_THROWABLE(element))     // can be collected and dropped
13843     {
13844       int i;
13845
13846       if (collect_count == 0)
13847         player->inventory_infinite_element = element;
13848       else
13849         for (i = 0; i < collect_count; i++)
13850           if (player->inventory_size < MAX_INVENTORY_SIZE)
13851             player->inventory_element[player->inventory_size++] = element;
13852
13853       DrawGameDoorValues();
13854     }
13855     else if (collect_count > 0)
13856     {
13857       local_player->gems_still_needed -= collect_count;
13858       if (local_player->gems_still_needed < 0)
13859         local_player->gems_still_needed = 0;
13860
13861       game.snapshot.collected_item = TRUE;
13862
13863       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13864
13865       DisplayGameControlValues();
13866     }
13867
13868     RaiseScoreElement(element);
13869     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13870
13871     if (is_player)
13872       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13873                                           player->index_bit, dig_side);
13874
13875     if (mode == DF_SNAP)
13876     {
13877       if (level.block_snap_field)
13878         setFieldForSnapping(x, y, element, move_direction);
13879       else
13880         TestIfElementTouchesCustomElement(x, y);        // for empty space
13881
13882       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13883                                           player->index_bit, dig_side);
13884     }
13885   }
13886   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13887   {
13888     if (mode == DF_SNAP && element != EL_BD_ROCK)
13889       return MP_NO_ACTION;
13890
13891     if (CAN_FALL(element) && dy)
13892       return MP_NO_ACTION;
13893
13894     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13895         !(element == EL_SPRING && level.use_spring_bug))
13896       return MP_NO_ACTION;
13897
13898     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13899         ((move_direction & MV_VERTICAL &&
13900           ((element_info[element].move_pattern & MV_LEFT &&
13901             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13902            (element_info[element].move_pattern & MV_RIGHT &&
13903             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13904          (move_direction & MV_HORIZONTAL &&
13905           ((element_info[element].move_pattern & MV_UP &&
13906             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13907            (element_info[element].move_pattern & MV_DOWN &&
13908             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13909       return MP_NO_ACTION;
13910
13911     // do not push elements already moving away faster than player
13912     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13913         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13914       return MP_NO_ACTION;
13915
13916     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13917     {
13918       if (player->push_delay_value == -1 || !player_was_pushing)
13919         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13920     }
13921     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13922     {
13923       if (player->push_delay_value == -1)
13924         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13925     }
13926     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13927     {
13928       if (!player->is_pushing)
13929         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13930     }
13931
13932     player->is_pushing = TRUE;
13933     player->is_active = TRUE;
13934
13935     if (!(IN_LEV_FIELD(nextx, nexty) &&
13936           (IS_FREE(nextx, nexty) ||
13937            (IS_SB_ELEMENT(element) &&
13938             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13939            (IS_CUSTOM_ELEMENT(element) &&
13940             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13941       return MP_NO_ACTION;
13942
13943     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13944       return MP_NO_ACTION;
13945
13946     if (player->push_delay == -1)       // new pushing; restart delay
13947       player->push_delay = 0;
13948
13949     if (player->push_delay < player->push_delay_value &&
13950         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13951         element != EL_SPRING && element != EL_BALLOON)
13952     {
13953       // make sure that there is no move delay before next try to push
13954       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13955         player->move_delay = 0;
13956
13957       return MP_NO_ACTION;
13958     }
13959
13960     if (IS_CUSTOM_ELEMENT(element) &&
13961         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13962     {
13963       if (!DigFieldByCE(nextx, nexty, element))
13964         return MP_NO_ACTION;
13965     }
13966
13967     if (IS_SB_ELEMENT(element))
13968     {
13969       boolean sokoban_task_solved = FALSE;
13970
13971       if (element == EL_SOKOBAN_FIELD_FULL)
13972       {
13973         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13974
13975         IncrementSokobanFieldsNeeded();
13976         IncrementSokobanObjectsNeeded();
13977       }
13978
13979       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13980       {
13981         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13982
13983         DecrementSokobanFieldsNeeded();
13984         DecrementSokobanObjectsNeeded();
13985
13986         // sokoban object was pushed from empty field to sokoban field
13987         if (Back[x][y] == EL_EMPTY)
13988           sokoban_task_solved = TRUE;
13989       }
13990
13991       Feld[x][y] = EL_SOKOBAN_OBJECT;
13992
13993       if (Back[x][y] == Back[nextx][nexty])
13994         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13995       else if (Back[x][y] != 0)
13996         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13997                                     ACTION_EMPTYING);
13998       else
13999         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14000                                     ACTION_FILLING);
14001
14002       if (sokoban_task_solved &&
14003           local_player->sokoban_fields_still_needed == 0 &&
14004           local_player->sokoban_objects_still_needed == 0 &&
14005           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14006       {
14007         local_player->players_still_needed = 0;
14008
14009         LevelSolved();
14010
14011         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14012       }
14013     }
14014     else
14015       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14016
14017     InitMovingField(x, y, move_direction);
14018     GfxAction[x][y] = ACTION_PUSHING;
14019
14020     if (mode == DF_SNAP)
14021       ContinueMoving(x, y);
14022     else
14023       MovPos[x][y] = (dx != 0 ? dx : dy);
14024
14025     Pushed[x][y] = TRUE;
14026     Pushed[nextx][nexty] = TRUE;
14027
14028     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14029       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14030     else
14031       player->push_delay_value = -1;    // get new value later
14032
14033     // check for element change _after_ element has been pushed
14034     if (game.use_change_when_pushing_bug)
14035     {
14036       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14037                                  player->index_bit, dig_side);
14038       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14039                                           player->index_bit, dig_side);
14040     }
14041   }
14042   else if (IS_SWITCHABLE(element))
14043   {
14044     if (PLAYER_SWITCHING(player, x, y))
14045     {
14046       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14047                                           player->index_bit, dig_side);
14048
14049       return MP_ACTION;
14050     }
14051
14052     player->is_switching = TRUE;
14053     player->switch_x = x;
14054     player->switch_y = y;
14055
14056     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14057
14058     if (element == EL_ROBOT_WHEEL)
14059     {
14060       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14061       ZX = x;
14062       ZY = y;
14063
14064       game.robot_wheel_active = TRUE;
14065
14066       TEST_DrawLevelField(x, y);
14067     }
14068     else if (element == EL_SP_TERMINAL)
14069     {
14070       int xx, yy;
14071
14072       SCAN_PLAYFIELD(xx, yy)
14073       {
14074         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14075         {
14076           Bang(xx, yy);
14077         }
14078         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14079         {
14080           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14081
14082           ResetGfxAnimation(xx, yy);
14083           TEST_DrawLevelField(xx, yy);
14084         }
14085       }
14086     }
14087     else if (IS_BELT_SWITCH(element))
14088     {
14089       ToggleBeltSwitch(x, y);
14090     }
14091     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14092              element == EL_SWITCHGATE_SWITCH_DOWN ||
14093              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14094              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14095     {
14096       ToggleSwitchgateSwitch(x, y);
14097     }
14098     else if (element == EL_LIGHT_SWITCH ||
14099              element == EL_LIGHT_SWITCH_ACTIVE)
14100     {
14101       ToggleLightSwitch(x, y);
14102     }
14103     else if (element == EL_TIMEGATE_SWITCH ||
14104              element == EL_DC_TIMEGATE_SWITCH)
14105     {
14106       ActivateTimegateSwitch(x, y);
14107     }
14108     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14109              element == EL_BALLOON_SWITCH_RIGHT ||
14110              element == EL_BALLOON_SWITCH_UP    ||
14111              element == EL_BALLOON_SWITCH_DOWN  ||
14112              element == EL_BALLOON_SWITCH_NONE  ||
14113              element == EL_BALLOON_SWITCH_ANY)
14114     {
14115       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14116                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14117                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14118                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14119                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14120                              move_direction);
14121     }
14122     else if (element == EL_LAMP)
14123     {
14124       Feld[x][y] = EL_LAMP_ACTIVE;
14125       local_player->lights_still_needed--;
14126
14127       ResetGfxAnimation(x, y);
14128       TEST_DrawLevelField(x, y);
14129     }
14130     else if (element == EL_TIME_ORB_FULL)
14131     {
14132       Feld[x][y] = EL_TIME_ORB_EMPTY;
14133
14134       if (level.time > 0 || level.use_time_orb_bug)
14135       {
14136         TimeLeft += level.time_orb_time;
14137         game.no_time_limit = FALSE;
14138
14139         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14140
14141         DisplayGameControlValues();
14142       }
14143
14144       ResetGfxAnimation(x, y);
14145       TEST_DrawLevelField(x, y);
14146     }
14147     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14148              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14149     {
14150       int xx, yy;
14151
14152       game.ball_state = !game.ball_state;
14153
14154       SCAN_PLAYFIELD(xx, yy)
14155       {
14156         int e = Feld[xx][yy];
14157
14158         if (game.ball_state)
14159         {
14160           if (e == EL_EMC_MAGIC_BALL)
14161             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14162           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14163             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14164         }
14165         else
14166         {
14167           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14168             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14169           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14170             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14171         }
14172       }
14173     }
14174
14175     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14176                                         player->index_bit, dig_side);
14177
14178     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14179                                         player->index_bit, dig_side);
14180
14181     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14182                                         player->index_bit, dig_side);
14183
14184     return MP_ACTION;
14185   }
14186   else
14187   {
14188     if (!PLAYER_SWITCHING(player, x, y))
14189     {
14190       player->is_switching = TRUE;
14191       player->switch_x = x;
14192       player->switch_y = y;
14193
14194       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14195                                  player->index_bit, dig_side);
14196       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14197                                           player->index_bit, dig_side);
14198
14199       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14200                                  player->index_bit, dig_side);
14201       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14202                                           player->index_bit, dig_side);
14203     }
14204
14205     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14206                                player->index_bit, dig_side);
14207     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14208                                         player->index_bit, dig_side);
14209
14210     return MP_NO_ACTION;
14211   }
14212
14213   player->push_delay = -1;
14214
14215   if (is_player)                // function can also be called by EL_PENGUIN
14216   {
14217     if (Feld[x][y] != element)          // really digged/collected something
14218     {
14219       player->is_collecting = !player->is_digging;
14220       player->is_active = TRUE;
14221     }
14222   }
14223
14224   return MP_MOVING;
14225 }
14226
14227 static boolean DigFieldByCE(int x, int y, int digging_element)
14228 {
14229   int element = Feld[x][y];
14230
14231   if (!IS_FREE(x, y))
14232   {
14233     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14234                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14235                   ACTION_BREAKING);
14236
14237     // no element can dig solid indestructible elements
14238     if (IS_INDESTRUCTIBLE(element) &&
14239         !IS_DIGGABLE(element) &&
14240         !IS_COLLECTIBLE(element))
14241       return FALSE;
14242
14243     if (AmoebaNr[x][y] &&
14244         (element == EL_AMOEBA_FULL ||
14245          element == EL_BD_AMOEBA ||
14246          element == EL_AMOEBA_GROWING))
14247     {
14248       AmoebaCnt[AmoebaNr[x][y]]--;
14249       AmoebaCnt2[AmoebaNr[x][y]]--;
14250     }
14251
14252     if (IS_MOVING(x, y))
14253       RemoveMovingField(x, y);
14254     else
14255     {
14256       RemoveField(x, y);
14257       TEST_DrawLevelField(x, y);
14258     }
14259
14260     // if digged element was about to explode, prevent the explosion
14261     ExplodeField[x][y] = EX_TYPE_NONE;
14262
14263     PlayLevelSoundAction(x, y, action);
14264   }
14265
14266   Store[x][y] = EL_EMPTY;
14267
14268   // this makes it possible to leave the removed element again
14269   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14270     Store[x][y] = element;
14271
14272   return TRUE;
14273 }
14274
14275 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14276 {
14277   int jx = player->jx, jy = player->jy;
14278   int x = jx + dx, y = jy + dy;
14279   int snap_direction = (dx == -1 ? MV_LEFT  :
14280                         dx == +1 ? MV_RIGHT :
14281                         dy == -1 ? MV_UP    :
14282                         dy == +1 ? MV_DOWN  : MV_NONE);
14283   boolean can_continue_snapping = (level.continuous_snapping &&
14284                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14285
14286   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14287     return FALSE;
14288
14289   if (!player->active || !IN_LEV_FIELD(x, y))
14290     return FALSE;
14291
14292   if (dx && dy)
14293     return FALSE;
14294
14295   if (!dx && !dy)
14296   {
14297     if (player->MovPos == 0)
14298       player->is_pushing = FALSE;
14299
14300     player->is_snapping = FALSE;
14301
14302     if (player->MovPos == 0)
14303     {
14304       player->is_moving = FALSE;
14305       player->is_digging = FALSE;
14306       player->is_collecting = FALSE;
14307     }
14308
14309     return FALSE;
14310   }
14311
14312   // prevent snapping with already pressed snap key when not allowed
14313   if (player->is_snapping && !can_continue_snapping)
14314     return FALSE;
14315
14316   player->MovDir = snap_direction;
14317
14318   if (player->MovPos == 0)
14319   {
14320     player->is_moving = FALSE;
14321     player->is_digging = FALSE;
14322     player->is_collecting = FALSE;
14323   }
14324
14325   player->is_dropping = FALSE;
14326   player->is_dropping_pressed = FALSE;
14327   player->drop_pressed_delay = 0;
14328
14329   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14330     return FALSE;
14331
14332   player->is_snapping = TRUE;
14333   player->is_active = TRUE;
14334
14335   if (player->MovPos == 0)
14336   {
14337     player->is_moving = FALSE;
14338     player->is_digging = FALSE;
14339     player->is_collecting = FALSE;
14340   }
14341
14342   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14343     TEST_DrawLevelField(player->last_jx, player->last_jy);
14344
14345   TEST_DrawLevelField(x, y);
14346
14347   return TRUE;
14348 }
14349
14350 static boolean DropElement(struct PlayerInfo *player)
14351 {
14352   int old_element, new_element;
14353   int dropx = player->jx, dropy = player->jy;
14354   int drop_direction = player->MovDir;
14355   int drop_side = drop_direction;
14356   int drop_element = get_next_dropped_element(player);
14357
14358   /* do not drop an element on top of another element; when holding drop key
14359      pressed without moving, dropped element must move away before the next
14360      element can be dropped (this is especially important if the next element
14361      is dynamite, which can be placed on background for historical reasons) */
14362   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14363     return MP_ACTION;
14364
14365   if (IS_THROWABLE(drop_element))
14366   {
14367     dropx += GET_DX_FROM_DIR(drop_direction);
14368     dropy += GET_DY_FROM_DIR(drop_direction);
14369
14370     if (!IN_LEV_FIELD(dropx, dropy))
14371       return FALSE;
14372   }
14373
14374   old_element = Feld[dropx][dropy];     // old element at dropping position
14375   new_element = drop_element;           // default: no change when dropping
14376
14377   // check if player is active, not moving and ready to drop
14378   if (!player->active || player->MovPos || player->drop_delay > 0)
14379     return FALSE;
14380
14381   // check if player has anything that can be dropped
14382   if (new_element == EL_UNDEFINED)
14383     return FALSE;
14384
14385   // only set if player has anything that can be dropped
14386   player->is_dropping_pressed = TRUE;
14387
14388   // check if drop key was pressed long enough for EM style dynamite
14389   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14390     return FALSE;
14391
14392   // check if anything can be dropped at the current position
14393   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14394     return FALSE;
14395
14396   // collected custom elements can only be dropped on empty fields
14397   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14398     return FALSE;
14399
14400   if (old_element != EL_EMPTY)
14401     Back[dropx][dropy] = old_element;   // store old element on this field
14402
14403   ResetGfxAnimation(dropx, dropy);
14404   ResetRandomAnimationValue(dropx, dropy);
14405
14406   if (player->inventory_size > 0 ||
14407       player->inventory_infinite_element != EL_UNDEFINED)
14408   {
14409     if (player->inventory_size > 0)
14410     {
14411       player->inventory_size--;
14412
14413       DrawGameDoorValues();
14414
14415       if (new_element == EL_DYNAMITE)
14416         new_element = EL_DYNAMITE_ACTIVE;
14417       else if (new_element == EL_EM_DYNAMITE)
14418         new_element = EL_EM_DYNAMITE_ACTIVE;
14419       else if (new_element == EL_SP_DISK_RED)
14420         new_element = EL_SP_DISK_RED_ACTIVE;
14421     }
14422
14423     Feld[dropx][dropy] = new_element;
14424
14425     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14426       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14427                           el2img(Feld[dropx][dropy]), 0);
14428
14429     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14430
14431     // needed if previous element just changed to "empty" in the last frame
14432     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14433
14434     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14435                                player->index_bit, drop_side);
14436     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14437                                         CE_PLAYER_DROPS_X,
14438                                         player->index_bit, drop_side);
14439
14440     TestIfElementTouchesCustomElement(dropx, dropy);
14441   }
14442   else          // player is dropping a dyna bomb
14443   {
14444     player->dynabombs_left--;
14445
14446     Feld[dropx][dropy] = new_element;
14447
14448     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14449       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14450                           el2img(Feld[dropx][dropy]), 0);
14451
14452     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14453   }
14454
14455   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14456     InitField_WithBug1(dropx, dropy, FALSE);
14457
14458   new_element = Feld[dropx][dropy];     // element might have changed
14459
14460   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14461       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14462   {
14463     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14464       MovDir[dropx][dropy] = drop_direction;
14465
14466     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14467
14468     // do not cause impact style collision by dropping elements that can fall
14469     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14470   }
14471
14472   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14473   player->is_dropping = TRUE;
14474
14475   player->drop_pressed_delay = 0;
14476   player->is_dropping_pressed = FALSE;
14477
14478   player->drop_x = dropx;
14479   player->drop_y = dropy;
14480
14481   return TRUE;
14482 }
14483
14484 // ----------------------------------------------------------------------------
14485 // game sound playing functions
14486 // ----------------------------------------------------------------------------
14487
14488 static int *loop_sound_frame = NULL;
14489 static int *loop_sound_volume = NULL;
14490
14491 void InitPlayLevelSound(void)
14492 {
14493   int num_sounds = getSoundListSize();
14494
14495   checked_free(loop_sound_frame);
14496   checked_free(loop_sound_volume);
14497
14498   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14499   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14500 }
14501
14502 static void PlayLevelSound(int x, int y, int nr)
14503 {
14504   int sx = SCREENX(x), sy = SCREENY(y);
14505   int volume, stereo_position;
14506   int max_distance = 8;
14507   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14508
14509   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14510       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14511     return;
14512
14513   if (!IN_LEV_FIELD(x, y) ||
14514       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14515       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14516     return;
14517
14518   volume = SOUND_MAX_VOLUME;
14519
14520   if (!IN_SCR_FIELD(sx, sy))
14521   {
14522     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14523     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14524
14525     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14526   }
14527
14528   stereo_position = (SOUND_MAX_LEFT +
14529                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14530                      (SCR_FIELDX + 2 * max_distance));
14531
14532   if (IS_LOOP_SOUND(nr))
14533   {
14534     /* This assures that quieter loop sounds do not overwrite louder ones,
14535        while restarting sound volume comparison with each new game frame. */
14536
14537     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14538       return;
14539
14540     loop_sound_volume[nr] = volume;
14541     loop_sound_frame[nr] = FrameCounter;
14542   }
14543
14544   PlaySoundExt(nr, volume, stereo_position, type);
14545 }
14546
14547 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14548 {
14549   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14550                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14551                  y < LEVELY(BY1) ? LEVELY(BY1) :
14552                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14553                  sound_action);
14554 }
14555
14556 static void PlayLevelSoundAction(int x, int y, int action)
14557 {
14558   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14559 }
14560
14561 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14562 {
14563   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14564
14565   if (sound_effect != SND_UNDEFINED)
14566     PlayLevelSound(x, y, sound_effect);
14567 }
14568
14569 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14570                                               int action)
14571 {
14572   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14573
14574   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14575     PlayLevelSound(x, y, sound_effect);
14576 }
14577
14578 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14579 {
14580   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14581
14582   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14583     PlayLevelSound(x, y, sound_effect);
14584 }
14585
14586 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14587 {
14588   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14589
14590   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14591     StopSound(sound_effect);
14592 }
14593
14594 static int getLevelMusicNr(void)
14595 {
14596   if (levelset.music[level_nr] != MUS_UNDEFINED)
14597     return levelset.music[level_nr];            // from config file
14598   else
14599     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14600 }
14601
14602 static void FadeLevelSounds(void)
14603 {
14604   FadeSounds();
14605 }
14606
14607 static void FadeLevelMusic(void)
14608 {
14609   int music_nr = getLevelMusicNr();
14610   char *curr_music = getCurrentlyPlayingMusicFilename();
14611   char *next_music = getMusicInfoEntryFilename(music_nr);
14612
14613   if (!strEqual(curr_music, next_music))
14614     FadeMusic();
14615 }
14616
14617 void FadeLevelSoundsAndMusic(void)
14618 {
14619   FadeLevelSounds();
14620   FadeLevelMusic();
14621 }
14622
14623 static void PlayLevelMusic(void)
14624 {
14625   int music_nr = getLevelMusicNr();
14626   char *curr_music = getCurrentlyPlayingMusicFilename();
14627   char *next_music = getMusicInfoEntryFilename(music_nr);
14628
14629   if (!strEqual(curr_music, next_music))
14630     PlayMusicLoop(music_nr);
14631 }
14632
14633 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14634 {
14635   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14636   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14637   int x = xx - 1 - offset;
14638   int y = yy - 1 - offset;
14639
14640   switch (sample)
14641   {
14642     case SAMPLE_blank:
14643       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14644       break;
14645
14646     case SAMPLE_roll:
14647       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14648       break;
14649
14650     case SAMPLE_stone:
14651       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14652       break;
14653
14654     case SAMPLE_nut:
14655       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14656       break;
14657
14658     case SAMPLE_crack:
14659       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14660       break;
14661
14662     case SAMPLE_bug:
14663       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14664       break;
14665
14666     case SAMPLE_tank:
14667       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14668       break;
14669
14670     case SAMPLE_android_clone:
14671       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14672       break;
14673
14674     case SAMPLE_android_move:
14675       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14676       break;
14677
14678     case SAMPLE_spring:
14679       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14680       break;
14681
14682     case SAMPLE_slurp:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14684       break;
14685
14686     case SAMPLE_eater:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14688       break;
14689
14690     case SAMPLE_eater_eat:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14692       break;
14693
14694     case SAMPLE_alien:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14696       break;
14697
14698     case SAMPLE_collect:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14700       break;
14701
14702     case SAMPLE_diamond:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14704       break;
14705
14706     case SAMPLE_squash:
14707       // !!! CHECK THIS !!!
14708 #if 1
14709       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14710 #else
14711       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14712 #endif
14713       break;
14714
14715     case SAMPLE_wonderfall:
14716       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14717       break;
14718
14719     case SAMPLE_drip:
14720       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14721       break;
14722
14723     case SAMPLE_push:
14724       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14725       break;
14726
14727     case SAMPLE_dirt:
14728       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14729       break;
14730
14731     case SAMPLE_acid:
14732       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14733       break;
14734
14735     case SAMPLE_ball:
14736       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14737       break;
14738
14739     case SAMPLE_grow:
14740       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14741       break;
14742
14743     case SAMPLE_wonder:
14744       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14745       break;
14746
14747     case SAMPLE_door:
14748       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14749       break;
14750
14751     case SAMPLE_exit_open:
14752       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14753       break;
14754
14755     case SAMPLE_exit_leave:
14756       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14757       break;
14758
14759     case SAMPLE_dynamite:
14760       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14761       break;
14762
14763     case SAMPLE_tick:
14764       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14765       break;
14766
14767     case SAMPLE_press:
14768       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14769       break;
14770
14771     case SAMPLE_wheel:
14772       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14773       break;
14774
14775     case SAMPLE_boom:
14776       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14777       break;
14778
14779     case SAMPLE_die:
14780       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14781       break;
14782
14783     case SAMPLE_time:
14784       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14785       break;
14786
14787     default:
14788       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14789       break;
14790   }
14791 }
14792
14793 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14794 {
14795   int element = map_element_SP_to_RND(element_sp);
14796   int action = map_action_SP_to_RND(action_sp);
14797   int offset = (setup.sp_show_border_elements ? 0 : 1);
14798   int x = xx - offset;
14799   int y = yy - offset;
14800
14801   PlayLevelSoundElementAction(x, y, element, action);
14802 }
14803
14804 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14805 {
14806   int element = map_element_MM_to_RND(element_mm);
14807   int action = map_action_MM_to_RND(action_mm);
14808   int offset = 0;
14809   int x = xx - offset;
14810   int y = yy - offset;
14811
14812   if (!IS_MM_ELEMENT(element))
14813     element = EL_MM_DEFAULT;
14814
14815   PlayLevelSoundElementAction(x, y, element, action);
14816 }
14817
14818 void PlaySound_MM(int sound_mm)
14819 {
14820   int sound = map_sound_MM_to_RND(sound_mm);
14821
14822   if (sound == SND_UNDEFINED)
14823     return;
14824
14825   PlaySound(sound);
14826 }
14827
14828 void PlaySoundLoop_MM(int sound_mm)
14829 {
14830   int sound = map_sound_MM_to_RND(sound_mm);
14831
14832   if (sound == SND_UNDEFINED)
14833     return;
14834
14835   PlaySoundLoop(sound);
14836 }
14837
14838 void StopSound_MM(int sound_mm)
14839 {
14840   int sound = map_sound_MM_to_RND(sound_mm);
14841
14842   if (sound == SND_UNDEFINED)
14843     return;
14844
14845   StopSound(sound);
14846 }
14847
14848 void RaiseScore(int value)
14849 {
14850   local_player->score += value;
14851
14852   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14853
14854   DisplayGameControlValues();
14855 }
14856
14857 void RaiseScoreElement(int element)
14858 {
14859   switch (element)
14860   {
14861     case EL_EMERALD:
14862     case EL_BD_DIAMOND:
14863     case EL_EMERALD_YELLOW:
14864     case EL_EMERALD_RED:
14865     case EL_EMERALD_PURPLE:
14866     case EL_SP_INFOTRON:
14867       RaiseScore(level.score[SC_EMERALD]);
14868       break;
14869     case EL_DIAMOND:
14870       RaiseScore(level.score[SC_DIAMOND]);
14871       break;
14872     case EL_CRYSTAL:
14873       RaiseScore(level.score[SC_CRYSTAL]);
14874       break;
14875     case EL_PEARL:
14876       RaiseScore(level.score[SC_PEARL]);
14877       break;
14878     case EL_BUG:
14879     case EL_BD_BUTTERFLY:
14880     case EL_SP_ELECTRON:
14881       RaiseScore(level.score[SC_BUG]);
14882       break;
14883     case EL_SPACESHIP:
14884     case EL_BD_FIREFLY:
14885     case EL_SP_SNIKSNAK:
14886       RaiseScore(level.score[SC_SPACESHIP]);
14887       break;
14888     case EL_YAMYAM:
14889     case EL_DARK_YAMYAM:
14890       RaiseScore(level.score[SC_YAMYAM]);
14891       break;
14892     case EL_ROBOT:
14893       RaiseScore(level.score[SC_ROBOT]);
14894       break;
14895     case EL_PACMAN:
14896       RaiseScore(level.score[SC_PACMAN]);
14897       break;
14898     case EL_NUT:
14899       RaiseScore(level.score[SC_NUT]);
14900       break;
14901     case EL_DYNAMITE:
14902     case EL_EM_DYNAMITE:
14903     case EL_SP_DISK_RED:
14904     case EL_DYNABOMB_INCREASE_NUMBER:
14905     case EL_DYNABOMB_INCREASE_SIZE:
14906     case EL_DYNABOMB_INCREASE_POWER:
14907       RaiseScore(level.score[SC_DYNAMITE]);
14908       break;
14909     case EL_SHIELD_NORMAL:
14910     case EL_SHIELD_DEADLY:
14911       RaiseScore(level.score[SC_SHIELD]);
14912       break;
14913     case EL_EXTRA_TIME:
14914       RaiseScore(level.extra_time_score);
14915       break;
14916     case EL_KEY_1:
14917     case EL_KEY_2:
14918     case EL_KEY_3:
14919     case EL_KEY_4:
14920     case EL_EM_KEY_1:
14921     case EL_EM_KEY_2:
14922     case EL_EM_KEY_3:
14923     case EL_EM_KEY_4:
14924     case EL_EMC_KEY_5:
14925     case EL_EMC_KEY_6:
14926     case EL_EMC_KEY_7:
14927     case EL_EMC_KEY_8:
14928     case EL_DC_KEY_WHITE:
14929       RaiseScore(level.score[SC_KEY]);
14930       break;
14931     default:
14932       RaiseScore(element_info[element].collect_score);
14933       break;
14934   }
14935 }
14936
14937 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14938 {
14939   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14940   {
14941     // closing door required in case of envelope style request dialogs
14942     if (!skip_request)
14943       CloseDoor(DOOR_CLOSE_1);
14944
14945     if (network.enabled)
14946       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14947     else
14948     {
14949       if (quick_quit)
14950         FadeSkipNextFadeIn();
14951
14952       SetGameStatus(GAME_MODE_MAIN);
14953
14954       DrawMainMenu();
14955     }
14956   }
14957   else          // continue playing the game
14958   {
14959     if (tape.playing && tape.deactivate_display)
14960       TapeDeactivateDisplayOff(TRUE);
14961
14962     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14963
14964     if (tape.playing && tape.deactivate_display)
14965       TapeDeactivateDisplayOn();
14966   }
14967 }
14968
14969 void RequestQuitGame(boolean ask_if_really_quit)
14970 {
14971   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14972   boolean skip_request = AllPlayersGone || quick_quit;
14973
14974   RequestQuitGameExt(skip_request, quick_quit,
14975                      "Do you really want to quit the game?");
14976 }
14977
14978 void RequestRestartGame(char *message)
14979 {
14980   game.restart_game_message = NULL;
14981
14982   boolean has_started_game = hasStartedNetworkGame();
14983   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
14984
14985   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
14986   {
14987     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14988   }
14989   else
14990   {
14991     SetGameStatus(GAME_MODE_MAIN);
14992
14993     DrawMainMenu();
14994   }
14995 }
14996
14997 void CheckGameOver(void)
14998 {
14999   static boolean last_game_over = FALSE;
15000   static int game_over_delay = 0;
15001   int game_over_delay_value = 50;
15002   boolean game_over = checkGameFailed();
15003
15004   // do not handle game over if request dialog is already active
15005   if (game.request_active)
15006     return;
15007
15008   if (!game_over)
15009   {
15010     last_game_over = FALSE;
15011     game_over_delay = game_over_delay_value;
15012
15013     return;
15014   }
15015
15016   if (game_over_delay > 0)
15017   {
15018     game_over_delay--;
15019
15020     return;
15021   }
15022
15023   if (last_game_over != game_over)
15024     game.restart_game_message = (hasStartedNetworkGame() ?
15025                                  "Game over! Play it again?" :
15026                                  "Game over!");
15027
15028   last_game_over = game_over;
15029 }
15030
15031 boolean checkGameSolved(void)
15032 {
15033   // set for all game engines if level was solved
15034   return game.LevelSolved_GameEnd;
15035 }
15036
15037 boolean checkGameFailed(void)
15038 {
15039   if (!AllPlayersGone)
15040     return FALSE;
15041
15042   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15043     return (game_em.game_over && !game_em.level_solved);
15044   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15045     return (game_sp.game_over && !game_sp.level_solved);
15046   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15047     return (game_mm.game_over && !game_mm.level_solved);
15048   else                          // GAME_ENGINE_TYPE_RND
15049     return (local_player->GameOver && !game.LevelSolved);
15050 }
15051
15052 boolean checkGameEnded(void)
15053 {
15054   return (checkGameSolved() || checkGameFailed());
15055 }
15056
15057
15058 // ----------------------------------------------------------------------------
15059 // random generator functions
15060 // ----------------------------------------------------------------------------
15061
15062 unsigned int InitEngineRandom_RND(int seed)
15063 {
15064   game.num_random_calls = 0;
15065
15066   return InitEngineRandom(seed);
15067 }
15068
15069 unsigned int RND(int max)
15070 {
15071   if (max > 0)
15072   {
15073     game.num_random_calls++;
15074
15075     return GetEngineRandom(max);
15076   }
15077
15078   return 0;
15079 }
15080
15081
15082 // ----------------------------------------------------------------------------
15083 // game engine snapshot handling functions
15084 // ----------------------------------------------------------------------------
15085
15086 struct EngineSnapshotInfo
15087 {
15088   // runtime values for custom element collect score
15089   int collect_score[NUM_CUSTOM_ELEMENTS];
15090
15091   // runtime values for group element choice position
15092   int choice_pos[NUM_GROUP_ELEMENTS];
15093
15094   // runtime values for belt position animations
15095   int belt_graphic[4][NUM_BELT_PARTS];
15096   int belt_anim_mode[4][NUM_BELT_PARTS];
15097 };
15098
15099 static struct EngineSnapshotInfo engine_snapshot_rnd;
15100 static char *snapshot_level_identifier = NULL;
15101 static int snapshot_level_nr = -1;
15102
15103 static void SaveEngineSnapshotValues_RND(void)
15104 {
15105   static int belt_base_active_element[4] =
15106   {
15107     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15108     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15109     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15110     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15111   };
15112   int i, j;
15113
15114   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15115   {
15116     int element = EL_CUSTOM_START + i;
15117
15118     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15119   }
15120
15121   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15122   {
15123     int element = EL_GROUP_START + i;
15124
15125     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15126   }
15127
15128   for (i = 0; i < 4; i++)
15129   {
15130     for (j = 0; j < NUM_BELT_PARTS; j++)
15131     {
15132       int element = belt_base_active_element[i] + j;
15133       int graphic = el2img(element);
15134       int anim_mode = graphic_info[graphic].anim_mode;
15135
15136       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15137       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15138     }
15139   }
15140 }
15141
15142 static void LoadEngineSnapshotValues_RND(void)
15143 {
15144   unsigned int num_random_calls = game.num_random_calls;
15145   int i, j;
15146
15147   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15148   {
15149     int element = EL_CUSTOM_START + i;
15150
15151     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15152   }
15153
15154   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15155   {
15156     int element = EL_GROUP_START + i;
15157
15158     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15159   }
15160
15161   for (i = 0; i < 4; i++)
15162   {
15163     for (j = 0; j < NUM_BELT_PARTS; j++)
15164     {
15165       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15166       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15167
15168       graphic_info[graphic].anim_mode = anim_mode;
15169     }
15170   }
15171
15172   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15173   {
15174     InitRND(tape.random_seed);
15175     for (i = 0; i < num_random_calls; i++)
15176       RND(1);
15177   }
15178
15179   if (game.num_random_calls != num_random_calls)
15180   {
15181     Error(ERR_INFO, "number of random calls out of sync");
15182     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15183     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15184     Error(ERR_EXIT, "this should not happen -- please debug");
15185   }
15186 }
15187
15188 void FreeEngineSnapshotSingle(void)
15189 {
15190   FreeSnapshotSingle();
15191
15192   setString(&snapshot_level_identifier, NULL);
15193   snapshot_level_nr = -1;
15194 }
15195
15196 void FreeEngineSnapshotList(void)
15197 {
15198   FreeSnapshotList();
15199 }
15200
15201 static ListNode *SaveEngineSnapshotBuffers(void)
15202 {
15203   ListNode *buffers = NULL;
15204
15205   // copy some special values to a structure better suited for the snapshot
15206
15207   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15208     SaveEngineSnapshotValues_RND();
15209   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15210     SaveEngineSnapshotValues_EM();
15211   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15212     SaveEngineSnapshotValues_SP(&buffers);
15213   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15214     SaveEngineSnapshotValues_MM(&buffers);
15215
15216   // save values stored in special snapshot structure
15217
15218   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15219     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15220   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15221     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15222   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15223     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15224   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15225     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15226
15227   // save further RND engine values
15228
15229   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15230   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15231   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15232
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15235   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15237
15238   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15243
15244   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15247
15248   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15249
15250   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15251
15252   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15253   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15254
15255   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15256   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15257   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15258   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15259   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15260   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15261   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15262   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15263   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15264   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15265   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15266   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15267   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15268   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15269   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15270   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15271   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15272   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15273
15274   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15275   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15276
15277   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15278   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15279   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15280
15281   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15282   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15283
15284   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15285   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15287   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15288   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15289
15290   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15292
15293 #if 0
15294   ListNode *node = engine_snapshot_list_rnd;
15295   int num_bytes = 0;
15296
15297   while (node != NULL)
15298   {
15299     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15300
15301     node = node->next;
15302   }
15303
15304   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15305 #endif
15306
15307   return buffers;
15308 }
15309
15310 void SaveEngineSnapshotSingle(void)
15311 {
15312   ListNode *buffers = SaveEngineSnapshotBuffers();
15313
15314   // finally save all snapshot buffers to single snapshot
15315   SaveSnapshotSingle(buffers);
15316
15317   // save level identification information
15318   setString(&snapshot_level_identifier, leveldir_current->identifier);
15319   snapshot_level_nr = level_nr;
15320 }
15321
15322 boolean CheckSaveEngineSnapshotToList(void)
15323 {
15324   boolean save_snapshot =
15325     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15326      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15327       game.snapshot.changed_action) ||
15328      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15329       game.snapshot.collected_item));
15330
15331   game.snapshot.changed_action = FALSE;
15332   game.snapshot.collected_item = FALSE;
15333   game.snapshot.save_snapshot = save_snapshot;
15334
15335   return save_snapshot;
15336 }
15337
15338 void SaveEngineSnapshotToList(void)
15339 {
15340   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15341       tape.quick_resume)
15342     return;
15343
15344   ListNode *buffers = SaveEngineSnapshotBuffers();
15345
15346   // finally save all snapshot buffers to snapshot list
15347   SaveSnapshotToList(buffers);
15348 }
15349
15350 void SaveEngineSnapshotToListInitial(void)
15351 {
15352   FreeEngineSnapshotList();
15353
15354   SaveEngineSnapshotToList();
15355 }
15356
15357 static void LoadEngineSnapshotValues(void)
15358 {
15359   // restore special values from snapshot structure
15360
15361   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15362     LoadEngineSnapshotValues_RND();
15363   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15364     LoadEngineSnapshotValues_EM();
15365   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15366     LoadEngineSnapshotValues_SP();
15367   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15368     LoadEngineSnapshotValues_MM();
15369 }
15370
15371 void LoadEngineSnapshotSingle(void)
15372 {
15373   LoadSnapshotSingle();
15374
15375   LoadEngineSnapshotValues();
15376 }
15377
15378 static void LoadEngineSnapshot_Undo(int steps)
15379 {
15380   LoadSnapshotFromList_Older(steps);
15381
15382   LoadEngineSnapshotValues();
15383 }
15384
15385 static void LoadEngineSnapshot_Redo(int steps)
15386 {
15387   LoadSnapshotFromList_Newer(steps);
15388
15389   LoadEngineSnapshotValues();
15390 }
15391
15392 boolean CheckEngineSnapshotSingle(void)
15393 {
15394   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15395           snapshot_level_nr == level_nr);
15396 }
15397
15398 boolean CheckEngineSnapshotList(void)
15399 {
15400   return CheckSnapshotList();
15401 }
15402
15403
15404 // ---------- new game button stuff -------------------------------------------
15405
15406 static struct
15407 {
15408   int graphic;
15409   struct XY *pos;
15410   int gadget_id;
15411   boolean *setup_value;
15412   boolean allowed_on_tape;
15413   char *infotext;
15414 } gamebutton_info[NUM_GAME_BUTTONS] =
15415 {
15416   {
15417     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15418     GAME_CTRL_ID_STOP,                          NULL,
15419     TRUE,                                       "stop game"
15420   },
15421   {
15422     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15423     GAME_CTRL_ID_PAUSE,                         NULL,
15424     TRUE,                                       "pause game"
15425   },
15426   {
15427     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15428     GAME_CTRL_ID_PLAY,                          NULL,
15429     TRUE,                                       "play game"
15430   },
15431   {
15432     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15433     GAME_CTRL_ID_UNDO,                          NULL,
15434     TRUE,                                       "undo step"
15435   },
15436   {
15437     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15438     GAME_CTRL_ID_REDO,                          NULL,
15439     TRUE,                                       "redo step"
15440   },
15441   {
15442     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15443     GAME_CTRL_ID_SAVE,                          NULL,
15444     TRUE,                                       "save game"
15445   },
15446   {
15447     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15448     GAME_CTRL_ID_PAUSE2,                        NULL,
15449     TRUE,                                       "pause game"
15450   },
15451   {
15452     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15453     GAME_CTRL_ID_LOAD,                          NULL,
15454     TRUE,                                       "load game"
15455   },
15456   {
15457     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15458     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15459     FALSE,                                      "stop game"
15460   },
15461   {
15462     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15463     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15464     FALSE,                                      "pause game"
15465   },
15466   {
15467     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15468     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15469     FALSE,                                      "play game"
15470   },
15471   {
15472     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15473     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15474     TRUE,                                       "background music on/off"
15475   },
15476   {
15477     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15478     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15479     TRUE,                                       "sound loops on/off"
15480   },
15481   {
15482     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15483     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15484     TRUE,                                       "normal sounds on/off"
15485   },
15486   {
15487     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15488     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15489     FALSE,                                      "background music on/off"
15490   },
15491   {
15492     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15493     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15494     FALSE,                                      "sound loops on/off"
15495   },
15496   {
15497     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15498     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15499     FALSE,                                      "normal sounds on/off"
15500   }
15501 };
15502
15503 void CreateGameButtons(void)
15504 {
15505   int i;
15506
15507   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15508   {
15509     int graphic = gamebutton_info[i].graphic;
15510     struct GraphicInfo *gfx = &graphic_info[graphic];
15511     struct XY *pos = gamebutton_info[i].pos;
15512     struct GadgetInfo *gi;
15513     int button_type;
15514     boolean checked;
15515     unsigned int event_mask;
15516     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15517     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15518     int base_x = (on_tape ? VX : DX);
15519     int base_y = (on_tape ? VY : DY);
15520     int gd_x   = gfx->src_x;
15521     int gd_y   = gfx->src_y;
15522     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15523     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15524     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15525     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15526     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15527     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15528     int id = i;
15529
15530     if (gfx->bitmap == NULL)
15531     {
15532       game_gadget[id] = NULL;
15533
15534       continue;
15535     }
15536
15537     if (id == GAME_CTRL_ID_STOP ||
15538         id == GAME_CTRL_ID_PANEL_STOP ||
15539         id == GAME_CTRL_ID_PLAY ||
15540         id == GAME_CTRL_ID_PANEL_PLAY ||
15541         id == GAME_CTRL_ID_SAVE ||
15542         id == GAME_CTRL_ID_LOAD)
15543     {
15544       button_type = GD_TYPE_NORMAL_BUTTON;
15545       checked = FALSE;
15546       event_mask = GD_EVENT_RELEASED;
15547     }
15548     else if (id == GAME_CTRL_ID_UNDO ||
15549              id == GAME_CTRL_ID_REDO)
15550     {
15551       button_type = GD_TYPE_NORMAL_BUTTON;
15552       checked = FALSE;
15553       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15554     }
15555     else
15556     {
15557       button_type = GD_TYPE_CHECK_BUTTON;
15558       checked = (gamebutton_info[i].setup_value != NULL ?
15559                  *gamebutton_info[i].setup_value : FALSE);
15560       event_mask = GD_EVENT_PRESSED;
15561     }
15562
15563     gi = CreateGadget(GDI_CUSTOM_ID, id,
15564                       GDI_IMAGE_ID, graphic,
15565                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15566                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15567                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15568                       GDI_WIDTH, gfx->width,
15569                       GDI_HEIGHT, gfx->height,
15570                       GDI_TYPE, button_type,
15571                       GDI_STATE, GD_BUTTON_UNPRESSED,
15572                       GDI_CHECKED, checked,
15573                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15574                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15575                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15576                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15577                       GDI_DIRECT_DRAW, FALSE,
15578                       GDI_EVENT_MASK, event_mask,
15579                       GDI_CALLBACK_ACTION, HandleGameButtons,
15580                       GDI_END);
15581
15582     if (gi == NULL)
15583       Error(ERR_EXIT, "cannot create gadget");
15584
15585     game_gadget[id] = gi;
15586   }
15587 }
15588
15589 void FreeGameButtons(void)
15590 {
15591   int i;
15592
15593   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15594     FreeGadget(game_gadget[i]);
15595 }
15596
15597 static void UnmapGameButtonsAtSamePosition(int id)
15598 {
15599   int i;
15600
15601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15602     if (i != id &&
15603         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15604         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15605       UnmapGadget(game_gadget[i]);
15606 }
15607
15608 static void UnmapGameButtonsAtSamePosition_All(void)
15609 {
15610   if (setup.show_snapshot_buttons)
15611   {
15612     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15613     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15614     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15615   }
15616   else
15617   {
15618     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15619     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15620     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15621
15622     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15623     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15624     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15625   }
15626 }
15627
15628 static void MapGameButtonsAtSamePosition(int id)
15629 {
15630   int i;
15631
15632   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15633     if (i != id &&
15634         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15635         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15636       MapGadget(game_gadget[i]);
15637
15638   UnmapGameButtonsAtSamePosition_All();
15639 }
15640
15641 void MapUndoRedoButtons(void)
15642 {
15643   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15644   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15645
15646   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15647   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15648
15649   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15650 }
15651
15652 void UnmapUndoRedoButtons(void)
15653 {
15654   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15655   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15656
15657   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15658   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15659
15660   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15661 }
15662
15663 static void MapGameButtonsExt(boolean on_tape)
15664 {
15665   int i;
15666
15667   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15668     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15669         i != GAME_CTRL_ID_UNDO &&
15670         i != GAME_CTRL_ID_REDO)
15671       MapGadget(game_gadget[i]);
15672
15673   UnmapGameButtonsAtSamePosition_All();
15674
15675   RedrawGameButtons();
15676 }
15677
15678 static void UnmapGameButtonsExt(boolean on_tape)
15679 {
15680   int i;
15681
15682   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15683     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15684       UnmapGadget(game_gadget[i]);
15685 }
15686
15687 static void RedrawGameButtonsExt(boolean on_tape)
15688 {
15689   int i;
15690
15691   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15692     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15693       RedrawGadget(game_gadget[i]);
15694
15695   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15696   redraw_mask &= ~REDRAW_ALL;
15697 }
15698
15699 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15700 {
15701   if (gi == NULL)
15702     return;
15703
15704   gi->checked = state;
15705 }
15706
15707 static void RedrawSoundButtonGadget(int id)
15708 {
15709   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15710              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15711              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15712              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15713              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15714              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15715              id);
15716
15717   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15718   RedrawGadget(game_gadget[id2]);
15719 }
15720
15721 void MapGameButtons(void)
15722 {
15723   MapGameButtonsExt(FALSE);
15724 }
15725
15726 void UnmapGameButtons(void)
15727 {
15728   UnmapGameButtonsExt(FALSE);
15729 }
15730
15731 void RedrawGameButtons(void)
15732 {
15733   RedrawGameButtonsExt(FALSE);
15734 }
15735
15736 void MapGameButtonsOnTape(void)
15737 {
15738   MapGameButtonsExt(TRUE);
15739 }
15740
15741 void UnmapGameButtonsOnTape(void)
15742 {
15743   UnmapGameButtonsExt(TRUE);
15744 }
15745
15746 void RedrawGameButtonsOnTape(void)
15747 {
15748   RedrawGameButtonsExt(TRUE);
15749 }
15750
15751 static void GameUndoRedoExt(void)
15752 {
15753   ClearPlayerAction();
15754
15755   tape.pausing = TRUE;
15756
15757   RedrawPlayfield();
15758   UpdateAndDisplayGameControlValues();
15759
15760   DrawCompleteVideoDisplay();
15761   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15762   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15763   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15764
15765   BackToFront();
15766 }
15767
15768 static void GameUndo(int steps)
15769 {
15770   if (!CheckEngineSnapshotList())
15771     return;
15772
15773   LoadEngineSnapshot_Undo(steps);
15774
15775   GameUndoRedoExt();
15776 }
15777
15778 static void GameRedo(int steps)
15779 {
15780   if (!CheckEngineSnapshotList())
15781     return;
15782
15783   LoadEngineSnapshot_Redo(steps);
15784
15785   GameUndoRedoExt();
15786 }
15787
15788 static void HandleGameButtonsExt(int id, int button)
15789 {
15790   static boolean game_undo_executed = FALSE;
15791   int steps = BUTTON_STEPSIZE(button);
15792   boolean handle_game_buttons =
15793     (game_status == GAME_MODE_PLAYING ||
15794      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15795
15796   if (!handle_game_buttons)
15797     return;
15798
15799   switch (id)
15800   {
15801     case GAME_CTRL_ID_STOP:
15802     case GAME_CTRL_ID_PANEL_STOP:
15803       if (game_status == GAME_MODE_MAIN)
15804         break;
15805
15806       if (tape.playing)
15807         TapeStop();
15808       else
15809         RequestQuitGame(TRUE);
15810
15811       break;
15812
15813     case GAME_CTRL_ID_PAUSE:
15814     case GAME_CTRL_ID_PAUSE2:
15815     case GAME_CTRL_ID_PANEL_PAUSE:
15816       if (network.enabled && game_status == GAME_MODE_PLAYING)
15817       {
15818         if (tape.pausing)
15819           SendToServer_ContinuePlaying();
15820         else
15821           SendToServer_PausePlaying();
15822       }
15823       else
15824         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15825
15826       game_undo_executed = FALSE;
15827
15828       break;
15829
15830     case GAME_CTRL_ID_PLAY:
15831     case GAME_CTRL_ID_PANEL_PLAY:
15832       if (game_status == GAME_MODE_MAIN)
15833       {
15834         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15835       }
15836       else if (tape.pausing)
15837       {
15838         if (network.enabled)
15839           SendToServer_ContinuePlaying();
15840         else
15841           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15842       }
15843       break;
15844
15845     case GAME_CTRL_ID_UNDO:
15846       // Important: When using "save snapshot when collecting an item" mode,
15847       // load last (current) snapshot for first "undo" after pressing "pause"
15848       // (else the last-but-one snapshot would be loaded, because the snapshot
15849       // pointer already points to the last snapshot when pressing "pause",
15850       // which is fine for "every step/move" mode, but not for "every collect")
15851       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15852           !game_undo_executed)
15853         steps--;
15854
15855       game_undo_executed = TRUE;
15856
15857       GameUndo(steps);
15858       break;
15859
15860     case GAME_CTRL_ID_REDO:
15861       GameRedo(steps);
15862       break;
15863
15864     case GAME_CTRL_ID_SAVE:
15865       TapeQuickSave();
15866       break;
15867
15868     case GAME_CTRL_ID_LOAD:
15869       TapeQuickLoad();
15870       break;
15871
15872     case SOUND_CTRL_ID_MUSIC:
15873     case SOUND_CTRL_ID_PANEL_MUSIC:
15874       if (setup.sound_music)
15875       { 
15876         setup.sound_music = FALSE;
15877
15878         FadeMusic();
15879       }
15880       else if (audio.music_available)
15881       { 
15882         setup.sound = setup.sound_music = TRUE;
15883
15884         SetAudioMode(setup.sound);
15885
15886         if (game_status == GAME_MODE_PLAYING)
15887           PlayLevelMusic();
15888       }
15889
15890       RedrawSoundButtonGadget(id);
15891
15892       break;
15893
15894     case SOUND_CTRL_ID_LOOPS:
15895     case SOUND_CTRL_ID_PANEL_LOOPS:
15896       if (setup.sound_loops)
15897         setup.sound_loops = FALSE;
15898       else if (audio.loops_available)
15899       {
15900         setup.sound = setup.sound_loops = TRUE;
15901
15902         SetAudioMode(setup.sound);
15903       }
15904
15905       RedrawSoundButtonGadget(id);
15906
15907       break;
15908
15909     case SOUND_CTRL_ID_SIMPLE:
15910     case SOUND_CTRL_ID_PANEL_SIMPLE:
15911       if (setup.sound_simple)
15912         setup.sound_simple = FALSE;
15913       else if (audio.sound_available)
15914       {
15915         setup.sound = setup.sound_simple = TRUE;
15916
15917         SetAudioMode(setup.sound);
15918       }
15919
15920       RedrawSoundButtonGadget(id);
15921
15922       break;
15923
15924     default:
15925       break;
15926   }
15927 }
15928
15929 static void HandleGameButtons(struct GadgetInfo *gi)
15930 {
15931   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15932 }
15933
15934 void HandleSoundButtonKeys(Key key)
15935 {
15936   if (key == setup.shortcut.sound_simple)
15937     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15938   else if (key == setup.shortcut.sound_loops)
15939     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15940   else if (key == setup.shortcut.sound_music)
15941     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15942 }