fixed bug with Sokoban auto-exit option only working for local player
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     // ---------- initialize player's last field block delay ------------------
1723
1724     // always start with reliable default value (no adjustment needed)
1725     player->block_delay_adjustment = 0;
1726
1727     // special case 1: in Supaplex, Murphy blocks last field one more frame
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     // special case 2: in game engines before 3.1.1, blocking was different
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       // remove potentially duplicate players
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; // if player was just killed, reanimate him
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    // more than one switch -- set it like the first switch
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       // reference elements should not be used on the playfield
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   // not needed to call InitMovDir() -- already done by InitField()!
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   // not needed to call InitMovDir() -- already done by InitField()!
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   // pos >= 0: get element from bottom of the stack;
2050   // pos <  0: get element from top of the stack
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 static void InitGameControlValues(void)
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     // force update of game controls after initialization
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     // determine panel value width for later calculation of alignment
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     // fill structure for game panel draw order
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   // sort game panel controls according to sort_priority and control number
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 static void UpdatePlayfieldElementCount(void)
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   // first check if it is needed at all to calculate playfield element count
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 static void UpdateGameControlValues(void)
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   // update game panel control values
2226
2227   // used instead of "level_nr" (for network games)
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       // only one player in Supaplex game engine
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   // update game panel control frames
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 static void DisplayGameControlValues(void)
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   // copy default game door content to main double buffer
2533
2534   // !!! CHECK AGAIN !!!
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   // redraw game control buttons
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           // use dynamic number of digits
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       // correct text size if "digits" is zero or less
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       // dynamically correct text alignment
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          // (used for normal state)
2728         int font2 = pos->font_alt;      // (used for active state)
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           // don't truncate output if "chars" is zero or less
2740           size = strlen(s);
2741
2742           // dynamically correct text alignment
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues(void)
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 #if 0
2771 static void UpdateGameDoorValues(void)
2772 {
2773   UpdateGameControlValues();
2774 }
2775 #endif
2776
2777 void DrawGameDoorValues(void)
2778 {
2779   DisplayGameControlValues();
2780 }
2781
2782
2783 // ============================================================================
2784 // InitGameEngine()
2785 // ----------------------------------------------------------------------------
2786 // initialize game engine due to level / tape version number
2787 // ============================================================================
2788
2789 static void InitGameEngine(void)
2790 {
2791   int i, j, k, l, x, y;
2792
2793   // set game engine from tape file when re-playing, else from level file
2794   game.engine_version = (tape.playing ? tape.engine_version :
2795                          level.game_version);
2796
2797   // set single or multi-player game mode (needed for re-playing tapes)
2798   game.team_mode = setup.team_mode;
2799
2800   if (tape.playing)
2801   {
2802     int num_players = 0;
2803
2804     for (i = 0; i < MAX_PLAYERS; i++)
2805       if (tape.player_participates[i])
2806         num_players++;
2807
2808     // multi-player tapes contain input data for more than one player
2809     game.team_mode = (num_players > 1);
2810   }
2811
2812   // --------------------------------------------------------------------------
2813   // set flags for bugs and changes according to active game engine version
2814   // --------------------------------------------------------------------------
2815
2816   /*
2817     Summary of bugfix/change:
2818     Fixed handling for custom elements that change when pushed by the player.
2819
2820     Fixed/changed in version:
2821     3.1.0
2822
2823     Description:
2824     Before 3.1.0, custom elements that "change when pushing" changed directly
2825     after the player started pushing them (until then handled in "DigField()").
2826     Since 3.1.0, these custom elements are not changed until the "pushing"
2827     move of the element is finished (now handled in "ContinueMoving()").
2828
2829     Affected levels/tapes:
2830     The first condition is generally needed for all levels/tapes before version
2831     3.1.0, which might use the old behaviour before it was changed; known tapes
2832     that are affected are some tapes from the level set "Walpurgis Gardens" by
2833     Jamie Cullen.
2834     The second condition is an exception from the above case and is needed for
2835     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2836     above (including some development versions of 3.1.0), but before it was
2837     known that this change would break tapes like the above and was fixed in
2838     3.1.1, so that the changed behaviour was active although the engine version
2839     while recording maybe was before 3.1.0. There is at least one tape that is
2840     affected by this exception, which is the tape for the one-level set "Bug
2841     Machine" by Juergen Bonhagen.
2842   */
2843
2844   game.use_change_when_pushing_bug =
2845     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2846      !(tape.playing &&
2847        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2848        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2849
2850   /*
2851     Summary of bugfix/change:
2852     Fixed handling for blocking the field the player leaves when moving.
2853
2854     Fixed/changed in version:
2855     3.1.1
2856
2857     Description:
2858     Before 3.1.1, when "block last field when moving" was enabled, the field
2859     the player is leaving when moving was blocked for the time of the move,
2860     and was directly unblocked afterwards. This resulted in the last field
2861     being blocked for exactly one less than the number of frames of one player
2862     move. Additionally, even when blocking was disabled, the last field was
2863     blocked for exactly one frame.
2864     Since 3.1.1, due to changes in player movement handling, the last field
2865     is not blocked at all when blocking is disabled. When blocking is enabled,
2866     the last field is blocked for exactly the number of frames of one player
2867     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2868     last field is blocked for exactly one more than the number of frames of
2869     one player move.
2870
2871     Affected levels/tapes:
2872     (!!! yet to be determined -- probably many !!!)
2873   */
2874
2875   game.use_block_last_field_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,1,0));
2877
2878   game_em.use_single_button =
2879     (game.engine_version > VERSION_IDENT(4,0,0,2));
2880
2881   game_em.use_snap_key_bug =
2882     (game.engine_version < VERSION_IDENT(4,0,1,0));
2883
2884   // --------------------------------------------------------------------------
2885
2886   // set maximal allowed number of custom element changes per game frame
2887   game.max_num_changes_per_frame = 1;
2888
2889   // default scan direction: scan playfield from top/left to bottom/right
2890   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2891
2892   // dynamically adjust element properties according to game engine version
2893   InitElementPropertiesEngine(game.engine_version);
2894
2895 #if 0
2896   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2897   printf("          tape version == %06d [%s] [file: %06d]\n",
2898          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2899          tape.file_version);
2900   printf("       => game.engine_version == %06d\n", game.engine_version);
2901 #endif
2902
2903   // ---------- initialize player's initial move delay ------------------------
2904
2905   // dynamically adjust player properties according to level information
2906   for (i = 0; i < MAX_PLAYERS; i++)
2907     game.initial_move_delay_value[i] =
2908       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2909
2910   // dynamically adjust player properties according to game engine version
2911   for (i = 0; i < MAX_PLAYERS; i++)
2912     game.initial_move_delay[i] =
2913       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2914        game.initial_move_delay_value[i] : 0);
2915
2916   // ---------- initialize player's initial push delay ------------------------
2917
2918   // dynamically adjust player properties according to game engine version
2919   game.initial_push_delay_value =
2920     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2921
2922   // ---------- initialize changing elements ----------------------------------
2923
2924   // initialize changing elements information
2925   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2926   {
2927     struct ElementInfo *ei = &element_info[i];
2928
2929     // this pointer might have been changed in the level editor
2930     ei->change = &ei->change_page[0];
2931
2932     if (!IS_CUSTOM_ELEMENT(i))
2933     {
2934       ei->change->target_element = EL_EMPTY_SPACE;
2935       ei->change->delay_fixed = 0;
2936       ei->change->delay_random = 0;
2937       ei->change->delay_frames = 1;
2938     }
2939
2940     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2941     {
2942       ei->has_change_event[j] = FALSE;
2943
2944       ei->event_page_nr[j] = 0;
2945       ei->event_page[j] = &ei->change_page[0];
2946     }
2947   }
2948
2949   // add changing elements from pre-defined list
2950   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2951   {
2952     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2953     struct ElementInfo *ei = &element_info[ch_delay->element];
2954
2955     ei->change->target_element       = ch_delay->target_element;
2956     ei->change->delay_fixed          = ch_delay->change_delay;
2957
2958     ei->change->pre_change_function  = ch_delay->pre_change_function;
2959     ei->change->change_function      = ch_delay->change_function;
2960     ei->change->post_change_function = ch_delay->post_change_function;
2961
2962     ei->change->can_change = TRUE;
2963     ei->change->can_change_or_has_action = TRUE;
2964
2965     ei->has_change_event[CE_DELAY] = TRUE;
2966
2967     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2968     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969   }
2970
2971   // ---------- initialize internal run-time variables ------------------------
2972
2973   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974   {
2975     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976
2977     for (j = 0; j < ei->num_change_pages; j++)
2978     {
2979       ei->change_page[j].can_change_or_has_action =
2980         (ei->change_page[j].can_change |
2981          ei->change_page[j].has_action);
2982     }
2983   }
2984
2985   // add change events from custom element configuration
2986   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2987   {
2988     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2989
2990     for (j = 0; j < ei->num_change_pages; j++)
2991     {
2992       if (!ei->change_page[j].can_change_or_has_action)
2993         continue;
2994
2995       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2996       {
2997         // only add event page for the first page found with this event
2998         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2999         {
3000           ei->has_change_event[k] = TRUE;
3001
3002           ei->event_page_nr[k] = j;
3003           ei->event_page[k] = &ei->change_page[j];
3004         }
3005       }
3006     }
3007   }
3008
3009   // ---------- initialize reference elements in change conditions ------------
3010
3011   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3012   {
3013     int element = EL_CUSTOM_START + i;
3014     struct ElementInfo *ei = &element_info[element];
3015
3016     for (j = 0; j < ei->num_change_pages; j++)
3017     {
3018       int trigger_element = ei->change_page[j].initial_trigger_element;
3019
3020       if (trigger_element >= EL_PREV_CE_8 &&
3021           trigger_element <= EL_NEXT_CE_8)
3022         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3023
3024       ei->change_page[j].trigger_element = trigger_element;
3025     }
3026   }
3027
3028   // ---------- initialize run-time trigger player and element ----------------
3029
3030   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3031   {
3032     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3033
3034     for (j = 0; j < ei->num_change_pages; j++)
3035     {
3036       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3037       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3038       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3039       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3040       ei->change_page[j].actual_trigger_ce_value = 0;
3041       ei->change_page[j].actual_trigger_ce_score = 0;
3042     }
3043   }
3044
3045   // ---------- initialize trigger events -------------------------------------
3046
3047   // initialize trigger events information
3048   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3049     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3050       trigger_events[i][j] = FALSE;
3051
3052   // add trigger events from element change event properties
3053   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054   {
3055     struct ElementInfo *ei = &element_info[i];
3056
3057     for (j = 0; j < ei->num_change_pages; j++)
3058     {
3059       if (!ei->change_page[j].can_change_or_has_action)
3060         continue;
3061
3062       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3063       {
3064         int trigger_element = ei->change_page[j].trigger_element;
3065
3066         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3067         {
3068           if (ei->change_page[j].has_event[k])
3069           {
3070             if (IS_GROUP_ELEMENT(trigger_element))
3071             {
3072               struct ElementGroupInfo *group =
3073                 element_info[trigger_element].group;
3074
3075               for (l = 0; l < group->num_elements_resolved; l++)
3076                 trigger_events[group->element_resolved[l]][k] = TRUE;
3077             }
3078             else if (trigger_element == EL_ANY_ELEMENT)
3079               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3080                 trigger_events[l][k] = TRUE;
3081             else
3082               trigger_events[trigger_element][k] = TRUE;
3083           }
3084         }
3085       }
3086     }
3087   }
3088
3089   // ---------- initialize push delay -----------------------------------------
3090
3091   // initialize push delay values to default
3092   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3093   {
3094     if (!IS_CUSTOM_ELEMENT(i))
3095     {
3096       // set default push delay values (corrected since version 3.0.7-1)
3097       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3098       {
3099         element_info[i].push_delay_fixed = 2;
3100         element_info[i].push_delay_random = 8;
3101       }
3102       else
3103       {
3104         element_info[i].push_delay_fixed = 8;
3105         element_info[i].push_delay_random = 8;
3106       }
3107     }
3108   }
3109
3110   // set push delay value for certain elements from pre-defined list
3111   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3112   {
3113     int e = push_delay_list[i].element;
3114
3115     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3116     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117   }
3118
3119   // set push delay value for Supaplex elements for newer engine versions
3120   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3121   {
3122     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123     {
3124       if (IS_SP_ELEMENT(i))
3125       {
3126         // set SP push delay to just enough to push under a falling zonk
3127         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3128
3129         element_info[i].push_delay_fixed  = delay;
3130         element_info[i].push_delay_random = 0;
3131       }
3132     }
3133   }
3134
3135   // ---------- initialize move stepsize --------------------------------------
3136
3137   // initialize move stepsize values to default
3138   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3139     if (!IS_CUSTOM_ELEMENT(i))
3140       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3141
3142   // set move stepsize value for certain elements from pre-defined list
3143   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3144   {
3145     int e = move_stepsize_list[i].element;
3146
3147     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148   }
3149
3150   // ---------- initialize collect score --------------------------------------
3151
3152   // initialize collect score values for custom elements from initial value
3153   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3154     if (IS_CUSTOM_ELEMENT(i))
3155       element_info[i].collect_score = element_info[i].collect_score_initial;
3156
3157   // ---------- initialize collect count --------------------------------------
3158
3159   // initialize collect count values for non-custom elements
3160   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3161     if (!IS_CUSTOM_ELEMENT(i))
3162       element_info[i].collect_count_initial = 0;
3163
3164   // add collect count values for all elements from pre-defined list
3165   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3166     element_info[collect_count_list[i].element].collect_count_initial =
3167       collect_count_list[i].count;
3168
3169   // ---------- initialize access direction -----------------------------------
3170
3171   // initialize access direction values to default (access from every side)
3172   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3173     if (!IS_CUSTOM_ELEMENT(i))
3174       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3175
3176   // set access direction value for certain elements from pre-defined list
3177   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3178     element_info[access_direction_list[i].element].access_direction =
3179       access_direction_list[i].direction;
3180
3181   // ---------- initialize explosion content ----------------------------------
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183   {
3184     if (IS_CUSTOM_ELEMENT(i))
3185       continue;
3186
3187     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3188     {
3189       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3190
3191       element_info[i].content.e[x][y] =
3192         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3193          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3194          i == EL_PLAYER_3 ? EL_EMERALD :
3195          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3196          i == EL_MOLE ? EL_EMERALD_RED :
3197          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3198          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3199          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3200          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3201          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3202          i == EL_WALL_EMERALD ? EL_EMERALD :
3203          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3204          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3205          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3206          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3207          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3208          i == EL_WALL_PEARL ? EL_PEARL :
3209          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3210          EL_EMPTY);
3211     }
3212   }
3213
3214   // ---------- initialize recursion detection --------------------------------
3215   recursion_loop_depth = 0;
3216   recursion_loop_detected = FALSE;
3217   recursion_loop_element = EL_UNDEFINED;
3218
3219   // ---------- initialize graphics engine ------------------------------------
3220   game.scroll_delay_value =
3221     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3222      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3223   game.scroll_delay_value =
3224     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3225
3226   // ---------- initialize game engine snapshots ------------------------------
3227   for (i = 0; i < MAX_PLAYERS; i++)
3228     game.snapshot.last_action[i] = 0;
3229   game.snapshot.changed_action = FALSE;
3230   game.snapshot.collected_item = FALSE;
3231   game.snapshot.mode =
3232     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3233      SNAPSHOT_MODE_EVERY_STEP :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3235      SNAPSHOT_MODE_EVERY_MOVE :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3237      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3238   game.snapshot.save_snapshot = FALSE;
3239
3240   // ---------- initialize level time for Supaplex engine ---------------------
3241   // Supaplex levels with time limit currently unsupported -- should be added
3242   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3243     level.time = 0;
3244 }
3245
3246 static int get_num_special_action(int element, int action_first,
3247                                   int action_last)
3248 {
3249   int num_special_action = 0;
3250   int i, j;
3251
3252   for (i = action_first; i <= action_last; i++)
3253   {
3254     boolean found = FALSE;
3255
3256     for (j = 0; j < NUM_DIRECTIONS; j++)
3257       if (el_act_dir2img(element, i, j) !=
3258           el_act_dir2img(element, ACTION_DEFAULT, j))
3259         found = TRUE;
3260
3261     if (found)
3262       num_special_action++;
3263     else
3264       break;
3265   }
3266
3267   return num_special_action;
3268 }
3269
3270
3271 // ============================================================================
3272 // InitGame()
3273 // ----------------------------------------------------------------------------
3274 // initialize and start new game
3275 // ============================================================================
3276
3277 #if DEBUG_INIT_PLAYER
3278 static void DebugPrintPlayerStatus(char *message)
3279 {
3280   int i;
3281
3282   if (!options.debug)
3283     return;
3284
3285   printf("%s:\n", message);
3286
3287   for (i = 0; i < MAX_PLAYERS; i++)
3288   {
3289     struct PlayerInfo *player = &stored_player[i];
3290
3291     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3292            i + 1,
3293            player->present,
3294            player->connected,
3295            player->connected_locally,
3296            player->connected_network,
3297            player->active);
3298
3299     if (local_player == player)
3300       printf(" (local player)");
3301
3302     printf("\n");
3303   }
3304 }
3305 #endif
3306
3307 void InitGame(void)
3308 {
3309   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3310   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int fade_mask = REDRAW_FIELD;
3312
3313   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3314   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3315   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3316   int initial_move_dir = MV_DOWN;
3317   int i, j, x, y;
3318
3319   // required here to update video display before fading (FIX THIS)
3320   DrawMaskedBorder(REDRAW_DOOR_2);
3321
3322   if (!game.restart_level)
3323     CloseDoor(DOOR_CLOSE_1);
3324
3325   SetGameStatus(GAME_MODE_PLAYING);
3326
3327   if (level_editor_test_game)
3328     FadeSkipNextFadeIn();
3329   else
3330     FadeSetEnterScreen();
3331
3332   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3333     fade_mask = REDRAW_ALL;
3334
3335   FadeLevelSoundsAndMusic();
3336
3337   ExpireSoundLoops(TRUE);
3338
3339   FadeOut(fade_mask);
3340
3341   // needed if different viewport properties defined for playing
3342   ChangeViewportPropertiesIfNeeded();
3343
3344   ClearField();
3345
3346   DrawCompleteVideoDisplay();
3347
3348   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3349
3350   InitGameEngine();
3351   InitGameControlValues();
3352
3353   // don't play tapes over network
3354   network_playing = (network.enabled && !tape.playing);
3355
3356   for (i = 0; i < MAX_PLAYERS; i++)
3357   {
3358     struct PlayerInfo *player = &stored_player[i];
3359
3360     player->index_nr = i;
3361     player->index_bit = (1 << i);
3362     player->element_nr = EL_PLAYER_1 + i;
3363
3364     player->present = FALSE;
3365     player->active = FALSE;
3366     player->mapped = FALSE;
3367
3368     player->killed = FALSE;
3369     player->reanimated = FALSE;
3370
3371     player->action = 0;
3372     player->effective_action = 0;
3373     player->programmed_action = 0;
3374
3375     player->mouse_action.lx = 0;
3376     player->mouse_action.ly = 0;
3377     player->mouse_action.button = 0;
3378     player->mouse_action.button_hint = 0;
3379
3380     player->effective_mouse_action.lx = 0;
3381     player->effective_mouse_action.ly = 0;
3382     player->effective_mouse_action.button = 0;
3383     player->effective_mouse_action.button_hint = 0;
3384
3385     player->score = 0;
3386     player->score_final = 0;
3387
3388     player->health = MAX_HEALTH;
3389     player->health_final = MAX_HEALTH;
3390
3391     player->gems_still_needed = level.gems_needed;
3392     player->sokobanfields_still_needed = 0;
3393     player->lights_still_needed = 0;
3394     player->players_still_needed = 0;
3395     player->friends_still_needed = 0;
3396
3397     for (j = 0; j < MAX_NUM_KEYS; j++)
3398       player->key[j] = FALSE;
3399
3400     player->num_white_keys = 0;
3401
3402     player->dynabomb_count = 0;
3403     player->dynabomb_size = 1;
3404     player->dynabombs_left = 0;
3405     player->dynabomb_xl = FALSE;
3406
3407     player->MovDir = initial_move_dir;
3408     player->MovPos = 0;
3409     player->GfxPos = 0;
3410     player->GfxDir = initial_move_dir;
3411     player->GfxAction = ACTION_DEFAULT;
3412     player->Frame = 0;
3413     player->StepFrame = 0;
3414
3415     player->initial_element = player->element_nr;
3416     player->artwork_element =
3417       (level.use_artwork_element[i] ? level.artwork_element[i] :
3418        player->element_nr);
3419     player->use_murphy = FALSE;
3420
3421     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3422     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3423
3424     player->gravity = level.initial_player_gravity[i];
3425
3426     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3427
3428     player->actual_frame_counter = 0;
3429
3430     player->step_counter = 0;
3431
3432     player->last_move_dir = initial_move_dir;
3433
3434     player->is_active = FALSE;
3435
3436     player->is_waiting = FALSE;
3437     player->is_moving = FALSE;
3438     player->is_auto_moving = FALSE;
3439     player->is_digging = FALSE;
3440     player->is_snapping = FALSE;
3441     player->is_collecting = FALSE;
3442     player->is_pushing = FALSE;
3443     player->is_switching = FALSE;
3444     player->is_dropping = FALSE;
3445     player->is_dropping_pressed = FALSE;
3446
3447     player->is_bored = FALSE;
3448     player->is_sleeping = FALSE;
3449
3450     player->was_waiting = TRUE;
3451     player->was_moving = FALSE;
3452     player->was_snapping = FALSE;
3453     player->was_dropping = FALSE;
3454
3455     player->force_dropping = FALSE;
3456
3457     player->frame_counter_bored = -1;
3458     player->frame_counter_sleeping = -1;
3459
3460     player->anim_delay_counter = 0;
3461     player->post_delay_counter = 0;
3462
3463     player->dir_waiting = initial_move_dir;
3464     player->action_waiting = ACTION_DEFAULT;
3465     player->last_action_waiting = ACTION_DEFAULT;
3466     player->special_action_bored = ACTION_DEFAULT;
3467     player->special_action_sleeping = ACTION_DEFAULT;
3468
3469     player->switch_x = -1;
3470     player->switch_y = -1;
3471
3472     player->drop_x = -1;
3473     player->drop_y = -1;
3474
3475     player->show_envelope = 0;
3476
3477     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3478
3479     player->push_delay       = -1;      // initialized when pushing starts
3480     player->push_delay_value = game.initial_push_delay_value;
3481
3482     player->drop_delay = 0;
3483     player->drop_pressed_delay = 0;
3484
3485     player->last_jx = -1;
3486     player->last_jy = -1;
3487     player->jx = -1;
3488     player->jy = -1;
3489
3490     player->shield_normal_time_left = 0;
3491     player->shield_deadly_time_left = 0;
3492
3493     player->inventory_infinite_element = EL_UNDEFINED;
3494     player->inventory_size = 0;
3495
3496     if (level.use_initial_inventory[i])
3497     {
3498       for (j = 0; j < level.initial_inventory_size[i]; j++)
3499       {
3500         int element = level.initial_inventory_content[i][j];
3501         int collect_count = element_info[element].collect_count_initial;
3502         int k;
3503
3504         if (!IS_CUSTOM_ELEMENT(element))
3505           collect_count = 1;
3506
3507         if (collect_count == 0)
3508           player->inventory_infinite_element = element;
3509         else
3510           for (k = 0; k < collect_count; k++)
3511             if (player->inventory_size < MAX_INVENTORY_SIZE)
3512               player->inventory_element[player->inventory_size++] = element;
3513       }
3514     }
3515
3516     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3517     SnapField(player, 0, 0);
3518
3519     player->LevelSolved = FALSE;
3520     player->GameOver = FALSE;
3521
3522     player->LevelSolved_GameWon = FALSE;
3523     player->LevelSolved_GameEnd = FALSE;
3524     player->LevelSolved_SaveTape = FALSE;
3525     player->LevelSolved_SaveScore = FALSE;
3526
3527     player->LevelSolved_CountingTime = 0;
3528     player->LevelSolved_CountingScore = 0;
3529     player->LevelSolved_CountingHealth = 0;
3530
3531     map_player_action[i] = i;
3532   }
3533
3534   network_player_action_received = FALSE;
3535
3536   // initial null action
3537   if (network_playing)
3538     SendToServer_MovePlayer(MV_NONE);
3539
3540   ZX = ZY = -1;
3541   ExitX = ExitY = -1;
3542
3543   FrameCounter = 0;
3544   TimeFrames = 0;
3545   TimePlayed = 0;
3546   TimeLeft = level.time;
3547   TapeTime = 0;
3548
3549   ScreenMovDir = MV_NONE;
3550   ScreenMovPos = 0;
3551   ScreenGfxPos = 0;
3552
3553   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3554
3555   AllPlayersGone = FALSE;
3556
3557   game.panel.active = TRUE;
3558
3559   game.no_time_limit = (level.time == 0);
3560
3561   game.yamyam_content_nr = 0;
3562   game.robot_wheel_active = FALSE;
3563   game.magic_wall_active = FALSE;
3564   game.magic_wall_time_left = 0;
3565   game.light_time_left = 0;
3566   game.timegate_time_left = 0;
3567   game.switchgate_pos = 0;
3568   game.wind_direction = level.wind_direction_initial;
3569
3570   game.lenses_time_left = 0;
3571   game.magnify_time_left = 0;
3572
3573   game.ball_state = level.ball_state_initial;
3574   game.ball_content_nr = 0;
3575
3576   game.explosions_delayed = TRUE;
3577
3578   game.envelope_active = FALSE;
3579
3580   for (i = 0; i < NUM_BELTS; i++)
3581   {
3582     game.belt_dir[i] = MV_NONE;
3583     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3584   }
3585
3586   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3587     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3588
3589 #if DEBUG_INIT_PLAYER
3590   DebugPrintPlayerStatus("Player status at level initialization");
3591 #endif
3592
3593   SCAN_PLAYFIELD(x, y)
3594   {
3595     Feld[x][y] = Last[x][y] = level.field[x][y];
3596     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3597     ChangeDelay[x][y] = 0;
3598     ChangePage[x][y] = -1;
3599     CustomValue[x][y] = 0;              // initialized in InitField()
3600     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3601     AmoebaNr[x][y] = 0;
3602     WasJustMoving[x][y] = 0;
3603     WasJustFalling[x][y] = 0;
3604     CheckCollision[x][y] = 0;
3605     CheckImpact[x][y] = 0;
3606     Stop[x][y] = FALSE;
3607     Pushed[x][y] = FALSE;
3608
3609     ChangeCount[x][y] = 0;
3610     ChangeEvent[x][y] = -1;
3611
3612     ExplodePhase[x][y] = 0;
3613     ExplodeDelay[x][y] = 0;
3614     ExplodeField[x][y] = EX_TYPE_NONE;
3615
3616     RunnerVisit[x][y] = 0;
3617     PlayerVisit[x][y] = 0;
3618
3619     GfxFrame[x][y] = 0;
3620     GfxRandom[x][y] = INIT_GFX_RANDOM();
3621     GfxElement[x][y] = EL_UNDEFINED;
3622     GfxAction[x][y] = ACTION_DEFAULT;
3623     GfxDir[x][y] = MV_NONE;
3624     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3625   }
3626
3627   SCAN_PLAYFIELD(x, y)
3628   {
3629     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3630       emulate_bd = FALSE;
3631     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3632       emulate_sb = FALSE;
3633     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3634       emulate_sp = FALSE;
3635
3636     InitField(x, y, TRUE);
3637
3638     ResetGfxAnimation(x, y);
3639   }
3640
3641   InitBeltMovement();
3642
3643   for (i = 0; i < MAX_PLAYERS; i++)
3644   {
3645     struct PlayerInfo *player = &stored_player[i];
3646
3647     // set number of special actions for bored and sleeping animation
3648     player->num_special_action_bored =
3649       get_num_special_action(player->artwork_element,
3650                              ACTION_BORING_1, ACTION_BORING_LAST);
3651     player->num_special_action_sleeping =
3652       get_num_special_action(player->artwork_element,
3653                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3654   }
3655
3656   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3657                     emulate_sb ? EMU_SOKOBAN :
3658                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3659
3660   // initialize type of slippery elements
3661   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3662   {
3663     if (!IS_CUSTOM_ELEMENT(i))
3664     {
3665       // default: elements slip down either to the left or right randomly
3666       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3667
3668       // SP style elements prefer to slip down on the left side
3669       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3670         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3671
3672       // BD style elements prefer to slip down on the left side
3673       if (game.emulation == EMU_BOULDERDASH)
3674         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3675     }
3676   }
3677
3678   // initialize explosion and ignition delay
3679   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3680   {
3681     if (!IS_CUSTOM_ELEMENT(i))
3682     {
3683       int num_phase = 8;
3684       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3685                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3686                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3687       int last_phase = (num_phase + 1) * delay;
3688       int half_phase = (num_phase / 2) * delay;
3689
3690       element_info[i].explosion_delay = last_phase - 1;
3691       element_info[i].ignition_delay = half_phase;
3692
3693       if (i == EL_BLACK_ORB)
3694         element_info[i].ignition_delay = 1;
3695     }
3696   }
3697
3698   // correct non-moving belts to start moving left
3699   for (i = 0; i < NUM_BELTS; i++)
3700     if (game.belt_dir[i] == MV_NONE)
3701       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3702
3703 #if USE_NEW_PLAYER_ASSIGNMENTS
3704   for (i = 0; i < MAX_PLAYERS; i++)
3705   {
3706     stored_player[i].connected = FALSE;
3707
3708     // in network game mode, the local player might not be the first player
3709     if (stored_player[i].connected_locally)
3710       local_player = &stored_player[i];
3711   }
3712
3713   if (!network.enabled)
3714     local_player->connected = TRUE;
3715
3716   if (tape.playing)
3717   {
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719       stored_player[i].connected = tape.player_participates[i];
3720   }
3721   else if (network.enabled)
3722   {
3723     // add team mode players connected over the network (needed for correct
3724     // assignment of player figures from level to locally playing players)
3725
3726     for (i = 0; i < MAX_PLAYERS; i++)
3727       if (stored_player[i].connected_network)
3728         stored_player[i].connected = TRUE;
3729   }
3730   else if (game.team_mode)
3731   {
3732     // try to guess locally connected team mode players (needed for correct
3733     // assignment of player figures from level to locally playing players)
3734
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736       if (setup.input[i].use_joystick ||
3737           setup.input[i].key.left != KSYM_UNDEFINED)
3738         stored_player[i].connected = TRUE;
3739   }
3740
3741 #if DEBUG_INIT_PLAYER
3742   DebugPrintPlayerStatus("Player status after level initialization");
3743 #endif
3744
3745 #if DEBUG_INIT_PLAYER
3746   if (options.debug)
3747     printf("Reassigning players ...\n");
3748 #endif
3749
3750   // check if any connected player was not found in playfield
3751   for (i = 0; i < MAX_PLAYERS; i++)
3752   {
3753     struct PlayerInfo *player = &stored_player[i];
3754
3755     if (player->connected && !player->present)
3756     {
3757       struct PlayerInfo *field_player = NULL;
3758
3759 #if DEBUG_INIT_PLAYER
3760       if (options.debug)
3761         printf("- looking for field player for player %d ...\n", i + 1);
3762 #endif
3763
3764       // assign first free player found that is present in the playfield
3765
3766       // first try: look for unmapped playfield player that is not connected
3767       for (j = 0; j < MAX_PLAYERS; j++)
3768         if (field_player == NULL &&
3769             stored_player[j].present &&
3770             !stored_player[j].mapped &&
3771             !stored_player[j].connected)
3772           field_player = &stored_player[j];
3773
3774       // second try: look for *any* unmapped playfield player
3775       for (j = 0; j < MAX_PLAYERS; j++)
3776         if (field_player == NULL &&
3777             stored_player[j].present &&
3778             !stored_player[j].mapped)
3779           field_player = &stored_player[j];
3780
3781       if (field_player != NULL)
3782       {
3783         int jx = field_player->jx, jy = field_player->jy;
3784
3785 #if DEBUG_INIT_PLAYER
3786         if (options.debug)
3787           printf("- found player %d\n", field_player->index_nr + 1);
3788 #endif
3789
3790         player->present = FALSE;
3791         player->active = FALSE;
3792
3793         field_player->present = TRUE;
3794         field_player->active = TRUE;
3795
3796         /*
3797         player->initial_element = field_player->initial_element;
3798         player->artwork_element = field_player->artwork_element;
3799
3800         player->block_last_field       = field_player->block_last_field;
3801         player->block_delay_adjustment = field_player->block_delay_adjustment;
3802         */
3803
3804         StorePlayer[jx][jy] = field_player->element_nr;
3805
3806         field_player->jx = field_player->last_jx = jx;
3807         field_player->jy = field_player->last_jy = jy;
3808
3809         if (local_player == player)
3810           local_player = field_player;
3811
3812         map_player_action[field_player->index_nr] = i;
3813
3814         field_player->mapped = TRUE;
3815
3816 #if DEBUG_INIT_PLAYER
3817         if (options.debug)
3818           printf("- map_player_action[%d] == %d\n",
3819                  field_player->index_nr + 1, i + 1);
3820 #endif
3821       }
3822     }
3823
3824     if (player->connected && player->present)
3825       player->mapped = TRUE;
3826   }
3827
3828 #if DEBUG_INIT_PLAYER
3829   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3830 #endif
3831
3832 #else
3833
3834   // check if any connected player was not found in playfield
3835   for (i = 0; i < MAX_PLAYERS; i++)
3836   {
3837     struct PlayerInfo *player = &stored_player[i];
3838
3839     if (player->connected && !player->present)
3840     {
3841       for (j = 0; j < MAX_PLAYERS; j++)
3842       {
3843         struct PlayerInfo *field_player = &stored_player[j];
3844         int jx = field_player->jx, jy = field_player->jy;
3845
3846         // assign first free player found that is present in the playfield
3847         if (field_player->present && !field_player->connected)
3848         {
3849           player->present = TRUE;
3850           player->active = TRUE;
3851
3852           field_player->present = FALSE;
3853           field_player->active = FALSE;
3854
3855           player->initial_element = field_player->initial_element;
3856           player->artwork_element = field_player->artwork_element;
3857
3858           player->block_last_field       = field_player->block_last_field;
3859           player->block_delay_adjustment = field_player->block_delay_adjustment;
3860
3861           StorePlayer[jx][jy] = player->element_nr;
3862
3863           player->jx = player->last_jx = jx;
3864           player->jy = player->last_jy = jy;
3865
3866           break;
3867         }
3868       }
3869     }
3870   }
3871 #endif
3872
3873 #if 0
3874   printf("::: local_player->present == %d\n", local_player->present);
3875 #endif
3876
3877   // set focus to local player for network games, else to all players
3878   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3879   game.centered_player_nr_next = game.centered_player_nr;
3880   game.set_centered_player = FALSE;
3881
3882   if (network_playing && tape.recording)
3883   {
3884     // store client dependent player focus when recording network games
3885     tape.centered_player_nr_next = game.centered_player_nr_next;
3886     tape.set_centered_player = TRUE;
3887   }
3888
3889   if (tape.playing)
3890   {
3891     // when playing a tape, eliminate all players who do not participate
3892
3893 #if USE_NEW_PLAYER_ASSIGNMENTS
3894
3895     if (!game.team_mode)
3896     {
3897       for (i = 0; i < MAX_PLAYERS; i++)
3898       {
3899         if (stored_player[i].active &&
3900             !tape.player_participates[map_player_action[i]])
3901         {
3902           struct PlayerInfo *player = &stored_player[i];
3903           int jx = player->jx, jy = player->jy;
3904
3905 #if DEBUG_INIT_PLAYER
3906           if (options.debug)
3907             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3908 #endif
3909
3910           player->active = FALSE;
3911           StorePlayer[jx][jy] = 0;
3912           Feld[jx][jy] = EL_EMPTY;
3913         }
3914       }
3915     }
3916
3917 #else
3918
3919     for (i = 0; i < MAX_PLAYERS; i++)
3920     {
3921       if (stored_player[i].active &&
3922           !tape.player_participates[i])
3923       {
3924         struct PlayerInfo *player = &stored_player[i];
3925         int jx = player->jx, jy = player->jy;
3926
3927         player->active = FALSE;
3928         StorePlayer[jx][jy] = 0;
3929         Feld[jx][jy] = EL_EMPTY;
3930       }
3931     }
3932 #endif
3933   }
3934   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3935   {
3936     // when in single player mode, eliminate all but the local player
3937
3938     for (i = 0; i < MAX_PLAYERS; i++)
3939     {
3940       struct PlayerInfo *player = &stored_player[i];
3941
3942       if (player->active && player != local_player)
3943       {
3944         int jx = player->jx, jy = player->jy;
3945
3946         player->active = FALSE;
3947         player->present = FALSE;
3948
3949         StorePlayer[jx][jy] = 0;
3950         Feld[jx][jy] = EL_EMPTY;
3951       }
3952     }
3953   }
3954
3955   for (i = 0; i < MAX_PLAYERS; i++)
3956     if (stored_player[i].active)
3957       local_player->players_still_needed++;
3958
3959   if (level.solved_by_one_player)
3960     local_player->players_still_needed = 1;
3961
3962   // when recording the game, store which players take part in the game
3963   if (tape.recording)
3964   {
3965 #if USE_NEW_PLAYER_ASSIGNMENTS
3966     for (i = 0; i < MAX_PLAYERS; i++)
3967       if (stored_player[i].connected)
3968         tape.player_participates[i] = TRUE;
3969 #else
3970     for (i = 0; i < MAX_PLAYERS; i++)
3971       if (stored_player[i].active)
3972         tape.player_participates[i] = TRUE;
3973 #endif
3974   }
3975
3976 #if DEBUG_INIT_PLAYER
3977   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3978 #endif
3979
3980   if (BorderElement == EL_EMPTY)
3981   {
3982     SBX_Left = 0;
3983     SBX_Right = lev_fieldx - SCR_FIELDX;
3984     SBY_Upper = 0;
3985     SBY_Lower = lev_fieldy - SCR_FIELDY;
3986   }
3987   else
3988   {
3989     SBX_Left = -1;
3990     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3991     SBY_Upper = -1;
3992     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3993   }
3994
3995   if (full_lev_fieldx <= SCR_FIELDX)
3996     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3997   if (full_lev_fieldy <= SCR_FIELDY)
3998     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3999
4000   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4001     SBX_Left--;
4002   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4003     SBY_Upper--;
4004
4005   // if local player not found, look for custom element that might create
4006   // the player (make some assumptions about the right custom element)
4007   if (!local_player->present)
4008   {
4009     int start_x = 0, start_y = 0;
4010     int found_rating = 0;
4011     int found_element = EL_UNDEFINED;
4012     int player_nr = local_player->index_nr;
4013
4014     SCAN_PLAYFIELD(x, y)
4015     {
4016       int element = Feld[x][y];
4017       int content;
4018       int xx, yy;
4019       boolean is_player;
4020
4021       if (level.use_start_element[player_nr] &&
4022           level.start_element[player_nr] == element &&
4023           found_rating < 4)
4024       {
4025         start_x = x;
4026         start_y = y;
4027
4028         found_rating = 4;
4029         found_element = element;
4030       }
4031
4032       if (!IS_CUSTOM_ELEMENT(element))
4033         continue;
4034
4035       if (CAN_CHANGE(element))
4036       {
4037         for (i = 0; i < element_info[element].num_change_pages; i++)
4038         {
4039           // check for player created from custom element as single target
4040           content = element_info[element].change_page[i].target_element;
4041           is_player = ELEM_IS_PLAYER(content);
4042
4043           if (is_player && (found_rating < 3 ||
4044                             (found_rating == 3 && element < found_element)))
4045           {
4046             start_x = x;
4047             start_y = y;
4048
4049             found_rating = 3;
4050             found_element = element;
4051           }
4052         }
4053       }
4054
4055       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4056       {
4057         // check for player created from custom element as explosion content
4058         content = element_info[element].content.e[xx][yy];
4059         is_player = ELEM_IS_PLAYER(content);
4060
4061         if (is_player && (found_rating < 2 ||
4062                           (found_rating == 2 && element < found_element)))
4063         {
4064           start_x = x + xx - 1;
4065           start_y = y + yy - 1;
4066
4067           found_rating = 2;
4068           found_element = element;
4069         }
4070
4071         if (!CAN_CHANGE(element))
4072           continue;
4073
4074         for (i = 0; i < element_info[element].num_change_pages; i++)
4075         {
4076           // check for player created from custom element as extended target
4077           content =
4078             element_info[element].change_page[i].target_content.e[xx][yy];
4079
4080           is_player = ELEM_IS_PLAYER(content);
4081
4082           if (is_player && (found_rating < 1 ||
4083                             (found_rating == 1 && element < found_element)))
4084           {
4085             start_x = x + xx - 1;
4086             start_y = y + yy - 1;
4087
4088             found_rating = 1;
4089             found_element = element;
4090           }
4091         }
4092       }
4093     }
4094
4095     scroll_x = SCROLL_POSITION_X(start_x);
4096     scroll_y = SCROLL_POSITION_Y(start_y);
4097   }
4098   else
4099   {
4100     scroll_x = SCROLL_POSITION_X(local_player->jx);
4101     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4102   }
4103
4104   // !!! FIX THIS (START) !!!
4105   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4106   {
4107     InitGameEngine_EM();
4108   }
4109   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4110   {
4111     InitGameEngine_SP();
4112   }
4113   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4114   {
4115     InitGameEngine_MM();
4116   }
4117   else
4118   {
4119     DrawLevel(REDRAW_FIELD);
4120     DrawAllPlayers();
4121
4122     // after drawing the level, correct some elements
4123     if (game.timegate_time_left == 0)
4124       CloseAllOpenTimegates();
4125   }
4126
4127   // blit playfield from scroll buffer to normal back buffer for fading in
4128   BlitScreenToBitmap(backbuffer);
4129   // !!! FIX THIS (END) !!!
4130
4131   DrawMaskedBorder(fade_mask);
4132
4133   FadeIn(fade_mask);
4134
4135 #if 1
4136   // full screen redraw is required at this point in the following cases:
4137   // - special editor door undrawn when game was started from level editor
4138   // - drawing area (playfield) was changed and has to be removed completely
4139   redraw_mask = REDRAW_ALL;
4140   BackToFront();
4141 #endif
4142
4143   if (!game.restart_level)
4144   {
4145     // copy default game door content to main double buffer
4146
4147     // !!! CHECK AGAIN !!!
4148     SetPanelBackground();
4149     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4150     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4151   }
4152
4153   SetPanelBackground();
4154   SetDrawBackgroundMask(REDRAW_DOOR_1);
4155
4156   UpdateAndDisplayGameControlValues();
4157
4158   if (!game.restart_level)
4159   {
4160     UnmapGameButtons();
4161     UnmapTapeButtons();
4162
4163     FreeGameButtons();
4164     CreateGameButtons();
4165
4166     MapGameButtons();
4167     MapTapeButtons();
4168
4169     // copy actual game door content to door double buffer for OpenDoor()
4170     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4171
4172     OpenDoor(DOOR_OPEN_ALL);
4173
4174     KeyboardAutoRepeatOffUnlessAutoplay();
4175
4176 #if DEBUG_INIT_PLAYER
4177     DebugPrintPlayerStatus("Player status (final)");
4178 #endif
4179   }
4180
4181   UnmapAllGadgets();
4182
4183   MapGameButtons();
4184   MapTapeButtons();
4185
4186   if (!game.restart_level && !tape.playing)
4187   {
4188     LevelStats_incPlayed(level_nr);
4189
4190     SaveLevelSetup_SeriesInfo();
4191   }
4192
4193   game.restart_level = FALSE;
4194   game.restart_game_message = NULL;
4195   game.request_active = FALSE;
4196
4197   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4198     InitGameActions_MM();
4199
4200   SaveEngineSnapshotToListInitial();
4201
4202   if (!game.restart_level)
4203   {
4204     PlaySound(SND_GAME_STARTING);
4205
4206     if (setup.sound_music)
4207       PlayLevelMusic();
4208   }
4209 }
4210
4211 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4212                         int actual_player_x, int actual_player_y)
4213 {
4214   // this is used for non-R'n'D game engines to update certain engine values
4215
4216   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4217   {
4218     actual_player_x = correctLevelPosX_EM(actual_player_x);
4219     actual_player_y = correctLevelPosY_EM(actual_player_y);
4220   }
4221
4222   // needed to determine if sounds are played within the visible screen area
4223   scroll_x = actual_scroll_x;
4224   scroll_y = actual_scroll_y;
4225
4226   // needed to get player position for "follow finger" playing input method
4227   local_player->jx = actual_player_x;
4228   local_player->jy = actual_player_y;
4229 }
4230
4231 void InitMovDir(int x, int y)
4232 {
4233   int i, element = Feld[x][y];
4234   static int xy[4][2] =
4235   {
4236     {  0, +1 },
4237     { +1,  0 },
4238     {  0, -1 },
4239     { -1,  0 }
4240   };
4241   static int direction[3][4] =
4242   {
4243     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4244     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4245     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4246   };
4247
4248   switch (element)
4249   {
4250     case EL_BUG_RIGHT:
4251     case EL_BUG_UP:
4252     case EL_BUG_LEFT:
4253     case EL_BUG_DOWN:
4254       Feld[x][y] = EL_BUG;
4255       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4256       break;
4257
4258     case EL_SPACESHIP_RIGHT:
4259     case EL_SPACESHIP_UP:
4260     case EL_SPACESHIP_LEFT:
4261     case EL_SPACESHIP_DOWN:
4262       Feld[x][y] = EL_SPACESHIP;
4263       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4264       break;
4265
4266     case EL_BD_BUTTERFLY_RIGHT:
4267     case EL_BD_BUTTERFLY_UP:
4268     case EL_BD_BUTTERFLY_LEFT:
4269     case EL_BD_BUTTERFLY_DOWN:
4270       Feld[x][y] = EL_BD_BUTTERFLY;
4271       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4272       break;
4273
4274     case EL_BD_FIREFLY_RIGHT:
4275     case EL_BD_FIREFLY_UP:
4276     case EL_BD_FIREFLY_LEFT:
4277     case EL_BD_FIREFLY_DOWN:
4278       Feld[x][y] = EL_BD_FIREFLY;
4279       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4280       break;
4281
4282     case EL_PACMAN_RIGHT:
4283     case EL_PACMAN_UP:
4284     case EL_PACMAN_LEFT:
4285     case EL_PACMAN_DOWN:
4286       Feld[x][y] = EL_PACMAN;
4287       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4288       break;
4289
4290     case EL_YAMYAM_LEFT:
4291     case EL_YAMYAM_RIGHT:
4292     case EL_YAMYAM_UP:
4293     case EL_YAMYAM_DOWN:
4294       Feld[x][y] = EL_YAMYAM;
4295       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4296       break;
4297
4298     case EL_SP_SNIKSNAK:
4299       MovDir[x][y] = MV_UP;
4300       break;
4301
4302     case EL_SP_ELECTRON:
4303       MovDir[x][y] = MV_LEFT;
4304       break;
4305
4306     case EL_MOLE_LEFT:
4307     case EL_MOLE_RIGHT:
4308     case EL_MOLE_UP:
4309     case EL_MOLE_DOWN:
4310       Feld[x][y] = EL_MOLE;
4311       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4312       break;
4313
4314     default:
4315       if (IS_CUSTOM_ELEMENT(element))
4316       {
4317         struct ElementInfo *ei = &element_info[element];
4318         int move_direction_initial = ei->move_direction_initial;
4319         int move_pattern = ei->move_pattern;
4320
4321         if (move_direction_initial == MV_START_PREVIOUS)
4322         {
4323           if (MovDir[x][y] != MV_NONE)
4324             return;
4325
4326           move_direction_initial = MV_START_AUTOMATIC;
4327         }
4328
4329         if (move_direction_initial == MV_START_RANDOM)
4330           MovDir[x][y] = 1 << RND(4);
4331         else if (move_direction_initial & MV_ANY_DIRECTION)
4332           MovDir[x][y] = move_direction_initial;
4333         else if (move_pattern == MV_ALL_DIRECTIONS ||
4334                  move_pattern == MV_TURNING_LEFT ||
4335                  move_pattern == MV_TURNING_RIGHT ||
4336                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4337                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4338                  move_pattern == MV_TURNING_RANDOM)
4339           MovDir[x][y] = 1 << RND(4);
4340         else if (move_pattern == MV_HORIZONTAL)
4341           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4342         else if (move_pattern == MV_VERTICAL)
4343           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4344         else if (move_pattern & MV_ANY_DIRECTION)
4345           MovDir[x][y] = element_info[element].move_pattern;
4346         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4347                  move_pattern == MV_ALONG_RIGHT_SIDE)
4348         {
4349           // use random direction as default start direction
4350           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4351             MovDir[x][y] = 1 << RND(4);
4352
4353           for (i = 0; i < NUM_DIRECTIONS; i++)
4354           {
4355             int x1 = x + xy[i][0];
4356             int y1 = y + xy[i][1];
4357
4358             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4359             {
4360               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4361                 MovDir[x][y] = direction[0][i];
4362               else
4363                 MovDir[x][y] = direction[1][i];
4364
4365               break;
4366             }
4367           }
4368         }                
4369       }
4370       else
4371       {
4372         MovDir[x][y] = 1 << RND(4);
4373
4374         if (element != EL_BUG &&
4375             element != EL_SPACESHIP &&
4376             element != EL_BD_BUTTERFLY &&
4377             element != EL_BD_FIREFLY)
4378           break;
4379
4380         for (i = 0; i < NUM_DIRECTIONS; i++)
4381         {
4382           int x1 = x + xy[i][0];
4383           int y1 = y + xy[i][1];
4384
4385           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4386           {
4387             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4388             {
4389               MovDir[x][y] = direction[0][i];
4390               break;
4391             }
4392             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4393                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4394             {
4395               MovDir[x][y] = direction[1][i];
4396               break;
4397             }
4398           }
4399         }
4400       }
4401       break;
4402   }
4403
4404   GfxDir[x][y] = MovDir[x][y];
4405 }
4406
4407 void InitAmoebaNr(int x, int y)
4408 {
4409   int i;
4410   int group_nr = AmoebeNachbarNr(x, y);
4411
4412   if (group_nr == 0)
4413   {
4414     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4415     {
4416       if (AmoebaCnt[i] == 0)
4417       {
4418         group_nr = i;
4419         break;
4420       }
4421     }
4422   }
4423
4424   AmoebaNr[x][y] = group_nr;
4425   AmoebaCnt[group_nr]++;
4426   AmoebaCnt2[group_nr]++;
4427 }
4428
4429 static void PlayerWins(struct PlayerInfo *player)
4430 {
4431   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4432       local_player->players_still_needed > 0)
4433     return;
4434
4435   player->LevelSolved = TRUE;
4436   player->GameOver = TRUE;
4437
4438   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4439                          level.native_em_level->lev->score :
4440                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4441                          game_mm.score :
4442                          player->score);
4443   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4444                           MM_HEALTH(game_mm.laser_overload_value) :
4445                           player->health);
4446
4447   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4448                                       TimeLeft);
4449   player->LevelSolved_CountingScore = player->score_final;
4450   player->LevelSolved_CountingHealth = player->health_final;
4451 }
4452
4453 void GameWon(void)
4454 {
4455   static int time_count_steps;
4456   static int time, time_final;
4457   static int score, score_final;
4458   static int health, health_final;
4459   static int game_over_delay_1 = 0;
4460   static int game_over_delay_2 = 0;
4461   static int game_over_delay_3 = 0;
4462   int game_over_delay_value_1 = 50;
4463   int game_over_delay_value_2 = 25;
4464   int game_over_delay_value_3 = 50;
4465
4466   if (!local_player->LevelSolved_GameWon)
4467   {
4468     int i;
4469
4470     // do not start end game actions before the player stops moving (to exit)
4471     if (local_player->MovPos)
4472       return;
4473
4474     local_player->LevelSolved_GameWon = TRUE;
4475     local_player->LevelSolved_SaveTape = tape.recording;
4476     local_player->LevelSolved_SaveScore = !tape.playing;
4477
4478     if (!tape.playing)
4479     {
4480       LevelStats_incSolved(level_nr);
4481
4482       SaveLevelSetup_SeriesInfo();
4483     }
4484
4485     if (tape.auto_play)         // tape might already be stopped here
4486       tape.auto_play_level_solved = TRUE;
4487
4488     TapeStop();
4489
4490     game_over_delay_1 = 0;
4491     game_over_delay_2 = 0;
4492     game_over_delay_3 = game_over_delay_value_3;
4493
4494     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4495     score = score_final = local_player->score_final;
4496     health = health_final = local_player->health_final;
4497
4498     if (level.score[SC_TIME_BONUS] > 0)
4499     {
4500       if (TimeLeft > 0)
4501       {
4502         time_final = 0;
4503         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4504       }
4505       else if (game.no_time_limit && TimePlayed < 999)
4506       {
4507         time_final = 999;
4508         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4509       }
4510
4511       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4512
4513       game_over_delay_1 = game_over_delay_value_1;
4514
4515       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4516       {
4517         health_final = 0;
4518         score_final += health * level.score[SC_TIME_BONUS];
4519
4520         game_over_delay_2 = game_over_delay_value_2;
4521       }
4522
4523       local_player->score_final = score_final;
4524       local_player->health_final = health_final;
4525     }
4526
4527     if (level_editor_test_game)
4528     {
4529       time = time_final;
4530       score = score_final;
4531
4532       local_player->LevelSolved_CountingTime = time;
4533       local_player->LevelSolved_CountingScore = score;
4534
4535       game_panel_controls[GAME_PANEL_TIME].value = time;
4536       game_panel_controls[GAME_PANEL_SCORE].value = score;
4537
4538       DisplayGameControlValues();
4539     }
4540
4541     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4542     {
4543       if (ExitX >= 0 && ExitY >= 0)     // local player has left the level
4544       {
4545         // close exit door after last player
4546         if ((AllPlayersGone &&
4547              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4548               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4549               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4550             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4551             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4552         {
4553           int element = Feld[ExitX][ExitY];
4554
4555           Feld[ExitX][ExitY] =
4556             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4557              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4558              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4559              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4560              EL_EM_STEEL_EXIT_CLOSING);
4561
4562           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4563         }
4564
4565         // player disappears
4566         DrawLevelField(ExitX, ExitY);
4567       }
4568
4569       for (i = 0; i < MAX_PLAYERS; i++)
4570       {
4571         struct PlayerInfo *player = &stored_player[i];
4572
4573         if (player->present)
4574         {
4575           RemovePlayer(player);
4576
4577           // player disappears
4578           DrawLevelField(player->jx, player->jy);
4579         }
4580       }
4581     }
4582
4583     PlaySound(SND_GAME_WINNING);
4584   }
4585
4586   if (game_over_delay_1 > 0)
4587   {
4588     game_over_delay_1--;
4589
4590     return;
4591   }
4592
4593   if (time != time_final)
4594   {
4595     int time_to_go = ABS(time_final - time);
4596     int time_count_dir = (time < time_final ? +1 : -1);
4597
4598     if (time_to_go < time_count_steps)
4599       time_count_steps = 1;
4600
4601     time  += time_count_steps * time_count_dir;
4602     score += time_count_steps * level.score[SC_TIME_BONUS];
4603
4604     local_player->LevelSolved_CountingTime = time;
4605     local_player->LevelSolved_CountingScore = score;
4606
4607     game_panel_controls[GAME_PANEL_TIME].value = time;
4608     game_panel_controls[GAME_PANEL_SCORE].value = score;
4609
4610     DisplayGameControlValues();
4611
4612     if (time == time_final)
4613       StopSound(SND_GAME_LEVELTIME_BONUS);
4614     else if (setup.sound_loops)
4615       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4616     else
4617       PlaySound(SND_GAME_LEVELTIME_BONUS);
4618
4619     return;
4620   }
4621
4622   if (game_over_delay_2 > 0)
4623   {
4624     game_over_delay_2--;
4625
4626     return;
4627   }
4628
4629   if (health != health_final)
4630   {
4631     int health_count_dir = (health < health_final ? +1 : -1);
4632
4633     health += health_count_dir;
4634     score  += level.score[SC_TIME_BONUS];
4635
4636     local_player->LevelSolved_CountingHealth = health;
4637     local_player->LevelSolved_CountingScore = score;
4638
4639     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4640     game_panel_controls[GAME_PANEL_SCORE].value = score;
4641
4642     DisplayGameControlValues();
4643
4644     if (health == health_final)
4645       StopSound(SND_GAME_LEVELTIME_BONUS);
4646     else if (setup.sound_loops)
4647       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4648     else
4649       PlaySound(SND_GAME_LEVELTIME_BONUS);
4650
4651     return;
4652   }
4653
4654   game.panel.active = FALSE;
4655
4656   if (game_over_delay_3 > 0)
4657   {
4658     game_over_delay_3--;
4659
4660     return;
4661   }
4662
4663   GameEnd();
4664 }
4665
4666 void GameEnd(void)
4667 {
4668   // used instead of "level_nr" (needed for network games)
4669   int last_level_nr = levelset.level_nr;
4670   int hi_pos;
4671
4672   local_player->LevelSolved_GameEnd = TRUE;
4673
4674   if (local_player->LevelSolved_SaveTape)
4675   {
4676     // make sure that request dialog to save tape does not open door again
4677     if (!global.use_envelope_request)
4678       CloseDoor(DOOR_CLOSE_1);
4679
4680     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4681   }
4682
4683   // if no tape is to be saved, close both doors simultaneously
4684   CloseDoor(DOOR_CLOSE_ALL);
4685
4686   if (level_editor_test_game)
4687   {
4688     SetGameStatus(GAME_MODE_MAIN);
4689
4690     DrawMainMenu();
4691
4692     return;
4693   }
4694
4695   if (!local_player->LevelSolved_SaveScore)
4696   {
4697     SetGameStatus(GAME_MODE_MAIN);
4698
4699     DrawMainMenu();
4700
4701     return;
4702   }
4703
4704   if (level_nr == leveldir_current->handicap_level)
4705   {
4706     leveldir_current->handicap_level++;
4707
4708     SaveLevelSetup_SeriesInfo();
4709   }
4710
4711   if (setup.increment_levels &&
4712       level_nr < leveldir_current->last_level &&
4713       !network_playing)
4714   {
4715     level_nr++;         // advance to next level
4716     TapeErase();        // start with empty tape
4717
4718     if (setup.auto_play_next_level)
4719     {
4720       LoadLevel(level_nr);
4721
4722       SaveLevelSetup_SeriesInfo();
4723     }
4724   }
4725
4726   hi_pos = NewHiScore(last_level_nr);
4727
4728   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4729   {
4730     SetGameStatus(GAME_MODE_SCORES);
4731
4732     DrawHallOfFame(last_level_nr, hi_pos);
4733   }
4734   else if (setup.auto_play_next_level && setup.increment_levels &&
4735            last_level_nr < leveldir_current->last_level &&
4736            !network_playing)
4737   {
4738     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4739   }
4740   else
4741   {
4742     SetGameStatus(GAME_MODE_MAIN);
4743
4744     DrawMainMenu();
4745   }
4746 }
4747
4748 int NewHiScore(int level_nr)
4749 {
4750   int k, l;
4751   int position = -1;
4752   boolean one_score_entry_per_name = !program.many_scores_per_name;
4753
4754   LoadScore(level_nr);
4755
4756   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4757       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4758     return -1;
4759
4760   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4761   {
4762     if (local_player->score_final > highscore[k].Score)
4763     {
4764       // player has made it to the hall of fame
4765
4766       if (k < MAX_SCORE_ENTRIES - 1)
4767       {
4768         int m = MAX_SCORE_ENTRIES - 1;
4769
4770         if (one_score_entry_per_name)
4771         {
4772           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4773             if (strEqual(setup.player_name, highscore[l].Name))
4774               m = l;
4775
4776           if (m == k)   // player's new highscore overwrites his old one
4777             goto put_into_list;
4778         }
4779
4780         for (l = m; l > k; l--)
4781         {
4782           strcpy(highscore[l].Name, highscore[l - 1].Name);
4783           highscore[l].Score = highscore[l - 1].Score;
4784         }
4785       }
4786
4787       put_into_list:
4788
4789       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4790       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4791       highscore[k].Score = local_player->score_final; 
4792       position = k;
4793
4794       break;
4795     }
4796     else if (one_score_entry_per_name &&
4797              !strncmp(setup.player_name, highscore[k].Name,
4798                       MAX_PLAYER_NAME_LEN))
4799       break;    // player already there with a higher score
4800   }
4801
4802   if (position >= 0) 
4803     SaveScore(level_nr);
4804
4805   return position;
4806 }
4807
4808 static int getElementMoveStepsizeExt(int x, int y, int direction)
4809 {
4810   int element = Feld[x][y];
4811   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4812   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4813   int horiz_move = (dx != 0);
4814   int sign = (horiz_move ? dx : dy);
4815   int step = sign * element_info[element].move_stepsize;
4816
4817   // special values for move stepsize for spring and things on conveyor belt
4818   if (horiz_move)
4819   {
4820     if (CAN_FALL(element) &&
4821         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4822       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4823     else if (element == EL_SPRING)
4824       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4825   }
4826
4827   return step;
4828 }
4829
4830 static int getElementMoveStepsize(int x, int y)
4831 {
4832   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4833 }
4834
4835 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4836 {
4837   if (player->GfxAction != action || player->GfxDir != dir)
4838   {
4839     player->GfxAction = action;
4840     player->GfxDir = dir;
4841     player->Frame = 0;
4842     player->StepFrame = 0;
4843   }
4844 }
4845
4846 static void ResetGfxFrame(int x, int y)
4847 {
4848   // profiling showed that "autotest" spends 10~20% of its time in this function
4849   if (DrawingDeactivatedField())
4850     return;
4851
4852   int element = Feld[x][y];
4853   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4854
4855   if (graphic_info[graphic].anim_global_sync)
4856     GfxFrame[x][y] = FrameCounter;
4857   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4858     GfxFrame[x][y] = CustomValue[x][y];
4859   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4860     GfxFrame[x][y] = element_info[element].collect_score;
4861   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4862     GfxFrame[x][y] = ChangeDelay[x][y];
4863 }
4864
4865 static void ResetGfxAnimation(int x, int y)
4866 {
4867   GfxAction[x][y] = ACTION_DEFAULT;
4868   GfxDir[x][y] = MovDir[x][y];
4869   GfxFrame[x][y] = 0;
4870
4871   ResetGfxFrame(x, y);
4872 }
4873
4874 static void ResetRandomAnimationValue(int x, int y)
4875 {
4876   GfxRandom[x][y] = INIT_GFX_RANDOM();
4877 }
4878
4879 static void InitMovingField(int x, int y, int direction)
4880 {
4881   int element = Feld[x][y];
4882   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4883   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4884   int newx = x + dx;
4885   int newy = y + dy;
4886   boolean is_moving_before, is_moving_after;
4887
4888   // check if element was/is moving or being moved before/after mode change
4889   is_moving_before = (WasJustMoving[x][y] != 0);
4890   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4891
4892   // reset animation only for moving elements which change direction of moving
4893   // or which just started or stopped moving
4894   // (else CEs with property "can move" / "not moving" are reset each frame)
4895   if (is_moving_before != is_moving_after ||
4896       direction != MovDir[x][y])
4897     ResetGfxAnimation(x, y);
4898
4899   MovDir[x][y] = direction;
4900   GfxDir[x][y] = direction;
4901
4902   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4903                      direction == MV_DOWN && CAN_FALL(element) ?
4904                      ACTION_FALLING : ACTION_MOVING);
4905
4906   // this is needed for CEs with property "can move" / "not moving"
4907
4908   if (is_moving_after)
4909   {
4910     if (Feld[newx][newy] == EL_EMPTY)
4911       Feld[newx][newy] = EL_BLOCKED;
4912
4913     MovDir[newx][newy] = MovDir[x][y];
4914
4915     CustomValue[newx][newy] = CustomValue[x][y];
4916
4917     GfxFrame[newx][newy] = GfxFrame[x][y];
4918     GfxRandom[newx][newy] = GfxRandom[x][y];
4919     GfxAction[newx][newy] = GfxAction[x][y];
4920     GfxDir[newx][newy] = GfxDir[x][y];
4921   }
4922 }
4923
4924 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4925 {
4926   int direction = MovDir[x][y];
4927   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4928   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4929
4930   *goes_to_x = newx;
4931   *goes_to_y = newy;
4932 }
4933
4934 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4935 {
4936   int oldx = x, oldy = y;
4937   int direction = MovDir[x][y];
4938
4939   if (direction == MV_LEFT)
4940     oldx++;
4941   else if (direction == MV_RIGHT)
4942     oldx--;
4943   else if (direction == MV_UP)
4944     oldy++;
4945   else if (direction == MV_DOWN)
4946     oldy--;
4947
4948   *comes_from_x = oldx;
4949   *comes_from_y = oldy;
4950 }
4951
4952 static int MovingOrBlocked2Element(int x, int y)
4953 {
4954   int element = Feld[x][y];
4955
4956   if (element == EL_BLOCKED)
4957   {
4958     int oldx, oldy;
4959
4960     Blocked2Moving(x, y, &oldx, &oldy);
4961     return Feld[oldx][oldy];
4962   }
4963   else
4964     return element;
4965 }
4966
4967 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4968 {
4969   // like MovingOrBlocked2Element(), but if element is moving
4970   // and (x,y) is the field the moving element is just leaving,
4971   // return EL_BLOCKED instead of the element value
4972   int element = Feld[x][y];
4973
4974   if (IS_MOVING(x, y))
4975   {
4976     if (element == EL_BLOCKED)
4977     {
4978       int oldx, oldy;
4979
4980       Blocked2Moving(x, y, &oldx, &oldy);
4981       return Feld[oldx][oldy];
4982     }
4983     else
4984       return EL_BLOCKED;
4985   }
4986   else
4987     return element;
4988 }
4989
4990 static void RemoveField(int x, int y)
4991 {
4992   Feld[x][y] = EL_EMPTY;
4993
4994   MovPos[x][y] = 0;
4995   MovDir[x][y] = 0;
4996   MovDelay[x][y] = 0;
4997
4998   CustomValue[x][y] = 0;
4999
5000   AmoebaNr[x][y] = 0;
5001   ChangeDelay[x][y] = 0;
5002   ChangePage[x][y] = -1;
5003   Pushed[x][y] = FALSE;
5004
5005   GfxElement[x][y] = EL_UNDEFINED;
5006   GfxAction[x][y] = ACTION_DEFAULT;
5007   GfxDir[x][y] = MV_NONE;
5008 }
5009
5010 static void RemoveMovingField(int x, int y)
5011 {
5012   int oldx = x, oldy = y, newx = x, newy = y;
5013   int element = Feld[x][y];
5014   int next_element = EL_UNDEFINED;
5015
5016   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5017     return;
5018
5019   if (IS_MOVING(x, y))
5020   {
5021     Moving2Blocked(x, y, &newx, &newy);
5022
5023     if (Feld[newx][newy] != EL_BLOCKED)
5024     {
5025       // element is moving, but target field is not free (blocked), but
5026       // already occupied by something different (example: acid pool);
5027       // in this case, only remove the moving field, but not the target
5028
5029       RemoveField(oldx, oldy);
5030
5031       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5032
5033       TEST_DrawLevelField(oldx, oldy);
5034
5035       return;
5036     }
5037   }
5038   else if (element == EL_BLOCKED)
5039   {
5040     Blocked2Moving(x, y, &oldx, &oldy);
5041     if (!IS_MOVING(oldx, oldy))
5042       return;
5043   }
5044
5045   if (element == EL_BLOCKED &&
5046       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5047        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5048        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5049        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5050        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5051        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5052     next_element = get_next_element(Feld[oldx][oldy]);
5053
5054   RemoveField(oldx, oldy);
5055   RemoveField(newx, newy);
5056
5057   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5058
5059   if (next_element != EL_UNDEFINED)
5060     Feld[oldx][oldy] = next_element;
5061
5062   TEST_DrawLevelField(oldx, oldy);
5063   TEST_DrawLevelField(newx, newy);
5064 }
5065
5066 void DrawDynamite(int x, int y)
5067 {
5068   int sx = SCREENX(x), sy = SCREENY(y);
5069   int graphic = el2img(Feld[x][y]);
5070   int frame;
5071
5072   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5073     return;
5074
5075   if (IS_WALKABLE_INSIDE(Back[x][y]))
5076     return;
5077
5078   if (Back[x][y])
5079     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5080   else if (Store[x][y])
5081     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5082
5083   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5084
5085   if (Back[x][y] || Store[x][y])
5086     DrawGraphicThruMask(sx, sy, graphic, frame);
5087   else
5088     DrawGraphic(sx, sy, graphic, frame);
5089 }
5090
5091 static void CheckDynamite(int x, int y)
5092 {
5093   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5094   {
5095     MovDelay[x][y]--;
5096
5097     if (MovDelay[x][y] != 0)
5098     {
5099       DrawDynamite(x, y);
5100       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5101
5102       return;
5103     }
5104   }
5105
5106   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5107
5108   Bang(x, y);
5109 }
5110
5111 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5112 {
5113   boolean num_checked_players = 0;
5114   int i;
5115
5116   for (i = 0; i < MAX_PLAYERS; i++)
5117   {
5118     if (stored_player[i].active)
5119     {
5120       int sx = stored_player[i].jx;
5121       int sy = stored_player[i].jy;
5122
5123       if (num_checked_players == 0)
5124       {
5125         *sx1 = *sx2 = sx;
5126         *sy1 = *sy2 = sy;
5127       }
5128       else
5129       {
5130         *sx1 = MIN(*sx1, sx);
5131         *sy1 = MIN(*sy1, sy);
5132         *sx2 = MAX(*sx2, sx);
5133         *sy2 = MAX(*sy2, sy);
5134       }
5135
5136       num_checked_players++;
5137     }
5138   }
5139 }
5140
5141 static boolean checkIfAllPlayersFitToScreen_RND(void)
5142 {
5143   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5144
5145   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5146
5147   return (sx2 - sx1 < SCR_FIELDX &&
5148           sy2 - sy1 < SCR_FIELDY);
5149 }
5150
5151 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5152 {
5153   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5154
5155   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5156
5157   *sx = (sx1 + sx2) / 2;
5158   *sy = (sy1 + sy2) / 2;
5159 }
5160
5161 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5162                                boolean center_screen, boolean quick_relocation)
5163 {
5164   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5165   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5166   boolean no_delay = (tape.warp_forward);
5167   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5168   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5169   int new_scroll_x, new_scroll_y;
5170
5171   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5172   {
5173     // case 1: quick relocation inside visible screen (without scrolling)
5174
5175     RedrawPlayfield();
5176
5177     return;
5178   }
5179
5180   if (!level.shifted_relocation || center_screen)
5181   {
5182     // relocation _with_ centering of screen
5183
5184     new_scroll_x = SCROLL_POSITION_X(x);
5185     new_scroll_y = SCROLL_POSITION_Y(y);
5186   }
5187   else
5188   {
5189     // relocation _without_ centering of screen
5190
5191     int center_scroll_x = SCROLL_POSITION_X(old_x);
5192     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5193     int offset_x = x + (scroll_x - center_scroll_x);
5194     int offset_y = y + (scroll_y - center_scroll_y);
5195
5196     // for new screen position, apply previous offset to center position
5197     new_scroll_x = SCROLL_POSITION_X(offset_x);
5198     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5199   }
5200
5201   if (quick_relocation)
5202   {
5203     // case 2: quick relocation (redraw without visible scrolling)
5204
5205     scroll_x = new_scroll_x;
5206     scroll_y = new_scroll_y;
5207
5208     RedrawPlayfield();
5209
5210     return;
5211   }
5212
5213   // case 3: visible relocation (with scrolling to new position)
5214
5215   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5216
5217   SetVideoFrameDelay(wait_delay_value);
5218
5219   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5220   {
5221     int dx = 0, dy = 0;
5222     int fx = FX, fy = FY;
5223
5224     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5225     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5226
5227     if (dx == 0 && dy == 0)             // no scrolling needed at all
5228       break;
5229
5230     scroll_x -= dx;
5231     scroll_y -= dy;
5232
5233     fx += dx * TILEX / 2;
5234     fy += dy * TILEY / 2;
5235
5236     ScrollLevel(dx, dy);
5237     DrawAllPlayers();
5238
5239     // scroll in two steps of half tile size to make things smoother
5240     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5241
5242     // scroll second step to align at full tile size
5243     BlitScreenToBitmap(window);
5244   }
5245
5246   DrawAllPlayers();
5247   BackToFront();
5248
5249   SetVideoFrameDelay(frame_delay_value_old);
5250 }
5251
5252 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5253 {
5254   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5255   int player_nr = GET_PLAYER_NR(el_player);
5256   struct PlayerInfo *player = &stored_player[player_nr];
5257   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5258   boolean no_delay = (tape.warp_forward);
5259   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5260   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5261   int old_jx = player->jx;
5262   int old_jy = player->jy;
5263   int old_element = Feld[old_jx][old_jy];
5264   int element = Feld[jx][jy];
5265   boolean player_relocated = (old_jx != jx || old_jy != jy);
5266
5267   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5268   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5269   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5270   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5271   int leave_side_horiz = move_dir_horiz;
5272   int leave_side_vert  = move_dir_vert;
5273   int enter_side = enter_side_horiz | enter_side_vert;
5274   int leave_side = leave_side_horiz | leave_side_vert;
5275
5276   if (player->GameOver)         // do not reanimate dead player
5277     return;
5278
5279   if (!player_relocated)        // no need to relocate the player
5280     return;
5281
5282   if (IS_PLAYER(jx, jy))        // player already placed at new position
5283   {
5284     RemoveField(jx, jy);        // temporarily remove newly placed player
5285     DrawLevelField(jx, jy);
5286   }
5287
5288   if (player->present)
5289   {
5290     while (player->MovPos)
5291     {
5292       ScrollPlayer(player, SCROLL_GO_ON);
5293       ScrollScreen(NULL, SCROLL_GO_ON);
5294
5295       AdvanceFrameAndPlayerCounters(player->index_nr);
5296
5297       DrawPlayer(player);
5298
5299       BackToFront_WithFrameDelay(wait_delay_value);
5300     }
5301
5302     DrawPlayer(player);         // needed here only to cleanup last field
5303     DrawLevelField(player->jx, player->jy);     // remove player graphic
5304
5305     player->is_moving = FALSE;
5306   }
5307
5308   if (IS_CUSTOM_ELEMENT(old_element))
5309     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5310                                CE_LEFT_BY_PLAYER,
5311                                player->index_bit, leave_side);
5312
5313   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5314                                       CE_PLAYER_LEAVES_X,
5315                                       player->index_bit, leave_side);
5316
5317   Feld[jx][jy] = el_player;
5318   InitPlayerField(jx, jy, el_player, TRUE);
5319
5320   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5321      possible that the relocation target field did not contain a player element,
5322      but a walkable element, to which the new player was relocated -- in this
5323      case, restore that (already initialized!) element on the player field */
5324   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5325   {
5326     Feld[jx][jy] = element;     // restore previously existing element
5327   }
5328
5329   // only visually relocate centered player
5330   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5331                      FALSE, level.instant_relocation);
5332
5333   TestIfPlayerTouchesBadThing(jx, jy);
5334   TestIfPlayerTouchesCustomElement(jx, jy);
5335
5336   if (IS_CUSTOM_ELEMENT(element))
5337     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5338                                player->index_bit, enter_side);
5339
5340   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5341                                       player->index_bit, enter_side);
5342
5343   if (player->is_switching)
5344   {
5345     /* ensure that relocation while still switching an element does not cause
5346        a new element to be treated as also switched directly after relocation
5347        (this is important for teleporter switches that teleport the player to
5348        a place where another teleporter switch is in the same direction, which
5349        would then incorrectly be treated as immediately switched before the
5350        direction key that caused the switch was released) */
5351
5352     player->switch_x += jx - old_jx;
5353     player->switch_y += jy - old_jy;
5354   }
5355 }
5356
5357 static void Explode(int ex, int ey, int phase, int mode)
5358 {
5359   int x, y;
5360   int last_phase;
5361   int border_element;
5362
5363   // !!! eliminate this variable !!!
5364   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5365
5366   if (game.explosions_delayed)
5367   {
5368     ExplodeField[ex][ey] = mode;
5369     return;
5370   }
5371
5372   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5373   {
5374     int center_element = Feld[ex][ey];
5375     int artwork_element, explosion_element;     // set these values later
5376
5377     // remove things displayed in background while burning dynamite
5378     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5379       Back[ex][ey] = 0;
5380
5381     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5382     {
5383       // put moving element to center field (and let it explode there)
5384       center_element = MovingOrBlocked2Element(ex, ey);
5385       RemoveMovingField(ex, ey);
5386       Feld[ex][ey] = center_element;
5387     }
5388
5389     // now "center_element" is finally determined -- set related values now
5390     artwork_element = center_element;           // for custom player artwork
5391     explosion_element = center_element;         // for custom player artwork
5392
5393     if (IS_PLAYER(ex, ey))
5394     {
5395       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5396
5397       artwork_element = stored_player[player_nr].artwork_element;
5398
5399       if (level.use_explosion_element[player_nr])
5400       {
5401         explosion_element = level.explosion_element[player_nr];
5402         artwork_element = explosion_element;
5403       }
5404     }
5405
5406     if (mode == EX_TYPE_NORMAL ||
5407         mode == EX_TYPE_CENTER ||
5408         mode == EX_TYPE_CROSS)
5409       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5410
5411     last_phase = element_info[explosion_element].explosion_delay + 1;
5412
5413     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5414     {
5415       int xx = x - ex + 1;
5416       int yy = y - ey + 1;
5417       int element;
5418
5419       if (!IN_LEV_FIELD(x, y) ||
5420           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5421           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5422         continue;
5423
5424       element = Feld[x][y];
5425
5426       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5427       {
5428         element = MovingOrBlocked2Element(x, y);
5429
5430         if (!IS_EXPLOSION_PROOF(element))
5431           RemoveMovingField(x, y);
5432       }
5433
5434       // indestructible elements can only explode in center (but not flames)
5435       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5436                                            mode == EX_TYPE_BORDER)) ||
5437           element == EL_FLAMES)
5438         continue;
5439
5440       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5441          behaviour, for example when touching a yamyam that explodes to rocks
5442          with active deadly shield, a rock is created under the player !!! */
5443       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5444 #if 0
5445       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5446           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5447            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5448 #else
5449       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5450 #endif
5451       {
5452         if (IS_ACTIVE_BOMB(element))
5453         {
5454           // re-activate things under the bomb like gate or penguin
5455           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5456           Back[x][y] = 0;
5457         }
5458
5459         continue;
5460       }
5461
5462       // save walkable background elements while explosion on same tile
5463       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5464           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5465         Back[x][y] = element;
5466
5467       // ignite explodable elements reached by other explosion
5468       if (element == EL_EXPLOSION)
5469         element = Store2[x][y];
5470
5471       if (AmoebaNr[x][y] &&
5472           (element == EL_AMOEBA_FULL ||
5473            element == EL_BD_AMOEBA ||
5474            element == EL_AMOEBA_GROWING))
5475       {
5476         AmoebaCnt[AmoebaNr[x][y]]--;
5477         AmoebaCnt2[AmoebaNr[x][y]]--;
5478       }
5479
5480       RemoveField(x, y);
5481
5482       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5483       {
5484         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5485
5486         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5487
5488         if (PLAYERINFO(ex, ey)->use_murphy)
5489           Store[x][y] = EL_EMPTY;
5490       }
5491
5492       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5493       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5494       else if (ELEM_IS_PLAYER(center_element))
5495         Store[x][y] = EL_EMPTY;
5496       else if (center_element == EL_YAMYAM)
5497         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5498       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5499         Store[x][y] = element_info[center_element].content.e[xx][yy];
5500 #if 1
5501       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5502       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5503       // otherwise) -- FIX THIS !!!
5504       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5505         Store[x][y] = element_info[element].content.e[1][1];
5506 #else
5507       else if (!CAN_EXPLODE(element))
5508         Store[x][y] = element_info[element].content.e[1][1];
5509 #endif
5510       else
5511         Store[x][y] = EL_EMPTY;
5512
5513       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5514           center_element == EL_AMOEBA_TO_DIAMOND)
5515         Store2[x][y] = element;
5516
5517       Feld[x][y] = EL_EXPLOSION;
5518       GfxElement[x][y] = artwork_element;
5519
5520       ExplodePhase[x][y] = 1;
5521       ExplodeDelay[x][y] = last_phase;
5522
5523       Stop[x][y] = TRUE;
5524     }
5525
5526     if (center_element == EL_YAMYAM)
5527       game.yamyam_content_nr =
5528         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5529
5530     return;
5531   }
5532
5533   if (Stop[ex][ey])
5534     return;
5535
5536   x = ex;
5537   y = ey;
5538
5539   if (phase == 1)
5540     GfxFrame[x][y] = 0;         // restart explosion animation
5541
5542   last_phase = ExplodeDelay[x][y];
5543
5544   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5545
5546   // this can happen if the player leaves an explosion just in time
5547   if (GfxElement[x][y] == EL_UNDEFINED)
5548     GfxElement[x][y] = EL_EMPTY;
5549
5550   border_element = Store2[x][y];
5551   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5552     border_element = StorePlayer[x][y];
5553
5554   if (phase == element_info[border_element].ignition_delay ||
5555       phase == last_phase)
5556   {
5557     boolean border_explosion = FALSE;
5558
5559     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5560         !PLAYER_EXPLOSION_PROTECTED(x, y))
5561     {
5562       KillPlayerUnlessExplosionProtected(x, y);
5563       border_explosion = TRUE;
5564     }
5565     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5566     {
5567       Feld[x][y] = Store2[x][y];
5568       Store2[x][y] = 0;
5569       Bang(x, y);
5570       border_explosion = TRUE;
5571     }
5572     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5573     {
5574       AmoebeUmwandeln(x, y);
5575       Store2[x][y] = 0;
5576       border_explosion = TRUE;
5577     }
5578
5579     // if an element just explodes due to another explosion (chain-reaction),
5580     // do not immediately end the new explosion when it was the last frame of
5581     // the explosion (as it would be done in the following "if"-statement!)
5582     if (border_explosion && phase == last_phase)
5583       return;
5584   }
5585
5586   if (phase == last_phase)
5587   {
5588     int element;
5589
5590     element = Feld[x][y] = Store[x][y];
5591     Store[x][y] = Store2[x][y] = 0;
5592     GfxElement[x][y] = EL_UNDEFINED;
5593
5594     // player can escape from explosions and might therefore be still alive
5595     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5596         element <= EL_PLAYER_IS_EXPLODING_4)
5597     {
5598       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5599       int explosion_element = EL_PLAYER_1 + player_nr;
5600       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5601       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5602
5603       if (level.use_explosion_element[player_nr])
5604         explosion_element = level.explosion_element[player_nr];
5605
5606       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5607                     element_info[explosion_element].content.e[xx][yy]);
5608     }
5609
5610     // restore probably existing indestructible background element
5611     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5612       element = Feld[x][y] = Back[x][y];
5613     Back[x][y] = 0;
5614
5615     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5616     GfxDir[x][y] = MV_NONE;
5617     ChangeDelay[x][y] = 0;
5618     ChangePage[x][y] = -1;
5619
5620     CustomValue[x][y] = 0;
5621
5622     InitField_WithBug2(x, y, FALSE);
5623
5624     TEST_DrawLevelField(x, y);
5625
5626     TestIfElementTouchesCustomElement(x, y);
5627
5628     if (GFX_CRUMBLED(element))
5629       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5630
5631     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5632       StorePlayer[x][y] = 0;
5633
5634     if (ELEM_IS_PLAYER(element))
5635       RelocatePlayer(x, y, element);
5636   }
5637   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5638   {
5639     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5640     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5641
5642     if (phase == delay)
5643       TEST_DrawLevelFieldCrumbled(x, y);
5644
5645     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5646     {
5647       DrawLevelElement(x, y, Back[x][y]);
5648       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5649     }
5650     else if (IS_WALKABLE_UNDER(Back[x][y]))
5651     {
5652       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5653       DrawLevelElementThruMask(x, y, Back[x][y]);
5654     }
5655     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5656       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5657   }
5658 }
5659
5660 static void DynaExplode(int ex, int ey)
5661 {
5662   int i, j;
5663   int dynabomb_element = Feld[ex][ey];
5664   int dynabomb_size = 1;
5665   boolean dynabomb_xl = FALSE;
5666   struct PlayerInfo *player;
5667   static int xy[4][2] =
5668   {
5669     { 0, -1 },
5670     { -1, 0 },
5671     { +1, 0 },
5672     { 0, +1 }
5673   };
5674
5675   if (IS_ACTIVE_BOMB(dynabomb_element))
5676   {
5677     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5678     dynabomb_size = player->dynabomb_size;
5679     dynabomb_xl = player->dynabomb_xl;
5680     player->dynabombs_left++;
5681   }
5682
5683   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5684
5685   for (i = 0; i < NUM_DIRECTIONS; i++)
5686   {
5687     for (j = 1; j <= dynabomb_size; j++)
5688     {
5689       int x = ex + j * xy[i][0];
5690       int y = ey + j * xy[i][1];
5691       int element;
5692
5693       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5694         break;
5695
5696       element = Feld[x][y];
5697
5698       // do not restart explosions of fields with active bombs
5699       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5700         continue;
5701
5702       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5703
5704       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5705           !IS_DIGGABLE(element) && !dynabomb_xl)
5706         break;
5707     }
5708   }
5709 }
5710
5711 void Bang(int x, int y)
5712 {
5713   int element = MovingOrBlocked2Element(x, y);
5714   int explosion_type = EX_TYPE_NORMAL;
5715
5716   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5717   {
5718     struct PlayerInfo *player = PLAYERINFO(x, y);
5719
5720     element = Feld[x][y] = player->initial_element;
5721
5722     if (level.use_explosion_element[player->index_nr])
5723     {
5724       int explosion_element = level.explosion_element[player->index_nr];
5725
5726       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5727         explosion_type = EX_TYPE_CROSS;
5728       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5729         explosion_type = EX_TYPE_CENTER;
5730     }
5731   }
5732
5733   switch (element)
5734   {
5735     case EL_BUG:
5736     case EL_SPACESHIP:
5737     case EL_BD_BUTTERFLY:
5738     case EL_BD_FIREFLY:
5739     case EL_YAMYAM:
5740     case EL_DARK_YAMYAM:
5741     case EL_ROBOT:
5742     case EL_PACMAN:
5743     case EL_MOLE:
5744       RaiseScoreElement(element);
5745       break;
5746
5747     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5748     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5749     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5750     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5751     case EL_DYNABOMB_INCREASE_NUMBER:
5752     case EL_DYNABOMB_INCREASE_SIZE:
5753     case EL_DYNABOMB_INCREASE_POWER:
5754       explosion_type = EX_TYPE_DYNA;
5755       break;
5756
5757     case EL_DC_LANDMINE:
5758       explosion_type = EX_TYPE_CENTER;
5759       break;
5760
5761     case EL_PENGUIN:
5762     case EL_LAMP:
5763     case EL_LAMP_ACTIVE:
5764     case EL_AMOEBA_TO_DIAMOND:
5765       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5766         explosion_type = EX_TYPE_CENTER;
5767       break;
5768
5769     default:
5770       if (element_info[element].explosion_type == EXPLODES_CROSS)
5771         explosion_type = EX_TYPE_CROSS;
5772       else if (element_info[element].explosion_type == EXPLODES_1X1)
5773         explosion_type = EX_TYPE_CENTER;
5774       break;
5775   }
5776
5777   if (explosion_type == EX_TYPE_DYNA)
5778     DynaExplode(x, y);
5779   else
5780     Explode(x, y, EX_PHASE_START, explosion_type);
5781
5782   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5783 }
5784
5785 static void SplashAcid(int x, int y)
5786 {
5787   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5788       (!IN_LEV_FIELD(x - 1, y - 2) ||
5789        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5790     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5791
5792   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5793       (!IN_LEV_FIELD(x + 1, y - 2) ||
5794        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5795     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5796
5797   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5798 }
5799
5800 static void InitBeltMovement(void)
5801 {
5802   static int belt_base_element[4] =
5803   {
5804     EL_CONVEYOR_BELT_1_LEFT,
5805     EL_CONVEYOR_BELT_2_LEFT,
5806     EL_CONVEYOR_BELT_3_LEFT,
5807     EL_CONVEYOR_BELT_4_LEFT
5808   };
5809   static int belt_base_active_element[4] =
5810   {
5811     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5812     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5813     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5814     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5815   };
5816
5817   int x, y, i, j;
5818
5819   // set frame order for belt animation graphic according to belt direction
5820   for (i = 0; i < NUM_BELTS; i++)
5821   {
5822     int belt_nr = i;
5823
5824     for (j = 0; j < NUM_BELT_PARTS; j++)
5825     {
5826       int element = belt_base_active_element[belt_nr] + j;
5827       int graphic_1 = el2img(element);
5828       int graphic_2 = el2panelimg(element);
5829
5830       if (game.belt_dir[i] == MV_LEFT)
5831       {
5832         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5833         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5834       }
5835       else
5836       {
5837         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5838         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5839       }
5840     }
5841   }
5842
5843   SCAN_PLAYFIELD(x, y)
5844   {
5845     int element = Feld[x][y];
5846
5847     for (i = 0; i < NUM_BELTS; i++)
5848     {
5849       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5850       {
5851         int e_belt_nr = getBeltNrFromBeltElement(element);
5852         int belt_nr = i;
5853
5854         if (e_belt_nr == belt_nr)
5855         {
5856           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5857
5858           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5859         }
5860       }
5861     }
5862   }
5863 }
5864
5865 static void ToggleBeltSwitch(int x, int y)
5866 {
5867   static int belt_base_element[4] =
5868   {
5869     EL_CONVEYOR_BELT_1_LEFT,
5870     EL_CONVEYOR_BELT_2_LEFT,
5871     EL_CONVEYOR_BELT_3_LEFT,
5872     EL_CONVEYOR_BELT_4_LEFT
5873   };
5874   static int belt_base_active_element[4] =
5875   {
5876     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5877     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5878     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5879     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5880   };
5881   static int belt_base_switch_element[4] =
5882   {
5883     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5884     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5885     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5886     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5887   };
5888   static int belt_move_dir[4] =
5889   {
5890     MV_LEFT,
5891     MV_NONE,
5892     MV_RIGHT,
5893     MV_NONE,
5894   };
5895
5896   int element = Feld[x][y];
5897   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5898   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5899   int belt_dir = belt_move_dir[belt_dir_nr];
5900   int xx, yy, i;
5901
5902   if (!IS_BELT_SWITCH(element))
5903     return;
5904
5905   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5906   game.belt_dir[belt_nr] = belt_dir;
5907
5908   if (belt_dir_nr == 3)
5909     belt_dir_nr = 1;
5910
5911   // set frame order for belt animation graphic according to belt direction
5912   for (i = 0; i < NUM_BELT_PARTS; i++)
5913   {
5914     int element = belt_base_active_element[belt_nr] + i;
5915     int graphic_1 = el2img(element);
5916     int graphic_2 = el2panelimg(element);
5917
5918     if (belt_dir == MV_LEFT)
5919     {
5920       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5921       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5922     }
5923     else
5924     {
5925       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5926       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5927     }
5928   }
5929
5930   SCAN_PLAYFIELD(xx, yy)
5931   {
5932     int element = Feld[xx][yy];
5933
5934     if (IS_BELT_SWITCH(element))
5935     {
5936       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5937
5938       if (e_belt_nr == belt_nr)
5939       {
5940         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5941         TEST_DrawLevelField(xx, yy);
5942       }
5943     }
5944     else if (IS_BELT(element) && belt_dir != MV_NONE)
5945     {
5946       int e_belt_nr = getBeltNrFromBeltElement(element);
5947
5948       if (e_belt_nr == belt_nr)
5949       {
5950         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5951
5952         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5953         TEST_DrawLevelField(xx, yy);
5954       }
5955     }
5956     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5957     {
5958       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5959
5960       if (e_belt_nr == belt_nr)
5961       {
5962         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5963
5964         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5965         TEST_DrawLevelField(xx, yy);
5966       }
5967     }
5968   }
5969 }
5970
5971 static void ToggleSwitchgateSwitch(int x, int y)
5972 {
5973   int xx, yy;
5974
5975   game.switchgate_pos = !game.switchgate_pos;
5976
5977   SCAN_PLAYFIELD(xx, yy)
5978   {
5979     int element = Feld[xx][yy];
5980
5981     if (element == EL_SWITCHGATE_SWITCH_UP)
5982     {
5983       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5984       TEST_DrawLevelField(xx, yy);
5985     }
5986     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5987     {
5988       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5989       TEST_DrawLevelField(xx, yy);
5990     }
5991     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5992     {
5993       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5994       TEST_DrawLevelField(xx, yy);
5995     }
5996     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5997     {
5998       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5999       TEST_DrawLevelField(xx, yy);
6000     }
6001     else if (element == EL_SWITCHGATE_OPEN ||
6002              element == EL_SWITCHGATE_OPENING)
6003     {
6004       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6005
6006       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6007     }
6008     else if (element == EL_SWITCHGATE_CLOSED ||
6009              element == EL_SWITCHGATE_CLOSING)
6010     {
6011       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6012
6013       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6014     }
6015   }
6016 }
6017
6018 static int getInvisibleActiveFromInvisibleElement(int element)
6019 {
6020   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6021           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6022           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6023           element);
6024 }
6025
6026 static int getInvisibleFromInvisibleActiveElement(int element)
6027 {
6028   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6029           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6030           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6031           element);
6032 }
6033
6034 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6035 {
6036   int x, y;
6037
6038   SCAN_PLAYFIELD(x, y)
6039   {
6040     int element = Feld[x][y];
6041
6042     if (element == EL_LIGHT_SWITCH &&
6043         game.light_time_left > 0)
6044     {
6045       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6046       TEST_DrawLevelField(x, y);
6047     }
6048     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6049              game.light_time_left == 0)
6050     {
6051       Feld[x][y] = EL_LIGHT_SWITCH;
6052       TEST_DrawLevelField(x, y);
6053     }
6054     else if (element == EL_EMC_DRIPPER &&
6055              game.light_time_left > 0)
6056     {
6057       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6058       TEST_DrawLevelField(x, y);
6059     }
6060     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6061              game.light_time_left == 0)
6062     {
6063       Feld[x][y] = EL_EMC_DRIPPER;
6064       TEST_DrawLevelField(x, y);
6065     }
6066     else if (element == EL_INVISIBLE_STEELWALL ||
6067              element == EL_INVISIBLE_WALL ||
6068              element == EL_INVISIBLE_SAND)
6069     {
6070       if (game.light_time_left > 0)
6071         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6072
6073       TEST_DrawLevelField(x, y);
6074
6075       // uncrumble neighbour fields, if needed
6076       if (element == EL_INVISIBLE_SAND)
6077         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6078     }
6079     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6080              element == EL_INVISIBLE_WALL_ACTIVE ||
6081              element == EL_INVISIBLE_SAND_ACTIVE)
6082     {
6083       if (game.light_time_left == 0)
6084         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6085
6086       TEST_DrawLevelField(x, y);
6087
6088       // re-crumble neighbour fields, if needed
6089       if (element == EL_INVISIBLE_SAND)
6090         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6091     }
6092   }
6093 }
6094
6095 static void RedrawAllInvisibleElementsForLenses(void)
6096 {
6097   int x, y;
6098
6099   SCAN_PLAYFIELD(x, y)
6100   {
6101     int element = Feld[x][y];
6102
6103     if (element == EL_EMC_DRIPPER &&
6104         game.lenses_time_left > 0)
6105     {
6106       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6107       TEST_DrawLevelField(x, y);
6108     }
6109     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6110              game.lenses_time_left == 0)
6111     {
6112       Feld[x][y] = EL_EMC_DRIPPER;
6113       TEST_DrawLevelField(x, y);
6114     }
6115     else if (element == EL_INVISIBLE_STEELWALL ||
6116              element == EL_INVISIBLE_WALL ||
6117              element == EL_INVISIBLE_SAND)
6118     {
6119       if (game.lenses_time_left > 0)
6120         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6121
6122       TEST_DrawLevelField(x, y);
6123
6124       // uncrumble neighbour fields, if needed
6125       if (element == EL_INVISIBLE_SAND)
6126         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6127     }
6128     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6129              element == EL_INVISIBLE_WALL_ACTIVE ||
6130              element == EL_INVISIBLE_SAND_ACTIVE)
6131     {
6132       if (game.lenses_time_left == 0)
6133         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6134
6135       TEST_DrawLevelField(x, y);
6136
6137       // re-crumble neighbour fields, if needed
6138       if (element == EL_INVISIBLE_SAND)
6139         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6140     }
6141   }
6142 }
6143
6144 static void RedrawAllInvisibleElementsForMagnifier(void)
6145 {
6146   int x, y;
6147
6148   SCAN_PLAYFIELD(x, y)
6149   {
6150     int element = Feld[x][y];
6151
6152     if (element == EL_EMC_FAKE_GRASS &&
6153         game.magnify_time_left > 0)
6154     {
6155       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6156       TEST_DrawLevelField(x, y);
6157     }
6158     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6159              game.magnify_time_left == 0)
6160     {
6161       Feld[x][y] = EL_EMC_FAKE_GRASS;
6162       TEST_DrawLevelField(x, y);
6163     }
6164     else if (IS_GATE_GRAY(element) &&
6165              game.magnify_time_left > 0)
6166     {
6167       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6168                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6169                     IS_EM_GATE_GRAY(element) ?
6170                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6171                     IS_EMC_GATE_GRAY(element) ?
6172                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6173                     IS_DC_GATE_GRAY(element) ?
6174                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6175                     element);
6176       TEST_DrawLevelField(x, y);
6177     }
6178     else if (IS_GATE_GRAY_ACTIVE(element) &&
6179              game.magnify_time_left == 0)
6180     {
6181       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6182                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6183                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6184                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6185                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6186                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6187                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6188                     EL_DC_GATE_WHITE_GRAY :
6189                     element);
6190       TEST_DrawLevelField(x, y);
6191     }
6192   }
6193 }
6194
6195 static void ToggleLightSwitch(int x, int y)
6196 {
6197   int element = Feld[x][y];
6198
6199   game.light_time_left =
6200     (element == EL_LIGHT_SWITCH ?
6201      level.time_light * FRAMES_PER_SECOND : 0);
6202
6203   RedrawAllLightSwitchesAndInvisibleElements();
6204 }
6205
6206 static void ActivateTimegateSwitch(int x, int y)
6207 {
6208   int xx, yy;
6209
6210   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6211
6212   SCAN_PLAYFIELD(xx, yy)
6213   {
6214     int element = Feld[xx][yy];
6215
6216     if (element == EL_TIMEGATE_CLOSED ||
6217         element == EL_TIMEGATE_CLOSING)
6218     {
6219       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6220       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6221     }
6222
6223     /*
6224     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6225     {
6226       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6227       TEST_DrawLevelField(xx, yy);
6228     }
6229     */
6230
6231   }
6232
6233   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6234                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6235 }
6236
6237 static void Impact(int x, int y)
6238 {
6239   boolean last_line = (y == lev_fieldy - 1);
6240   boolean object_hit = FALSE;
6241   boolean impact = (last_line || object_hit);
6242   int element = Feld[x][y];
6243   int smashed = EL_STEELWALL;
6244
6245   if (!last_line)       // check if element below was hit
6246   {
6247     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6248       return;
6249
6250     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6251                                          MovDir[x][y + 1] != MV_DOWN ||
6252                                          MovPos[x][y + 1] <= TILEY / 2));
6253
6254     // do not smash moving elements that left the smashed field in time
6255     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6256         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6257       object_hit = FALSE;
6258
6259 #if USE_QUICKSAND_IMPACT_BUGFIX
6260     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6261     {
6262       RemoveMovingField(x, y + 1);
6263       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6264       Feld[x][y + 2] = EL_ROCK;
6265       TEST_DrawLevelField(x, y + 2);
6266
6267       object_hit = TRUE;
6268     }
6269
6270     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6271     {
6272       RemoveMovingField(x, y + 1);
6273       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6274       Feld[x][y + 2] = EL_ROCK;
6275       TEST_DrawLevelField(x, y + 2);
6276
6277       object_hit = TRUE;
6278     }
6279 #endif
6280
6281     if (object_hit)
6282       smashed = MovingOrBlocked2Element(x, y + 1);
6283
6284     impact = (last_line || object_hit);
6285   }
6286
6287   if (!last_line && smashed == EL_ACID) // element falls into acid
6288   {
6289     SplashAcid(x, y + 1);
6290     return;
6291   }
6292
6293   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6294   // only reset graphic animation if graphic really changes after impact
6295   if (impact &&
6296       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6297   {
6298     ResetGfxAnimation(x, y);
6299     TEST_DrawLevelField(x, y);
6300   }
6301
6302   if (impact && CAN_EXPLODE_IMPACT(element))
6303   {
6304     Bang(x, y);
6305     return;
6306   }
6307   else if (impact && element == EL_PEARL &&
6308            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6309   {
6310     ResetGfxAnimation(x, y);
6311
6312     Feld[x][y] = EL_PEARL_BREAKING;
6313     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6314     return;
6315   }
6316   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6317   {
6318     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6319
6320     return;
6321   }
6322
6323   if (impact && element == EL_AMOEBA_DROP)
6324   {
6325     if (object_hit && IS_PLAYER(x, y + 1))
6326       KillPlayerUnlessEnemyProtected(x, y + 1);
6327     else if (object_hit && smashed == EL_PENGUIN)
6328       Bang(x, y + 1);
6329     else
6330     {
6331       Feld[x][y] = EL_AMOEBA_GROWING;
6332       Store[x][y] = EL_AMOEBA_WET;
6333
6334       ResetRandomAnimationValue(x, y);
6335     }
6336     return;
6337   }
6338
6339   if (object_hit)               // check which object was hit
6340   {
6341     if ((CAN_PASS_MAGIC_WALL(element) && 
6342          (smashed == EL_MAGIC_WALL ||
6343           smashed == EL_BD_MAGIC_WALL)) ||
6344         (CAN_PASS_DC_MAGIC_WALL(element) &&
6345          smashed == EL_DC_MAGIC_WALL))
6346     {
6347       int xx, yy;
6348       int activated_magic_wall =
6349         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6350          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6351          EL_DC_MAGIC_WALL_ACTIVE);
6352
6353       // activate magic wall / mill
6354       SCAN_PLAYFIELD(xx, yy)
6355       {
6356         if (Feld[xx][yy] == smashed)
6357           Feld[xx][yy] = activated_magic_wall;
6358       }
6359
6360       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6361       game.magic_wall_active = TRUE;
6362
6363       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6364                             SND_MAGIC_WALL_ACTIVATING :
6365                             smashed == EL_BD_MAGIC_WALL ?
6366                             SND_BD_MAGIC_WALL_ACTIVATING :
6367                             SND_DC_MAGIC_WALL_ACTIVATING));
6368     }
6369
6370     if (IS_PLAYER(x, y + 1))
6371     {
6372       if (CAN_SMASH_PLAYER(element))
6373       {
6374         KillPlayerUnlessEnemyProtected(x, y + 1);
6375         return;
6376       }
6377     }
6378     else if (smashed == EL_PENGUIN)
6379     {
6380       if (CAN_SMASH_PLAYER(element))
6381       {
6382         Bang(x, y + 1);
6383         return;
6384       }
6385     }
6386     else if (element == EL_BD_DIAMOND)
6387     {
6388       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6389       {
6390         Bang(x, y + 1);
6391         return;
6392       }
6393     }
6394     else if (((element == EL_SP_INFOTRON ||
6395                element == EL_SP_ZONK) &&
6396               (smashed == EL_SP_SNIKSNAK ||
6397                smashed == EL_SP_ELECTRON ||
6398                smashed == EL_SP_DISK_ORANGE)) ||
6399              (element == EL_SP_INFOTRON &&
6400               smashed == EL_SP_DISK_YELLOW))
6401     {
6402       Bang(x, y + 1);
6403       return;
6404     }
6405     else if (CAN_SMASH_EVERYTHING(element))
6406     {
6407       if (IS_CLASSIC_ENEMY(smashed) ||
6408           CAN_EXPLODE_SMASHED(smashed))
6409       {
6410         Bang(x, y + 1);
6411         return;
6412       }
6413       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6414       {
6415         if (smashed == EL_LAMP ||
6416             smashed == EL_LAMP_ACTIVE)
6417         {
6418           Bang(x, y + 1);
6419           return;
6420         }
6421         else if (smashed == EL_NUT)
6422         {
6423           Feld[x][y + 1] = EL_NUT_BREAKING;
6424           PlayLevelSound(x, y, SND_NUT_BREAKING);
6425           RaiseScoreElement(EL_NUT);
6426           return;
6427         }
6428         else if (smashed == EL_PEARL)
6429         {
6430           ResetGfxAnimation(x, y);
6431
6432           Feld[x][y + 1] = EL_PEARL_BREAKING;
6433           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6434           return;
6435         }
6436         else if (smashed == EL_DIAMOND)
6437         {
6438           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6439           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6440           return;
6441         }
6442         else if (IS_BELT_SWITCH(smashed))
6443         {
6444           ToggleBeltSwitch(x, y + 1);
6445         }
6446         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6447                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6448                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6449                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6450         {
6451           ToggleSwitchgateSwitch(x, y + 1);
6452         }
6453         else if (smashed == EL_LIGHT_SWITCH ||
6454                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6455         {
6456           ToggleLightSwitch(x, y + 1);
6457         }
6458         else
6459         {
6460           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6461
6462           CheckElementChangeBySide(x, y + 1, smashed, element,
6463                                    CE_SWITCHED, CH_SIDE_TOP);
6464           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6465                                             CH_SIDE_TOP);
6466         }
6467       }
6468       else
6469       {
6470         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6471       }
6472     }
6473   }
6474
6475   // play sound of magic wall / mill
6476   if (!last_line &&
6477       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6478        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6479        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6480   {
6481     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6482       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6483     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6484       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6485     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6486       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6487
6488     return;
6489   }
6490
6491   // play sound of object that hits the ground
6492   if (last_line || object_hit)
6493     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6494 }
6495
6496 static void TurnRoundExt(int x, int y)
6497 {
6498   static struct
6499   {
6500     int dx, dy;
6501   } move_xy[] =
6502   {
6503     {  0,  0 },
6504     { -1,  0 },
6505     { +1,  0 },
6506     {  0,  0 },
6507     {  0, -1 },
6508     {  0,  0 }, { 0, 0 }, { 0, 0 },
6509     {  0, +1 }
6510   };
6511   static struct
6512   {
6513     int left, right, back;
6514   } turn[] =
6515   {
6516     { 0,        0,              0        },
6517     { MV_DOWN,  MV_UP,          MV_RIGHT },
6518     { MV_UP,    MV_DOWN,        MV_LEFT  },
6519     { 0,        0,              0        },
6520     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6521     { 0,        0,              0        },
6522     { 0,        0,              0        },
6523     { 0,        0,              0        },
6524     { MV_RIGHT, MV_LEFT,        MV_UP    }
6525   };
6526
6527   int element = Feld[x][y];
6528   int move_pattern = element_info[element].move_pattern;
6529
6530   int old_move_dir = MovDir[x][y];
6531   int left_dir  = turn[old_move_dir].left;
6532   int right_dir = turn[old_move_dir].right;
6533   int back_dir  = turn[old_move_dir].back;
6534
6535   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6536   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6537   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6538   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6539
6540   int left_x  = x + left_dx,  left_y  = y + left_dy;
6541   int right_x = x + right_dx, right_y = y + right_dy;
6542   int move_x  = x + move_dx,  move_y  = y + move_dy;
6543
6544   int xx, yy;
6545
6546   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6547   {
6548     TestIfBadThingTouchesOtherBadThing(x, y);
6549
6550     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6551       MovDir[x][y] = right_dir;
6552     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6553       MovDir[x][y] = left_dir;
6554
6555     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6556       MovDelay[x][y] = 9;
6557     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6558       MovDelay[x][y] = 1;
6559   }
6560   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6561   {
6562     TestIfBadThingTouchesOtherBadThing(x, y);
6563
6564     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6565       MovDir[x][y] = left_dir;
6566     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6567       MovDir[x][y] = right_dir;
6568
6569     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6570       MovDelay[x][y] = 9;
6571     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6572       MovDelay[x][y] = 1;
6573   }
6574   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6575   {
6576     TestIfBadThingTouchesOtherBadThing(x, y);
6577
6578     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6579       MovDir[x][y] = left_dir;
6580     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6581       MovDir[x][y] = right_dir;
6582
6583     if (MovDir[x][y] != old_move_dir)
6584       MovDelay[x][y] = 9;
6585   }
6586   else if (element == EL_YAMYAM)
6587   {
6588     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6589     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6590
6591     if (can_turn_left && can_turn_right)
6592       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6593     else if (can_turn_left)
6594       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6595     else if (can_turn_right)
6596       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6597     else
6598       MovDir[x][y] = back_dir;
6599
6600     MovDelay[x][y] = 16 + 16 * RND(3);
6601   }
6602   else if (element == EL_DARK_YAMYAM)
6603   {
6604     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6605                                                          left_x, left_y);
6606     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6607                                                          right_x, right_y);
6608
6609     if (can_turn_left && can_turn_right)
6610       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6611     else if (can_turn_left)
6612       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6613     else if (can_turn_right)
6614       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6615     else
6616       MovDir[x][y] = back_dir;
6617
6618     MovDelay[x][y] = 16 + 16 * RND(3);
6619   }
6620   else if (element == EL_PACMAN)
6621   {
6622     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6623     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6624
6625     if (can_turn_left && can_turn_right)
6626       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6627     else if (can_turn_left)
6628       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6629     else if (can_turn_right)
6630       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6631     else
6632       MovDir[x][y] = back_dir;
6633
6634     MovDelay[x][y] = 6 + RND(40);
6635   }
6636   else if (element == EL_PIG)
6637   {
6638     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6639     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6640     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6641     boolean should_turn_left, should_turn_right, should_move_on;
6642     int rnd_value = 24;
6643     int rnd = RND(rnd_value);
6644
6645     should_turn_left = (can_turn_left &&
6646                         (!can_move_on ||
6647                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6648                                                    y + back_dy + left_dy)));
6649     should_turn_right = (can_turn_right &&
6650                          (!can_move_on ||
6651                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6652                                                     y + back_dy + right_dy)));
6653     should_move_on = (can_move_on &&
6654                       (!can_turn_left ||
6655                        !can_turn_right ||
6656                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6657                                                  y + move_dy + left_dy) ||
6658                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6659                                                  y + move_dy + right_dy)));
6660
6661     if (should_turn_left || should_turn_right || should_move_on)
6662     {
6663       if (should_turn_left && should_turn_right && should_move_on)
6664         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6665                         rnd < 2 * rnd_value / 3 ? right_dir :
6666                         old_move_dir);
6667       else if (should_turn_left && should_turn_right)
6668         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6669       else if (should_turn_left && should_move_on)
6670         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6671       else if (should_turn_right && should_move_on)
6672         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6673       else if (should_turn_left)
6674         MovDir[x][y] = left_dir;
6675       else if (should_turn_right)
6676         MovDir[x][y] = right_dir;
6677       else if (should_move_on)
6678         MovDir[x][y] = old_move_dir;
6679     }
6680     else if (can_move_on && rnd > rnd_value / 8)
6681       MovDir[x][y] = old_move_dir;
6682     else if (can_turn_left && can_turn_right)
6683       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6684     else if (can_turn_left && rnd > rnd_value / 8)
6685       MovDir[x][y] = left_dir;
6686     else if (can_turn_right && rnd > rnd_value/8)
6687       MovDir[x][y] = right_dir;
6688     else
6689       MovDir[x][y] = back_dir;
6690
6691     xx = x + move_xy[MovDir[x][y]].dx;
6692     yy = y + move_xy[MovDir[x][y]].dy;
6693
6694     if (!IN_LEV_FIELD(xx, yy) ||
6695         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6696       MovDir[x][y] = old_move_dir;
6697
6698     MovDelay[x][y] = 0;
6699   }
6700   else if (element == EL_DRAGON)
6701   {
6702     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6703     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6704     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6705     int rnd_value = 24;
6706     int rnd = RND(rnd_value);
6707
6708     if (can_move_on && rnd > rnd_value / 8)
6709       MovDir[x][y] = old_move_dir;
6710     else if (can_turn_left && can_turn_right)
6711       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6712     else if (can_turn_left && rnd > rnd_value / 8)
6713       MovDir[x][y] = left_dir;
6714     else if (can_turn_right && rnd > rnd_value / 8)
6715       MovDir[x][y] = right_dir;
6716     else
6717       MovDir[x][y] = back_dir;
6718
6719     xx = x + move_xy[MovDir[x][y]].dx;
6720     yy = y + move_xy[MovDir[x][y]].dy;
6721
6722     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6723       MovDir[x][y] = old_move_dir;
6724
6725     MovDelay[x][y] = 0;
6726   }
6727   else if (element == EL_MOLE)
6728   {
6729     boolean can_move_on =
6730       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6731                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6732                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6733     if (!can_move_on)
6734     {
6735       boolean can_turn_left =
6736         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6737                               IS_AMOEBOID(Feld[left_x][left_y])));
6738
6739       boolean can_turn_right =
6740         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6741                               IS_AMOEBOID(Feld[right_x][right_y])));
6742
6743       if (can_turn_left && can_turn_right)
6744         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6745       else if (can_turn_left)
6746         MovDir[x][y] = left_dir;
6747       else
6748         MovDir[x][y] = right_dir;
6749     }
6750
6751     if (MovDir[x][y] != old_move_dir)
6752       MovDelay[x][y] = 9;
6753   }
6754   else if (element == EL_BALLOON)
6755   {
6756     MovDir[x][y] = game.wind_direction;
6757     MovDelay[x][y] = 0;
6758   }
6759   else if (element == EL_SPRING)
6760   {
6761     if (MovDir[x][y] & MV_HORIZONTAL)
6762     {
6763       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6764           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6765       {
6766         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6767         ResetGfxAnimation(move_x, move_y);
6768         TEST_DrawLevelField(move_x, move_y);
6769
6770         MovDir[x][y] = back_dir;
6771       }
6772       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6773                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6774         MovDir[x][y] = MV_NONE;
6775     }
6776
6777     MovDelay[x][y] = 0;
6778   }
6779   else if (element == EL_ROBOT ||
6780            element == EL_SATELLITE ||
6781            element == EL_PENGUIN ||
6782            element == EL_EMC_ANDROID)
6783   {
6784     int attr_x = -1, attr_y = -1;
6785
6786     if (AllPlayersGone)
6787     {
6788       attr_x = ExitX;
6789       attr_y = ExitY;
6790     }
6791     else
6792     {
6793       int i;
6794
6795       for (i = 0; i < MAX_PLAYERS; i++)
6796       {
6797         struct PlayerInfo *player = &stored_player[i];
6798         int jx = player->jx, jy = player->jy;
6799
6800         if (!player->active)
6801           continue;
6802
6803         if (attr_x == -1 ||
6804             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6805         {
6806           attr_x = jx;
6807           attr_y = jy;
6808         }
6809       }
6810     }
6811
6812     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6813         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6814          game.engine_version < VERSION_IDENT(3,1,0,0)))
6815     {
6816       attr_x = ZX;
6817       attr_y = ZY;
6818     }
6819
6820     if (element == EL_PENGUIN)
6821     {
6822       int i;
6823       static int xy[4][2] =
6824       {
6825         { 0, -1 },
6826         { -1, 0 },
6827         { +1, 0 },
6828         { 0, +1 }
6829       };
6830
6831       for (i = 0; i < NUM_DIRECTIONS; i++)
6832       {
6833         int ex = x + xy[i][0];
6834         int ey = y + xy[i][1];
6835
6836         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6837                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6838                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6839                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6840         {
6841           attr_x = ex;
6842           attr_y = ey;
6843           break;
6844         }
6845       }
6846     }
6847
6848     MovDir[x][y] = MV_NONE;
6849     if (attr_x < x)
6850       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6851     else if (attr_x > x)
6852       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6853     if (attr_y < y)
6854       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6855     else if (attr_y > y)
6856       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6857
6858     if (element == EL_ROBOT)
6859     {
6860       int newx, newy;
6861
6862       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6863         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6864       Moving2Blocked(x, y, &newx, &newy);
6865
6866       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6867         MovDelay[x][y] = 8 + 8 * !RND(3);
6868       else
6869         MovDelay[x][y] = 16;
6870     }
6871     else if (element == EL_PENGUIN)
6872     {
6873       int newx, newy;
6874
6875       MovDelay[x][y] = 1;
6876
6877       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6878       {
6879         boolean first_horiz = RND(2);
6880         int new_move_dir = MovDir[x][y];
6881
6882         MovDir[x][y] =
6883           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6884         Moving2Blocked(x, y, &newx, &newy);
6885
6886         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6887           return;
6888
6889         MovDir[x][y] =
6890           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6891         Moving2Blocked(x, y, &newx, &newy);
6892
6893         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6894           return;
6895
6896         MovDir[x][y] = old_move_dir;
6897         return;
6898       }
6899     }
6900     else if (element == EL_SATELLITE)
6901     {
6902       int newx, newy;
6903
6904       MovDelay[x][y] = 1;
6905
6906       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6907       {
6908         boolean first_horiz = RND(2);
6909         int new_move_dir = MovDir[x][y];
6910
6911         MovDir[x][y] =
6912           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6913         Moving2Blocked(x, y, &newx, &newy);
6914
6915         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6916           return;
6917
6918         MovDir[x][y] =
6919           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6920         Moving2Blocked(x, y, &newx, &newy);
6921
6922         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6923           return;
6924
6925         MovDir[x][y] = old_move_dir;
6926         return;
6927       }
6928     }
6929     else if (element == EL_EMC_ANDROID)
6930     {
6931       static int check_pos[16] =
6932       {
6933         -1,             //  0 => (invalid)
6934         7,              //  1 => MV_LEFT
6935         3,              //  2 => MV_RIGHT
6936         -1,             //  3 => (invalid)
6937         1,              //  4 =>            MV_UP
6938         0,              //  5 => MV_LEFT  | MV_UP
6939         2,              //  6 => MV_RIGHT | MV_UP
6940         -1,             //  7 => (invalid)
6941         5,              //  8 =>            MV_DOWN
6942         6,              //  9 => MV_LEFT  | MV_DOWN
6943         4,              // 10 => MV_RIGHT | MV_DOWN
6944         -1,             // 11 => (invalid)
6945         -1,             // 12 => (invalid)
6946         -1,             // 13 => (invalid)
6947         -1,             // 14 => (invalid)
6948         -1,             // 15 => (invalid)
6949       };
6950       static struct
6951       {
6952         int dx, dy;
6953         int dir;
6954       } check_xy[8] =
6955       {
6956         { -1, -1,       MV_LEFT  | MV_UP   },
6957         {  0, -1,                  MV_UP   },
6958         { +1, -1,       MV_RIGHT | MV_UP   },
6959         { +1,  0,       MV_RIGHT           },
6960         { +1, +1,       MV_RIGHT | MV_DOWN },
6961         {  0, +1,                  MV_DOWN },
6962         { -1, +1,       MV_LEFT  | MV_DOWN },
6963         { -1,  0,       MV_LEFT            },
6964       };
6965       int start_pos, check_order;
6966       boolean can_clone = FALSE;
6967       int i;
6968
6969       // check if there is any free field around current position
6970       for (i = 0; i < 8; i++)
6971       {
6972         int newx = x + check_xy[i].dx;
6973         int newy = y + check_xy[i].dy;
6974
6975         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6976         {
6977           can_clone = TRUE;
6978
6979           break;
6980         }
6981       }
6982
6983       if (can_clone)            // randomly find an element to clone
6984       {
6985         can_clone = FALSE;
6986
6987         start_pos = check_pos[RND(8)];
6988         check_order = (RND(2) ? -1 : +1);
6989
6990         for (i = 0; i < 8; i++)
6991         {
6992           int pos_raw = start_pos + i * check_order;
6993           int pos = (pos_raw + 8) % 8;
6994           int newx = x + check_xy[pos].dx;
6995           int newy = y + check_xy[pos].dy;
6996
6997           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6998           {
6999             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7000             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7001
7002             Store[x][y] = Feld[newx][newy];
7003
7004             can_clone = TRUE;
7005
7006             break;
7007           }
7008         }
7009       }
7010
7011       if (can_clone)            // randomly find a direction to move
7012       {
7013         can_clone = FALSE;
7014
7015         start_pos = check_pos[RND(8)];
7016         check_order = (RND(2) ? -1 : +1);
7017
7018         for (i = 0; i < 8; i++)
7019         {
7020           int pos_raw = start_pos + i * check_order;
7021           int pos = (pos_raw + 8) % 8;
7022           int newx = x + check_xy[pos].dx;
7023           int newy = y + check_xy[pos].dy;
7024           int new_move_dir = check_xy[pos].dir;
7025
7026           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7027           {
7028             MovDir[x][y] = new_move_dir;
7029             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7030
7031             can_clone = TRUE;
7032
7033             break;
7034           }
7035         }
7036       }
7037
7038       if (can_clone)            // cloning and moving successful
7039         return;
7040
7041       // cannot clone -- try to move towards player
7042
7043       start_pos = check_pos[MovDir[x][y] & 0x0f];
7044       check_order = (RND(2) ? -1 : +1);
7045
7046       for (i = 0; i < 3; i++)
7047       {
7048         // first check start_pos, then previous/next or (next/previous) pos
7049         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7050         int pos = (pos_raw + 8) % 8;
7051         int newx = x + check_xy[pos].dx;
7052         int newy = y + check_xy[pos].dy;
7053         int new_move_dir = check_xy[pos].dir;
7054
7055         if (IS_PLAYER(newx, newy))
7056           break;
7057
7058         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7059         {
7060           MovDir[x][y] = new_move_dir;
7061           MovDelay[x][y] = level.android_move_time * 8 + 1;
7062
7063           break;
7064         }
7065       }
7066     }
7067   }
7068   else if (move_pattern == MV_TURNING_LEFT ||
7069            move_pattern == MV_TURNING_RIGHT ||
7070            move_pattern == MV_TURNING_LEFT_RIGHT ||
7071            move_pattern == MV_TURNING_RIGHT_LEFT ||
7072            move_pattern == MV_TURNING_RANDOM ||
7073            move_pattern == MV_ALL_DIRECTIONS)
7074   {
7075     boolean can_turn_left =
7076       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7077     boolean can_turn_right =
7078       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7079
7080     if (element_info[element].move_stepsize == 0)       // "not moving"
7081       return;
7082
7083     if (move_pattern == MV_TURNING_LEFT)
7084       MovDir[x][y] = left_dir;
7085     else if (move_pattern == MV_TURNING_RIGHT)
7086       MovDir[x][y] = right_dir;
7087     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7088       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7089     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7090       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7091     else if (move_pattern == MV_TURNING_RANDOM)
7092       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7093                       can_turn_right && !can_turn_left ? right_dir :
7094                       RND(2) ? left_dir : right_dir);
7095     else if (can_turn_left && can_turn_right)
7096       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7097     else if (can_turn_left)
7098       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7099     else if (can_turn_right)
7100       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7101     else
7102       MovDir[x][y] = back_dir;
7103
7104     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7105   }
7106   else if (move_pattern == MV_HORIZONTAL ||
7107            move_pattern == MV_VERTICAL)
7108   {
7109     if (move_pattern & old_move_dir)
7110       MovDir[x][y] = back_dir;
7111     else if (move_pattern == MV_HORIZONTAL)
7112       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7113     else if (move_pattern == MV_VERTICAL)
7114       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7115
7116     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7117   }
7118   else if (move_pattern & MV_ANY_DIRECTION)
7119   {
7120     MovDir[x][y] = move_pattern;
7121     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7122   }
7123   else if (move_pattern & MV_WIND_DIRECTION)
7124   {
7125     MovDir[x][y] = game.wind_direction;
7126     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7127   }
7128   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7129   {
7130     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7131       MovDir[x][y] = left_dir;
7132     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7133       MovDir[x][y] = right_dir;
7134
7135     if (MovDir[x][y] != old_move_dir)
7136       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7137   }
7138   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7139   {
7140     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7141       MovDir[x][y] = right_dir;
7142     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7143       MovDir[x][y] = left_dir;
7144
7145     if (MovDir[x][y] != old_move_dir)
7146       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7147   }
7148   else if (move_pattern == MV_TOWARDS_PLAYER ||
7149            move_pattern == MV_AWAY_FROM_PLAYER)
7150   {
7151     int attr_x = -1, attr_y = -1;
7152     int newx, newy;
7153     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7154
7155     if (AllPlayersGone)
7156     {
7157       attr_x = ExitX;
7158       attr_y = ExitY;
7159     }
7160     else
7161     {
7162       int i;
7163
7164       for (i = 0; i < MAX_PLAYERS; i++)
7165       {
7166         struct PlayerInfo *player = &stored_player[i];
7167         int jx = player->jx, jy = player->jy;
7168
7169         if (!player->active)
7170           continue;
7171
7172         if (attr_x == -1 ||
7173             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7174         {
7175           attr_x = jx;
7176           attr_y = jy;
7177         }
7178       }
7179     }
7180
7181     MovDir[x][y] = MV_NONE;
7182     if (attr_x < x)
7183       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7184     else if (attr_x > x)
7185       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7186     if (attr_y < y)
7187       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7188     else if (attr_y > y)
7189       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7190
7191     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7192
7193     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7194     {
7195       boolean first_horiz = RND(2);
7196       int new_move_dir = MovDir[x][y];
7197
7198       if (element_info[element].move_stepsize == 0)     // "not moving"
7199       {
7200         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7201         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7202
7203         return;
7204       }
7205
7206       MovDir[x][y] =
7207         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7208       Moving2Blocked(x, y, &newx, &newy);
7209
7210       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7211         return;
7212
7213       MovDir[x][y] =
7214         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7215       Moving2Blocked(x, y, &newx, &newy);
7216
7217       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7218         return;
7219
7220       MovDir[x][y] = old_move_dir;
7221     }
7222   }
7223   else if (move_pattern == MV_WHEN_PUSHED ||
7224            move_pattern == MV_WHEN_DROPPED)
7225   {
7226     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7227       MovDir[x][y] = MV_NONE;
7228
7229     MovDelay[x][y] = 0;
7230   }
7231   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7232   {
7233     static int test_xy[7][2] =
7234     {
7235       { 0, -1 },
7236       { -1, 0 },
7237       { +1, 0 },
7238       { 0, +1 },
7239       { 0, -1 },
7240       { -1, 0 },
7241       { +1, 0 },
7242     };
7243     static int test_dir[7] =
7244     {
7245       MV_UP,
7246       MV_LEFT,
7247       MV_RIGHT,
7248       MV_DOWN,
7249       MV_UP,
7250       MV_LEFT,
7251       MV_RIGHT,
7252     };
7253     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7254     int move_preference = -1000000;     // start with very low preference
7255     int new_move_dir = MV_NONE;
7256     int start_test = RND(4);
7257     int i;
7258
7259     for (i = 0; i < NUM_DIRECTIONS; i++)
7260     {
7261       int move_dir = test_dir[start_test + i];
7262       int move_dir_preference;
7263
7264       xx = x + test_xy[start_test + i][0];
7265       yy = y + test_xy[start_test + i][1];
7266
7267       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7268           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7269       {
7270         new_move_dir = move_dir;
7271
7272         break;
7273       }
7274
7275       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7276         continue;
7277
7278       move_dir_preference = -1 * RunnerVisit[xx][yy];
7279       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7280         move_dir_preference = PlayerVisit[xx][yy];
7281
7282       if (move_dir_preference > move_preference)
7283       {
7284         // prefer field that has not been visited for the longest time
7285         move_preference = move_dir_preference;
7286         new_move_dir = move_dir;
7287       }
7288       else if (move_dir_preference == move_preference &&
7289                move_dir == old_move_dir)
7290       {
7291         // prefer last direction when all directions are preferred equally
7292         move_preference = move_dir_preference;
7293         new_move_dir = move_dir;
7294       }
7295     }
7296
7297     MovDir[x][y] = new_move_dir;
7298     if (old_move_dir != new_move_dir)
7299       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7300   }
7301 }
7302
7303 static void TurnRound(int x, int y)
7304 {
7305   int direction = MovDir[x][y];
7306
7307   TurnRoundExt(x, y);
7308
7309   GfxDir[x][y] = MovDir[x][y];
7310
7311   if (direction != MovDir[x][y])
7312     GfxFrame[x][y] = 0;
7313
7314   if (MovDelay[x][y])
7315     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7316
7317   ResetGfxFrame(x, y);
7318 }
7319
7320 static boolean JustBeingPushed(int x, int y)
7321 {
7322   int i;
7323
7324   for (i = 0; i < MAX_PLAYERS; i++)
7325   {
7326     struct PlayerInfo *player = &stored_player[i];
7327
7328     if (player->active && player->is_pushing && player->MovPos)
7329     {
7330       int next_jx = player->jx + (player->jx - player->last_jx);
7331       int next_jy = player->jy + (player->jy - player->last_jy);
7332
7333       if (x == next_jx && y == next_jy)
7334         return TRUE;
7335     }
7336   }
7337
7338   return FALSE;
7339 }
7340
7341 static void StartMoving(int x, int y)
7342 {
7343   boolean started_moving = FALSE;       // some elements can fall _and_ move
7344   int element = Feld[x][y];
7345
7346   if (Stop[x][y])
7347     return;
7348
7349   if (MovDelay[x][y] == 0)
7350     GfxAction[x][y] = ACTION_DEFAULT;
7351
7352   if (CAN_FALL(element) && y < lev_fieldy - 1)
7353   {
7354     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7355         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7356       if (JustBeingPushed(x, y))
7357         return;
7358
7359     if (element == EL_QUICKSAND_FULL)
7360     {
7361       if (IS_FREE(x, y + 1))
7362       {
7363         InitMovingField(x, y, MV_DOWN);
7364         started_moving = TRUE;
7365
7366         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7367 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7368         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7369           Store[x][y] = EL_ROCK;
7370 #else
7371         Store[x][y] = EL_ROCK;
7372 #endif
7373
7374         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7375       }
7376       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7377       {
7378         if (!MovDelay[x][y])
7379         {
7380           MovDelay[x][y] = TILEY + 1;
7381
7382           ResetGfxAnimation(x, y);
7383           ResetGfxAnimation(x, y + 1);
7384         }
7385
7386         if (MovDelay[x][y])
7387         {
7388           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7389           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7390
7391           MovDelay[x][y]--;
7392           if (MovDelay[x][y])
7393             return;
7394         }
7395
7396         Feld[x][y] = EL_QUICKSAND_EMPTY;
7397         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7398         Store[x][y + 1] = Store[x][y];
7399         Store[x][y] = 0;
7400
7401         PlayLevelSoundAction(x, y, ACTION_FILLING);
7402       }
7403       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7404       {
7405         if (!MovDelay[x][y])
7406         {
7407           MovDelay[x][y] = TILEY + 1;
7408
7409           ResetGfxAnimation(x, y);
7410           ResetGfxAnimation(x, y + 1);
7411         }
7412
7413         if (MovDelay[x][y])
7414         {
7415           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7416           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7417
7418           MovDelay[x][y]--;
7419           if (MovDelay[x][y])
7420             return;
7421         }
7422
7423         Feld[x][y] = EL_QUICKSAND_EMPTY;
7424         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7425         Store[x][y + 1] = Store[x][y];
7426         Store[x][y] = 0;
7427
7428         PlayLevelSoundAction(x, y, ACTION_FILLING);
7429       }
7430     }
7431     else if (element == EL_QUICKSAND_FAST_FULL)
7432     {
7433       if (IS_FREE(x, y + 1))
7434       {
7435         InitMovingField(x, y, MV_DOWN);
7436         started_moving = TRUE;
7437
7438         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7439 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7440         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7441           Store[x][y] = EL_ROCK;
7442 #else
7443         Store[x][y] = EL_ROCK;
7444 #endif
7445
7446         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7447       }
7448       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7449       {
7450         if (!MovDelay[x][y])
7451         {
7452           MovDelay[x][y] = TILEY + 1;
7453
7454           ResetGfxAnimation(x, y);
7455           ResetGfxAnimation(x, y + 1);
7456         }
7457
7458         if (MovDelay[x][y])
7459         {
7460           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7461           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7462
7463           MovDelay[x][y]--;
7464           if (MovDelay[x][y])
7465             return;
7466         }
7467
7468         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7469         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7470         Store[x][y + 1] = Store[x][y];
7471         Store[x][y] = 0;
7472
7473         PlayLevelSoundAction(x, y, ACTION_FILLING);
7474       }
7475       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7476       {
7477         if (!MovDelay[x][y])
7478         {
7479           MovDelay[x][y] = TILEY + 1;
7480
7481           ResetGfxAnimation(x, y);
7482           ResetGfxAnimation(x, y + 1);
7483         }
7484
7485         if (MovDelay[x][y])
7486         {
7487           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7488           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7489
7490           MovDelay[x][y]--;
7491           if (MovDelay[x][y])
7492             return;
7493         }
7494
7495         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7496         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7497         Store[x][y + 1] = Store[x][y];
7498         Store[x][y] = 0;
7499
7500         PlayLevelSoundAction(x, y, ACTION_FILLING);
7501       }
7502     }
7503     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7504              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7505     {
7506       InitMovingField(x, y, MV_DOWN);
7507       started_moving = TRUE;
7508
7509       Feld[x][y] = EL_QUICKSAND_FILLING;
7510       Store[x][y] = element;
7511
7512       PlayLevelSoundAction(x, y, ACTION_FILLING);
7513     }
7514     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7515              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7516     {
7517       InitMovingField(x, y, MV_DOWN);
7518       started_moving = TRUE;
7519
7520       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7521       Store[x][y] = element;
7522
7523       PlayLevelSoundAction(x, y, ACTION_FILLING);
7524     }
7525     else if (element == EL_MAGIC_WALL_FULL)
7526     {
7527       if (IS_FREE(x, y + 1))
7528       {
7529         InitMovingField(x, y, MV_DOWN);
7530         started_moving = TRUE;
7531
7532         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7533         Store[x][y] = EL_CHANGED(Store[x][y]);
7534       }
7535       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7536       {
7537         if (!MovDelay[x][y])
7538           MovDelay[x][y] = TILEY / 4 + 1;
7539
7540         if (MovDelay[x][y])
7541         {
7542           MovDelay[x][y]--;
7543           if (MovDelay[x][y])
7544             return;
7545         }
7546
7547         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7548         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7549         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7550         Store[x][y] = 0;
7551       }
7552     }
7553     else if (element == EL_BD_MAGIC_WALL_FULL)
7554     {
7555       if (IS_FREE(x, y + 1))
7556       {
7557         InitMovingField(x, y, MV_DOWN);
7558         started_moving = TRUE;
7559
7560         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7561         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7562       }
7563       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7564       {
7565         if (!MovDelay[x][y])
7566           MovDelay[x][y] = TILEY / 4 + 1;
7567
7568         if (MovDelay[x][y])
7569         {
7570           MovDelay[x][y]--;
7571           if (MovDelay[x][y])
7572             return;
7573         }
7574
7575         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7576         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7577         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7578         Store[x][y] = 0;
7579       }
7580     }
7581     else if (element == EL_DC_MAGIC_WALL_FULL)
7582     {
7583       if (IS_FREE(x, y + 1))
7584       {
7585         InitMovingField(x, y, MV_DOWN);
7586         started_moving = TRUE;
7587
7588         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7589         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7590       }
7591       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7592       {
7593         if (!MovDelay[x][y])
7594           MovDelay[x][y] = TILEY / 4 + 1;
7595
7596         if (MovDelay[x][y])
7597         {
7598           MovDelay[x][y]--;
7599           if (MovDelay[x][y])
7600             return;
7601         }
7602
7603         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7604         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7605         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7606         Store[x][y] = 0;
7607       }
7608     }
7609     else if ((CAN_PASS_MAGIC_WALL(element) &&
7610               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7611                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7612              (CAN_PASS_DC_MAGIC_WALL(element) &&
7613               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7614
7615     {
7616       InitMovingField(x, y, MV_DOWN);
7617       started_moving = TRUE;
7618
7619       Feld[x][y] =
7620         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7621          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7622          EL_DC_MAGIC_WALL_FILLING);
7623       Store[x][y] = element;
7624     }
7625     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7626     {
7627       SplashAcid(x, y + 1);
7628
7629       InitMovingField(x, y, MV_DOWN);
7630       started_moving = TRUE;
7631
7632       Store[x][y] = EL_ACID;
7633     }
7634     else if (
7635              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7636               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7637              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7638               CAN_FALL(element) && WasJustFalling[x][y] &&
7639               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7640
7641              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7642               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7643               (Feld[x][y + 1] == EL_BLOCKED)))
7644     {
7645       /* this is needed for a special case not covered by calling "Impact()"
7646          from "ContinueMoving()": if an element moves to a tile directly below
7647          another element which was just falling on that tile (which was empty
7648          in the previous frame), the falling element above would just stop
7649          instead of smashing the element below (in previous version, the above
7650          element was just checked for "moving" instead of "falling", resulting
7651          in incorrect smashes caused by horizontal movement of the above
7652          element; also, the case of the player being the element to smash was
7653          simply not covered here... :-/ ) */
7654
7655       CheckCollision[x][y] = 0;
7656       CheckImpact[x][y] = 0;
7657
7658       Impact(x, y);
7659     }
7660     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7661     {
7662       if (MovDir[x][y] == MV_NONE)
7663       {
7664         InitMovingField(x, y, MV_DOWN);
7665         started_moving = TRUE;
7666       }
7667     }
7668     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7669     {
7670       if (WasJustFalling[x][y]) // prevent animation from being restarted
7671         MovDir[x][y] = MV_DOWN;
7672
7673       InitMovingField(x, y, MV_DOWN);
7674       started_moving = TRUE;
7675     }
7676     else if (element == EL_AMOEBA_DROP)
7677     {
7678       Feld[x][y] = EL_AMOEBA_GROWING;
7679       Store[x][y] = EL_AMOEBA_WET;
7680     }
7681     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7682               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7683              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7684              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7685     {
7686       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7687                                 (IS_FREE(x - 1, y + 1) ||
7688                                  Feld[x - 1][y + 1] == EL_ACID));
7689       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7690                                 (IS_FREE(x + 1, y + 1) ||
7691                                  Feld[x + 1][y + 1] == EL_ACID));
7692       boolean can_fall_any  = (can_fall_left || can_fall_right);
7693       boolean can_fall_both = (can_fall_left && can_fall_right);
7694       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7695
7696       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7697       {
7698         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7699           can_fall_right = FALSE;
7700         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7701           can_fall_left = FALSE;
7702         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7703           can_fall_right = FALSE;
7704         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7705           can_fall_left = FALSE;
7706
7707         can_fall_any  = (can_fall_left || can_fall_right);
7708         can_fall_both = FALSE;
7709       }
7710
7711       if (can_fall_both)
7712       {
7713         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7714           can_fall_right = FALSE;       // slip down on left side
7715         else
7716           can_fall_left = !(can_fall_right = RND(2));
7717
7718         can_fall_both = FALSE;
7719       }
7720
7721       if (can_fall_any)
7722       {
7723         // if not determined otherwise, prefer left side for slipping down
7724         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7725         started_moving = TRUE;
7726       }
7727     }
7728     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7729     {
7730       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7731       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7732       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7733       int belt_dir = game.belt_dir[belt_nr];
7734
7735       if ((belt_dir == MV_LEFT  && left_is_free) ||
7736           (belt_dir == MV_RIGHT && right_is_free))
7737       {
7738         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7739
7740         InitMovingField(x, y, belt_dir);
7741         started_moving = TRUE;
7742
7743         Pushed[x][y] = TRUE;
7744         Pushed[nextx][y] = TRUE;
7745
7746         GfxAction[x][y] = ACTION_DEFAULT;
7747       }
7748       else
7749       {
7750         MovDir[x][y] = 0;       // if element was moving, stop it
7751       }
7752     }
7753   }
7754
7755   // not "else if" because of elements that can fall and move (EL_SPRING)
7756   if (CAN_MOVE(element) && !started_moving)
7757   {
7758     int move_pattern = element_info[element].move_pattern;
7759     int newx, newy;
7760
7761     Moving2Blocked(x, y, &newx, &newy);
7762
7763     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7764       return;
7765
7766     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7767         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7768     {
7769       WasJustMoving[x][y] = 0;
7770       CheckCollision[x][y] = 0;
7771
7772       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7773
7774       if (Feld[x][y] != element)        // element has changed
7775         return;
7776     }
7777
7778     if (!MovDelay[x][y])        // start new movement phase
7779     {
7780       // all objects that can change their move direction after each step
7781       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7782
7783       if (element != EL_YAMYAM &&
7784           element != EL_DARK_YAMYAM &&
7785           element != EL_PACMAN &&
7786           !(move_pattern & MV_ANY_DIRECTION) &&
7787           move_pattern != MV_TURNING_LEFT &&
7788           move_pattern != MV_TURNING_RIGHT &&
7789           move_pattern != MV_TURNING_LEFT_RIGHT &&
7790           move_pattern != MV_TURNING_RIGHT_LEFT &&
7791           move_pattern != MV_TURNING_RANDOM)
7792       {
7793         TurnRound(x, y);
7794
7795         if (MovDelay[x][y] && (element == EL_BUG ||
7796                                element == EL_SPACESHIP ||
7797                                element == EL_SP_SNIKSNAK ||
7798                                element == EL_SP_ELECTRON ||
7799                                element == EL_MOLE))
7800           TEST_DrawLevelField(x, y);
7801       }
7802     }
7803
7804     if (MovDelay[x][y])         // wait some time before next movement
7805     {
7806       MovDelay[x][y]--;
7807
7808       if (element == EL_ROBOT ||
7809           element == EL_YAMYAM ||
7810           element == EL_DARK_YAMYAM)
7811       {
7812         DrawLevelElementAnimationIfNeeded(x, y, element);
7813         PlayLevelSoundAction(x, y, ACTION_WAITING);
7814       }
7815       else if (element == EL_SP_ELECTRON)
7816         DrawLevelElementAnimationIfNeeded(x, y, element);
7817       else if (element == EL_DRAGON)
7818       {
7819         int i;
7820         int dir = MovDir[x][y];
7821         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7822         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7823         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7824                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7825                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7826                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7827         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7828
7829         GfxAction[x][y] = ACTION_ATTACKING;
7830
7831         if (IS_PLAYER(x, y))
7832           DrawPlayerField(x, y);
7833         else
7834           TEST_DrawLevelField(x, y);
7835
7836         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7837
7838         for (i = 1; i <= 3; i++)
7839         {
7840           int xx = x + i * dx;
7841           int yy = y + i * dy;
7842           int sx = SCREENX(xx);
7843           int sy = SCREENY(yy);
7844           int flame_graphic = graphic + (i - 1);
7845
7846           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7847             break;
7848
7849           if (MovDelay[x][y])
7850           {
7851             int flamed = MovingOrBlocked2Element(xx, yy);
7852
7853             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7854               Bang(xx, yy);
7855             else
7856               RemoveMovingField(xx, yy);
7857
7858             ChangeDelay[xx][yy] = 0;
7859
7860             Feld[xx][yy] = EL_FLAMES;
7861
7862             if (IN_SCR_FIELD(sx, sy))
7863             {
7864               TEST_DrawLevelFieldCrumbled(xx, yy);
7865               DrawGraphic(sx, sy, flame_graphic, frame);
7866             }
7867           }
7868           else
7869           {
7870             if (Feld[xx][yy] == EL_FLAMES)
7871               Feld[xx][yy] = EL_EMPTY;
7872             TEST_DrawLevelField(xx, yy);
7873           }
7874         }
7875       }
7876
7877       if (MovDelay[x][y])       // element still has to wait some time
7878       {
7879         PlayLevelSoundAction(x, y, ACTION_WAITING);
7880
7881         return;
7882       }
7883     }
7884
7885     // now make next step
7886
7887     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7888
7889     if (DONT_COLLIDE_WITH(element) &&
7890         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7891         !PLAYER_ENEMY_PROTECTED(newx, newy))
7892     {
7893       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7894
7895       return;
7896     }
7897
7898     else if (CAN_MOVE_INTO_ACID(element) &&
7899              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7900              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7901              (MovDir[x][y] == MV_DOWN ||
7902               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7903     {
7904       SplashAcid(newx, newy);
7905       Store[x][y] = EL_ACID;
7906     }
7907     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7908     {
7909       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7910           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7911           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7912           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7913       {
7914         RemoveField(x, y);
7915         TEST_DrawLevelField(x, y);
7916
7917         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7918         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7919           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7920
7921         local_player->friends_still_needed--;
7922         if (!local_player->friends_still_needed &&
7923             !local_player->GameOver && AllPlayersGone)
7924           PlayerWins(local_player);
7925
7926         return;
7927       }
7928       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7929       {
7930         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7931           TEST_DrawLevelField(newx, newy);
7932         else
7933           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7934       }
7935       else if (!IS_FREE(newx, newy))
7936       {
7937         GfxAction[x][y] = ACTION_WAITING;
7938
7939         if (IS_PLAYER(x, y))
7940           DrawPlayerField(x, y);
7941         else
7942           TEST_DrawLevelField(x, y);
7943
7944         return;
7945       }
7946     }
7947     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7948     {
7949       if (IS_FOOD_PIG(Feld[newx][newy]))
7950       {
7951         if (IS_MOVING(newx, newy))
7952           RemoveMovingField(newx, newy);
7953         else
7954         {
7955           Feld[newx][newy] = EL_EMPTY;
7956           TEST_DrawLevelField(newx, newy);
7957         }
7958
7959         PlayLevelSound(x, y, SND_PIG_DIGGING);
7960       }
7961       else if (!IS_FREE(newx, newy))
7962       {
7963         if (IS_PLAYER(x, y))
7964           DrawPlayerField(x, y);
7965         else
7966           TEST_DrawLevelField(x, y);
7967
7968         return;
7969       }
7970     }
7971     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7972     {
7973       if (Store[x][y] != EL_EMPTY)
7974       {
7975         boolean can_clone = FALSE;
7976         int xx, yy;
7977
7978         // check if element to clone is still there
7979         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7980         {
7981           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7982           {
7983             can_clone = TRUE;
7984
7985             break;
7986           }
7987         }
7988
7989         // cannot clone or target field not free anymore -- do not clone
7990         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7991           Store[x][y] = EL_EMPTY;
7992       }
7993
7994       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7995       {
7996         if (IS_MV_DIAGONAL(MovDir[x][y]))
7997         {
7998           int diagonal_move_dir = MovDir[x][y];
7999           int stored = Store[x][y];
8000           int change_delay = 8;
8001           int graphic;
8002
8003           // android is moving diagonally
8004
8005           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8006
8007           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8008           GfxElement[x][y] = EL_EMC_ANDROID;
8009           GfxAction[x][y] = ACTION_SHRINKING;
8010           GfxDir[x][y] = diagonal_move_dir;
8011           ChangeDelay[x][y] = change_delay;
8012
8013           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8014                                    GfxDir[x][y]);
8015
8016           DrawLevelGraphicAnimation(x, y, graphic);
8017           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8018
8019           if (Feld[newx][newy] == EL_ACID)
8020           {
8021             SplashAcid(newx, newy);
8022
8023             return;
8024           }
8025
8026           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8027
8028           Store[newx][newy] = EL_EMC_ANDROID;
8029           GfxElement[newx][newy] = EL_EMC_ANDROID;
8030           GfxAction[newx][newy] = ACTION_GROWING;
8031           GfxDir[newx][newy] = diagonal_move_dir;
8032           ChangeDelay[newx][newy] = change_delay;
8033
8034           graphic = el_act_dir2img(GfxElement[newx][newy],
8035                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8036
8037           DrawLevelGraphicAnimation(newx, newy, graphic);
8038           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8039
8040           return;
8041         }
8042         else
8043         {
8044           Feld[newx][newy] = EL_EMPTY;
8045           TEST_DrawLevelField(newx, newy);
8046
8047           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8048         }
8049       }
8050       else if (!IS_FREE(newx, newy))
8051       {
8052         return;
8053       }
8054     }
8055     else if (IS_CUSTOM_ELEMENT(element) &&
8056              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8057     {
8058       if (!DigFieldByCE(newx, newy, element))
8059         return;
8060
8061       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8062       {
8063         RunnerVisit[x][y] = FrameCounter;
8064         PlayerVisit[x][y] /= 8;         // expire player visit path
8065       }
8066     }
8067     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8068     {
8069       if (!IS_FREE(newx, newy))
8070       {
8071         if (IS_PLAYER(x, y))
8072           DrawPlayerField(x, y);
8073         else
8074           TEST_DrawLevelField(x, y);
8075
8076         return;
8077       }
8078       else
8079       {
8080         boolean wanna_flame = !RND(10);
8081         int dx = newx - x, dy = newy - y;
8082         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8083         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8084         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8085                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8086         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8087                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8088
8089         if ((wanna_flame ||
8090              IS_CLASSIC_ENEMY(element1) ||
8091              IS_CLASSIC_ENEMY(element2)) &&
8092             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8093             element1 != EL_FLAMES && element2 != EL_FLAMES)
8094         {
8095           ResetGfxAnimation(x, y);
8096           GfxAction[x][y] = ACTION_ATTACKING;
8097
8098           if (IS_PLAYER(x, y))
8099             DrawPlayerField(x, y);
8100           else
8101             TEST_DrawLevelField(x, y);
8102
8103           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8104
8105           MovDelay[x][y] = 50;
8106
8107           Feld[newx][newy] = EL_FLAMES;
8108           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8109             Feld[newx1][newy1] = EL_FLAMES;
8110           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8111             Feld[newx2][newy2] = EL_FLAMES;
8112
8113           return;
8114         }
8115       }
8116     }
8117     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8118              Feld[newx][newy] == EL_DIAMOND)
8119     {
8120       if (IS_MOVING(newx, newy))
8121         RemoveMovingField(newx, newy);
8122       else
8123       {
8124         Feld[newx][newy] = EL_EMPTY;
8125         TEST_DrawLevelField(newx, newy);
8126       }
8127
8128       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8129     }
8130     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8131              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8132     {
8133       if (AmoebaNr[newx][newy])
8134       {
8135         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8136         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8137             Feld[newx][newy] == EL_BD_AMOEBA)
8138           AmoebaCnt[AmoebaNr[newx][newy]]--;
8139       }
8140
8141       if (IS_MOVING(newx, newy))
8142       {
8143         RemoveMovingField(newx, newy);
8144       }
8145       else
8146       {
8147         Feld[newx][newy] = EL_EMPTY;
8148         TEST_DrawLevelField(newx, newy);
8149       }
8150
8151       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8152     }
8153     else if ((element == EL_PACMAN || element == EL_MOLE)
8154              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8155     {
8156       if (AmoebaNr[newx][newy])
8157       {
8158         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8159         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8160             Feld[newx][newy] == EL_BD_AMOEBA)
8161           AmoebaCnt[AmoebaNr[newx][newy]]--;
8162       }
8163
8164       if (element == EL_MOLE)
8165       {
8166         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8167         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8168
8169         ResetGfxAnimation(x, y);
8170         GfxAction[x][y] = ACTION_DIGGING;
8171         TEST_DrawLevelField(x, y);
8172
8173         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8174
8175         return;                         // wait for shrinking amoeba
8176       }
8177       else      // element == EL_PACMAN
8178       {
8179         Feld[newx][newy] = EL_EMPTY;
8180         TEST_DrawLevelField(newx, newy);
8181         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8182       }
8183     }
8184     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8185              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8186               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8187     {
8188       // wait for shrinking amoeba to completely disappear
8189       return;
8190     }
8191     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8192     {
8193       // object was running against a wall
8194
8195       TurnRound(x, y);
8196
8197       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8198         DrawLevelElementAnimation(x, y, element);
8199
8200       if (DONT_TOUCH(element))
8201         TestIfBadThingTouchesPlayer(x, y);
8202
8203       return;
8204     }
8205
8206     InitMovingField(x, y, MovDir[x][y]);
8207
8208     PlayLevelSoundAction(x, y, ACTION_MOVING);
8209   }
8210
8211   if (MovDir[x][y])
8212     ContinueMoving(x, y);
8213 }
8214
8215 void ContinueMoving(int x, int y)
8216 {
8217   int element = Feld[x][y];
8218   struct ElementInfo *ei = &element_info[element];
8219   int direction = MovDir[x][y];
8220   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8221   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8222   int newx = x + dx, newy = y + dy;
8223   int stored = Store[x][y];
8224   int stored_new = Store[newx][newy];
8225   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8226   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8227   boolean last_line = (newy == lev_fieldy - 1);
8228
8229   MovPos[x][y] += getElementMoveStepsize(x, y);
8230
8231   if (pushed_by_player) // special case: moving object pushed by player
8232     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8233
8234   if (ABS(MovPos[x][y]) < TILEX)
8235   {
8236     TEST_DrawLevelField(x, y);
8237
8238     return;     // element is still moving
8239   }
8240
8241   // element reached destination field
8242
8243   Feld[x][y] = EL_EMPTY;
8244   Feld[newx][newy] = element;
8245   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8246
8247   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8248   {
8249     element = Feld[newx][newy] = EL_ACID;
8250   }
8251   else if (element == EL_MOLE)
8252   {
8253     Feld[x][y] = EL_SAND;
8254
8255     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8256   }
8257   else if (element == EL_QUICKSAND_FILLING)
8258   {
8259     element = Feld[newx][newy] = get_next_element(element);
8260     Store[newx][newy] = Store[x][y];
8261   }
8262   else if (element == EL_QUICKSAND_EMPTYING)
8263   {
8264     Feld[x][y] = get_next_element(element);
8265     element = Feld[newx][newy] = Store[x][y];
8266   }
8267   else if (element == EL_QUICKSAND_FAST_FILLING)
8268   {
8269     element = Feld[newx][newy] = get_next_element(element);
8270     Store[newx][newy] = Store[x][y];
8271   }
8272   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8273   {
8274     Feld[x][y] = get_next_element(element);
8275     element = Feld[newx][newy] = Store[x][y];
8276   }
8277   else if (element == EL_MAGIC_WALL_FILLING)
8278   {
8279     element = Feld[newx][newy] = get_next_element(element);
8280     if (!game.magic_wall_active)
8281       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8282     Store[newx][newy] = Store[x][y];
8283   }
8284   else if (element == EL_MAGIC_WALL_EMPTYING)
8285   {
8286     Feld[x][y] = get_next_element(element);
8287     if (!game.magic_wall_active)
8288       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8289     element = Feld[newx][newy] = Store[x][y];
8290
8291     InitField(newx, newy, FALSE);
8292   }
8293   else if (element == EL_BD_MAGIC_WALL_FILLING)
8294   {
8295     element = Feld[newx][newy] = get_next_element(element);
8296     if (!game.magic_wall_active)
8297       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8298     Store[newx][newy] = Store[x][y];
8299   }
8300   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8301   {
8302     Feld[x][y] = get_next_element(element);
8303     if (!game.magic_wall_active)
8304       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8305     element = Feld[newx][newy] = Store[x][y];
8306
8307     InitField(newx, newy, FALSE);
8308   }
8309   else if (element == EL_DC_MAGIC_WALL_FILLING)
8310   {
8311     element = Feld[newx][newy] = get_next_element(element);
8312     if (!game.magic_wall_active)
8313       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8314     Store[newx][newy] = Store[x][y];
8315   }
8316   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8317   {
8318     Feld[x][y] = get_next_element(element);
8319     if (!game.magic_wall_active)
8320       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8321     element = Feld[newx][newy] = Store[x][y];
8322
8323     InitField(newx, newy, FALSE);
8324   }
8325   else if (element == EL_AMOEBA_DROPPING)
8326   {
8327     Feld[x][y] = get_next_element(element);
8328     element = Feld[newx][newy] = Store[x][y];
8329   }
8330   else if (element == EL_SOKOBAN_OBJECT)
8331   {
8332     if (Back[x][y])
8333       Feld[x][y] = Back[x][y];
8334
8335     if (Back[newx][newy])
8336       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8337
8338     Back[x][y] = Back[newx][newy] = 0;
8339   }
8340
8341   Store[x][y] = EL_EMPTY;
8342   MovPos[x][y] = 0;
8343   MovDir[x][y] = 0;
8344   MovDelay[x][y] = 0;
8345
8346   MovDelay[newx][newy] = 0;
8347
8348   if (CAN_CHANGE_OR_HAS_ACTION(element))
8349   {
8350     // copy element change control values to new field
8351     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8352     ChangePage[newx][newy]  = ChangePage[x][y];
8353     ChangeCount[newx][newy] = ChangeCount[x][y];
8354     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8355   }
8356
8357   CustomValue[newx][newy] = CustomValue[x][y];
8358
8359   ChangeDelay[x][y] = 0;
8360   ChangePage[x][y] = -1;
8361   ChangeCount[x][y] = 0;
8362   ChangeEvent[x][y] = -1;
8363
8364   CustomValue[x][y] = 0;
8365
8366   // copy animation control values to new field
8367   GfxFrame[newx][newy]  = GfxFrame[x][y];
8368   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8369   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8370   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8371
8372   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8373
8374   // some elements can leave other elements behind after moving
8375   if (ei->move_leave_element != EL_EMPTY &&
8376       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8377       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8378   {
8379     int move_leave_element = ei->move_leave_element;
8380
8381     // this makes it possible to leave the removed element again
8382     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8383       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8384
8385     Feld[x][y] = move_leave_element;
8386
8387     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8388       MovDir[x][y] = direction;
8389
8390     InitField(x, y, FALSE);
8391
8392     if (GFX_CRUMBLED(Feld[x][y]))
8393       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8394
8395     if (ELEM_IS_PLAYER(move_leave_element))
8396       RelocatePlayer(x, y, move_leave_element);
8397   }
8398
8399   // do this after checking for left-behind element
8400   ResetGfxAnimation(x, y);      // reset animation values for old field
8401
8402   if (!CAN_MOVE(element) ||
8403       (CAN_FALL(element) && direction == MV_DOWN &&
8404        (element == EL_SPRING ||
8405         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8406         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8407     GfxDir[x][y] = MovDir[newx][newy] = 0;
8408
8409   TEST_DrawLevelField(x, y);
8410   TEST_DrawLevelField(newx, newy);
8411
8412   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8413
8414   // prevent pushed element from moving on in pushed direction
8415   if (pushed_by_player && CAN_MOVE(element) &&
8416       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8417       !(element_info[element].move_pattern & direction))
8418     TurnRound(newx, newy);
8419
8420   // prevent elements on conveyor belt from moving on in last direction
8421   if (pushed_by_conveyor && CAN_FALL(element) &&
8422       direction & MV_HORIZONTAL)
8423     MovDir[newx][newy] = 0;
8424
8425   if (!pushed_by_player)
8426   {
8427     int nextx = newx + dx, nexty = newy + dy;
8428     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8429
8430     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8431
8432     if (CAN_FALL(element) && direction == MV_DOWN)
8433       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8434
8435     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8436       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8437
8438     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8439       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8440   }
8441
8442   if (DONT_TOUCH(element))      // object may be nasty to player or others
8443   {
8444     TestIfBadThingTouchesPlayer(newx, newy);
8445     TestIfBadThingTouchesFriend(newx, newy);
8446
8447     if (!IS_CUSTOM_ELEMENT(element))
8448       TestIfBadThingTouchesOtherBadThing(newx, newy);
8449   }
8450   else if (element == EL_PENGUIN)
8451     TestIfFriendTouchesBadThing(newx, newy);
8452
8453   if (DONT_GET_HIT_BY(element))
8454   {
8455     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8456   }
8457
8458   // give the player one last chance (one more frame) to move away
8459   if (CAN_FALL(element) && direction == MV_DOWN &&
8460       (last_line || (!IS_FREE(x, newy + 1) &&
8461                      (!IS_PLAYER(x, newy + 1) ||
8462                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8463     Impact(x, newy);
8464
8465   if (pushed_by_player && !game.use_change_when_pushing_bug)
8466   {
8467     int push_side = MV_DIR_OPPOSITE(direction);
8468     struct PlayerInfo *player = PLAYERINFO(x, y);
8469
8470     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8471                                player->index_bit, push_side);
8472     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8473                                         player->index_bit, push_side);
8474   }
8475
8476   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8477     MovDelay[newx][newy] = 1;
8478
8479   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8480
8481   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8482   TestIfElementHitsCustomElement(newx, newy, direction);
8483   TestIfPlayerTouchesCustomElement(newx, newy);
8484   TestIfElementTouchesCustomElement(newx, newy);
8485
8486   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8487       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8488     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8489                              MV_DIR_OPPOSITE(direction));
8490 }
8491
8492 int AmoebeNachbarNr(int ax, int ay)
8493 {
8494   int i;
8495   int element = Feld[ax][ay];
8496   int group_nr = 0;
8497   static int xy[4][2] =
8498   {
8499     { 0, -1 },
8500     { -1, 0 },
8501     { +1, 0 },
8502     { 0, +1 }
8503   };
8504
8505   for (i = 0; i < NUM_DIRECTIONS; i++)
8506   {
8507     int x = ax + xy[i][0];
8508     int y = ay + xy[i][1];
8509
8510     if (!IN_LEV_FIELD(x, y))
8511       continue;
8512
8513     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8514       group_nr = AmoebaNr[x][y];
8515   }
8516
8517   return group_nr;
8518 }
8519
8520 static void AmoebenVereinigen(int ax, int ay)
8521 {
8522   int i, x, y, xx, yy;
8523   int new_group_nr = AmoebaNr[ax][ay];
8524   static int xy[4][2] =
8525   {
8526     { 0, -1 },
8527     { -1, 0 },
8528     { +1, 0 },
8529     { 0, +1 }
8530   };
8531
8532   if (new_group_nr == 0)
8533     return;
8534
8535   for (i = 0; i < NUM_DIRECTIONS; i++)
8536   {
8537     x = ax + xy[i][0];
8538     y = ay + xy[i][1];
8539
8540     if (!IN_LEV_FIELD(x, y))
8541       continue;
8542
8543     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8544          Feld[x][y] == EL_BD_AMOEBA ||
8545          Feld[x][y] == EL_AMOEBA_DEAD) &&
8546         AmoebaNr[x][y] != new_group_nr)
8547     {
8548       int old_group_nr = AmoebaNr[x][y];
8549
8550       if (old_group_nr == 0)
8551         return;
8552
8553       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8554       AmoebaCnt[old_group_nr] = 0;
8555       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8556       AmoebaCnt2[old_group_nr] = 0;
8557
8558       SCAN_PLAYFIELD(xx, yy)
8559       {
8560         if (AmoebaNr[xx][yy] == old_group_nr)
8561           AmoebaNr[xx][yy] = new_group_nr;
8562       }
8563     }
8564   }
8565 }
8566
8567 void AmoebeUmwandeln(int ax, int ay)
8568 {
8569   int i, x, y;
8570
8571   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8572   {
8573     int group_nr = AmoebaNr[ax][ay];
8574
8575 #ifdef DEBUG
8576     if (group_nr == 0)
8577     {
8578       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8579       printf("AmoebeUmwandeln(): This should never happen!\n");
8580       return;
8581     }
8582 #endif
8583
8584     SCAN_PLAYFIELD(x, y)
8585     {
8586       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8587       {
8588         AmoebaNr[x][y] = 0;
8589         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8590       }
8591     }
8592
8593     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8594                             SND_AMOEBA_TURNING_TO_GEM :
8595                             SND_AMOEBA_TURNING_TO_ROCK));
8596     Bang(ax, ay);
8597   }
8598   else
8599   {
8600     static int xy[4][2] =
8601     {
8602       { 0, -1 },
8603       { -1, 0 },
8604       { +1, 0 },
8605       { 0, +1 }
8606     };
8607
8608     for (i = 0; i < NUM_DIRECTIONS; i++)
8609     {
8610       x = ax + xy[i][0];
8611       y = ay + xy[i][1];
8612
8613       if (!IN_LEV_FIELD(x, y))
8614         continue;
8615
8616       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8617       {
8618         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8619                               SND_AMOEBA_TURNING_TO_GEM :
8620                               SND_AMOEBA_TURNING_TO_ROCK));
8621         Bang(x, y);
8622       }
8623     }
8624   }
8625 }
8626
8627 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8628 {
8629   int x, y;
8630   int group_nr = AmoebaNr[ax][ay];
8631   boolean done = FALSE;
8632
8633 #ifdef DEBUG
8634   if (group_nr == 0)
8635   {
8636     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8637     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8638     return;
8639   }
8640 #endif
8641
8642   SCAN_PLAYFIELD(x, y)
8643   {
8644     if (AmoebaNr[x][y] == group_nr &&
8645         (Feld[x][y] == EL_AMOEBA_DEAD ||
8646          Feld[x][y] == EL_BD_AMOEBA ||
8647          Feld[x][y] == EL_AMOEBA_GROWING))
8648     {
8649       AmoebaNr[x][y] = 0;
8650       Feld[x][y] = new_element;
8651       InitField(x, y, FALSE);
8652       TEST_DrawLevelField(x, y);
8653       done = TRUE;
8654     }
8655   }
8656
8657   if (done)
8658     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8659                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8660                             SND_BD_AMOEBA_TURNING_TO_GEM));
8661 }
8662
8663 static void AmoebeWaechst(int x, int y)
8664 {
8665   static unsigned int sound_delay = 0;
8666   static unsigned int sound_delay_value = 0;
8667
8668   if (!MovDelay[x][y])          // start new growing cycle
8669   {
8670     MovDelay[x][y] = 7;
8671
8672     if (DelayReached(&sound_delay, sound_delay_value))
8673     {
8674       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8675       sound_delay_value = 30;
8676     }
8677   }
8678
8679   if (MovDelay[x][y])           // wait some time before growing bigger
8680   {
8681     MovDelay[x][y]--;
8682     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8683     {
8684       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8685                                            6 - MovDelay[x][y]);
8686
8687       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8688     }
8689
8690     if (!MovDelay[x][y])
8691     {
8692       Feld[x][y] = Store[x][y];
8693       Store[x][y] = 0;
8694       TEST_DrawLevelField(x, y);
8695     }
8696   }
8697 }
8698
8699 static void AmoebaDisappearing(int x, int y)
8700 {
8701   static unsigned int sound_delay = 0;
8702   static unsigned int sound_delay_value = 0;
8703
8704   if (!MovDelay[x][y])          // start new shrinking cycle
8705   {
8706     MovDelay[x][y] = 7;
8707
8708     if (DelayReached(&sound_delay, sound_delay_value))
8709       sound_delay_value = 30;
8710   }
8711
8712   if (MovDelay[x][y])           // wait some time before shrinking
8713   {
8714     MovDelay[x][y]--;
8715     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8716     {
8717       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8718                                            6 - MovDelay[x][y]);
8719
8720       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8721     }
8722
8723     if (!MovDelay[x][y])
8724     {
8725       Feld[x][y] = EL_EMPTY;
8726       TEST_DrawLevelField(x, y);
8727
8728       // don't let mole enter this field in this cycle;
8729       // (give priority to objects falling to this field from above)
8730       Stop[x][y] = TRUE;
8731     }
8732   }
8733 }
8734
8735 static void AmoebeAbleger(int ax, int ay)
8736 {
8737   int i;
8738   int element = Feld[ax][ay];
8739   int graphic = el2img(element);
8740   int newax = ax, neway = ay;
8741   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8742   static int xy[4][2] =
8743   {
8744     { 0, -1 },
8745     { -1, 0 },
8746     { +1, 0 },
8747     { 0, +1 }
8748   };
8749
8750   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8751   {
8752     Feld[ax][ay] = EL_AMOEBA_DEAD;
8753     TEST_DrawLevelField(ax, ay);
8754     return;
8755   }
8756
8757   if (IS_ANIMATED(graphic))
8758     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8759
8760   if (!MovDelay[ax][ay])        // start making new amoeba field
8761     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8762
8763   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8764   {
8765     MovDelay[ax][ay]--;
8766     if (MovDelay[ax][ay])
8767       return;
8768   }
8769
8770   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8771   {
8772     int start = RND(4);
8773     int x = ax + xy[start][0];
8774     int y = ay + xy[start][1];
8775
8776     if (!IN_LEV_FIELD(x, y))
8777       return;
8778
8779     if (IS_FREE(x, y) ||
8780         CAN_GROW_INTO(Feld[x][y]) ||
8781         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8782         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8783     {
8784       newax = x;
8785       neway = y;
8786     }
8787
8788     if (newax == ax && neway == ay)
8789       return;
8790   }
8791   else                          // normal or "filled" (BD style) amoeba
8792   {
8793     int start = RND(4);
8794     boolean waiting_for_player = FALSE;
8795
8796     for (i = 0; i < NUM_DIRECTIONS; i++)
8797     {
8798       int j = (start + i) % 4;
8799       int x = ax + xy[j][0];
8800       int y = ay + xy[j][1];
8801
8802       if (!IN_LEV_FIELD(x, y))
8803         continue;
8804
8805       if (IS_FREE(x, y) ||
8806           CAN_GROW_INTO(Feld[x][y]) ||
8807           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8808           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8809       {
8810         newax = x;
8811         neway = y;
8812         break;
8813       }
8814       else if (IS_PLAYER(x, y))
8815         waiting_for_player = TRUE;
8816     }
8817
8818     if (newax == ax && neway == ay)             // amoeba cannot grow
8819     {
8820       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8821       {
8822         Feld[ax][ay] = EL_AMOEBA_DEAD;
8823         TEST_DrawLevelField(ax, ay);
8824         AmoebaCnt[AmoebaNr[ax][ay]]--;
8825
8826         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8827         {
8828           if (element == EL_AMOEBA_FULL)
8829             AmoebeUmwandeln(ax, ay);
8830           else if (element == EL_BD_AMOEBA)
8831             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8832         }
8833       }
8834       return;
8835     }
8836     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8837     {
8838       // amoeba gets larger by growing in some direction
8839
8840       int new_group_nr = AmoebaNr[ax][ay];
8841
8842 #ifdef DEBUG
8843   if (new_group_nr == 0)
8844   {
8845     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8846     printf("AmoebeAbleger(): This should never happen!\n");
8847     return;
8848   }
8849 #endif
8850
8851       AmoebaNr[newax][neway] = new_group_nr;
8852       AmoebaCnt[new_group_nr]++;
8853       AmoebaCnt2[new_group_nr]++;
8854
8855       // if amoeba touches other amoeba(s) after growing, unify them
8856       AmoebenVereinigen(newax, neway);
8857
8858       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8859       {
8860         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8861         return;
8862       }
8863     }
8864   }
8865
8866   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8867       (neway == lev_fieldy - 1 && newax != ax))
8868   {
8869     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8870     Store[newax][neway] = element;
8871   }
8872   else if (neway == ay || element == EL_EMC_DRIPPER)
8873   {
8874     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8875
8876     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8877   }
8878   else
8879   {
8880     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8881     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8882     Store[ax][ay] = EL_AMOEBA_DROP;
8883     ContinueMoving(ax, ay);
8884     return;
8885   }
8886
8887   TEST_DrawLevelField(newax, neway);
8888 }
8889
8890 static void Life(int ax, int ay)
8891 {
8892   int x1, y1, x2, y2;
8893   int life_time = 40;
8894   int element = Feld[ax][ay];
8895   int graphic = el2img(element);
8896   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8897                          level.biomaze);
8898   boolean changed = FALSE;
8899
8900   if (IS_ANIMATED(graphic))
8901     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8902
8903   if (Stop[ax][ay])
8904     return;
8905
8906   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8907     MovDelay[ax][ay] = life_time;
8908
8909   if (MovDelay[ax][ay])         // wait some time before next cycle
8910   {
8911     MovDelay[ax][ay]--;
8912     if (MovDelay[ax][ay])
8913       return;
8914   }
8915
8916   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8917   {
8918     int xx = ax+x1, yy = ay+y1;
8919     int old_element = Feld[xx][yy];
8920     int num_neighbours = 0;
8921
8922     if (!IN_LEV_FIELD(xx, yy))
8923       continue;
8924
8925     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8926     {
8927       int x = xx+x2, y = yy+y2;
8928
8929       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8930         continue;
8931
8932       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8933       boolean is_neighbour = FALSE;
8934
8935       if (level.use_life_bugs)
8936         is_neighbour =
8937           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8938            (IS_FREE(x, y)                             &&  Stop[x][y]));
8939       else
8940         is_neighbour =
8941           (Last[x][y] == element || is_player_cell);
8942
8943       if (is_neighbour)
8944         num_neighbours++;
8945     }
8946
8947     boolean is_free = FALSE;
8948
8949     if (level.use_life_bugs)
8950       is_free = (IS_FREE(xx, yy));
8951     else
8952       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8953
8954     if (xx == ax && yy == ay)           // field in the middle
8955     {
8956       if (num_neighbours < life_parameter[0] ||
8957           num_neighbours > life_parameter[1])
8958       {
8959         Feld[xx][yy] = EL_EMPTY;
8960         if (Feld[xx][yy] != old_element)
8961           TEST_DrawLevelField(xx, yy);
8962         Stop[xx][yy] = TRUE;
8963         changed = TRUE;
8964       }
8965     }
8966     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8967     {                                   // free border field
8968       if (num_neighbours >= life_parameter[2] &&
8969           num_neighbours <= life_parameter[3])
8970       {
8971         Feld[xx][yy] = element;
8972         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8973         if (Feld[xx][yy] != old_element)
8974           TEST_DrawLevelField(xx, yy);
8975         Stop[xx][yy] = TRUE;
8976         changed = TRUE;
8977       }
8978     }
8979   }
8980
8981   if (changed)
8982     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8983                    SND_GAME_OF_LIFE_GROWING);
8984 }
8985
8986 static void InitRobotWheel(int x, int y)
8987 {
8988   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8989 }
8990
8991 static void RunRobotWheel(int x, int y)
8992 {
8993   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8994 }
8995
8996 static void StopRobotWheel(int x, int y)
8997 {
8998   if (ZX == x && ZY == y)
8999   {
9000     ZX = ZY = -1;
9001
9002     game.robot_wheel_active = FALSE;
9003   }
9004 }
9005
9006 static void InitTimegateWheel(int x, int y)
9007 {
9008   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9009 }
9010
9011 static void RunTimegateWheel(int x, int y)
9012 {
9013   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9014 }
9015
9016 static void InitMagicBallDelay(int x, int y)
9017 {
9018   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9019 }
9020
9021 static void ActivateMagicBall(int bx, int by)
9022 {
9023   int x, y;
9024
9025   if (level.ball_random)
9026   {
9027     int pos_border = RND(8);    // select one of the eight border elements
9028     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9029     int xx = pos_content % 3;
9030     int yy = pos_content / 3;
9031
9032     x = bx - 1 + xx;
9033     y = by - 1 + yy;
9034
9035     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9036       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9037   }
9038   else
9039   {
9040     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9041     {
9042       int xx = x - bx + 1;
9043       int yy = y - by + 1;
9044
9045       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9046         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9047     }
9048   }
9049
9050   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9051 }
9052
9053 static void CheckExit(int x, int y)
9054 {
9055   if (local_player->gems_still_needed > 0 ||
9056       local_player->sokobanfields_still_needed > 0 ||
9057       local_player->lights_still_needed > 0)
9058   {
9059     int element = Feld[x][y];
9060     int graphic = el2img(element);
9061
9062     if (IS_ANIMATED(graphic))
9063       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9064
9065     return;
9066   }
9067
9068   if (AllPlayersGone)   // do not re-open exit door closed after last player
9069     return;
9070
9071   Feld[x][y] = EL_EXIT_OPENING;
9072
9073   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9074 }
9075
9076 static void CheckExitEM(int x, int y)
9077 {
9078   if (local_player->gems_still_needed > 0 ||
9079       local_player->sokobanfields_still_needed > 0 ||
9080       local_player->lights_still_needed > 0)
9081   {
9082     int element = Feld[x][y];
9083     int graphic = el2img(element);
9084
9085     if (IS_ANIMATED(graphic))
9086       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9087
9088     return;
9089   }
9090
9091   if (AllPlayersGone)   // do not re-open exit door closed after last player
9092     return;
9093
9094   Feld[x][y] = EL_EM_EXIT_OPENING;
9095
9096   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9097 }
9098
9099 static void CheckExitSteel(int x, int y)
9100 {
9101   if (local_player->gems_still_needed > 0 ||
9102       local_player->sokobanfields_still_needed > 0 ||
9103       local_player->lights_still_needed > 0)
9104   {
9105     int element = Feld[x][y];
9106     int graphic = el2img(element);
9107
9108     if (IS_ANIMATED(graphic))
9109       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9110
9111     return;
9112   }
9113
9114   if (AllPlayersGone)   // do not re-open exit door closed after last player
9115     return;
9116
9117   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9118
9119   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9120 }
9121
9122 static void CheckExitSteelEM(int x, int y)
9123 {
9124   if (local_player->gems_still_needed > 0 ||
9125       local_player->sokobanfields_still_needed > 0 ||
9126       local_player->lights_still_needed > 0)
9127   {
9128     int element = Feld[x][y];
9129     int graphic = el2img(element);
9130
9131     if (IS_ANIMATED(graphic))
9132       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9133
9134     return;
9135   }
9136
9137   if (AllPlayersGone)   // do not re-open exit door closed after last player
9138     return;
9139
9140   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9141
9142   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9143 }
9144
9145 static void CheckExitSP(int x, int y)
9146 {
9147   if (local_player->gems_still_needed > 0)
9148   {
9149     int element = Feld[x][y];
9150     int graphic = el2img(element);
9151
9152     if (IS_ANIMATED(graphic))
9153       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9154
9155     return;
9156   }
9157
9158   if (AllPlayersGone)   // do not re-open exit door closed after last player
9159     return;
9160
9161   Feld[x][y] = EL_SP_EXIT_OPENING;
9162
9163   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9164 }
9165
9166 static void CloseAllOpenTimegates(void)
9167 {
9168   int x, y;
9169
9170   SCAN_PLAYFIELD(x, y)
9171   {
9172     int element = Feld[x][y];
9173
9174     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9175     {
9176       Feld[x][y] = EL_TIMEGATE_CLOSING;
9177
9178       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9179     }
9180   }
9181 }
9182
9183 static void DrawTwinkleOnField(int x, int y)
9184 {
9185   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9186     return;
9187
9188   if (Feld[x][y] == EL_BD_DIAMOND)
9189     return;
9190
9191   if (MovDelay[x][y] == 0)      // next animation frame
9192     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9193
9194   if (MovDelay[x][y] != 0)      // wait some time before next frame
9195   {
9196     MovDelay[x][y]--;
9197
9198     DrawLevelElementAnimation(x, y, Feld[x][y]);
9199
9200     if (MovDelay[x][y] != 0)
9201     {
9202       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9203                                            10 - MovDelay[x][y]);
9204
9205       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9206     }
9207   }
9208 }
9209
9210 static void MauerWaechst(int x, int y)
9211 {
9212   int delay = 6;
9213
9214   if (!MovDelay[x][y])          // next animation frame
9215     MovDelay[x][y] = 3 * delay;
9216
9217   if (MovDelay[x][y])           // wait some time before next frame
9218   {
9219     MovDelay[x][y]--;
9220
9221     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9222     {
9223       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9224       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9225
9226       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9227     }
9228
9229     if (!MovDelay[x][y])
9230     {
9231       if (MovDir[x][y] == MV_LEFT)
9232       {
9233         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9234           TEST_DrawLevelField(x - 1, y);
9235       }
9236       else if (MovDir[x][y] == MV_RIGHT)
9237       {
9238         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9239           TEST_DrawLevelField(x + 1, y);
9240       }
9241       else if (MovDir[x][y] == MV_UP)
9242       {
9243         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9244           TEST_DrawLevelField(x, y - 1);
9245       }
9246       else
9247       {
9248         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9249           TEST_DrawLevelField(x, y + 1);
9250       }
9251
9252       Feld[x][y] = Store[x][y];
9253       Store[x][y] = 0;
9254       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9255       TEST_DrawLevelField(x, y);
9256     }
9257   }
9258 }
9259
9260 static void MauerAbleger(int ax, int ay)
9261 {
9262   int element = Feld[ax][ay];
9263   int graphic = el2img(element);
9264   boolean oben_frei = FALSE, unten_frei = FALSE;
9265   boolean links_frei = FALSE, rechts_frei = FALSE;
9266   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9267   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9268   boolean new_wall = FALSE;
9269
9270   if (IS_ANIMATED(graphic))
9271     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9272
9273   if (!MovDelay[ax][ay])        // start building new wall
9274     MovDelay[ax][ay] = 6;
9275
9276   if (MovDelay[ax][ay])         // wait some time before building new wall
9277   {
9278     MovDelay[ax][ay]--;
9279     if (MovDelay[ax][ay])
9280       return;
9281   }
9282
9283   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9284     oben_frei = TRUE;
9285   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9286     unten_frei = TRUE;
9287   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9288     links_frei = TRUE;
9289   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9290     rechts_frei = TRUE;
9291
9292   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9293       element == EL_EXPANDABLE_WALL_ANY)
9294   {
9295     if (oben_frei)
9296     {
9297       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9298       Store[ax][ay-1] = element;
9299       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9300       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9301         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9302                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9303       new_wall = TRUE;
9304     }
9305     if (unten_frei)
9306     {
9307       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9308       Store[ax][ay+1] = element;
9309       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9310       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9311         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9312                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9313       new_wall = TRUE;
9314     }
9315   }
9316
9317   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9318       element == EL_EXPANDABLE_WALL_ANY ||
9319       element == EL_EXPANDABLE_WALL ||
9320       element == EL_BD_EXPANDABLE_WALL)
9321   {
9322     if (links_frei)
9323     {
9324       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9325       Store[ax-1][ay] = element;
9326       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9327       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9328         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9329                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9330       new_wall = TRUE;
9331     }
9332
9333     if (rechts_frei)
9334     {
9335       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9336       Store[ax+1][ay] = element;
9337       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9338       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9339         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9340                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9341       new_wall = TRUE;
9342     }
9343   }
9344
9345   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9346     TEST_DrawLevelField(ax, ay);
9347
9348   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9349     oben_massiv = TRUE;
9350   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9351     unten_massiv = TRUE;
9352   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9353     links_massiv = TRUE;
9354   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9355     rechts_massiv = TRUE;
9356
9357   if (((oben_massiv && unten_massiv) ||
9358        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9359        element == EL_EXPANDABLE_WALL) &&
9360       ((links_massiv && rechts_massiv) ||
9361        element == EL_EXPANDABLE_WALL_VERTICAL))
9362     Feld[ax][ay] = EL_WALL;
9363
9364   if (new_wall)
9365     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9366 }
9367
9368 static void MauerAblegerStahl(int ax, int ay)
9369 {
9370   int element = Feld[ax][ay];
9371   int graphic = el2img(element);
9372   boolean oben_frei = FALSE, unten_frei = FALSE;
9373   boolean links_frei = FALSE, rechts_frei = FALSE;
9374   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9375   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9376   boolean new_wall = FALSE;
9377
9378   if (IS_ANIMATED(graphic))
9379     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9380
9381   if (!MovDelay[ax][ay])        // start building new wall
9382     MovDelay[ax][ay] = 6;
9383
9384   if (MovDelay[ax][ay])         // wait some time before building new wall
9385   {
9386     MovDelay[ax][ay]--;
9387     if (MovDelay[ax][ay])
9388       return;
9389   }
9390
9391   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9392     oben_frei = TRUE;
9393   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9394     unten_frei = TRUE;
9395   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9396     links_frei = TRUE;
9397   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9398     rechts_frei = TRUE;
9399
9400   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9401       element == EL_EXPANDABLE_STEELWALL_ANY)
9402   {
9403     if (oben_frei)
9404     {
9405       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9406       Store[ax][ay-1] = element;
9407       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9408       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9409         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9410                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9411       new_wall = TRUE;
9412     }
9413     if (unten_frei)
9414     {
9415       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9416       Store[ax][ay+1] = element;
9417       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9418       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9419         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9420                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9421       new_wall = TRUE;
9422     }
9423   }
9424
9425   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9426       element == EL_EXPANDABLE_STEELWALL_ANY)
9427   {
9428     if (links_frei)
9429     {
9430       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9431       Store[ax-1][ay] = element;
9432       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9433       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9434         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9435                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9436       new_wall = TRUE;
9437     }
9438
9439     if (rechts_frei)
9440     {
9441       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9442       Store[ax+1][ay] = element;
9443       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9444       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9445         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9446                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9447       new_wall = TRUE;
9448     }
9449   }
9450
9451   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9452     oben_massiv = TRUE;
9453   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9454     unten_massiv = TRUE;
9455   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9456     links_massiv = TRUE;
9457   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9458     rechts_massiv = TRUE;
9459
9460   if (((oben_massiv && unten_massiv) ||
9461        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9462       ((links_massiv && rechts_massiv) ||
9463        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9464     Feld[ax][ay] = EL_STEELWALL;
9465
9466   if (new_wall)
9467     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9468 }
9469
9470 static void CheckForDragon(int x, int y)
9471 {
9472   int i, j;
9473   boolean dragon_found = FALSE;
9474   static int xy[4][2] =
9475   {
9476     { 0, -1 },
9477     { -1, 0 },
9478     { +1, 0 },
9479     { 0, +1 }
9480   };
9481
9482   for (i = 0; i < NUM_DIRECTIONS; i++)
9483   {
9484     for (j = 0; j < 4; j++)
9485     {
9486       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9487
9488       if (IN_LEV_FIELD(xx, yy) &&
9489           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9490       {
9491         if (Feld[xx][yy] == EL_DRAGON)
9492           dragon_found = TRUE;
9493       }
9494       else
9495         break;
9496     }
9497   }
9498
9499   if (!dragon_found)
9500   {
9501     for (i = 0; i < NUM_DIRECTIONS; i++)
9502     {
9503       for (j = 0; j < 3; j++)
9504       {
9505         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9506   
9507         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9508         {
9509           Feld[xx][yy] = EL_EMPTY;
9510           TEST_DrawLevelField(xx, yy);
9511         }
9512         else
9513           break;
9514       }
9515     }
9516   }
9517 }
9518
9519 static void InitBuggyBase(int x, int y)
9520 {
9521   int element = Feld[x][y];
9522   int activating_delay = FRAMES_PER_SECOND / 4;
9523
9524   ChangeDelay[x][y] =
9525     (element == EL_SP_BUGGY_BASE ?
9526      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9527      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9528      activating_delay :
9529      element == EL_SP_BUGGY_BASE_ACTIVE ?
9530      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9531 }
9532
9533 static void WarnBuggyBase(int x, int y)
9534 {
9535   int i;
9536   static int xy[4][2] =
9537   {
9538     { 0, -1 },
9539     { -1, 0 },
9540     { +1, 0 },
9541     { 0, +1 }
9542   };
9543
9544   for (i = 0; i < NUM_DIRECTIONS; i++)
9545   {
9546     int xx = x + xy[i][0];
9547     int yy = y + xy[i][1];
9548
9549     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9550     {
9551       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9552
9553       break;
9554     }
9555   }
9556 }
9557
9558 static void InitTrap(int x, int y)
9559 {
9560   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9561 }
9562
9563 static void ActivateTrap(int x, int y)
9564 {
9565   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9566 }
9567
9568 static void ChangeActiveTrap(int x, int y)
9569 {
9570   int graphic = IMG_TRAP_ACTIVE;
9571
9572   // if new animation frame was drawn, correct crumbled sand border
9573   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9574     TEST_DrawLevelFieldCrumbled(x, y);
9575 }
9576
9577 static int getSpecialActionElement(int element, int number, int base_element)
9578 {
9579   return (element != EL_EMPTY ? element :
9580           number != -1 ? base_element + number - 1 :
9581           EL_EMPTY);
9582 }
9583
9584 static int getModifiedActionNumber(int value_old, int operator, int operand,
9585                                    int value_min, int value_max)
9586 {
9587   int value_new = (operator == CA_MODE_SET      ? operand :
9588                    operator == CA_MODE_ADD      ? value_old + operand :
9589                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9590                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9591                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9592                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9593                    value_old);
9594
9595   return (value_new < value_min ? value_min :
9596           value_new > value_max ? value_max :
9597           value_new);
9598 }
9599
9600 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9601 {
9602   struct ElementInfo *ei = &element_info[element];
9603   struct ElementChangeInfo *change = &ei->change_page[page];
9604   int target_element = change->target_element;
9605   int action_type = change->action_type;
9606   int action_mode = change->action_mode;
9607   int action_arg = change->action_arg;
9608   int action_element = change->action_element;
9609   int i;
9610
9611   if (!change->has_action)
9612     return;
9613
9614   // ---------- determine action paramater values -----------------------------
9615
9616   int level_time_value =
9617     (level.time > 0 ? TimeLeft :
9618      TimePlayed);
9619
9620   int action_arg_element_raw =
9621     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9622      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9623      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9624      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9625      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9626      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9627      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9628      EL_EMPTY);
9629   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9630
9631   int action_arg_direction =
9632     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9633      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9634      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9635      change->actual_trigger_side :
9636      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9637      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9638      MV_NONE);
9639
9640   int action_arg_number_min =
9641     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9642      CA_ARG_MIN);
9643
9644   int action_arg_number_max =
9645     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9646      action_type == CA_SET_LEVEL_GEMS ? 999 :
9647      action_type == CA_SET_LEVEL_TIME ? 9999 :
9648      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9649      action_type == CA_SET_CE_VALUE ? 9999 :
9650      action_type == CA_SET_CE_SCORE ? 9999 :
9651      CA_ARG_MAX);
9652
9653   int action_arg_number_reset =
9654     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9655      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9656      action_type == CA_SET_LEVEL_TIME ? level.time :
9657      action_type == CA_SET_LEVEL_SCORE ? 0 :
9658      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9659      action_type == CA_SET_CE_SCORE ? 0 :
9660      0);
9661
9662   int action_arg_number =
9663     (action_arg <= CA_ARG_MAX ? action_arg :
9664      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9665      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9666      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9667      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9668      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9669      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9670      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9671      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9672      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9673      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9674      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9675      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9676      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9677      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9678      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9679      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9680      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9681      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9682      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9683      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9684      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9685      -1);
9686
9687   int action_arg_number_old =
9688     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9689      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9690      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9691      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9692      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9693      0);
9694
9695   int action_arg_number_new =
9696     getModifiedActionNumber(action_arg_number_old,
9697                             action_mode, action_arg_number,
9698                             action_arg_number_min, action_arg_number_max);
9699
9700   int trigger_player_bits =
9701     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9702      change->actual_trigger_player_bits : change->trigger_player);
9703
9704   int action_arg_player_bits =
9705     (action_arg >= CA_ARG_PLAYER_1 &&
9706      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9707      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9708      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9709      PLAYER_BITS_ANY);
9710
9711   // ---------- execute action  -----------------------------------------------
9712
9713   switch (action_type)
9714   {
9715     case CA_NO_ACTION:
9716     {
9717       return;
9718     }
9719
9720     // ---------- level actions  ----------------------------------------------
9721
9722     case CA_RESTART_LEVEL:
9723     {
9724       game.restart_level = TRUE;
9725
9726       break;
9727     }
9728
9729     case CA_SHOW_ENVELOPE:
9730     {
9731       int element = getSpecialActionElement(action_arg_element,
9732                                             action_arg_number, EL_ENVELOPE_1);
9733
9734       if (IS_ENVELOPE(element))
9735         local_player->show_envelope = element;
9736
9737       break;
9738     }
9739
9740     case CA_SET_LEVEL_TIME:
9741     {
9742       if (level.time > 0)       // only modify limited time value
9743       {
9744         TimeLeft = action_arg_number_new;
9745
9746         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9747
9748         DisplayGameControlValues();
9749
9750         if (!TimeLeft && setup.time_limit)
9751           for (i = 0; i < MAX_PLAYERS; i++)
9752             KillPlayer(&stored_player[i]);
9753       }
9754
9755       break;
9756     }
9757
9758     case CA_SET_LEVEL_SCORE:
9759     {
9760       local_player->score = action_arg_number_new;
9761
9762       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9763
9764       DisplayGameControlValues();
9765
9766       break;
9767     }
9768
9769     case CA_SET_LEVEL_GEMS:
9770     {
9771       local_player->gems_still_needed = action_arg_number_new;
9772
9773       game.snapshot.collected_item = TRUE;
9774
9775       game_panel_controls[GAME_PANEL_GEMS].value =
9776         local_player->gems_still_needed;
9777
9778       DisplayGameControlValues();
9779
9780       break;
9781     }
9782
9783     case CA_SET_LEVEL_WIND:
9784     {
9785       game.wind_direction = action_arg_direction;
9786
9787       break;
9788     }
9789
9790     case CA_SET_LEVEL_RANDOM_SEED:
9791     {
9792       // ensure that setting a new random seed while playing is predictable
9793       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9794
9795       break;
9796     }
9797
9798     // ---------- player actions  ---------------------------------------------
9799
9800     case CA_MOVE_PLAYER:
9801     {
9802       // automatically move to the next field in specified direction
9803       for (i = 0; i < MAX_PLAYERS; i++)
9804         if (trigger_player_bits & (1 << i))
9805           stored_player[i].programmed_action = action_arg_direction;
9806
9807       break;
9808     }
9809
9810     case CA_EXIT_PLAYER:
9811     {
9812       for (i = 0; i < MAX_PLAYERS; i++)
9813         if (action_arg_player_bits & (1 << i))
9814           ExitPlayer(&stored_player[i]);
9815
9816       if (AllPlayersGone)
9817         PlayerWins(local_player);
9818
9819       break;
9820     }
9821
9822     case CA_KILL_PLAYER:
9823     {
9824       for (i = 0; i < MAX_PLAYERS; i++)
9825         if (action_arg_player_bits & (1 << i))
9826           KillPlayer(&stored_player[i]);
9827
9828       break;
9829     }
9830
9831     case CA_SET_PLAYER_KEYS:
9832     {
9833       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9834       int element = getSpecialActionElement(action_arg_element,
9835                                             action_arg_number, EL_KEY_1);
9836
9837       if (IS_KEY(element))
9838       {
9839         for (i = 0; i < MAX_PLAYERS; i++)
9840         {
9841           if (trigger_player_bits & (1 << i))
9842           {
9843             stored_player[i].key[KEY_NR(element)] = key_state;
9844
9845             DrawGameDoorValues();
9846           }
9847         }
9848       }
9849
9850       break;
9851     }
9852
9853     case CA_SET_PLAYER_SPEED:
9854     {
9855       for (i = 0; i < MAX_PLAYERS; i++)
9856       {
9857         if (trigger_player_bits & (1 << i))
9858         {
9859           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9860
9861           if (action_arg == CA_ARG_SPEED_FASTER &&
9862               stored_player[i].cannot_move)
9863           {
9864             action_arg_number = STEPSIZE_VERY_SLOW;
9865           }
9866           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9867                    action_arg == CA_ARG_SPEED_FASTER)
9868           {
9869             action_arg_number = 2;
9870             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9871                            CA_MODE_MULTIPLY);
9872           }
9873           else if (action_arg == CA_ARG_NUMBER_RESET)
9874           {
9875             action_arg_number = level.initial_player_stepsize[i];
9876           }
9877
9878           move_stepsize =
9879             getModifiedActionNumber(move_stepsize,
9880                                     action_mode,
9881                                     action_arg_number,
9882                                     action_arg_number_min,
9883                                     action_arg_number_max);
9884
9885           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9886         }
9887       }
9888
9889       break;
9890     }
9891
9892     case CA_SET_PLAYER_SHIELD:
9893     {
9894       for (i = 0; i < MAX_PLAYERS; i++)
9895       {
9896         if (trigger_player_bits & (1 << i))
9897         {
9898           if (action_arg == CA_ARG_SHIELD_OFF)
9899           {
9900             stored_player[i].shield_normal_time_left = 0;
9901             stored_player[i].shield_deadly_time_left = 0;
9902           }
9903           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9904           {
9905             stored_player[i].shield_normal_time_left = 999999;
9906           }
9907           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9908           {
9909             stored_player[i].shield_normal_time_left = 999999;
9910             stored_player[i].shield_deadly_time_left = 999999;
9911           }
9912         }
9913       }
9914
9915       break;
9916     }
9917
9918     case CA_SET_PLAYER_GRAVITY:
9919     {
9920       for (i = 0; i < MAX_PLAYERS; i++)
9921       {
9922         if (trigger_player_bits & (1 << i))
9923         {
9924           stored_player[i].gravity =
9925             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9926              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9927              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9928              stored_player[i].gravity);
9929         }
9930       }
9931
9932       break;
9933     }
9934
9935     case CA_SET_PLAYER_ARTWORK:
9936     {
9937       for (i = 0; i < MAX_PLAYERS; i++)
9938       {
9939         if (trigger_player_bits & (1 << i))
9940         {
9941           int artwork_element = action_arg_element;
9942
9943           if (action_arg == CA_ARG_ELEMENT_RESET)
9944             artwork_element =
9945               (level.use_artwork_element[i] ? level.artwork_element[i] :
9946                stored_player[i].element_nr);
9947
9948           if (stored_player[i].artwork_element != artwork_element)
9949             stored_player[i].Frame = 0;
9950
9951           stored_player[i].artwork_element = artwork_element;
9952
9953           SetPlayerWaiting(&stored_player[i], FALSE);
9954
9955           // set number of special actions for bored and sleeping animation
9956           stored_player[i].num_special_action_bored =
9957             get_num_special_action(artwork_element,
9958                                    ACTION_BORING_1, ACTION_BORING_LAST);
9959           stored_player[i].num_special_action_sleeping =
9960             get_num_special_action(artwork_element,
9961                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9962         }
9963       }
9964
9965       break;
9966     }
9967
9968     case CA_SET_PLAYER_INVENTORY:
9969     {
9970       for (i = 0; i < MAX_PLAYERS; i++)
9971       {
9972         struct PlayerInfo *player = &stored_player[i];
9973         int j, k;
9974
9975         if (trigger_player_bits & (1 << i))
9976         {
9977           int inventory_element = action_arg_element;
9978
9979           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9980               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9981               action_arg == CA_ARG_ELEMENT_ACTION)
9982           {
9983             int element = inventory_element;
9984             int collect_count = element_info[element].collect_count_initial;
9985
9986             if (!IS_CUSTOM_ELEMENT(element))
9987               collect_count = 1;
9988
9989             if (collect_count == 0)
9990               player->inventory_infinite_element = element;
9991             else
9992               for (k = 0; k < collect_count; k++)
9993                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9994                   player->inventory_element[player->inventory_size++] =
9995                     element;
9996           }
9997           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9998                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9999                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10000           {
10001             if (player->inventory_infinite_element != EL_UNDEFINED &&
10002                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10003                                      action_arg_element_raw))
10004               player->inventory_infinite_element = EL_UNDEFINED;
10005
10006             for (k = 0, j = 0; j < player->inventory_size; j++)
10007             {
10008               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10009                                         action_arg_element_raw))
10010                 player->inventory_element[k++] = player->inventory_element[j];
10011             }
10012
10013             player->inventory_size = k;
10014           }
10015           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10016           {
10017             if (player->inventory_size > 0)
10018             {
10019               for (j = 0; j < player->inventory_size - 1; j++)
10020                 player->inventory_element[j] = player->inventory_element[j + 1];
10021
10022               player->inventory_size--;
10023             }
10024           }
10025           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10026           {
10027             if (player->inventory_size > 0)
10028               player->inventory_size--;
10029           }
10030           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10031           {
10032             player->inventory_infinite_element = EL_UNDEFINED;
10033             player->inventory_size = 0;
10034           }
10035           else if (action_arg == CA_ARG_INVENTORY_RESET)
10036           {
10037             player->inventory_infinite_element = EL_UNDEFINED;
10038             player->inventory_size = 0;
10039
10040             if (level.use_initial_inventory[i])
10041             {
10042               for (j = 0; j < level.initial_inventory_size[i]; j++)
10043               {
10044                 int element = level.initial_inventory_content[i][j];
10045                 int collect_count = element_info[element].collect_count_initial;
10046
10047                 if (!IS_CUSTOM_ELEMENT(element))
10048                   collect_count = 1;
10049
10050                 if (collect_count == 0)
10051                   player->inventory_infinite_element = element;
10052                 else
10053                   for (k = 0; k < collect_count; k++)
10054                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10055                       player->inventory_element[player->inventory_size++] =
10056                         element;
10057               }
10058             }
10059           }
10060         }
10061       }
10062
10063       break;
10064     }
10065
10066     // ---------- CE actions  -------------------------------------------------
10067
10068     case CA_SET_CE_VALUE:
10069     {
10070       int last_ce_value = CustomValue[x][y];
10071
10072       CustomValue[x][y] = action_arg_number_new;
10073
10074       if (CustomValue[x][y] != last_ce_value)
10075       {
10076         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10077         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10078
10079         if (CustomValue[x][y] == 0)
10080         {
10081           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10082           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10083         }
10084       }
10085
10086       break;
10087     }
10088
10089     case CA_SET_CE_SCORE:
10090     {
10091       int last_ce_score = ei->collect_score;
10092
10093       ei->collect_score = action_arg_number_new;
10094
10095       if (ei->collect_score != last_ce_score)
10096       {
10097         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10098         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10099
10100         if (ei->collect_score == 0)
10101         {
10102           int xx, yy;
10103
10104           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10105           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10106
10107           /*
10108             This is a very special case that seems to be a mixture between
10109             CheckElementChange() and CheckTriggeredElementChange(): while
10110             the first one only affects single elements that are triggered
10111             directly, the second one affects multiple elements in the playfield
10112             that are triggered indirectly by another element. This is a third
10113             case: Changing the CE score always affects multiple identical CEs,
10114             so every affected CE must be checked, not only the single CE for
10115             which the CE score was changed in the first place (as every instance
10116             of that CE shares the same CE score, and therefore also can change)!
10117           */
10118           SCAN_PLAYFIELD(xx, yy)
10119           {
10120             if (Feld[xx][yy] == element)
10121               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10122                                  CE_SCORE_GETS_ZERO);
10123           }
10124         }
10125       }
10126
10127       break;
10128     }
10129
10130     case CA_SET_CE_ARTWORK:
10131     {
10132       int artwork_element = action_arg_element;
10133       boolean reset_frame = FALSE;
10134       int xx, yy;
10135
10136       if (action_arg == CA_ARG_ELEMENT_RESET)
10137         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10138                            element);
10139
10140       if (ei->gfx_element != artwork_element)
10141         reset_frame = TRUE;
10142
10143       ei->gfx_element = artwork_element;
10144
10145       SCAN_PLAYFIELD(xx, yy)
10146       {
10147         if (Feld[xx][yy] == element)
10148         {
10149           if (reset_frame)
10150           {
10151             ResetGfxAnimation(xx, yy);
10152             ResetRandomAnimationValue(xx, yy);
10153           }
10154
10155           TEST_DrawLevelField(xx, yy);
10156         }
10157       }
10158
10159       break;
10160     }
10161
10162     // ---------- engine actions  ---------------------------------------------
10163
10164     case CA_SET_ENGINE_SCAN_MODE:
10165     {
10166       InitPlayfieldScanMode(action_arg);
10167
10168       break;
10169     }
10170
10171     default:
10172       break;
10173   }
10174 }
10175
10176 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10177 {
10178   int old_element = Feld[x][y];
10179   int new_element = GetElementFromGroupElement(element);
10180   int previous_move_direction = MovDir[x][y];
10181   int last_ce_value = CustomValue[x][y];
10182   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10183   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10184   boolean add_player_onto_element = (new_element_is_player &&
10185                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10186                                      IS_WALKABLE(old_element));
10187
10188   if (!add_player_onto_element)
10189   {
10190     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10191       RemoveMovingField(x, y);
10192     else
10193       RemoveField(x, y);
10194
10195     Feld[x][y] = new_element;
10196
10197     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10198       MovDir[x][y] = previous_move_direction;
10199
10200     if (element_info[new_element].use_last_ce_value)
10201       CustomValue[x][y] = last_ce_value;
10202
10203     InitField_WithBug1(x, y, FALSE);
10204
10205     new_element = Feld[x][y];   // element may have changed
10206
10207     ResetGfxAnimation(x, y);
10208     ResetRandomAnimationValue(x, y);
10209
10210     TEST_DrawLevelField(x, y);
10211
10212     if (GFX_CRUMBLED(new_element))
10213       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10214   }
10215
10216   // check if element under the player changes from accessible to unaccessible
10217   // (needed for special case of dropping element which then changes)
10218   // (must be checked after creating new element for walkable group elements)
10219   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10220       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10221   {
10222     Bang(x, y);
10223
10224     return;
10225   }
10226
10227   // "ChangeCount" not set yet to allow "entered by player" change one time
10228   if (new_element_is_player)
10229     RelocatePlayer(x, y, new_element);
10230
10231   if (is_change)
10232     ChangeCount[x][y]++;        // count number of changes in the same frame
10233
10234   TestIfBadThingTouchesPlayer(x, y);
10235   TestIfPlayerTouchesCustomElement(x, y);
10236   TestIfElementTouchesCustomElement(x, y);
10237 }
10238
10239 static void CreateField(int x, int y, int element)
10240 {
10241   CreateFieldExt(x, y, element, FALSE);
10242 }
10243
10244 static void CreateElementFromChange(int x, int y, int element)
10245 {
10246   element = GET_VALID_RUNTIME_ELEMENT(element);
10247
10248   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10249   {
10250     int old_element = Feld[x][y];
10251
10252     // prevent changed element from moving in same engine frame
10253     // unless both old and new element can either fall or move
10254     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10255         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10256       Stop[x][y] = TRUE;
10257   }
10258
10259   CreateFieldExt(x, y, element, TRUE);
10260 }
10261
10262 static boolean ChangeElement(int x, int y, int element, int page)
10263 {
10264   struct ElementInfo *ei = &element_info[element];
10265   struct ElementChangeInfo *change = &ei->change_page[page];
10266   int ce_value = CustomValue[x][y];
10267   int ce_score = ei->collect_score;
10268   int target_element;
10269   int old_element = Feld[x][y];
10270
10271   // always use default change event to prevent running into a loop
10272   if (ChangeEvent[x][y] == -1)
10273     ChangeEvent[x][y] = CE_DELAY;
10274
10275   if (ChangeEvent[x][y] == CE_DELAY)
10276   {
10277     // reset actual trigger element, trigger player and action element
10278     change->actual_trigger_element = EL_EMPTY;
10279     change->actual_trigger_player = EL_EMPTY;
10280     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10281     change->actual_trigger_side = CH_SIDE_NONE;
10282     change->actual_trigger_ce_value = 0;
10283     change->actual_trigger_ce_score = 0;
10284   }
10285
10286   // do not change elements more than a specified maximum number of changes
10287   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10288     return FALSE;
10289
10290   ChangeCount[x][y]++;          // count number of changes in the same frame
10291
10292   if (change->explode)
10293   {
10294     Bang(x, y);
10295
10296     return TRUE;
10297   }
10298
10299   if (change->use_target_content)
10300   {
10301     boolean complete_replace = TRUE;
10302     boolean can_replace[3][3];
10303     int xx, yy;
10304
10305     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10306     {
10307       boolean is_empty;
10308       boolean is_walkable;
10309       boolean is_diggable;
10310       boolean is_collectible;
10311       boolean is_removable;
10312       boolean is_destructible;
10313       int ex = x + xx - 1;
10314       int ey = y + yy - 1;
10315       int content_element = change->target_content.e[xx][yy];
10316       int e;
10317
10318       can_replace[xx][yy] = TRUE;
10319
10320       if (ex == x && ey == y)   // do not check changing element itself
10321         continue;
10322
10323       if (content_element == EL_EMPTY_SPACE)
10324       {
10325         can_replace[xx][yy] = FALSE;    // do not replace border with space
10326
10327         continue;
10328       }
10329
10330       if (!IN_LEV_FIELD(ex, ey))
10331       {
10332         can_replace[xx][yy] = FALSE;
10333         complete_replace = FALSE;
10334
10335         continue;
10336       }
10337
10338       e = Feld[ex][ey];
10339
10340       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10341         e = MovingOrBlocked2Element(ex, ey);
10342
10343       is_empty = (IS_FREE(ex, ey) ||
10344                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10345
10346       is_walkable     = (is_empty || IS_WALKABLE(e));
10347       is_diggable     = (is_empty || IS_DIGGABLE(e));
10348       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10349       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10350       is_removable    = (is_diggable || is_collectible);
10351
10352       can_replace[xx][yy] =
10353         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10354           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10355           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10356           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10357           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10358           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10359          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10360
10361       if (!can_replace[xx][yy])
10362         complete_replace = FALSE;
10363     }
10364
10365     if (!change->only_if_complete || complete_replace)
10366     {
10367       boolean something_has_changed = FALSE;
10368
10369       if (change->only_if_complete && change->use_random_replace &&
10370           RND(100) < change->random_percentage)
10371         return FALSE;
10372
10373       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10374       {
10375         int ex = x + xx - 1;
10376         int ey = y + yy - 1;
10377         int content_element;
10378
10379         if (can_replace[xx][yy] && (!change->use_random_replace ||
10380                                     RND(100) < change->random_percentage))
10381         {
10382           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10383             RemoveMovingField(ex, ey);
10384
10385           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10386
10387           content_element = change->target_content.e[xx][yy];
10388           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10389                                               ce_value, ce_score);
10390
10391           CreateElementFromChange(ex, ey, target_element);
10392
10393           something_has_changed = TRUE;
10394
10395           // for symmetry reasons, freeze newly created border elements
10396           if (ex != x || ey != y)
10397             Stop[ex][ey] = TRUE;        // no more moving in this frame
10398         }
10399       }
10400
10401       if (something_has_changed)
10402       {
10403         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10404         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10405       }
10406     }
10407   }
10408   else
10409   {
10410     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10411                                         ce_value, ce_score);
10412
10413     if (element == EL_DIAGONAL_GROWING ||
10414         element == EL_DIAGONAL_SHRINKING)
10415     {
10416       target_element = Store[x][y];
10417
10418       Store[x][y] = EL_EMPTY;
10419     }
10420
10421     CreateElementFromChange(x, y, target_element);
10422
10423     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10424     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10425   }
10426
10427   // this uses direct change before indirect change
10428   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10429
10430   return TRUE;
10431 }
10432
10433 static void HandleElementChange(int x, int y, int page)
10434 {
10435   int element = MovingOrBlocked2Element(x, y);
10436   struct ElementInfo *ei = &element_info[element];
10437   struct ElementChangeInfo *change = &ei->change_page[page];
10438   boolean handle_action_before_change = FALSE;
10439
10440 #ifdef DEBUG
10441   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10442       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10443   {
10444     printf("\n\n");
10445     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10446            x, y, element, element_info[element].token_name);
10447     printf("HandleElementChange(): This should never happen!\n");
10448     printf("\n\n");
10449   }
10450 #endif
10451
10452   // this can happen with classic bombs on walkable, changing elements
10453   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10454   {
10455     return;
10456   }
10457
10458   if (ChangeDelay[x][y] == 0)           // initialize element change
10459   {
10460     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10461
10462     if (change->can_change)
10463     {
10464       // !!! not clear why graphic animation should be reset at all here !!!
10465       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10466       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10467
10468       /*
10469         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10470
10471         When using an animation frame delay of 1 (this only happens with
10472         "sp_zonk.moving.left/right" in the classic graphics), the default
10473         (non-moving) animation shows wrong animation frames (while the
10474         moving animation, like "sp_zonk.moving.left/right", is correct,
10475         so this graphical bug never shows up with the classic graphics).
10476         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10477         be drawn instead of the correct frames 0,1,2,3. This is caused by
10478         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10479         an element change: First when the change delay ("ChangeDelay[][]")
10480         counter has reached zero after decrementing, then a second time in
10481         the next frame (after "GfxFrame[][]" was already incremented) when
10482         "ChangeDelay[][]" is reset to the initial delay value again.
10483
10484         This causes frame 0 to be drawn twice, while the last frame won't
10485         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10486
10487         As some animations may already be cleverly designed around this bug
10488         (at least the "Snake Bite" snake tail animation does this), it cannot
10489         simply be fixed here without breaking such existing animations.
10490         Unfortunately, it cannot easily be detected if a graphics set was
10491         designed "before" or "after" the bug was fixed. As a workaround,
10492         a new graphics set option "game.graphics_engine_version" was added
10493         to be able to specify the game's major release version for which the
10494         graphics set was designed, which can then be used to decide if the
10495         bugfix should be used (version 4 and above) or not (version 3 or
10496         below, or if no version was specified at all, as with old sets).
10497
10498         (The wrong/fixed animation frames can be tested with the test level set
10499         "test_gfxframe" and level "000", which contains a specially prepared
10500         custom element at level position (x/y) == (11/9) which uses the zonk
10501         animation mentioned above. Using "game.graphics_engine_version: 4"
10502         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10503         This can also be seen from the debug output for this test element.)
10504       */
10505
10506       // when a custom element is about to change (for example by change delay),
10507       // do not reset graphic animation when the custom element is moving
10508       if (game.graphics_engine_version < 4 &&
10509           !IS_MOVING(x, y))
10510       {
10511         ResetGfxAnimation(x, y);
10512         ResetRandomAnimationValue(x, y);
10513       }
10514
10515       if (change->pre_change_function)
10516         change->pre_change_function(x, y);
10517     }
10518   }
10519
10520   ChangeDelay[x][y]--;
10521
10522   if (ChangeDelay[x][y] != 0)           // continue element change
10523   {
10524     if (change->can_change)
10525     {
10526       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10527
10528       if (IS_ANIMATED(graphic))
10529         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10530
10531       if (change->change_function)
10532         change->change_function(x, y);
10533     }
10534   }
10535   else                                  // finish element change
10536   {
10537     if (ChangePage[x][y] != -1)         // remember page from delayed change
10538     {
10539       page = ChangePage[x][y];
10540       ChangePage[x][y] = -1;
10541
10542       change = &ei->change_page[page];
10543     }
10544
10545     if (IS_MOVING(x, y))                // never change a running system ;-)
10546     {
10547       ChangeDelay[x][y] = 1;            // try change after next move step
10548       ChangePage[x][y] = page;          // remember page to use for change
10549
10550       return;
10551     }
10552
10553     // special case: set new level random seed before changing element
10554     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10555       handle_action_before_change = TRUE;
10556
10557     if (change->has_action && handle_action_before_change)
10558       ExecuteCustomElementAction(x, y, element, page);
10559
10560     if (change->can_change)
10561     {
10562       if (ChangeElement(x, y, element, page))
10563       {
10564         if (change->post_change_function)
10565           change->post_change_function(x, y);
10566       }
10567     }
10568
10569     if (change->has_action && !handle_action_before_change)
10570       ExecuteCustomElementAction(x, y, element, page);
10571   }
10572 }
10573
10574 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10575                                               int trigger_element,
10576                                               int trigger_event,
10577                                               int trigger_player,
10578                                               int trigger_side,
10579                                               int trigger_page)
10580 {
10581   boolean change_done_any = FALSE;
10582   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10583   int i;
10584
10585   if (!(trigger_events[trigger_element][trigger_event]))
10586     return FALSE;
10587
10588   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10589
10590   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10591   {
10592     int element = EL_CUSTOM_START + i;
10593     boolean change_done = FALSE;
10594     int p;
10595
10596     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10597         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10598       continue;
10599
10600     for (p = 0; p < element_info[element].num_change_pages; p++)
10601     {
10602       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10603
10604       if (change->can_change_or_has_action &&
10605           change->has_event[trigger_event] &&
10606           change->trigger_side & trigger_side &&
10607           change->trigger_player & trigger_player &&
10608           change->trigger_page & trigger_page_bits &&
10609           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10610       {
10611         change->actual_trigger_element = trigger_element;
10612         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10613         change->actual_trigger_player_bits = trigger_player;
10614         change->actual_trigger_side = trigger_side;
10615         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10616         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10617
10618         if ((change->can_change && !change_done) || change->has_action)
10619         {
10620           int x, y;
10621
10622           SCAN_PLAYFIELD(x, y)
10623           {
10624             if (Feld[x][y] == element)
10625             {
10626               if (change->can_change && !change_done)
10627               {
10628                 // if element already changed in this frame, not only prevent
10629                 // another element change (checked in ChangeElement()), but
10630                 // also prevent additional element actions for this element
10631
10632                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10633                     !level.use_action_after_change_bug)
10634                   continue;
10635
10636                 ChangeDelay[x][y] = 1;
10637                 ChangeEvent[x][y] = trigger_event;
10638
10639                 HandleElementChange(x, y, p);
10640               }
10641               else if (change->has_action)
10642               {
10643                 // if element already changed in this frame, not only prevent
10644                 // another element change (checked in ChangeElement()), but
10645                 // also prevent additional element actions for this element
10646
10647                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10648                     !level.use_action_after_change_bug)
10649                   continue;
10650
10651                 ExecuteCustomElementAction(x, y, element, p);
10652                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10653               }
10654             }
10655           }
10656
10657           if (change->can_change)
10658           {
10659             change_done = TRUE;
10660             change_done_any = TRUE;
10661           }
10662         }
10663       }
10664     }
10665   }
10666
10667   RECURSION_LOOP_DETECTION_END();
10668
10669   return change_done_any;
10670 }
10671
10672 static boolean CheckElementChangeExt(int x, int y,
10673                                      int element,
10674                                      int trigger_element,
10675                                      int trigger_event,
10676                                      int trigger_player,
10677                                      int trigger_side)
10678 {
10679   boolean change_done = FALSE;
10680   int p;
10681
10682   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10683       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10684     return FALSE;
10685
10686   if (Feld[x][y] == EL_BLOCKED)
10687   {
10688     Blocked2Moving(x, y, &x, &y);
10689     element = Feld[x][y];
10690   }
10691
10692   // check if element has already changed or is about to change after moving
10693   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10694        Feld[x][y] != element) ||
10695
10696       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10697        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10698         ChangePage[x][y] != -1)))
10699     return FALSE;
10700
10701   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10702
10703   for (p = 0; p < element_info[element].num_change_pages; p++)
10704   {
10705     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10706
10707     /* check trigger element for all events where the element that is checked
10708        for changing interacts with a directly adjacent element -- this is
10709        different to element changes that affect other elements to change on the
10710        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10711     boolean check_trigger_element =
10712       (trigger_event == CE_TOUCHING_X ||
10713        trigger_event == CE_HITTING_X ||
10714        trigger_event == CE_HIT_BY_X ||
10715        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10716
10717     if (change->can_change_or_has_action &&
10718         change->has_event[trigger_event] &&
10719         change->trigger_side & trigger_side &&
10720         change->trigger_player & trigger_player &&
10721         (!check_trigger_element ||
10722          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10723     {
10724       change->actual_trigger_element = trigger_element;
10725       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10726       change->actual_trigger_player_bits = trigger_player;
10727       change->actual_trigger_side = trigger_side;
10728       change->actual_trigger_ce_value = CustomValue[x][y];
10729       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10730
10731       // special case: trigger element not at (x,y) position for some events
10732       if (check_trigger_element)
10733       {
10734         static struct
10735         {
10736           int dx, dy;
10737         } move_xy[] =
10738           {
10739             {  0,  0 },
10740             { -1,  0 },
10741             { +1,  0 },
10742             {  0,  0 },
10743             {  0, -1 },
10744             {  0,  0 }, { 0, 0 }, { 0, 0 },
10745             {  0, +1 }
10746           };
10747
10748         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10749         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10750
10751         change->actual_trigger_ce_value = CustomValue[xx][yy];
10752         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10753       }
10754
10755       if (change->can_change && !change_done)
10756       {
10757         ChangeDelay[x][y] = 1;
10758         ChangeEvent[x][y] = trigger_event;
10759
10760         HandleElementChange(x, y, p);
10761
10762         change_done = TRUE;
10763       }
10764       else if (change->has_action)
10765       {
10766         ExecuteCustomElementAction(x, y, element, p);
10767         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10768       }
10769     }
10770   }
10771
10772   RECURSION_LOOP_DETECTION_END();
10773
10774   return change_done;
10775 }
10776
10777 static void PlayPlayerSound(struct PlayerInfo *player)
10778 {
10779   int jx = player->jx, jy = player->jy;
10780   int sound_element = player->artwork_element;
10781   int last_action = player->last_action_waiting;
10782   int action = player->action_waiting;
10783
10784   if (player->is_waiting)
10785   {
10786     if (action != last_action)
10787       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10788     else
10789       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10790   }
10791   else
10792   {
10793     if (action != last_action)
10794       StopSound(element_info[sound_element].sound[last_action]);
10795
10796     if (last_action == ACTION_SLEEPING)
10797       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10798   }
10799 }
10800
10801 static void PlayAllPlayersSound(void)
10802 {
10803   int i;
10804
10805   for (i = 0; i < MAX_PLAYERS; i++)
10806     if (stored_player[i].active)
10807       PlayPlayerSound(&stored_player[i]);
10808 }
10809
10810 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10811 {
10812   boolean last_waiting = player->is_waiting;
10813   int move_dir = player->MovDir;
10814
10815   player->dir_waiting = move_dir;
10816   player->last_action_waiting = player->action_waiting;
10817
10818   if (is_waiting)
10819   {
10820     if (!last_waiting)          // not waiting -> waiting
10821     {
10822       player->is_waiting = TRUE;
10823
10824       player->frame_counter_bored =
10825         FrameCounter +
10826         game.player_boring_delay_fixed +
10827         GetSimpleRandom(game.player_boring_delay_random);
10828       player->frame_counter_sleeping =
10829         FrameCounter +
10830         game.player_sleeping_delay_fixed +
10831         GetSimpleRandom(game.player_sleeping_delay_random);
10832
10833       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10834     }
10835
10836     if (game.player_sleeping_delay_fixed +
10837         game.player_sleeping_delay_random > 0 &&
10838         player->anim_delay_counter == 0 &&
10839         player->post_delay_counter == 0 &&
10840         FrameCounter >= player->frame_counter_sleeping)
10841       player->is_sleeping = TRUE;
10842     else if (game.player_boring_delay_fixed +
10843              game.player_boring_delay_random > 0 &&
10844              FrameCounter >= player->frame_counter_bored)
10845       player->is_bored = TRUE;
10846
10847     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10848                               player->is_bored ? ACTION_BORING :
10849                               ACTION_WAITING);
10850
10851     if (player->is_sleeping && player->use_murphy)
10852     {
10853       // special case for sleeping Murphy when leaning against non-free tile
10854
10855       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10856           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10857            !IS_MOVING(player->jx - 1, player->jy)))
10858         move_dir = MV_LEFT;
10859       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10860                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10861                 !IS_MOVING(player->jx + 1, player->jy)))
10862         move_dir = MV_RIGHT;
10863       else
10864         player->is_sleeping = FALSE;
10865
10866       player->dir_waiting = move_dir;
10867     }
10868
10869     if (player->is_sleeping)
10870     {
10871       if (player->num_special_action_sleeping > 0)
10872       {
10873         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10874         {
10875           int last_special_action = player->special_action_sleeping;
10876           int num_special_action = player->num_special_action_sleeping;
10877           int special_action =
10878             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10879              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10880              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10881              last_special_action + 1 : ACTION_SLEEPING);
10882           int special_graphic =
10883             el_act_dir2img(player->artwork_element, special_action, move_dir);
10884
10885           player->anim_delay_counter =
10886             graphic_info[special_graphic].anim_delay_fixed +
10887             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10888           player->post_delay_counter =
10889             graphic_info[special_graphic].post_delay_fixed +
10890             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10891
10892           player->special_action_sleeping = special_action;
10893         }
10894
10895         if (player->anim_delay_counter > 0)
10896         {
10897           player->action_waiting = player->special_action_sleeping;
10898           player->anim_delay_counter--;
10899         }
10900         else if (player->post_delay_counter > 0)
10901         {
10902           player->post_delay_counter--;
10903         }
10904       }
10905     }
10906     else if (player->is_bored)
10907     {
10908       if (player->num_special_action_bored > 0)
10909       {
10910         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10911         {
10912           int special_action =
10913             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10914           int special_graphic =
10915             el_act_dir2img(player->artwork_element, special_action, move_dir);
10916
10917           player->anim_delay_counter =
10918             graphic_info[special_graphic].anim_delay_fixed +
10919             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10920           player->post_delay_counter =
10921             graphic_info[special_graphic].post_delay_fixed +
10922             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10923
10924           player->special_action_bored = special_action;
10925         }
10926
10927         if (player->anim_delay_counter > 0)
10928         {
10929           player->action_waiting = player->special_action_bored;
10930           player->anim_delay_counter--;
10931         }
10932         else if (player->post_delay_counter > 0)
10933         {
10934           player->post_delay_counter--;
10935         }
10936       }
10937     }
10938   }
10939   else if (last_waiting)        // waiting -> not waiting
10940   {
10941     player->is_waiting = FALSE;
10942     player->is_bored = FALSE;
10943     player->is_sleeping = FALSE;
10944
10945     player->frame_counter_bored = -1;
10946     player->frame_counter_sleeping = -1;
10947
10948     player->anim_delay_counter = 0;
10949     player->post_delay_counter = 0;
10950
10951     player->dir_waiting = player->MovDir;
10952     player->action_waiting = ACTION_DEFAULT;
10953
10954     player->special_action_bored = ACTION_DEFAULT;
10955     player->special_action_sleeping = ACTION_DEFAULT;
10956   }
10957 }
10958
10959 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10960 {
10961   if ((!player->is_moving  && player->was_moving) ||
10962       (player->MovPos == 0 && player->was_moving) ||
10963       (player->is_snapping && !player->was_snapping) ||
10964       (player->is_dropping && !player->was_dropping))
10965   {
10966     if (!CheckSaveEngineSnapshotToList())
10967       return;
10968
10969     player->was_moving = FALSE;
10970     player->was_snapping = TRUE;
10971     player->was_dropping = TRUE;
10972   }
10973   else
10974   {
10975     if (player->is_moving)
10976       player->was_moving = TRUE;
10977
10978     if (!player->is_snapping)
10979       player->was_snapping = FALSE;
10980
10981     if (!player->is_dropping)
10982       player->was_dropping = FALSE;
10983   }
10984 }
10985
10986 static void CheckSingleStepMode(struct PlayerInfo *player)
10987 {
10988   if (tape.single_step && tape.recording && !tape.pausing)
10989   {
10990     /* as it is called "single step mode", just return to pause mode when the
10991        player stopped moving after one tile (or never starts moving at all) */
10992     if (!player->is_moving &&
10993         !player->is_pushing &&
10994         !player->is_dropping_pressed)
10995     {
10996       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10997       SnapField(player, 0, 0);                  // stop snapping
10998     }
10999   }
11000
11001   CheckSaveEngineSnapshot(player);
11002 }
11003
11004 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11005 {
11006   int left      = player_action & JOY_LEFT;
11007   int right     = player_action & JOY_RIGHT;
11008   int up        = player_action & JOY_UP;
11009   int down      = player_action & JOY_DOWN;
11010   int button1   = player_action & JOY_BUTTON_1;
11011   int button2   = player_action & JOY_BUTTON_2;
11012   int dx        = (left ? -1 : right ? 1 : 0);
11013   int dy        = (up   ? -1 : down  ? 1 : 0);
11014
11015   if (!player->active || tape.pausing)
11016     return 0;
11017
11018   if (player_action)
11019   {
11020     if (button1)
11021       SnapField(player, dx, dy);
11022     else
11023     {
11024       if (button2)
11025         DropElement(player);
11026
11027       MovePlayer(player, dx, dy);
11028     }
11029
11030     CheckSingleStepMode(player);
11031
11032     SetPlayerWaiting(player, FALSE);
11033
11034     return player_action;
11035   }
11036   else
11037   {
11038     // no actions for this player (no input at player's configured device)
11039
11040     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11041     SnapField(player, 0, 0);
11042     CheckGravityMovementWhenNotMoving(player);
11043
11044     if (player->MovPos == 0)
11045       SetPlayerWaiting(player, TRUE);
11046
11047     if (player->MovPos == 0)    // needed for tape.playing
11048       player->is_moving = FALSE;
11049
11050     player->is_dropping = FALSE;
11051     player->is_dropping_pressed = FALSE;
11052     player->drop_pressed_delay = 0;
11053
11054     CheckSingleStepMode(player);
11055
11056     return 0;
11057   }
11058 }
11059
11060 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11061                                          byte *tape_action)
11062 {
11063   if (!tape.use_mouse)
11064     return;
11065
11066   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11067   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11068   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11069 }
11070
11071 static void SetTapeActionFromMouseAction(byte *tape_action,
11072                                          struct MouseActionInfo *mouse_action)
11073 {
11074   if (!tape.use_mouse)
11075     return;
11076
11077   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11078   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11079   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11080 }
11081
11082 static void CheckLevelSolved(void)
11083 {
11084   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11085   {
11086     if (level.native_em_level->lev->home == 0)  // all players at home
11087     {
11088       PlayerWins(local_player);
11089
11090       AllPlayersGone = TRUE;
11091
11092       level.native_em_level->lev->home = -1;
11093     }
11094
11095     if (level.native_em_level->ply[0]->alive == 0 &&
11096         level.native_em_level->ply[1]->alive == 0 &&
11097         level.native_em_level->ply[2]->alive == 0 &&
11098         level.native_em_level->ply[3]->alive == 0)      // all dead
11099       AllPlayersGone = TRUE;
11100   }
11101   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11102   {
11103     if (game_sp.LevelSolved &&
11104         !game_sp.GameOver)                              // game won
11105     {
11106       PlayerWins(local_player);
11107
11108       game_sp.GameOver = TRUE;
11109
11110       AllPlayersGone = TRUE;
11111     }
11112
11113     if (game_sp.GameOver)                               // game lost
11114       AllPlayersGone = TRUE;
11115   }
11116   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11117   {
11118     if (game_mm.level_solved &&
11119         !game_mm.game_over)                             // game won
11120     {
11121       PlayerWins(local_player);
11122
11123       game_mm.game_over = TRUE;
11124
11125       AllPlayersGone = TRUE;
11126     }
11127
11128     if (game_mm.game_over)                              // game lost
11129       AllPlayersGone = TRUE;
11130   }
11131 }
11132
11133 static void CheckLevelTime(void)
11134 {
11135   int i;
11136
11137   if (TimeFrames >= FRAMES_PER_SECOND)
11138   {
11139     TimeFrames = 0;
11140     TapeTime++;
11141
11142     for (i = 0; i < MAX_PLAYERS; i++)
11143     {
11144       struct PlayerInfo *player = &stored_player[i];
11145
11146       if (SHIELD_ON(player))
11147       {
11148         player->shield_normal_time_left--;
11149
11150         if (player->shield_deadly_time_left > 0)
11151           player->shield_deadly_time_left--;
11152       }
11153     }
11154
11155     if (!local_player->LevelSolved && !level.use_step_counter)
11156     {
11157       TimePlayed++;
11158
11159       if (TimeLeft > 0)
11160       {
11161         TimeLeft--;
11162
11163         if (TimeLeft <= 10 && setup.time_limit)
11164           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11165
11166         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11167            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11168
11169         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11170
11171         if (!TimeLeft && setup.time_limit)
11172         {
11173           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11174             level.native_em_level->lev->killed_out_of_time = TRUE;
11175           else
11176             for (i = 0; i < MAX_PLAYERS; i++)
11177               KillPlayer(&stored_player[i]);
11178         }
11179       }
11180       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
11181       {
11182         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11183       }
11184
11185       level.native_em_level->lev->time =
11186         (game.no_time_limit ? TimePlayed : TimeLeft);
11187     }
11188
11189     if (tape.recording || tape.playing)
11190       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11191   }
11192
11193   if (tape.recording || tape.playing)
11194     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11195
11196   UpdateAndDisplayGameControlValues();
11197 }
11198
11199 void AdvanceFrameAndPlayerCounters(int player_nr)
11200 {
11201   int i;
11202
11203   // advance frame counters (global frame counter and time frame counter)
11204   FrameCounter++;
11205   TimeFrames++;
11206
11207   // advance player counters (counters for move delay, move animation etc.)
11208   for (i = 0; i < MAX_PLAYERS; i++)
11209   {
11210     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11211     int move_delay_value = stored_player[i].move_delay_value;
11212     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11213
11214     if (!advance_player_counters)       // not all players may be affected
11215       continue;
11216
11217     if (move_frames == 0)       // less than one move per game frame
11218     {
11219       int stepsize = TILEX / move_delay_value;
11220       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11221       int count = (stored_player[i].is_moving ?
11222                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11223
11224       if (count % delay == 0)
11225         move_frames = 1;
11226     }
11227
11228     stored_player[i].Frame += move_frames;
11229
11230     if (stored_player[i].MovPos != 0)
11231       stored_player[i].StepFrame += move_frames;
11232
11233     if (stored_player[i].move_delay > 0)
11234       stored_player[i].move_delay--;
11235
11236     // due to bugs in previous versions, counter must count up, not down
11237     if (stored_player[i].push_delay != -1)
11238       stored_player[i].push_delay++;
11239
11240     if (stored_player[i].drop_delay > 0)
11241       stored_player[i].drop_delay--;
11242
11243     if (stored_player[i].is_dropping_pressed)
11244       stored_player[i].drop_pressed_delay++;
11245   }
11246 }
11247
11248 void StartGameActions(boolean init_network_game, boolean record_tape,
11249                       int random_seed)
11250 {
11251   unsigned int new_random_seed = InitRND(random_seed);
11252
11253   if (record_tape)
11254     TapeStartRecording(new_random_seed);
11255
11256   if (init_network_game)
11257   {
11258     SendToServer_LevelFile();
11259     SendToServer_StartPlaying();
11260
11261     return;
11262   }
11263
11264   InitGame();
11265 }
11266
11267 static void GameActionsExt(void)
11268 {
11269 #if 0
11270   static unsigned int game_frame_delay = 0;
11271 #endif
11272   unsigned int game_frame_delay_value;
11273   byte *recorded_player_action;
11274   byte summarized_player_action = 0;
11275   byte tape_action[MAX_PLAYERS];
11276   int i;
11277
11278   // detect endless loops, caused by custom element programming
11279   if (recursion_loop_detected && recursion_loop_depth == 0)
11280   {
11281     char *message = getStringCat3("Internal Error! Element ",
11282                                   EL_NAME(recursion_loop_element),
11283                                   " caused endless loop! Quit the game?");
11284
11285     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11286           EL_NAME(recursion_loop_element));
11287
11288     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11289
11290     recursion_loop_detected = FALSE;    // if game should be continued
11291
11292     free(message);
11293
11294     return;
11295   }
11296
11297   if (game.restart_level)
11298     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11299
11300   CheckLevelSolved();
11301
11302   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11303     GameWon();
11304
11305   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11306     TapeStop();
11307
11308   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11309     return;
11310
11311   game_frame_delay_value =
11312     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11313
11314   if (tape.playing && tape.warp_forward && !tape.pausing)
11315     game_frame_delay_value = 0;
11316
11317   SetVideoFrameDelay(game_frame_delay_value);
11318
11319 #if 0
11320 #if 0
11321   // ---------- main game synchronization point ----------
11322
11323   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11324
11325   printf("::: skip == %d\n", skip);
11326
11327 #else
11328   // ---------- main game synchronization point ----------
11329
11330   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11331 #endif
11332 #endif
11333
11334   if (network_playing && !network_player_action_received)
11335   {
11336     // try to get network player actions in time
11337
11338     // last chance to get network player actions without main loop delay
11339     HandleNetworking();
11340
11341     // game was quit by network peer
11342     if (game_status != GAME_MODE_PLAYING)
11343       return;
11344
11345     // check if network player actions still missing and game still running
11346     if (!network_player_action_received && !checkGameEnded())
11347       return;           // failed to get network player actions in time
11348
11349     // do not yet reset "network_player_action_received" (for tape.pausing)
11350   }
11351
11352   if (tape.pausing)
11353     return;
11354
11355   // at this point we know that we really continue executing the game
11356
11357   network_player_action_received = FALSE;
11358
11359   // when playing tape, read previously recorded player input from tape data
11360   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11361
11362   local_player->effective_mouse_action = local_player->mouse_action;
11363
11364   if (recorded_player_action != NULL)
11365     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11366                                  recorded_player_action);
11367
11368   // TapePlayAction() may return NULL when toggling to "pause before death"
11369   if (tape.pausing)
11370     return;
11371
11372   if (tape.set_centered_player)
11373   {
11374     game.centered_player_nr_next = tape.centered_player_nr_next;
11375     game.set_centered_player = TRUE;
11376   }
11377
11378   for (i = 0; i < MAX_PLAYERS; i++)
11379   {
11380     summarized_player_action |= stored_player[i].action;
11381
11382     if (!network_playing && (game.team_mode || tape.playing))
11383       stored_player[i].effective_action = stored_player[i].action;
11384   }
11385
11386   if (network_playing && !checkGameEnded())
11387     SendToServer_MovePlayer(summarized_player_action);
11388
11389   // summarize all actions at local players mapped input device position
11390   // (this allows using different input devices in single player mode)
11391   if (!network.enabled && !game.team_mode)
11392     stored_player[map_player_action[local_player->index_nr]].effective_action =
11393       summarized_player_action;
11394
11395   if (tape.recording &&
11396       setup.team_mode &&
11397       setup.input_on_focus &&
11398       game.centered_player_nr != -1)
11399   {
11400     for (i = 0; i < MAX_PLAYERS; i++)
11401       stored_player[i].effective_action =
11402         (i == game.centered_player_nr ? summarized_player_action : 0);
11403   }
11404
11405   if (recorded_player_action != NULL)
11406     for (i = 0; i < MAX_PLAYERS; i++)
11407       stored_player[i].effective_action = recorded_player_action[i];
11408
11409   for (i = 0; i < MAX_PLAYERS; i++)
11410   {
11411     tape_action[i] = stored_player[i].effective_action;
11412
11413     /* (this may happen in the RND game engine if a player was not present on
11414        the playfield on level start, but appeared later from a custom element */
11415     if (setup.team_mode &&
11416         tape.recording &&
11417         tape_action[i] &&
11418         !tape.player_participates[i])
11419       tape.player_participates[i] = TRUE;
11420   }
11421
11422   SetTapeActionFromMouseAction(tape_action,
11423                                &local_player->effective_mouse_action);
11424
11425   // only record actions from input devices, but not programmed actions
11426   if (tape.recording)
11427     TapeRecordAction(tape_action);
11428
11429 #if USE_NEW_PLAYER_ASSIGNMENTS
11430   // !!! also map player actions in single player mode !!!
11431   // if (game.team_mode)
11432   if (1)
11433   {
11434     byte mapped_action[MAX_PLAYERS];
11435
11436 #if DEBUG_PLAYER_ACTIONS
11437     printf(":::");
11438     for (i = 0; i < MAX_PLAYERS; i++)
11439       printf(" %d, ", stored_player[i].effective_action);
11440 #endif
11441
11442     for (i = 0; i < MAX_PLAYERS; i++)
11443       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11444
11445     for (i = 0; i < MAX_PLAYERS; i++)
11446       stored_player[i].effective_action = mapped_action[i];
11447
11448 #if DEBUG_PLAYER_ACTIONS
11449     printf(" =>");
11450     for (i = 0; i < MAX_PLAYERS; i++)
11451       printf(" %d, ", stored_player[i].effective_action);
11452     printf("\n");
11453 #endif
11454   }
11455 #if DEBUG_PLAYER_ACTIONS
11456   else
11457   {
11458     printf(":::");
11459     for (i = 0; i < MAX_PLAYERS; i++)
11460       printf(" %d, ", stored_player[i].effective_action);
11461     printf("\n");
11462   }
11463 #endif
11464 #endif
11465
11466   for (i = 0; i < MAX_PLAYERS; i++)
11467   {
11468     // allow engine snapshot in case of changed movement attempt
11469     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11470         (stored_player[i].effective_action & KEY_MOTION))
11471       game.snapshot.changed_action = TRUE;
11472
11473     // allow engine snapshot in case of snapping/dropping attempt
11474     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11475         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11476       game.snapshot.changed_action = TRUE;
11477
11478     game.snapshot.last_action[i] = stored_player[i].effective_action;
11479   }
11480
11481   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11482   {
11483     GameActions_EM_Main();
11484   }
11485   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11486   {
11487     GameActions_SP_Main();
11488   }
11489   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11490   {
11491     GameActions_MM_Main();
11492   }
11493   else
11494   {
11495     GameActions_RND_Main();
11496   }
11497
11498   BlitScreenToBitmap(backbuffer);
11499
11500   CheckLevelSolved();
11501   CheckLevelTime();
11502
11503   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11504
11505   if (global.show_frames_per_second)
11506   {
11507     static unsigned int fps_counter = 0;
11508     static int fps_frames = 0;
11509     unsigned int fps_delay_ms = Counter() - fps_counter;
11510
11511     fps_frames++;
11512
11513     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11514     {
11515       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11516
11517       fps_frames = 0;
11518       fps_counter = Counter();
11519
11520       // always draw FPS to screen after FPS value was updated
11521       redraw_mask |= REDRAW_FPS;
11522     }
11523
11524     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11525     if (GetDrawDeactivationMask() == REDRAW_NONE)
11526       redraw_mask |= REDRAW_FPS;
11527   }
11528 }
11529
11530 static void GameActions_CheckSaveEngineSnapshot(void)
11531 {
11532   if (!game.snapshot.save_snapshot)
11533     return;
11534
11535   // clear flag for saving snapshot _before_ saving snapshot
11536   game.snapshot.save_snapshot = FALSE;
11537
11538   SaveEngineSnapshotToList();
11539 }
11540
11541 void GameActions(void)
11542 {
11543   GameActionsExt();
11544
11545   GameActions_CheckSaveEngineSnapshot();
11546 }
11547
11548 void GameActions_EM_Main(void)
11549 {
11550   byte effective_action[MAX_PLAYERS];
11551   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11552   int i;
11553
11554   for (i = 0; i < MAX_PLAYERS; i++)
11555     effective_action[i] = stored_player[i].effective_action;
11556
11557   GameActions_EM(effective_action, warp_mode);
11558 }
11559
11560 void GameActions_SP_Main(void)
11561 {
11562   byte effective_action[MAX_PLAYERS];
11563   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11564   int i;
11565
11566   for (i = 0; i < MAX_PLAYERS; i++)
11567     effective_action[i] = stored_player[i].effective_action;
11568
11569   GameActions_SP(effective_action, warp_mode);
11570
11571   for (i = 0; i < MAX_PLAYERS; i++)
11572   {
11573     if (stored_player[i].force_dropping)
11574       stored_player[i].action |= KEY_BUTTON_DROP;
11575
11576     stored_player[i].force_dropping = FALSE;
11577   }
11578 }
11579
11580 void GameActions_MM_Main(void)
11581 {
11582   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11583
11584   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11585 }
11586
11587 void GameActions_RND_Main(void)
11588 {
11589   GameActions_RND();
11590 }
11591
11592 void GameActions_RND(void)
11593 {
11594   int magic_wall_x = 0, magic_wall_y = 0;
11595   int i, x, y, element, graphic, last_gfx_frame;
11596
11597   InitPlayfieldScanModeVars();
11598
11599   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11600   {
11601     SCAN_PLAYFIELD(x, y)
11602     {
11603       ChangeCount[x][y] = 0;
11604       ChangeEvent[x][y] = -1;
11605     }
11606   }
11607
11608   if (game.set_centered_player)
11609   {
11610     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11611
11612     // switching to "all players" only possible if all players fit to screen
11613     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11614     {
11615       game.centered_player_nr_next = game.centered_player_nr;
11616       game.set_centered_player = FALSE;
11617     }
11618
11619     // do not switch focus to non-existing (or non-active) player
11620     if (game.centered_player_nr_next >= 0 &&
11621         !stored_player[game.centered_player_nr_next].active)
11622     {
11623       game.centered_player_nr_next = game.centered_player_nr;
11624       game.set_centered_player = FALSE;
11625     }
11626   }
11627
11628   if (game.set_centered_player &&
11629       ScreenMovPos == 0)        // screen currently aligned at tile position
11630   {
11631     int sx, sy;
11632
11633     if (game.centered_player_nr_next == -1)
11634     {
11635       setScreenCenteredToAllPlayers(&sx, &sy);
11636     }
11637     else
11638     {
11639       sx = stored_player[game.centered_player_nr_next].jx;
11640       sy = stored_player[game.centered_player_nr_next].jy;
11641     }
11642
11643     game.centered_player_nr = game.centered_player_nr_next;
11644     game.set_centered_player = FALSE;
11645
11646     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11647     DrawGameDoorValues();
11648   }
11649
11650   for (i = 0; i < MAX_PLAYERS; i++)
11651   {
11652     int actual_player_action = stored_player[i].effective_action;
11653
11654 #if 1
11655     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11656        - rnd_equinox_tetrachloride 048
11657        - rnd_equinox_tetrachloride_ii 096
11658        - rnd_emanuel_schmieg 002
11659        - doctor_sloan_ww 001, 020
11660     */
11661     if (stored_player[i].MovPos == 0)
11662       CheckGravityMovement(&stored_player[i]);
11663 #endif
11664
11665     // overwrite programmed action with tape action
11666     if (stored_player[i].programmed_action)
11667       actual_player_action = stored_player[i].programmed_action;
11668
11669     PlayerActions(&stored_player[i], actual_player_action);
11670
11671     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11672   }
11673
11674   ScrollScreen(NULL, SCROLL_GO_ON);
11675
11676   /* for backwards compatibility, the following code emulates a fixed bug that
11677      occured when pushing elements (causing elements that just made their last
11678      pushing step to already (if possible) make their first falling step in the
11679      same game frame, which is bad); this code is also needed to use the famous
11680      "spring push bug" which is used in older levels and might be wanted to be
11681      used also in newer levels, but in this case the buggy pushing code is only
11682      affecting the "spring" element and no other elements */
11683
11684   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11685   {
11686     for (i = 0; i < MAX_PLAYERS; i++)
11687     {
11688       struct PlayerInfo *player = &stored_player[i];
11689       int x = player->jx;
11690       int y = player->jy;
11691
11692       if (player->active && player->is_pushing && player->is_moving &&
11693           IS_MOVING(x, y) &&
11694           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11695            Feld[x][y] == EL_SPRING))
11696       {
11697         ContinueMoving(x, y);
11698
11699         // continue moving after pushing (this is actually a bug)
11700         if (!IS_MOVING(x, y))
11701           Stop[x][y] = FALSE;
11702       }
11703     }
11704   }
11705
11706   SCAN_PLAYFIELD(x, y)
11707   {
11708     Last[x][y] = Feld[x][y];
11709
11710     ChangeCount[x][y] = 0;
11711     ChangeEvent[x][y] = -1;
11712
11713     // this must be handled before main playfield loop
11714     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11715     {
11716       MovDelay[x][y]--;
11717       if (MovDelay[x][y] <= 0)
11718         RemoveField(x, y);
11719     }
11720
11721     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11722     {
11723       MovDelay[x][y]--;
11724       if (MovDelay[x][y] <= 0)
11725       {
11726         RemoveField(x, y);
11727         TEST_DrawLevelField(x, y);
11728
11729         TestIfElementTouchesCustomElement(x, y);        // for empty space
11730       }
11731     }
11732
11733 #if DEBUG
11734     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11735     {
11736       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11737       printf("GameActions(): This should never happen!\n");
11738
11739       ChangePage[x][y] = -1;
11740     }
11741 #endif
11742
11743     Stop[x][y] = FALSE;
11744     if (WasJustMoving[x][y] > 0)
11745       WasJustMoving[x][y]--;
11746     if (WasJustFalling[x][y] > 0)
11747       WasJustFalling[x][y]--;
11748     if (CheckCollision[x][y] > 0)
11749       CheckCollision[x][y]--;
11750     if (CheckImpact[x][y] > 0)
11751       CheckImpact[x][y]--;
11752
11753     GfxFrame[x][y]++;
11754
11755     /* reset finished pushing action (not done in ContinueMoving() to allow
11756        continuous pushing animation for elements with zero push delay) */
11757     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11758     {
11759       ResetGfxAnimation(x, y);
11760       TEST_DrawLevelField(x, y);
11761     }
11762
11763 #if DEBUG
11764     if (IS_BLOCKED(x, y))
11765     {
11766       int oldx, oldy;
11767
11768       Blocked2Moving(x, y, &oldx, &oldy);
11769       if (!IS_MOVING(oldx, oldy))
11770       {
11771         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11772         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11773         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11774         printf("GameActions(): This should never happen!\n");
11775       }
11776     }
11777 #endif
11778   }
11779
11780   SCAN_PLAYFIELD(x, y)
11781   {
11782     element = Feld[x][y];
11783     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11784     last_gfx_frame = GfxFrame[x][y];
11785
11786     ResetGfxFrame(x, y);
11787
11788     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11789       DrawLevelGraphicAnimation(x, y, graphic);
11790
11791     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11792         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11793       ResetRandomAnimationValue(x, y);
11794
11795     SetRandomAnimationValue(x, y);
11796
11797     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11798
11799     if (IS_INACTIVE(element))
11800     {
11801       if (IS_ANIMATED(graphic))
11802         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11803
11804       continue;
11805     }
11806
11807     // this may take place after moving, so 'element' may have changed
11808     if (IS_CHANGING(x, y) &&
11809         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11810     {
11811       int page = element_info[element].event_page_nr[CE_DELAY];
11812
11813       HandleElementChange(x, y, page);
11814
11815       element = Feld[x][y];
11816       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11817     }
11818
11819     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11820     {
11821       StartMoving(x, y);
11822
11823       element = Feld[x][y];
11824       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11825
11826       if (IS_ANIMATED(graphic) &&
11827           !IS_MOVING(x, y) &&
11828           !Stop[x][y])
11829         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11830
11831       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11832         TEST_DrawTwinkleOnField(x, y);
11833     }
11834     else if (element == EL_ACID)
11835     {
11836       if (!Stop[x][y])
11837         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11838     }
11839     else if ((element == EL_EXIT_OPEN ||
11840               element == EL_EM_EXIT_OPEN ||
11841               element == EL_SP_EXIT_OPEN ||
11842               element == EL_STEEL_EXIT_OPEN ||
11843               element == EL_EM_STEEL_EXIT_OPEN ||
11844               element == EL_SP_TERMINAL ||
11845               element == EL_SP_TERMINAL_ACTIVE ||
11846               element == EL_EXTRA_TIME ||
11847               element == EL_SHIELD_NORMAL ||
11848               element == EL_SHIELD_DEADLY) &&
11849              IS_ANIMATED(graphic))
11850       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11851     else if (IS_MOVING(x, y))
11852       ContinueMoving(x, y);
11853     else if (IS_ACTIVE_BOMB(element))
11854       CheckDynamite(x, y);
11855     else if (element == EL_AMOEBA_GROWING)
11856       AmoebeWaechst(x, y);
11857     else if (element == EL_AMOEBA_SHRINKING)
11858       AmoebaDisappearing(x, y);
11859
11860 #if !USE_NEW_AMOEBA_CODE
11861     else if (IS_AMOEBALIVE(element))
11862       AmoebeAbleger(x, y);
11863 #endif
11864
11865     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11866       Life(x, y);
11867     else if (element == EL_EXIT_CLOSED)
11868       CheckExit(x, y);
11869     else if (element == EL_EM_EXIT_CLOSED)
11870       CheckExitEM(x, y);
11871     else if (element == EL_STEEL_EXIT_CLOSED)
11872       CheckExitSteel(x, y);
11873     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11874       CheckExitSteelEM(x, y);
11875     else if (element == EL_SP_EXIT_CLOSED)
11876       CheckExitSP(x, y);
11877     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11878              element == EL_EXPANDABLE_STEELWALL_GROWING)
11879       MauerWaechst(x, y);
11880     else if (element == EL_EXPANDABLE_WALL ||
11881              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11882              element == EL_EXPANDABLE_WALL_VERTICAL ||
11883              element == EL_EXPANDABLE_WALL_ANY ||
11884              element == EL_BD_EXPANDABLE_WALL)
11885       MauerAbleger(x, y);
11886     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11887              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11888              element == EL_EXPANDABLE_STEELWALL_ANY)
11889       MauerAblegerStahl(x, y);
11890     else if (element == EL_FLAMES)
11891       CheckForDragon(x, y);
11892     else if (element == EL_EXPLOSION)
11893       ; // drawing of correct explosion animation is handled separately
11894     else if (element == EL_ELEMENT_SNAPPING ||
11895              element == EL_DIAGONAL_SHRINKING ||
11896              element == EL_DIAGONAL_GROWING)
11897     {
11898       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11899
11900       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11901     }
11902     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11903       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11904
11905     if (IS_BELT_ACTIVE(element))
11906       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11907
11908     if (game.magic_wall_active)
11909     {
11910       int jx = local_player->jx, jy = local_player->jy;
11911
11912       // play the element sound at the position nearest to the player
11913       if ((element == EL_MAGIC_WALL_FULL ||
11914            element == EL_MAGIC_WALL_ACTIVE ||
11915            element == EL_MAGIC_WALL_EMPTYING ||
11916            element == EL_BD_MAGIC_WALL_FULL ||
11917            element == EL_BD_MAGIC_WALL_ACTIVE ||
11918            element == EL_BD_MAGIC_WALL_EMPTYING ||
11919            element == EL_DC_MAGIC_WALL_FULL ||
11920            element == EL_DC_MAGIC_WALL_ACTIVE ||
11921            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11922           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11923       {
11924         magic_wall_x = x;
11925         magic_wall_y = y;
11926       }
11927     }
11928   }
11929
11930 #if USE_NEW_AMOEBA_CODE
11931   // new experimental amoeba growth stuff
11932   if (!(FrameCounter % 8))
11933   {
11934     static unsigned int random = 1684108901;
11935
11936     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11937     {
11938       x = RND(lev_fieldx);
11939       y = RND(lev_fieldy);
11940       element = Feld[x][y];
11941
11942       if (!IS_PLAYER(x,y) &&
11943           (element == EL_EMPTY ||
11944            CAN_GROW_INTO(element) ||
11945            element == EL_QUICKSAND_EMPTY ||
11946            element == EL_QUICKSAND_FAST_EMPTY ||
11947            element == EL_ACID_SPLASH_LEFT ||
11948            element == EL_ACID_SPLASH_RIGHT))
11949       {
11950         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11951             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11952             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11953             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11954           Feld[x][y] = EL_AMOEBA_DROP;
11955       }
11956
11957       random = random * 129 + 1;
11958     }
11959   }
11960 #endif
11961
11962   game.explosions_delayed = FALSE;
11963
11964   SCAN_PLAYFIELD(x, y)
11965   {
11966     element = Feld[x][y];
11967
11968     if (ExplodeField[x][y])
11969       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11970     else if (element == EL_EXPLOSION)
11971       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11972
11973     ExplodeField[x][y] = EX_TYPE_NONE;
11974   }
11975
11976   game.explosions_delayed = TRUE;
11977
11978   if (game.magic_wall_active)
11979   {
11980     if (!(game.magic_wall_time_left % 4))
11981     {
11982       int element = Feld[magic_wall_x][magic_wall_y];
11983
11984       if (element == EL_BD_MAGIC_WALL_FULL ||
11985           element == EL_BD_MAGIC_WALL_ACTIVE ||
11986           element == EL_BD_MAGIC_WALL_EMPTYING)
11987         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11988       else if (element == EL_DC_MAGIC_WALL_FULL ||
11989                element == EL_DC_MAGIC_WALL_ACTIVE ||
11990                element == EL_DC_MAGIC_WALL_EMPTYING)
11991         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11992       else
11993         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11994     }
11995
11996     if (game.magic_wall_time_left > 0)
11997     {
11998       game.magic_wall_time_left--;
11999
12000       if (!game.magic_wall_time_left)
12001       {
12002         SCAN_PLAYFIELD(x, y)
12003         {
12004           element = Feld[x][y];
12005
12006           if (element == EL_MAGIC_WALL_ACTIVE ||
12007               element == EL_MAGIC_WALL_FULL)
12008           {
12009             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12010             TEST_DrawLevelField(x, y);
12011           }
12012           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12013                    element == EL_BD_MAGIC_WALL_FULL)
12014           {
12015             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12016             TEST_DrawLevelField(x, y);
12017           }
12018           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12019                    element == EL_DC_MAGIC_WALL_FULL)
12020           {
12021             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12022             TEST_DrawLevelField(x, y);
12023           }
12024         }
12025
12026         game.magic_wall_active = FALSE;
12027       }
12028     }
12029   }
12030
12031   if (game.light_time_left > 0)
12032   {
12033     game.light_time_left--;
12034
12035     if (game.light_time_left == 0)
12036       RedrawAllLightSwitchesAndInvisibleElements();
12037   }
12038
12039   if (game.timegate_time_left > 0)
12040   {
12041     game.timegate_time_left--;
12042
12043     if (game.timegate_time_left == 0)
12044       CloseAllOpenTimegates();
12045   }
12046
12047   if (game.lenses_time_left > 0)
12048   {
12049     game.lenses_time_left--;
12050
12051     if (game.lenses_time_left == 0)
12052       RedrawAllInvisibleElementsForLenses();
12053   }
12054
12055   if (game.magnify_time_left > 0)
12056   {
12057     game.magnify_time_left--;
12058
12059     if (game.magnify_time_left == 0)
12060       RedrawAllInvisibleElementsForMagnifier();
12061   }
12062
12063   for (i = 0; i < MAX_PLAYERS; i++)
12064   {
12065     struct PlayerInfo *player = &stored_player[i];
12066
12067     if (SHIELD_ON(player))
12068     {
12069       if (player->shield_deadly_time_left)
12070         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12071       else if (player->shield_normal_time_left)
12072         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12073     }
12074   }
12075
12076 #if USE_DELAYED_GFX_REDRAW
12077   SCAN_PLAYFIELD(x, y)
12078   {
12079     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12080     {
12081       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12082          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12083
12084       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12085         DrawLevelField(x, y);
12086
12087       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12088         DrawLevelFieldCrumbled(x, y);
12089
12090       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12091         DrawLevelFieldCrumbledNeighbours(x, y);
12092
12093       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12094         DrawTwinkleOnField(x, y);
12095     }
12096
12097     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12098   }
12099 #endif
12100
12101   DrawAllPlayers();
12102   PlayAllPlayersSound();
12103
12104   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12105   {
12106     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12107
12108     local_player->show_envelope = 0;
12109   }
12110
12111   // use random number generator in every frame to make it less predictable
12112   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12113     RND(1);
12114 }
12115
12116 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12117 {
12118   int min_x = x, min_y = y, max_x = x, max_y = y;
12119   int i;
12120
12121   for (i = 0; i < MAX_PLAYERS; i++)
12122   {
12123     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12124
12125     if (!stored_player[i].active || &stored_player[i] == player)
12126       continue;
12127
12128     min_x = MIN(min_x, jx);
12129     min_y = MIN(min_y, jy);
12130     max_x = MAX(max_x, jx);
12131     max_y = MAX(max_y, jy);
12132   }
12133
12134   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12135 }
12136
12137 static boolean AllPlayersInVisibleScreen(void)
12138 {
12139   int i;
12140
12141   for (i = 0; i < MAX_PLAYERS; i++)
12142   {
12143     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12144
12145     if (!stored_player[i].active)
12146       continue;
12147
12148     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12149       return FALSE;
12150   }
12151
12152   return TRUE;
12153 }
12154
12155 void ScrollLevel(int dx, int dy)
12156 {
12157   int scroll_offset = 2 * TILEX_VAR;
12158   int x, y;
12159
12160   BlitBitmap(drawto_field, drawto_field,
12161              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12162              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12163              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12164              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12165              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12166              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12167
12168   if (dx != 0)
12169   {
12170     x = (dx == 1 ? BX1 : BX2);
12171     for (y = BY1; y <= BY2; y++)
12172       DrawScreenField(x, y);
12173   }
12174
12175   if (dy != 0)
12176   {
12177     y = (dy == 1 ? BY1 : BY2);
12178     for (x = BX1; x <= BX2; x++)
12179       DrawScreenField(x, y);
12180   }
12181
12182   redraw_mask |= REDRAW_FIELD;
12183 }
12184
12185 static boolean canFallDown(struct PlayerInfo *player)
12186 {
12187   int jx = player->jx, jy = player->jy;
12188
12189   return (IN_LEV_FIELD(jx, jy + 1) &&
12190           (IS_FREE(jx, jy + 1) ||
12191            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12192           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12193           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12194 }
12195
12196 static boolean canPassField(int x, int y, int move_dir)
12197 {
12198   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12199   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12200   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12201   int nextx = x + dx;
12202   int nexty = y + dy;
12203   int element = Feld[x][y];
12204
12205   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12206           !CAN_MOVE(element) &&
12207           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12208           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12209           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12210 }
12211
12212 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12213 {
12214   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12215   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12216   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12217   int newx = x + dx;
12218   int newy = y + dy;
12219
12220   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12221           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12222           (IS_DIGGABLE(Feld[newx][newy]) ||
12223            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12224            canPassField(newx, newy, move_dir)));
12225 }
12226
12227 static void CheckGravityMovement(struct PlayerInfo *player)
12228 {
12229   if (player->gravity && !player->programmed_action)
12230   {
12231     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12232     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12233     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12234     int jx = player->jx, jy = player->jy;
12235     boolean player_is_moving_to_valid_field =
12236       (!player_is_snapping &&
12237        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12238         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12239     boolean player_can_fall_down = canFallDown(player);
12240
12241     if (player_can_fall_down &&
12242         !player_is_moving_to_valid_field)
12243       player->programmed_action = MV_DOWN;
12244   }
12245 }
12246
12247 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12248 {
12249   return CheckGravityMovement(player);
12250
12251   if (player->gravity && !player->programmed_action)
12252   {
12253     int jx = player->jx, jy = player->jy;
12254     boolean field_under_player_is_free =
12255       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12256     boolean player_is_standing_on_valid_field =
12257       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12258        (IS_WALKABLE(Feld[jx][jy]) &&
12259         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12260
12261     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12262       player->programmed_action = MV_DOWN;
12263   }
12264 }
12265
12266 /*
12267   MovePlayerOneStep()
12268   -----------------------------------------------------------------------------
12269   dx, dy:               direction (non-diagonal) to try to move the player to
12270   real_dx, real_dy:     direction as read from input device (can be diagonal)
12271 */
12272
12273 boolean MovePlayerOneStep(struct PlayerInfo *player,
12274                           int dx, int dy, int real_dx, int real_dy)
12275 {
12276   int jx = player->jx, jy = player->jy;
12277   int new_jx = jx + dx, new_jy = jy + dy;
12278   int can_move;
12279   boolean player_can_move = !player->cannot_move;
12280
12281   if (!player->active || (!dx && !dy))
12282     return MP_NO_ACTION;
12283
12284   player->MovDir = (dx < 0 ? MV_LEFT :
12285                     dx > 0 ? MV_RIGHT :
12286                     dy < 0 ? MV_UP :
12287                     dy > 0 ? MV_DOWN :  MV_NONE);
12288
12289   if (!IN_LEV_FIELD(new_jx, new_jy))
12290     return MP_NO_ACTION;
12291
12292   if (!player_can_move)
12293   {
12294     if (player->MovPos == 0)
12295     {
12296       player->is_moving = FALSE;
12297       player->is_digging = FALSE;
12298       player->is_collecting = FALSE;
12299       player->is_snapping = FALSE;
12300       player->is_pushing = FALSE;
12301     }
12302   }
12303
12304   if (!network.enabled && game.centered_player_nr == -1 &&
12305       !AllPlayersInSight(player, new_jx, new_jy))
12306     return MP_NO_ACTION;
12307
12308   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12309   if (can_move != MP_MOVING)
12310     return can_move;
12311
12312   // check if DigField() has caused relocation of the player
12313   if (player->jx != jx || player->jy != jy)
12314     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12315
12316   StorePlayer[jx][jy] = 0;
12317   player->last_jx = jx;
12318   player->last_jy = jy;
12319   player->jx = new_jx;
12320   player->jy = new_jy;
12321   StorePlayer[new_jx][new_jy] = player->element_nr;
12322
12323   if (player->move_delay_value_next != -1)
12324   {
12325     player->move_delay_value = player->move_delay_value_next;
12326     player->move_delay_value_next = -1;
12327   }
12328
12329   player->MovPos =
12330     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12331
12332   player->step_counter++;
12333
12334   PlayerVisit[jx][jy] = FrameCounter;
12335
12336   player->is_moving = TRUE;
12337
12338 #if 1
12339   // should better be called in MovePlayer(), but this breaks some tapes
12340   ScrollPlayer(player, SCROLL_INIT);
12341 #endif
12342
12343   return MP_MOVING;
12344 }
12345
12346 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12347 {
12348   int jx = player->jx, jy = player->jy;
12349   int old_jx = jx, old_jy = jy;
12350   int moved = MP_NO_ACTION;
12351
12352   if (!player->active)
12353     return FALSE;
12354
12355   if (!dx && !dy)
12356   {
12357     if (player->MovPos == 0)
12358     {
12359       player->is_moving = FALSE;
12360       player->is_digging = FALSE;
12361       player->is_collecting = FALSE;
12362       player->is_snapping = FALSE;
12363       player->is_pushing = FALSE;
12364     }
12365
12366     return FALSE;
12367   }
12368
12369   if (player->move_delay > 0)
12370     return FALSE;
12371
12372   player->move_delay = -1;              // set to "uninitialized" value
12373
12374   // store if player is automatically moved to next field
12375   player->is_auto_moving = (player->programmed_action != MV_NONE);
12376
12377   // remove the last programmed player action
12378   player->programmed_action = 0;
12379
12380   if (player->MovPos)
12381   {
12382     // should only happen if pre-1.2 tape recordings are played
12383     // this is only for backward compatibility
12384
12385     int original_move_delay_value = player->move_delay_value;
12386
12387 #if DEBUG
12388     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12389            tape.counter);
12390 #endif
12391
12392     // scroll remaining steps with finest movement resolution
12393     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12394
12395     while (player->MovPos)
12396     {
12397       ScrollPlayer(player, SCROLL_GO_ON);
12398       ScrollScreen(NULL, SCROLL_GO_ON);
12399
12400       AdvanceFrameAndPlayerCounters(player->index_nr);
12401
12402       DrawAllPlayers();
12403       BackToFront_WithFrameDelay(0);
12404     }
12405
12406     player->move_delay_value = original_move_delay_value;
12407   }
12408
12409   player->is_active = FALSE;
12410
12411   if (player->last_move_dir & MV_HORIZONTAL)
12412   {
12413     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12414       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12415   }
12416   else
12417   {
12418     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12419       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12420   }
12421
12422   if (!moved && !player->is_active)
12423   {
12424     player->is_moving = FALSE;
12425     player->is_digging = FALSE;
12426     player->is_collecting = FALSE;
12427     player->is_snapping = FALSE;
12428     player->is_pushing = FALSE;
12429   }
12430
12431   jx = player->jx;
12432   jy = player->jy;
12433
12434   if (moved & MP_MOVING && !ScreenMovPos &&
12435       (player->index_nr == game.centered_player_nr ||
12436        game.centered_player_nr == -1))
12437   {
12438     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12439     int offset = game.scroll_delay_value;
12440
12441     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12442     {
12443       // actual player has left the screen -- scroll in that direction
12444       if (jx != old_jx)         // player has moved horizontally
12445         scroll_x += (jx - old_jx);
12446       else                      // player has moved vertically
12447         scroll_y += (jy - old_jy);
12448     }
12449     else
12450     {
12451       if (jx != old_jx)         // player has moved horizontally
12452       {
12453         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12454             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12455           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12456
12457         // don't scroll over playfield boundaries
12458         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12459           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12460
12461         // don't scroll more than one field at a time
12462         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12463
12464         // don't scroll against the player's moving direction
12465         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12466             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12467           scroll_x = old_scroll_x;
12468       }
12469       else                      // player has moved vertically
12470       {
12471         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12472             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12473           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12474
12475         // don't scroll over playfield boundaries
12476         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12477           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12478
12479         // don't scroll more than one field at a time
12480         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12481
12482         // don't scroll against the player's moving direction
12483         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12484             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12485           scroll_y = old_scroll_y;
12486       }
12487     }
12488
12489     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12490     {
12491       if (!network.enabled && game.centered_player_nr == -1 &&
12492           !AllPlayersInVisibleScreen())
12493       {
12494         scroll_x = old_scroll_x;
12495         scroll_y = old_scroll_y;
12496       }
12497       else
12498       {
12499         ScrollScreen(player, SCROLL_INIT);
12500         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12501       }
12502     }
12503   }
12504
12505   player->StepFrame = 0;
12506
12507   if (moved & MP_MOVING)
12508   {
12509     if (old_jx != jx && old_jy == jy)
12510       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12511     else if (old_jx == jx && old_jy != jy)
12512       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12513
12514     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12515
12516     player->last_move_dir = player->MovDir;
12517     player->is_moving = TRUE;
12518     player->is_snapping = FALSE;
12519     player->is_switching = FALSE;
12520     player->is_dropping = FALSE;
12521     player->is_dropping_pressed = FALSE;
12522     player->drop_pressed_delay = 0;
12523
12524 #if 0
12525     // should better be called here than above, but this breaks some tapes
12526     ScrollPlayer(player, SCROLL_INIT);
12527 #endif
12528   }
12529   else
12530   {
12531     CheckGravityMovementWhenNotMoving(player);
12532
12533     player->is_moving = FALSE;
12534
12535     /* at this point, the player is allowed to move, but cannot move right now
12536        (e.g. because of something blocking the way) -- ensure that the player
12537        is also allowed to move in the next frame (in old versions before 3.1.1,
12538        the player was forced to wait again for eight frames before next try) */
12539
12540     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12541       player->move_delay = 0;   // allow direct movement in the next frame
12542   }
12543
12544   if (player->move_delay == -1)         // not yet initialized by DigField()
12545     player->move_delay = player->move_delay_value;
12546
12547   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12548   {
12549     TestIfPlayerTouchesBadThing(jx, jy);
12550     TestIfPlayerTouchesCustomElement(jx, jy);
12551   }
12552
12553   if (!player->active)
12554     RemovePlayer(player);
12555
12556   return moved;
12557 }
12558
12559 void ScrollPlayer(struct PlayerInfo *player, int mode)
12560 {
12561   int jx = player->jx, jy = player->jy;
12562   int last_jx = player->last_jx, last_jy = player->last_jy;
12563   int move_stepsize = TILEX / player->move_delay_value;
12564
12565   if (!player->active)
12566     return;
12567
12568   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12569     return;
12570
12571   if (mode == SCROLL_INIT)
12572   {
12573     player->actual_frame_counter = FrameCounter;
12574     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12575
12576     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12577         Feld[last_jx][last_jy] == EL_EMPTY)
12578     {
12579       int last_field_block_delay = 0;   // start with no blocking at all
12580       int block_delay_adjustment = player->block_delay_adjustment;
12581
12582       // if player blocks last field, add delay for exactly one move
12583       if (player->block_last_field)
12584       {
12585         last_field_block_delay += player->move_delay_value;
12586
12587         // when blocking enabled, prevent moving up despite gravity
12588         if (player->gravity && player->MovDir == MV_UP)
12589           block_delay_adjustment = -1;
12590       }
12591
12592       // add block delay adjustment (also possible when not blocking)
12593       last_field_block_delay += block_delay_adjustment;
12594
12595       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12596       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12597     }
12598
12599     if (player->MovPos != 0)    // player has not yet reached destination
12600       return;
12601   }
12602   else if (!FrameReached(&player->actual_frame_counter, 1))
12603     return;
12604
12605   if (player->MovPos != 0)
12606   {
12607     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12608     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12609
12610     // before DrawPlayer() to draw correct player graphic for this case
12611     if (player->MovPos == 0)
12612       CheckGravityMovement(player);
12613   }
12614
12615   if (player->MovPos == 0)      // player reached destination field
12616   {
12617     if (player->move_delay_reset_counter > 0)
12618     {
12619       player->move_delay_reset_counter--;
12620
12621       if (player->move_delay_reset_counter == 0)
12622       {
12623         // continue with normal speed after quickly moving through gate
12624         HALVE_PLAYER_SPEED(player);
12625
12626         // be able to make the next move without delay
12627         player->move_delay = 0;
12628       }
12629     }
12630
12631     player->last_jx = jx;
12632     player->last_jy = jy;
12633
12634     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12635         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12636         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12637         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12638         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12639         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12640         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12641         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12642     {
12643       ExitPlayer(player);
12644
12645       if ((local_player->friends_still_needed == 0 ||
12646            IS_SP_ELEMENT(Feld[jx][jy])) &&
12647           AllPlayersGone)
12648         PlayerWins(local_player);
12649     }
12650
12651     // this breaks one level: "machine", level 000
12652     {
12653       int move_direction = player->MovDir;
12654       int enter_side = MV_DIR_OPPOSITE(move_direction);
12655       int leave_side = move_direction;
12656       int old_jx = last_jx;
12657       int old_jy = last_jy;
12658       int old_element = Feld[old_jx][old_jy];
12659       int new_element = Feld[jx][jy];
12660
12661       if (IS_CUSTOM_ELEMENT(old_element))
12662         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12663                                    CE_LEFT_BY_PLAYER,
12664                                    player->index_bit, leave_side);
12665
12666       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12667                                           CE_PLAYER_LEAVES_X,
12668                                           player->index_bit, leave_side);
12669
12670       if (IS_CUSTOM_ELEMENT(new_element))
12671         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12672                                    player->index_bit, enter_side);
12673
12674       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12675                                           CE_PLAYER_ENTERS_X,
12676                                           player->index_bit, enter_side);
12677
12678       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12679                                         CE_MOVE_OF_X, move_direction);
12680     }
12681
12682     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12683     {
12684       TestIfPlayerTouchesBadThing(jx, jy);
12685       TestIfPlayerTouchesCustomElement(jx, jy);
12686
12687       /* needed because pushed element has not yet reached its destination,
12688          so it would trigger a change event at its previous field location */
12689       if (!player->is_pushing)
12690         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12691
12692       if (!player->active)
12693         RemovePlayer(player);
12694     }
12695
12696     if (!local_player->LevelSolved && level.use_step_counter)
12697     {
12698       int i;
12699
12700       TimePlayed++;
12701
12702       if (TimeLeft > 0)
12703       {
12704         TimeLeft--;
12705
12706         if (TimeLeft <= 10 && setup.time_limit)
12707           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12708
12709         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12710
12711         DisplayGameControlValues();
12712
12713         if (!TimeLeft && setup.time_limit)
12714           for (i = 0; i < MAX_PLAYERS; i++)
12715             KillPlayer(&stored_player[i]);
12716       }
12717       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
12718       {
12719         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12720
12721         DisplayGameControlValues();
12722       }
12723     }
12724
12725     if (tape.single_step && tape.recording && !tape.pausing &&
12726         !player->programmed_action)
12727       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12728
12729     if (!player->programmed_action)
12730       CheckSaveEngineSnapshot(player);
12731   }
12732 }
12733
12734 void ScrollScreen(struct PlayerInfo *player, int mode)
12735 {
12736   static unsigned int screen_frame_counter = 0;
12737
12738   if (mode == SCROLL_INIT)
12739   {
12740     // set scrolling step size according to actual player's moving speed
12741     ScrollStepSize = TILEX / player->move_delay_value;
12742
12743     screen_frame_counter = FrameCounter;
12744     ScreenMovDir = player->MovDir;
12745     ScreenMovPos = player->MovPos;
12746     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12747     return;
12748   }
12749   else if (!FrameReached(&screen_frame_counter, 1))
12750     return;
12751
12752   if (ScreenMovPos)
12753   {
12754     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12755     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12756     redraw_mask |= REDRAW_FIELD;
12757   }
12758   else
12759     ScreenMovDir = MV_NONE;
12760 }
12761
12762 void TestIfPlayerTouchesCustomElement(int x, int y)
12763 {
12764   static int xy[4][2] =
12765   {
12766     { 0, -1 },
12767     { -1, 0 },
12768     { +1, 0 },
12769     { 0, +1 }
12770   };
12771   static int trigger_sides[4][2] =
12772   {
12773     // center side       border side
12774     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12775     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12776     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12777     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12778   };
12779   static int touch_dir[4] =
12780   {
12781     MV_LEFT | MV_RIGHT,
12782     MV_UP   | MV_DOWN,
12783     MV_UP   | MV_DOWN,
12784     MV_LEFT | MV_RIGHT
12785   };
12786   int center_element = Feld[x][y];      // should always be non-moving!
12787   int i;
12788
12789   for (i = 0; i < NUM_DIRECTIONS; i++)
12790   {
12791     int xx = x + xy[i][0];
12792     int yy = y + xy[i][1];
12793     int center_side = trigger_sides[i][0];
12794     int border_side = trigger_sides[i][1];
12795     int border_element;
12796
12797     if (!IN_LEV_FIELD(xx, yy))
12798       continue;
12799
12800     if (IS_PLAYER(x, y))                // player found at center element
12801     {
12802       struct PlayerInfo *player = PLAYERINFO(x, y);
12803
12804       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12805         border_element = Feld[xx][yy];          // may be moving!
12806       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12807         border_element = Feld[xx][yy];
12808       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12809         border_element = MovingOrBlocked2Element(xx, yy);
12810       else
12811         continue;               // center and border element do not touch
12812
12813       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12814                                  player->index_bit, border_side);
12815       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12816                                           CE_PLAYER_TOUCHES_X,
12817                                           player->index_bit, border_side);
12818
12819       {
12820         /* use player element that is initially defined in the level playfield,
12821            not the player element that corresponds to the runtime player number
12822            (example: a level that contains EL_PLAYER_3 as the only player would
12823            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12824         int player_element = PLAYERINFO(x, y)->initial_element;
12825
12826         CheckElementChangeBySide(xx, yy, border_element, player_element,
12827                                  CE_TOUCHING_X, border_side);
12828       }
12829     }
12830     else if (IS_PLAYER(xx, yy))         // player found at border element
12831     {
12832       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12833
12834       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12835       {
12836         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12837           continue;             // center and border element do not touch
12838       }
12839
12840       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12841                                  player->index_bit, center_side);
12842       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12843                                           CE_PLAYER_TOUCHES_X,
12844                                           player->index_bit, center_side);
12845
12846       {
12847         /* use player element that is initially defined in the level playfield,
12848            not the player element that corresponds to the runtime player number
12849            (example: a level that contains EL_PLAYER_3 as the only player would
12850            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12851         int player_element = PLAYERINFO(xx, yy)->initial_element;
12852
12853         CheckElementChangeBySide(x, y, center_element, player_element,
12854                                  CE_TOUCHING_X, center_side);
12855       }
12856
12857       break;
12858     }
12859   }
12860 }
12861
12862 void TestIfElementTouchesCustomElement(int x, int y)
12863 {
12864   static int xy[4][2] =
12865   {
12866     { 0, -1 },
12867     { -1, 0 },
12868     { +1, 0 },
12869     { 0, +1 }
12870   };
12871   static int trigger_sides[4][2] =
12872   {
12873     // center side      border side
12874     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12875     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12876     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12877     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12878   };
12879   static int touch_dir[4] =
12880   {
12881     MV_LEFT | MV_RIGHT,
12882     MV_UP   | MV_DOWN,
12883     MV_UP   | MV_DOWN,
12884     MV_LEFT | MV_RIGHT
12885   };
12886   boolean change_center_element = FALSE;
12887   int center_element = Feld[x][y];      // should always be non-moving!
12888   int border_element_old[NUM_DIRECTIONS];
12889   int i;
12890
12891   for (i = 0; i < NUM_DIRECTIONS; i++)
12892   {
12893     int xx = x + xy[i][0];
12894     int yy = y + xy[i][1];
12895     int border_element;
12896
12897     border_element_old[i] = -1;
12898
12899     if (!IN_LEV_FIELD(xx, yy))
12900       continue;
12901
12902     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12903       border_element = Feld[xx][yy];    // may be moving!
12904     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12905       border_element = Feld[xx][yy];
12906     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12907       border_element = MovingOrBlocked2Element(xx, yy);
12908     else
12909       continue;                 // center and border element do not touch
12910
12911     border_element_old[i] = border_element;
12912   }
12913
12914   for (i = 0; i < NUM_DIRECTIONS; i++)
12915   {
12916     int xx = x + xy[i][0];
12917     int yy = y + xy[i][1];
12918     int center_side = trigger_sides[i][0];
12919     int border_element = border_element_old[i];
12920
12921     if (border_element == -1)
12922       continue;
12923
12924     // check for change of border element
12925     CheckElementChangeBySide(xx, yy, border_element, center_element,
12926                              CE_TOUCHING_X, center_side);
12927
12928     // (center element cannot be player, so we dont have to check this here)
12929   }
12930
12931   for (i = 0; i < NUM_DIRECTIONS; i++)
12932   {
12933     int xx = x + xy[i][0];
12934     int yy = y + xy[i][1];
12935     int border_side = trigger_sides[i][1];
12936     int border_element = border_element_old[i];
12937
12938     if (border_element == -1)
12939       continue;
12940
12941     // check for change of center element (but change it only once)
12942     if (!change_center_element)
12943       change_center_element =
12944         CheckElementChangeBySide(x, y, center_element, border_element,
12945                                  CE_TOUCHING_X, border_side);
12946
12947     if (IS_PLAYER(xx, yy))
12948     {
12949       /* use player element that is initially defined in the level playfield,
12950          not the player element that corresponds to the runtime player number
12951          (example: a level that contains EL_PLAYER_3 as the only player would
12952          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12953       int player_element = PLAYERINFO(xx, yy)->initial_element;
12954
12955       CheckElementChangeBySide(x, y, center_element, player_element,
12956                                CE_TOUCHING_X, border_side);
12957     }
12958   }
12959 }
12960
12961 void TestIfElementHitsCustomElement(int x, int y, int direction)
12962 {
12963   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12964   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12965   int hitx = x + dx, hity = y + dy;
12966   int hitting_element = Feld[x][y];
12967   int touched_element;
12968
12969   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12970     return;
12971
12972   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12973                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12974
12975   if (IN_LEV_FIELD(hitx, hity))
12976   {
12977     int opposite_direction = MV_DIR_OPPOSITE(direction);
12978     int hitting_side = direction;
12979     int touched_side = opposite_direction;
12980     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12981                           MovDir[hitx][hity] != direction ||
12982                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12983
12984     object_hit = TRUE;
12985
12986     if (object_hit)
12987     {
12988       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12989                                CE_HITTING_X, touched_side);
12990
12991       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12992                                CE_HIT_BY_X, hitting_side);
12993
12994       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12995                                CE_HIT_BY_SOMETHING, opposite_direction);
12996
12997       if (IS_PLAYER(hitx, hity))
12998       {
12999         /* use player element that is initially defined in the level playfield,
13000            not the player element that corresponds to the runtime player number
13001            (example: a level that contains EL_PLAYER_3 as the only player would
13002            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13003         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13004
13005         CheckElementChangeBySide(x, y, hitting_element, player_element,
13006                                  CE_HITTING_X, touched_side);
13007       }
13008     }
13009   }
13010
13011   // "hitting something" is also true when hitting the playfield border
13012   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13013                            CE_HITTING_SOMETHING, direction);
13014 }
13015
13016 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13017 {
13018   int i, kill_x = -1, kill_y = -1;
13019
13020   int bad_element = -1;
13021   static int test_xy[4][2] =
13022   {
13023     { 0, -1 },
13024     { -1, 0 },
13025     { +1, 0 },
13026     { 0, +1 }
13027   };
13028   static int test_dir[4] =
13029   {
13030     MV_UP,
13031     MV_LEFT,
13032     MV_RIGHT,
13033     MV_DOWN
13034   };
13035
13036   for (i = 0; i < NUM_DIRECTIONS; i++)
13037   {
13038     int test_x, test_y, test_move_dir, test_element;
13039
13040     test_x = good_x + test_xy[i][0];
13041     test_y = good_y + test_xy[i][1];
13042
13043     if (!IN_LEV_FIELD(test_x, test_y))
13044       continue;
13045
13046     test_move_dir =
13047       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13048
13049     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13050
13051     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13052        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13053     */
13054     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13055         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13056     {
13057       kill_x = test_x;
13058       kill_y = test_y;
13059       bad_element = test_element;
13060
13061       break;
13062     }
13063   }
13064
13065   if (kill_x != -1 || kill_y != -1)
13066   {
13067     if (IS_PLAYER(good_x, good_y))
13068     {
13069       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13070
13071       if (player->shield_deadly_time_left > 0 &&
13072           !IS_INDESTRUCTIBLE(bad_element))
13073         Bang(kill_x, kill_y);
13074       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13075         KillPlayer(player);
13076     }
13077     else
13078       Bang(good_x, good_y);
13079   }
13080 }
13081
13082 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13083 {
13084   int i, kill_x = -1, kill_y = -1;
13085   int bad_element = Feld[bad_x][bad_y];
13086   static int test_xy[4][2] =
13087   {
13088     { 0, -1 },
13089     { -1, 0 },
13090     { +1, 0 },
13091     { 0, +1 }
13092   };
13093   static int touch_dir[4] =
13094   {
13095     MV_LEFT | MV_RIGHT,
13096     MV_UP   | MV_DOWN,
13097     MV_UP   | MV_DOWN,
13098     MV_LEFT | MV_RIGHT
13099   };
13100   static int test_dir[4] =
13101   {
13102     MV_UP,
13103     MV_LEFT,
13104     MV_RIGHT,
13105     MV_DOWN
13106   };
13107
13108   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13109     return;
13110
13111   for (i = 0; i < NUM_DIRECTIONS; i++)
13112   {
13113     int test_x, test_y, test_move_dir, test_element;
13114
13115     test_x = bad_x + test_xy[i][0];
13116     test_y = bad_y + test_xy[i][1];
13117
13118     if (!IN_LEV_FIELD(test_x, test_y))
13119       continue;
13120
13121     test_move_dir =
13122       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13123
13124     test_element = Feld[test_x][test_y];
13125
13126     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13127        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13128     */
13129     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13130         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13131     {
13132       // good thing is player or penguin that does not move away
13133       if (IS_PLAYER(test_x, test_y))
13134       {
13135         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13136
13137         if (bad_element == EL_ROBOT && player->is_moving)
13138           continue;     // robot does not kill player if he is moving
13139
13140         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13141         {
13142           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13143             continue;           // center and border element do not touch
13144         }
13145
13146         kill_x = test_x;
13147         kill_y = test_y;
13148
13149         break;
13150       }
13151       else if (test_element == EL_PENGUIN)
13152       {
13153         kill_x = test_x;
13154         kill_y = test_y;
13155
13156         break;
13157       }
13158     }
13159   }
13160
13161   if (kill_x != -1 || kill_y != -1)
13162   {
13163     if (IS_PLAYER(kill_x, kill_y))
13164     {
13165       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13166
13167       if (player->shield_deadly_time_left > 0 &&
13168           !IS_INDESTRUCTIBLE(bad_element))
13169         Bang(bad_x, bad_y);
13170       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13171         KillPlayer(player);
13172     }
13173     else
13174       Bang(kill_x, kill_y);
13175   }
13176 }
13177
13178 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13179 {
13180   int bad_element = Feld[bad_x][bad_y];
13181   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13182   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13183   int test_x = bad_x + dx, test_y = bad_y + dy;
13184   int test_move_dir, test_element;
13185   int kill_x = -1, kill_y = -1;
13186
13187   if (!IN_LEV_FIELD(test_x, test_y))
13188     return;
13189
13190   test_move_dir =
13191     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13192
13193   test_element = Feld[test_x][test_y];
13194
13195   if (test_move_dir != bad_move_dir)
13196   {
13197     // good thing can be player or penguin that does not move away
13198     if (IS_PLAYER(test_x, test_y))
13199     {
13200       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13201
13202       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13203          player as being hit when he is moving towards the bad thing, because
13204          the "get hit by" condition would be lost after the player stops) */
13205       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13206         return;         // player moves away from bad thing
13207
13208       kill_x = test_x;
13209       kill_y = test_y;
13210     }
13211     else if (test_element == EL_PENGUIN)
13212     {
13213       kill_x = test_x;
13214       kill_y = test_y;
13215     }
13216   }
13217
13218   if (kill_x != -1 || kill_y != -1)
13219   {
13220     if (IS_PLAYER(kill_x, kill_y))
13221     {
13222       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13223
13224       if (player->shield_deadly_time_left > 0 &&
13225           !IS_INDESTRUCTIBLE(bad_element))
13226         Bang(bad_x, bad_y);
13227       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13228         KillPlayer(player);
13229     }
13230     else
13231       Bang(kill_x, kill_y);
13232   }
13233 }
13234
13235 void TestIfPlayerTouchesBadThing(int x, int y)
13236 {
13237   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13238 }
13239
13240 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13241 {
13242   TestIfGoodThingHitsBadThing(x, y, move_dir);
13243 }
13244
13245 void TestIfBadThingTouchesPlayer(int x, int y)
13246 {
13247   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13248 }
13249
13250 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13251 {
13252   TestIfBadThingHitsGoodThing(x, y, move_dir);
13253 }
13254
13255 void TestIfFriendTouchesBadThing(int x, int y)
13256 {
13257   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13258 }
13259
13260 void TestIfBadThingTouchesFriend(int x, int y)
13261 {
13262   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13263 }
13264
13265 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13266 {
13267   int i, kill_x = bad_x, kill_y = bad_y;
13268   static int xy[4][2] =
13269   {
13270     { 0, -1 },
13271     { -1, 0 },
13272     { +1, 0 },
13273     { 0, +1 }
13274   };
13275
13276   for (i = 0; i < NUM_DIRECTIONS; i++)
13277   {
13278     int x, y, element;
13279
13280     x = bad_x + xy[i][0];
13281     y = bad_y + xy[i][1];
13282     if (!IN_LEV_FIELD(x, y))
13283       continue;
13284
13285     element = Feld[x][y];
13286     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13287         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13288     {
13289       kill_x = x;
13290       kill_y = y;
13291       break;
13292     }
13293   }
13294
13295   if (kill_x != bad_x || kill_y != bad_y)
13296     Bang(bad_x, bad_y);
13297 }
13298
13299 void KillPlayer(struct PlayerInfo *player)
13300 {
13301   int jx = player->jx, jy = player->jy;
13302
13303   if (!player->active)
13304     return;
13305
13306 #if 0
13307   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13308          player->killed, player->active, player->reanimated);
13309 #endif
13310
13311   /* the following code was introduced to prevent an infinite loop when calling
13312      -> Bang()
13313      -> CheckTriggeredElementChangeExt()
13314      -> ExecuteCustomElementAction()
13315      -> KillPlayer()
13316      -> (infinitely repeating the above sequence of function calls)
13317      which occurs when killing the player while having a CE with the setting
13318      "kill player X when explosion of <player X>"; the solution using a new
13319      field "player->killed" was chosen for backwards compatibility, although
13320      clever use of the fields "player->active" etc. would probably also work */
13321 #if 1
13322   if (player->killed)
13323     return;
13324 #endif
13325
13326   player->killed = TRUE;
13327
13328   // remove accessible field at the player's position
13329   Feld[jx][jy] = EL_EMPTY;
13330
13331   // deactivate shield (else Bang()/Explode() would not work right)
13332   player->shield_normal_time_left = 0;
13333   player->shield_deadly_time_left = 0;
13334
13335 #if 0
13336   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13337          player->killed, player->active, player->reanimated);
13338 #endif
13339
13340   Bang(jx, jy);
13341
13342 #if 0
13343   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13344          player->killed, player->active, player->reanimated);
13345 #endif
13346
13347   if (player->reanimated)       // killed player may have been reanimated
13348     player->killed = player->reanimated = FALSE;
13349   else
13350     BuryPlayer(player);
13351 }
13352
13353 static void KillPlayerUnlessEnemyProtected(int x, int y)
13354 {
13355   if (!PLAYER_ENEMY_PROTECTED(x, y))
13356     KillPlayer(PLAYERINFO(x, y));
13357 }
13358
13359 static void KillPlayerUnlessExplosionProtected(int x, int y)
13360 {
13361   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13362     KillPlayer(PLAYERINFO(x, y));
13363 }
13364
13365 void BuryPlayer(struct PlayerInfo *player)
13366 {
13367   int jx = player->jx, jy = player->jy;
13368
13369   if (!player->active)
13370     return;
13371
13372   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13373   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13374
13375   player->GameOver = TRUE;
13376   RemovePlayer(player);
13377 }
13378
13379 void RemovePlayer(struct PlayerInfo *player)
13380 {
13381   int jx = player->jx, jy = player->jy;
13382   int i, found = FALSE;
13383
13384   player->present = FALSE;
13385   player->active = FALSE;
13386
13387   if (!ExplodeField[jx][jy])
13388     StorePlayer[jx][jy] = 0;
13389
13390   if (player->is_moving)
13391     TEST_DrawLevelField(player->last_jx, player->last_jy);
13392
13393   for (i = 0; i < MAX_PLAYERS; i++)
13394     if (stored_player[i].active)
13395       found = TRUE;
13396
13397   if (!found)
13398     AllPlayersGone = TRUE;
13399
13400   ExitX = ZX = jx;
13401   ExitY = ZY = jy;
13402 }
13403
13404 void ExitPlayer(struct PlayerInfo *player)
13405 {
13406   DrawPlayer(player);   // needed here only to cleanup last field
13407   RemovePlayer(player);
13408
13409   if (local_player->players_still_needed > 0)
13410     local_player->players_still_needed--;
13411
13412   // also set if some players not yet gone, but not needed to solve level
13413   if (local_player->players_still_needed == 0)
13414     AllPlayersGone = TRUE;
13415 }
13416
13417 static void setFieldForSnapping(int x, int y, int element, int direction)
13418 {
13419   struct ElementInfo *ei = &element_info[element];
13420   int direction_bit = MV_DIR_TO_BIT(direction);
13421   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13422   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13423                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13424
13425   Feld[x][y] = EL_ELEMENT_SNAPPING;
13426   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13427
13428   ResetGfxAnimation(x, y);
13429
13430   GfxElement[x][y] = element;
13431   GfxAction[x][y] = action;
13432   GfxDir[x][y] = direction;
13433   GfxFrame[x][y] = -1;
13434 }
13435
13436 /*
13437   =============================================================================
13438   checkDiagonalPushing()
13439   -----------------------------------------------------------------------------
13440   check if diagonal input device direction results in pushing of object
13441   (by checking if the alternative direction is walkable, diggable, ...)
13442   =============================================================================
13443 */
13444
13445 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13446                                     int x, int y, int real_dx, int real_dy)
13447 {
13448   int jx, jy, dx, dy, xx, yy;
13449
13450   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13451     return TRUE;
13452
13453   // diagonal direction: check alternative direction
13454   jx = player->jx;
13455   jy = player->jy;
13456   dx = x - jx;
13457   dy = y - jy;
13458   xx = jx + (dx == 0 ? real_dx : 0);
13459   yy = jy + (dy == 0 ? real_dy : 0);
13460
13461   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13462 }
13463
13464 /*
13465   =============================================================================
13466   DigField()
13467   -----------------------------------------------------------------------------
13468   x, y:                 field next to player (non-diagonal) to try to dig to
13469   real_dx, real_dy:     direction as read from input device (can be diagonal)
13470   =============================================================================
13471 */
13472
13473 static int DigField(struct PlayerInfo *player,
13474                     int oldx, int oldy, int x, int y,
13475                     int real_dx, int real_dy, int mode)
13476 {
13477   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13478   boolean player_was_pushing = player->is_pushing;
13479   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13480   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13481   int jx = oldx, jy = oldy;
13482   int dx = x - jx, dy = y - jy;
13483   int nextx = x + dx, nexty = y + dy;
13484   int move_direction = (dx == -1 ? MV_LEFT  :
13485                         dx == +1 ? MV_RIGHT :
13486                         dy == -1 ? MV_UP    :
13487                         dy == +1 ? MV_DOWN  : MV_NONE);
13488   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13489   int dig_side = MV_DIR_OPPOSITE(move_direction);
13490   int old_element = Feld[jx][jy];
13491   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13492   int collect_count;
13493
13494   if (is_player)                // function can also be called by EL_PENGUIN
13495   {
13496     if (player->MovPos == 0)
13497     {
13498       player->is_digging = FALSE;
13499       player->is_collecting = FALSE;
13500     }
13501
13502     if (player->MovPos == 0)    // last pushing move finished
13503       player->is_pushing = FALSE;
13504
13505     if (mode == DF_NO_PUSH)     // player just stopped pushing
13506     {
13507       player->is_switching = FALSE;
13508       player->push_delay = -1;
13509
13510       return MP_NO_ACTION;
13511     }
13512   }
13513
13514   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13515     old_element = Back[jx][jy];
13516
13517   // in case of element dropped at player position, check background
13518   else if (Back[jx][jy] != EL_EMPTY &&
13519            game.engine_version >= VERSION_IDENT(2,2,0,0))
13520     old_element = Back[jx][jy];
13521
13522   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13523     return MP_NO_ACTION;        // field has no opening in this direction
13524
13525   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13526     return MP_NO_ACTION;        // field has no opening in this direction
13527
13528   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13529   {
13530     SplashAcid(x, y);
13531
13532     Feld[jx][jy] = player->artwork_element;
13533     InitMovingField(jx, jy, MV_DOWN);
13534     Store[jx][jy] = EL_ACID;
13535     ContinueMoving(jx, jy);
13536     BuryPlayer(player);
13537
13538     return MP_DONT_RUN_INTO;
13539   }
13540
13541   if (player_can_move && DONT_RUN_INTO(element))
13542   {
13543     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13544
13545     return MP_DONT_RUN_INTO;
13546   }
13547
13548   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13549     return MP_NO_ACTION;
13550
13551   collect_count = element_info[element].collect_count_initial;
13552
13553   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13554     return MP_NO_ACTION;
13555
13556   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13557     player_can_move = player_can_move_or_snap;
13558
13559   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13560       game.engine_version >= VERSION_IDENT(2,2,0,0))
13561   {
13562     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13563                                player->index_bit, dig_side);
13564     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13565                                         player->index_bit, dig_side);
13566
13567     if (element == EL_DC_LANDMINE)
13568       Bang(x, y);
13569
13570     if (Feld[x][y] != element)          // field changed by snapping
13571       return MP_ACTION;
13572
13573     return MP_NO_ACTION;
13574   }
13575
13576   if (player->gravity && is_player && !player->is_auto_moving &&
13577       canFallDown(player) && move_direction != MV_DOWN &&
13578       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13579     return MP_NO_ACTION;        // player cannot walk here due to gravity
13580
13581   if (player_can_move &&
13582       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13583   {
13584     int sound_element = SND_ELEMENT(element);
13585     int sound_action = ACTION_WALKING;
13586
13587     if (IS_RND_GATE(element))
13588     {
13589       if (!player->key[RND_GATE_NR(element)])
13590         return MP_NO_ACTION;
13591     }
13592     else if (IS_RND_GATE_GRAY(element))
13593     {
13594       if (!player->key[RND_GATE_GRAY_NR(element)])
13595         return MP_NO_ACTION;
13596     }
13597     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13598     {
13599       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13600         return MP_NO_ACTION;
13601     }
13602     else if (element == EL_EXIT_OPEN ||
13603              element == EL_EM_EXIT_OPEN ||
13604              element == EL_EM_EXIT_OPENING ||
13605              element == EL_STEEL_EXIT_OPEN ||
13606              element == EL_EM_STEEL_EXIT_OPEN ||
13607              element == EL_EM_STEEL_EXIT_OPENING ||
13608              element == EL_SP_EXIT_OPEN ||
13609              element == EL_SP_EXIT_OPENING)
13610     {
13611       sound_action = ACTION_PASSING;    // player is passing exit
13612     }
13613     else if (element == EL_EMPTY)
13614     {
13615       sound_action = ACTION_MOVING;             // nothing to walk on
13616     }
13617
13618     // play sound from background or player, whatever is available
13619     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13620       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13621     else
13622       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13623   }
13624   else if (player_can_move &&
13625            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13626   {
13627     if (!ACCESS_FROM(element, opposite_direction))
13628       return MP_NO_ACTION;      // field not accessible from this direction
13629
13630     if (CAN_MOVE(element))      // only fixed elements can be passed!
13631       return MP_NO_ACTION;
13632
13633     if (IS_EM_GATE(element))
13634     {
13635       if (!player->key[EM_GATE_NR(element)])
13636         return MP_NO_ACTION;
13637     }
13638     else if (IS_EM_GATE_GRAY(element))
13639     {
13640       if (!player->key[EM_GATE_GRAY_NR(element)])
13641         return MP_NO_ACTION;
13642     }
13643     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13644     {
13645       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13646         return MP_NO_ACTION;
13647     }
13648     else if (IS_EMC_GATE(element))
13649     {
13650       if (!player->key[EMC_GATE_NR(element)])
13651         return MP_NO_ACTION;
13652     }
13653     else if (IS_EMC_GATE_GRAY(element))
13654     {
13655       if (!player->key[EMC_GATE_GRAY_NR(element)])
13656         return MP_NO_ACTION;
13657     }
13658     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13659     {
13660       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13661         return MP_NO_ACTION;
13662     }
13663     else if (element == EL_DC_GATE_WHITE ||
13664              element == EL_DC_GATE_WHITE_GRAY ||
13665              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13666     {
13667       if (player->num_white_keys == 0)
13668         return MP_NO_ACTION;
13669
13670       player->num_white_keys--;
13671     }
13672     else if (IS_SP_PORT(element))
13673     {
13674       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13675           element == EL_SP_GRAVITY_PORT_RIGHT ||
13676           element == EL_SP_GRAVITY_PORT_UP ||
13677           element == EL_SP_GRAVITY_PORT_DOWN)
13678         player->gravity = !player->gravity;
13679       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13680                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13681                element == EL_SP_GRAVITY_ON_PORT_UP ||
13682                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13683         player->gravity = TRUE;
13684       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13685                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13686                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13687                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13688         player->gravity = FALSE;
13689     }
13690
13691     // automatically move to the next field with double speed
13692     player->programmed_action = move_direction;
13693
13694     if (player->move_delay_reset_counter == 0)
13695     {
13696       player->move_delay_reset_counter = 2;     // two double speed steps
13697
13698       DOUBLE_PLAYER_SPEED(player);
13699     }
13700
13701     PlayLevelSoundAction(x, y, ACTION_PASSING);
13702   }
13703   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13704   {
13705     RemoveField(x, y);
13706
13707     if (mode != DF_SNAP)
13708     {
13709       GfxElement[x][y] = GFX_ELEMENT(element);
13710       player->is_digging = TRUE;
13711     }
13712
13713     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13714
13715     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13716                                         player->index_bit, dig_side);
13717
13718     if (mode == DF_SNAP)
13719     {
13720       if (level.block_snap_field)
13721         setFieldForSnapping(x, y, element, move_direction);
13722       else
13723         TestIfElementTouchesCustomElement(x, y);        // for empty space
13724
13725       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13726                                           player->index_bit, dig_side);
13727     }
13728   }
13729   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13730   {
13731     RemoveField(x, y);
13732
13733     if (is_player && mode != DF_SNAP)
13734     {
13735       GfxElement[x][y] = element;
13736       player->is_collecting = TRUE;
13737     }
13738
13739     if (element == EL_SPEED_PILL)
13740     {
13741       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13742     }
13743     else if (element == EL_EXTRA_TIME && level.time > 0)
13744     {
13745       TimeLeft += level.extra_time;
13746
13747       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13748
13749       DisplayGameControlValues();
13750     }
13751     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13752     {
13753       player->shield_normal_time_left += level.shield_normal_time;
13754       if (element == EL_SHIELD_DEADLY)
13755         player->shield_deadly_time_left += level.shield_deadly_time;
13756     }
13757     else if (element == EL_DYNAMITE ||
13758              element == EL_EM_DYNAMITE ||
13759              element == EL_SP_DISK_RED)
13760     {
13761       if (player->inventory_size < MAX_INVENTORY_SIZE)
13762         player->inventory_element[player->inventory_size++] = element;
13763
13764       DrawGameDoorValues();
13765     }
13766     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13767     {
13768       player->dynabomb_count++;
13769       player->dynabombs_left++;
13770     }
13771     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13772     {
13773       player->dynabomb_size++;
13774     }
13775     else if (element == EL_DYNABOMB_INCREASE_POWER)
13776     {
13777       player->dynabomb_xl = TRUE;
13778     }
13779     else if (IS_KEY(element))
13780     {
13781       player->key[KEY_NR(element)] = TRUE;
13782
13783       DrawGameDoorValues();
13784     }
13785     else if (element == EL_DC_KEY_WHITE)
13786     {
13787       player->num_white_keys++;
13788
13789       // display white keys?
13790       // DrawGameDoorValues();
13791     }
13792     else if (IS_ENVELOPE(element))
13793     {
13794       player->show_envelope = element;
13795     }
13796     else if (element == EL_EMC_LENSES)
13797     {
13798       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13799
13800       RedrawAllInvisibleElementsForLenses();
13801     }
13802     else if (element == EL_EMC_MAGNIFIER)
13803     {
13804       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13805
13806       RedrawAllInvisibleElementsForMagnifier();
13807     }
13808     else if (IS_DROPPABLE(element) ||
13809              IS_THROWABLE(element))     // can be collected and dropped
13810     {
13811       int i;
13812
13813       if (collect_count == 0)
13814         player->inventory_infinite_element = element;
13815       else
13816         for (i = 0; i < collect_count; i++)
13817           if (player->inventory_size < MAX_INVENTORY_SIZE)
13818             player->inventory_element[player->inventory_size++] = element;
13819
13820       DrawGameDoorValues();
13821     }
13822     else if (collect_count > 0)
13823     {
13824       local_player->gems_still_needed -= collect_count;
13825       if (local_player->gems_still_needed < 0)
13826         local_player->gems_still_needed = 0;
13827
13828       game.snapshot.collected_item = TRUE;
13829
13830       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13831
13832       DisplayGameControlValues();
13833     }
13834
13835     RaiseScoreElement(element);
13836     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13837
13838     if (is_player)
13839       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13840                                           player->index_bit, dig_side);
13841
13842     if (mode == DF_SNAP)
13843     {
13844       if (level.block_snap_field)
13845         setFieldForSnapping(x, y, element, move_direction);
13846       else
13847         TestIfElementTouchesCustomElement(x, y);        // for empty space
13848
13849       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13850                                           player->index_bit, dig_side);
13851     }
13852   }
13853   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13854   {
13855     if (mode == DF_SNAP && element != EL_BD_ROCK)
13856       return MP_NO_ACTION;
13857
13858     if (CAN_FALL(element) && dy)
13859       return MP_NO_ACTION;
13860
13861     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13862         !(element == EL_SPRING && level.use_spring_bug))
13863       return MP_NO_ACTION;
13864
13865     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13866         ((move_direction & MV_VERTICAL &&
13867           ((element_info[element].move_pattern & MV_LEFT &&
13868             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13869            (element_info[element].move_pattern & MV_RIGHT &&
13870             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13871          (move_direction & MV_HORIZONTAL &&
13872           ((element_info[element].move_pattern & MV_UP &&
13873             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13874            (element_info[element].move_pattern & MV_DOWN &&
13875             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13876       return MP_NO_ACTION;
13877
13878     // do not push elements already moving away faster than player
13879     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13880         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13881       return MP_NO_ACTION;
13882
13883     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13884     {
13885       if (player->push_delay_value == -1 || !player_was_pushing)
13886         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13887     }
13888     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13889     {
13890       if (player->push_delay_value == -1)
13891         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13892     }
13893     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13894     {
13895       if (!player->is_pushing)
13896         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13897     }
13898
13899     player->is_pushing = TRUE;
13900     player->is_active = TRUE;
13901
13902     if (!(IN_LEV_FIELD(nextx, nexty) &&
13903           (IS_FREE(nextx, nexty) ||
13904            (IS_SB_ELEMENT(element) &&
13905             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13906            (IS_CUSTOM_ELEMENT(element) &&
13907             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13908       return MP_NO_ACTION;
13909
13910     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13911       return MP_NO_ACTION;
13912
13913     if (player->push_delay == -1)       // new pushing; restart delay
13914       player->push_delay = 0;
13915
13916     if (player->push_delay < player->push_delay_value &&
13917         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13918         element != EL_SPRING && element != EL_BALLOON)
13919     {
13920       // make sure that there is no move delay before next try to push
13921       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13922         player->move_delay = 0;
13923
13924       return MP_NO_ACTION;
13925     }
13926
13927     if (IS_CUSTOM_ELEMENT(element) &&
13928         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13929     {
13930       if (!DigFieldByCE(nextx, nexty, element))
13931         return MP_NO_ACTION;
13932     }
13933
13934     if (IS_SB_ELEMENT(element))
13935     {
13936       if (element == EL_SOKOBAN_FIELD_FULL)
13937       {
13938         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13939         local_player->sokobanfields_still_needed++;
13940       }
13941
13942       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13943       {
13944         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13945         local_player->sokobanfields_still_needed--;
13946       }
13947
13948       Feld[x][y] = EL_SOKOBAN_OBJECT;
13949
13950       if (Back[x][y] == Back[nextx][nexty])
13951         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13952       else if (Back[x][y] != 0)
13953         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13954                                     ACTION_EMPTYING);
13955       else
13956         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13957                                     ACTION_FILLING);
13958
13959       if (local_player->sokobanfields_still_needed == 0 &&
13960           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13961       {
13962         local_player->players_still_needed = 0;
13963
13964         PlayerWins(local_player);
13965
13966         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13967       }
13968     }
13969     else
13970       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13971
13972     InitMovingField(x, y, move_direction);
13973     GfxAction[x][y] = ACTION_PUSHING;
13974
13975     if (mode == DF_SNAP)
13976       ContinueMoving(x, y);
13977     else
13978       MovPos[x][y] = (dx != 0 ? dx : dy);
13979
13980     Pushed[x][y] = TRUE;
13981     Pushed[nextx][nexty] = TRUE;
13982
13983     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13984       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13985     else
13986       player->push_delay_value = -1;    // get new value later
13987
13988     // check for element change _after_ element has been pushed
13989     if (game.use_change_when_pushing_bug)
13990     {
13991       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13992                                  player->index_bit, dig_side);
13993       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13994                                           player->index_bit, dig_side);
13995     }
13996   }
13997   else if (IS_SWITCHABLE(element))
13998   {
13999     if (PLAYER_SWITCHING(player, x, y))
14000     {
14001       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14002                                           player->index_bit, dig_side);
14003
14004       return MP_ACTION;
14005     }
14006
14007     player->is_switching = TRUE;
14008     player->switch_x = x;
14009     player->switch_y = y;
14010
14011     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14012
14013     if (element == EL_ROBOT_WHEEL)
14014     {
14015       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14016       ZX = x;
14017       ZY = y;
14018
14019       game.robot_wheel_active = TRUE;
14020
14021       TEST_DrawLevelField(x, y);
14022     }
14023     else if (element == EL_SP_TERMINAL)
14024     {
14025       int xx, yy;
14026
14027       SCAN_PLAYFIELD(xx, yy)
14028       {
14029         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14030         {
14031           Bang(xx, yy);
14032         }
14033         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14034         {
14035           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14036
14037           ResetGfxAnimation(xx, yy);
14038           TEST_DrawLevelField(xx, yy);
14039         }
14040       }
14041     }
14042     else if (IS_BELT_SWITCH(element))
14043     {
14044       ToggleBeltSwitch(x, y);
14045     }
14046     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14047              element == EL_SWITCHGATE_SWITCH_DOWN ||
14048              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14049              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14050     {
14051       ToggleSwitchgateSwitch(x, y);
14052     }
14053     else if (element == EL_LIGHT_SWITCH ||
14054              element == EL_LIGHT_SWITCH_ACTIVE)
14055     {
14056       ToggleLightSwitch(x, y);
14057     }
14058     else if (element == EL_TIMEGATE_SWITCH ||
14059              element == EL_DC_TIMEGATE_SWITCH)
14060     {
14061       ActivateTimegateSwitch(x, y);
14062     }
14063     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14064              element == EL_BALLOON_SWITCH_RIGHT ||
14065              element == EL_BALLOON_SWITCH_UP    ||
14066              element == EL_BALLOON_SWITCH_DOWN  ||
14067              element == EL_BALLOON_SWITCH_NONE  ||
14068              element == EL_BALLOON_SWITCH_ANY)
14069     {
14070       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14071                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14072                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14073                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14074                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14075                              move_direction);
14076     }
14077     else if (element == EL_LAMP)
14078     {
14079       Feld[x][y] = EL_LAMP_ACTIVE;
14080       local_player->lights_still_needed--;
14081
14082       ResetGfxAnimation(x, y);
14083       TEST_DrawLevelField(x, y);
14084     }
14085     else if (element == EL_TIME_ORB_FULL)
14086     {
14087       Feld[x][y] = EL_TIME_ORB_EMPTY;
14088
14089       if (level.time > 0 || level.use_time_orb_bug)
14090       {
14091         TimeLeft += level.time_orb_time;
14092         game.no_time_limit = FALSE;
14093
14094         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14095
14096         DisplayGameControlValues();
14097       }
14098
14099       ResetGfxAnimation(x, y);
14100       TEST_DrawLevelField(x, y);
14101     }
14102     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14103              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14104     {
14105       int xx, yy;
14106
14107       game.ball_state = !game.ball_state;
14108
14109       SCAN_PLAYFIELD(xx, yy)
14110       {
14111         int e = Feld[xx][yy];
14112
14113         if (game.ball_state)
14114         {
14115           if (e == EL_EMC_MAGIC_BALL)
14116             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14117           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14118             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14119         }
14120         else
14121         {
14122           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14123             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14124           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14125             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14126         }
14127       }
14128     }
14129
14130     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14131                                         player->index_bit, dig_side);
14132
14133     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14134                                         player->index_bit, dig_side);
14135
14136     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14137                                         player->index_bit, dig_side);
14138
14139     return MP_ACTION;
14140   }
14141   else
14142   {
14143     if (!PLAYER_SWITCHING(player, x, y))
14144     {
14145       player->is_switching = TRUE;
14146       player->switch_x = x;
14147       player->switch_y = y;
14148
14149       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14150                                  player->index_bit, dig_side);
14151       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14152                                           player->index_bit, dig_side);
14153
14154       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14155                                  player->index_bit, dig_side);
14156       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14157                                           player->index_bit, dig_side);
14158     }
14159
14160     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14161                                player->index_bit, dig_side);
14162     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14163                                         player->index_bit, dig_side);
14164
14165     return MP_NO_ACTION;
14166   }
14167
14168   player->push_delay = -1;
14169
14170   if (is_player)                // function can also be called by EL_PENGUIN
14171   {
14172     if (Feld[x][y] != element)          // really digged/collected something
14173     {
14174       player->is_collecting = !player->is_digging;
14175       player->is_active = TRUE;
14176     }
14177   }
14178
14179   return MP_MOVING;
14180 }
14181
14182 static boolean DigFieldByCE(int x, int y, int digging_element)
14183 {
14184   int element = Feld[x][y];
14185
14186   if (!IS_FREE(x, y))
14187   {
14188     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14189                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14190                   ACTION_BREAKING);
14191
14192     // no element can dig solid indestructible elements
14193     if (IS_INDESTRUCTIBLE(element) &&
14194         !IS_DIGGABLE(element) &&
14195         !IS_COLLECTIBLE(element))
14196       return FALSE;
14197
14198     if (AmoebaNr[x][y] &&
14199         (element == EL_AMOEBA_FULL ||
14200          element == EL_BD_AMOEBA ||
14201          element == EL_AMOEBA_GROWING))
14202     {
14203       AmoebaCnt[AmoebaNr[x][y]]--;
14204       AmoebaCnt2[AmoebaNr[x][y]]--;
14205     }
14206
14207     if (IS_MOVING(x, y))
14208       RemoveMovingField(x, y);
14209     else
14210     {
14211       RemoveField(x, y);
14212       TEST_DrawLevelField(x, y);
14213     }
14214
14215     // if digged element was about to explode, prevent the explosion
14216     ExplodeField[x][y] = EX_TYPE_NONE;
14217
14218     PlayLevelSoundAction(x, y, action);
14219   }
14220
14221   Store[x][y] = EL_EMPTY;
14222
14223   // this makes it possible to leave the removed element again
14224   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14225     Store[x][y] = element;
14226
14227   return TRUE;
14228 }
14229
14230 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14231 {
14232   int jx = player->jx, jy = player->jy;
14233   int x = jx + dx, y = jy + dy;
14234   int snap_direction = (dx == -1 ? MV_LEFT  :
14235                         dx == +1 ? MV_RIGHT :
14236                         dy == -1 ? MV_UP    :
14237                         dy == +1 ? MV_DOWN  : MV_NONE);
14238   boolean can_continue_snapping = (level.continuous_snapping &&
14239                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14240
14241   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14242     return FALSE;
14243
14244   if (!player->active || !IN_LEV_FIELD(x, y))
14245     return FALSE;
14246
14247   if (dx && dy)
14248     return FALSE;
14249
14250   if (!dx && !dy)
14251   {
14252     if (player->MovPos == 0)
14253       player->is_pushing = FALSE;
14254
14255     player->is_snapping = FALSE;
14256
14257     if (player->MovPos == 0)
14258     {
14259       player->is_moving = FALSE;
14260       player->is_digging = FALSE;
14261       player->is_collecting = FALSE;
14262     }
14263
14264     return FALSE;
14265   }
14266
14267   // prevent snapping with already pressed snap key when not allowed
14268   if (player->is_snapping && !can_continue_snapping)
14269     return FALSE;
14270
14271   player->MovDir = snap_direction;
14272
14273   if (player->MovPos == 0)
14274   {
14275     player->is_moving = FALSE;
14276     player->is_digging = FALSE;
14277     player->is_collecting = FALSE;
14278   }
14279
14280   player->is_dropping = FALSE;
14281   player->is_dropping_pressed = FALSE;
14282   player->drop_pressed_delay = 0;
14283
14284   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14285     return FALSE;
14286
14287   player->is_snapping = TRUE;
14288   player->is_active = TRUE;
14289
14290   if (player->MovPos == 0)
14291   {
14292     player->is_moving = FALSE;
14293     player->is_digging = FALSE;
14294     player->is_collecting = FALSE;
14295   }
14296
14297   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14298     TEST_DrawLevelField(player->last_jx, player->last_jy);
14299
14300   TEST_DrawLevelField(x, y);
14301
14302   return TRUE;
14303 }
14304
14305 static boolean DropElement(struct PlayerInfo *player)
14306 {
14307   int old_element, new_element;
14308   int dropx = player->jx, dropy = player->jy;
14309   int drop_direction = player->MovDir;
14310   int drop_side = drop_direction;
14311   int drop_element = get_next_dropped_element(player);
14312
14313   /* do not drop an element on top of another element; when holding drop key
14314      pressed without moving, dropped element must move away before the next
14315      element can be dropped (this is especially important if the next element
14316      is dynamite, which can be placed on background for historical reasons) */
14317   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14318     return MP_ACTION;
14319
14320   if (IS_THROWABLE(drop_element))
14321   {
14322     dropx += GET_DX_FROM_DIR(drop_direction);
14323     dropy += GET_DY_FROM_DIR(drop_direction);
14324
14325     if (!IN_LEV_FIELD(dropx, dropy))
14326       return FALSE;
14327   }
14328
14329   old_element = Feld[dropx][dropy];     // old element at dropping position
14330   new_element = drop_element;           // default: no change when dropping
14331
14332   // check if player is active, not moving and ready to drop
14333   if (!player->active || player->MovPos || player->drop_delay > 0)
14334     return FALSE;
14335
14336   // check if player has anything that can be dropped
14337   if (new_element == EL_UNDEFINED)
14338     return FALSE;
14339
14340   // only set if player has anything that can be dropped
14341   player->is_dropping_pressed = TRUE;
14342
14343   // check if drop key was pressed long enough for EM style dynamite
14344   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14345     return FALSE;
14346
14347   // check if anything can be dropped at the current position
14348   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14349     return FALSE;
14350
14351   // collected custom elements can only be dropped on empty fields
14352   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14353     return FALSE;
14354
14355   if (old_element != EL_EMPTY)
14356     Back[dropx][dropy] = old_element;   // store old element on this field
14357
14358   ResetGfxAnimation(dropx, dropy);
14359   ResetRandomAnimationValue(dropx, dropy);
14360
14361   if (player->inventory_size > 0 ||
14362       player->inventory_infinite_element != EL_UNDEFINED)
14363   {
14364     if (player->inventory_size > 0)
14365     {
14366       player->inventory_size--;
14367
14368       DrawGameDoorValues();
14369
14370       if (new_element == EL_DYNAMITE)
14371         new_element = EL_DYNAMITE_ACTIVE;
14372       else if (new_element == EL_EM_DYNAMITE)
14373         new_element = EL_EM_DYNAMITE_ACTIVE;
14374       else if (new_element == EL_SP_DISK_RED)
14375         new_element = EL_SP_DISK_RED_ACTIVE;
14376     }
14377
14378     Feld[dropx][dropy] = new_element;
14379
14380     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14381       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14382                           el2img(Feld[dropx][dropy]), 0);
14383
14384     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14385
14386     // needed if previous element just changed to "empty" in the last frame
14387     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14388
14389     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14390                                player->index_bit, drop_side);
14391     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14392                                         CE_PLAYER_DROPS_X,
14393                                         player->index_bit, drop_side);
14394
14395     TestIfElementTouchesCustomElement(dropx, dropy);
14396   }
14397   else          // player is dropping a dyna bomb
14398   {
14399     player->dynabombs_left--;
14400
14401     Feld[dropx][dropy] = new_element;
14402
14403     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14404       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14405                           el2img(Feld[dropx][dropy]), 0);
14406
14407     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14408   }
14409
14410   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14411     InitField_WithBug1(dropx, dropy, FALSE);
14412
14413   new_element = Feld[dropx][dropy];     // element might have changed
14414
14415   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14416       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14417   {
14418     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14419       MovDir[dropx][dropy] = drop_direction;
14420
14421     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14422
14423     // do not cause impact style collision by dropping elements that can fall
14424     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14425   }
14426
14427   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14428   player->is_dropping = TRUE;
14429
14430   player->drop_pressed_delay = 0;
14431   player->is_dropping_pressed = FALSE;
14432
14433   player->drop_x = dropx;
14434   player->drop_y = dropy;
14435
14436   return TRUE;
14437 }
14438
14439 // ----------------------------------------------------------------------------
14440 // game sound playing functions
14441 // ----------------------------------------------------------------------------
14442
14443 static int *loop_sound_frame = NULL;
14444 static int *loop_sound_volume = NULL;
14445
14446 void InitPlayLevelSound(void)
14447 {
14448   int num_sounds = getSoundListSize();
14449
14450   checked_free(loop_sound_frame);
14451   checked_free(loop_sound_volume);
14452
14453   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14454   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14455 }
14456
14457 static void PlayLevelSound(int x, int y, int nr)
14458 {
14459   int sx = SCREENX(x), sy = SCREENY(y);
14460   int volume, stereo_position;
14461   int max_distance = 8;
14462   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14463
14464   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14465       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14466     return;
14467
14468   if (!IN_LEV_FIELD(x, y) ||
14469       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14470       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14471     return;
14472
14473   volume = SOUND_MAX_VOLUME;
14474
14475   if (!IN_SCR_FIELD(sx, sy))
14476   {
14477     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14478     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14479
14480     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14481   }
14482
14483   stereo_position = (SOUND_MAX_LEFT +
14484                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14485                      (SCR_FIELDX + 2 * max_distance));
14486
14487   if (IS_LOOP_SOUND(nr))
14488   {
14489     /* This assures that quieter loop sounds do not overwrite louder ones,
14490        while restarting sound volume comparison with each new game frame. */
14491
14492     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14493       return;
14494
14495     loop_sound_volume[nr] = volume;
14496     loop_sound_frame[nr] = FrameCounter;
14497   }
14498
14499   PlaySoundExt(nr, volume, stereo_position, type);
14500 }
14501
14502 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14503 {
14504   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14505                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14506                  y < LEVELY(BY1) ? LEVELY(BY1) :
14507                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14508                  sound_action);
14509 }
14510
14511 static void PlayLevelSoundAction(int x, int y, int action)
14512 {
14513   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14514 }
14515
14516 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14517 {
14518   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14519
14520   if (sound_effect != SND_UNDEFINED)
14521     PlayLevelSound(x, y, sound_effect);
14522 }
14523
14524 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14525                                               int action)
14526 {
14527   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14528
14529   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14530     PlayLevelSound(x, y, sound_effect);
14531 }
14532
14533 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14534 {
14535   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14536
14537   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14538     PlayLevelSound(x, y, sound_effect);
14539 }
14540
14541 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14542 {
14543   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14544
14545   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14546     StopSound(sound_effect);
14547 }
14548
14549 static int getLevelMusicNr(void)
14550 {
14551   if (levelset.music[level_nr] != MUS_UNDEFINED)
14552     return levelset.music[level_nr];            // from config file
14553   else
14554     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14555 }
14556
14557 static void FadeLevelSounds(void)
14558 {
14559   FadeSounds();
14560 }
14561
14562 static void FadeLevelMusic(void)
14563 {
14564   int music_nr = getLevelMusicNr();
14565   char *curr_music = getCurrentlyPlayingMusicFilename();
14566   char *next_music = getMusicInfoEntryFilename(music_nr);
14567
14568   if (!strEqual(curr_music, next_music))
14569     FadeMusic();
14570 }
14571
14572 void FadeLevelSoundsAndMusic(void)
14573 {
14574   FadeLevelSounds();
14575   FadeLevelMusic();
14576 }
14577
14578 static void PlayLevelMusic(void)
14579 {
14580   int music_nr = getLevelMusicNr();
14581   char *curr_music = getCurrentlyPlayingMusicFilename();
14582   char *next_music = getMusicInfoEntryFilename(music_nr);
14583
14584   if (!strEqual(curr_music, next_music))
14585     PlayMusicLoop(music_nr);
14586 }
14587
14588 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14589 {
14590   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14591   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14592   int x = xx - 1 - offset;
14593   int y = yy - 1 - offset;
14594
14595   switch (sample)
14596   {
14597     case SAMPLE_blank:
14598       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14599       break;
14600
14601     case SAMPLE_roll:
14602       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14603       break;
14604
14605     case SAMPLE_stone:
14606       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14607       break;
14608
14609     case SAMPLE_nut:
14610       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14611       break;
14612
14613     case SAMPLE_crack:
14614       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14615       break;
14616
14617     case SAMPLE_bug:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14619       break;
14620
14621     case SAMPLE_tank:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14623       break;
14624
14625     case SAMPLE_android_clone:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14627       break;
14628
14629     case SAMPLE_android_move:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14631       break;
14632
14633     case SAMPLE_spring:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14635       break;
14636
14637     case SAMPLE_slurp:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14639       break;
14640
14641     case SAMPLE_eater:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14643       break;
14644
14645     case SAMPLE_eater_eat:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14647       break;
14648
14649     case SAMPLE_alien:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14651       break;
14652
14653     case SAMPLE_collect:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14655       break;
14656
14657     case SAMPLE_diamond:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14659       break;
14660
14661     case SAMPLE_squash:
14662       // !!! CHECK THIS !!!
14663 #if 1
14664       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14665 #else
14666       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14667 #endif
14668       break;
14669
14670     case SAMPLE_wonderfall:
14671       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14672       break;
14673
14674     case SAMPLE_drip:
14675       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14676       break;
14677
14678     case SAMPLE_push:
14679       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14680       break;
14681
14682     case SAMPLE_dirt:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14684       break;
14685
14686     case SAMPLE_acid:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14688       break;
14689
14690     case SAMPLE_ball:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14692       break;
14693
14694     case SAMPLE_grow:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14696       break;
14697
14698     case SAMPLE_wonder:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14700       break;
14701
14702     case SAMPLE_door:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14704       break;
14705
14706     case SAMPLE_exit_open:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14708       break;
14709
14710     case SAMPLE_exit_leave:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14712       break;
14713
14714     case SAMPLE_dynamite:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14716       break;
14717
14718     case SAMPLE_tick:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14720       break;
14721
14722     case SAMPLE_press:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14724       break;
14725
14726     case SAMPLE_wheel:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14728       break;
14729
14730     case SAMPLE_boom:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14732       break;
14733
14734     case SAMPLE_die:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14736       break;
14737
14738     case SAMPLE_time:
14739       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14740       break;
14741
14742     default:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14744       break;
14745   }
14746 }
14747
14748 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14749 {
14750   int element = map_element_SP_to_RND(element_sp);
14751   int action = map_action_SP_to_RND(action_sp);
14752   int offset = (setup.sp_show_border_elements ? 0 : 1);
14753   int x = xx - offset;
14754   int y = yy - offset;
14755
14756   PlayLevelSoundElementAction(x, y, element, action);
14757 }
14758
14759 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14760 {
14761   int element = map_element_MM_to_RND(element_mm);
14762   int action = map_action_MM_to_RND(action_mm);
14763   int offset = 0;
14764   int x = xx - offset;
14765   int y = yy - offset;
14766
14767   if (!IS_MM_ELEMENT(element))
14768     element = EL_MM_DEFAULT;
14769
14770   PlayLevelSoundElementAction(x, y, element, action);
14771 }
14772
14773 void PlaySound_MM(int sound_mm)
14774 {
14775   int sound = map_sound_MM_to_RND(sound_mm);
14776
14777   if (sound == SND_UNDEFINED)
14778     return;
14779
14780   PlaySound(sound);
14781 }
14782
14783 void PlaySoundLoop_MM(int sound_mm)
14784 {
14785   int sound = map_sound_MM_to_RND(sound_mm);
14786
14787   if (sound == SND_UNDEFINED)
14788     return;
14789
14790   PlaySoundLoop(sound);
14791 }
14792
14793 void StopSound_MM(int sound_mm)
14794 {
14795   int sound = map_sound_MM_to_RND(sound_mm);
14796
14797   if (sound == SND_UNDEFINED)
14798     return;
14799
14800   StopSound(sound);
14801 }
14802
14803 void RaiseScore(int value)
14804 {
14805   local_player->score += value;
14806
14807   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14808
14809   DisplayGameControlValues();
14810 }
14811
14812 void RaiseScoreElement(int element)
14813 {
14814   switch (element)
14815   {
14816     case EL_EMERALD:
14817     case EL_BD_DIAMOND:
14818     case EL_EMERALD_YELLOW:
14819     case EL_EMERALD_RED:
14820     case EL_EMERALD_PURPLE:
14821     case EL_SP_INFOTRON:
14822       RaiseScore(level.score[SC_EMERALD]);
14823       break;
14824     case EL_DIAMOND:
14825       RaiseScore(level.score[SC_DIAMOND]);
14826       break;
14827     case EL_CRYSTAL:
14828       RaiseScore(level.score[SC_CRYSTAL]);
14829       break;
14830     case EL_PEARL:
14831       RaiseScore(level.score[SC_PEARL]);
14832       break;
14833     case EL_BUG:
14834     case EL_BD_BUTTERFLY:
14835     case EL_SP_ELECTRON:
14836       RaiseScore(level.score[SC_BUG]);
14837       break;
14838     case EL_SPACESHIP:
14839     case EL_BD_FIREFLY:
14840     case EL_SP_SNIKSNAK:
14841       RaiseScore(level.score[SC_SPACESHIP]);
14842       break;
14843     case EL_YAMYAM:
14844     case EL_DARK_YAMYAM:
14845       RaiseScore(level.score[SC_YAMYAM]);
14846       break;
14847     case EL_ROBOT:
14848       RaiseScore(level.score[SC_ROBOT]);
14849       break;
14850     case EL_PACMAN:
14851       RaiseScore(level.score[SC_PACMAN]);
14852       break;
14853     case EL_NUT:
14854       RaiseScore(level.score[SC_NUT]);
14855       break;
14856     case EL_DYNAMITE:
14857     case EL_EM_DYNAMITE:
14858     case EL_SP_DISK_RED:
14859     case EL_DYNABOMB_INCREASE_NUMBER:
14860     case EL_DYNABOMB_INCREASE_SIZE:
14861     case EL_DYNABOMB_INCREASE_POWER:
14862       RaiseScore(level.score[SC_DYNAMITE]);
14863       break;
14864     case EL_SHIELD_NORMAL:
14865     case EL_SHIELD_DEADLY:
14866       RaiseScore(level.score[SC_SHIELD]);
14867       break;
14868     case EL_EXTRA_TIME:
14869       RaiseScore(level.extra_time_score);
14870       break;
14871     case EL_KEY_1:
14872     case EL_KEY_2:
14873     case EL_KEY_3:
14874     case EL_KEY_4:
14875     case EL_EM_KEY_1:
14876     case EL_EM_KEY_2:
14877     case EL_EM_KEY_3:
14878     case EL_EM_KEY_4:
14879     case EL_EMC_KEY_5:
14880     case EL_EMC_KEY_6:
14881     case EL_EMC_KEY_7:
14882     case EL_EMC_KEY_8:
14883     case EL_DC_KEY_WHITE:
14884       RaiseScore(level.score[SC_KEY]);
14885       break;
14886     default:
14887       RaiseScore(element_info[element].collect_score);
14888       break;
14889   }
14890 }
14891
14892 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14893 {
14894   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14895   {
14896     // closing door required in case of envelope style request dialogs
14897     if (!skip_request)
14898       CloseDoor(DOOR_CLOSE_1);
14899
14900     if (network.enabled)
14901       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14902     else
14903     {
14904       if (quick_quit)
14905         FadeSkipNextFadeIn();
14906
14907       SetGameStatus(GAME_MODE_MAIN);
14908
14909       DrawMainMenu();
14910     }
14911   }
14912   else          // continue playing the game
14913   {
14914     if (tape.playing && tape.deactivate_display)
14915       TapeDeactivateDisplayOff(TRUE);
14916
14917     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14918
14919     if (tape.playing && tape.deactivate_display)
14920       TapeDeactivateDisplayOn();
14921   }
14922 }
14923
14924 void RequestQuitGame(boolean ask_if_really_quit)
14925 {
14926   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14927   boolean skip_request = AllPlayersGone || quick_quit;
14928
14929   RequestQuitGameExt(skip_request, quick_quit,
14930                      "Do you really want to quit the game?");
14931 }
14932
14933 void RequestRestartGame(char *message)
14934 {
14935   game.restart_game_message = NULL;
14936
14937   boolean has_started_game = hasStartedNetworkGame();
14938   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
14939
14940   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
14941   {
14942     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14943   }
14944   else
14945   {
14946     SetGameStatus(GAME_MODE_MAIN);
14947
14948     DrawMainMenu();
14949   }
14950 }
14951
14952 void CheckGameOver(void)
14953 {
14954   static boolean last_game_over = FALSE;
14955   static int game_over_delay = 0;
14956   int game_over_delay_value = 50;
14957   boolean game_over = checkGameFailed();
14958
14959   // do not handle game over if request dialog is already active
14960   if (game.request_active)
14961     return;
14962
14963   if (!game_over)
14964   {
14965     last_game_over = FALSE;
14966     game_over_delay = game_over_delay_value;
14967
14968     return;
14969   }
14970
14971   if (game_over_delay > 0)
14972   {
14973     game_over_delay--;
14974
14975     return;
14976   }
14977
14978   if (last_game_over != game_over)
14979     game.restart_game_message = (hasStartedNetworkGame() ?
14980                                  "Game over! Play it again?" :
14981                                  "Game over!");
14982
14983   last_game_over = game_over;
14984 }
14985
14986 boolean checkGameSolved(void)
14987 {
14988   // set for all game engines if level was solved
14989   return local_player->LevelSolved_GameEnd;
14990 }
14991
14992 boolean checkGameFailed(void)
14993 {
14994   if (!AllPlayersGone)
14995     return FALSE;
14996
14997   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14998     return (level.native_em_level->lev->home > 0);
14999   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15000     return (game_sp.GameOver && !game_sp.LevelSolved);
15001   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15002     return (game_mm.game_over && !game_mm.level_solved);
15003   else                          // GAME_ENGINE_TYPE_RND
15004     return (local_player->GameOver && !local_player->LevelSolved);
15005 }
15006
15007 boolean checkGameEnded(void)
15008 {
15009   return (checkGameSolved() || checkGameFailed());
15010 }
15011
15012
15013 // ----------------------------------------------------------------------------
15014 // random generator functions
15015 // ----------------------------------------------------------------------------
15016
15017 unsigned int InitEngineRandom_RND(int seed)
15018 {
15019   game.num_random_calls = 0;
15020
15021   return InitEngineRandom(seed);
15022 }
15023
15024 unsigned int RND(int max)
15025 {
15026   if (max > 0)
15027   {
15028     game.num_random_calls++;
15029
15030     return GetEngineRandom(max);
15031   }
15032
15033   return 0;
15034 }
15035
15036
15037 // ----------------------------------------------------------------------------
15038 // game engine snapshot handling functions
15039 // ----------------------------------------------------------------------------
15040
15041 struct EngineSnapshotInfo
15042 {
15043   // runtime values for custom element collect score
15044   int collect_score[NUM_CUSTOM_ELEMENTS];
15045
15046   // runtime values for group element choice position
15047   int choice_pos[NUM_GROUP_ELEMENTS];
15048
15049   // runtime values for belt position animations
15050   int belt_graphic[4][NUM_BELT_PARTS];
15051   int belt_anim_mode[4][NUM_BELT_PARTS];
15052 };
15053
15054 static struct EngineSnapshotInfo engine_snapshot_rnd;
15055 static char *snapshot_level_identifier = NULL;
15056 static int snapshot_level_nr = -1;
15057
15058 static void SaveEngineSnapshotValues_RND(void)
15059 {
15060   static int belt_base_active_element[4] =
15061   {
15062     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15063     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15064     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15065     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15066   };
15067   int i, j;
15068
15069   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15070   {
15071     int element = EL_CUSTOM_START + i;
15072
15073     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15074   }
15075
15076   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15077   {
15078     int element = EL_GROUP_START + i;
15079
15080     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15081   }
15082
15083   for (i = 0; i < 4; i++)
15084   {
15085     for (j = 0; j < NUM_BELT_PARTS; j++)
15086     {
15087       int element = belt_base_active_element[i] + j;
15088       int graphic = el2img(element);
15089       int anim_mode = graphic_info[graphic].anim_mode;
15090
15091       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15092       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15093     }
15094   }
15095 }
15096
15097 static void LoadEngineSnapshotValues_RND(void)
15098 {
15099   unsigned int num_random_calls = game.num_random_calls;
15100   int i, j;
15101
15102   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15103   {
15104     int element = EL_CUSTOM_START + i;
15105
15106     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15107   }
15108
15109   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15110   {
15111     int element = EL_GROUP_START + i;
15112
15113     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15114   }
15115
15116   for (i = 0; i < 4; i++)
15117   {
15118     for (j = 0; j < NUM_BELT_PARTS; j++)
15119     {
15120       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15121       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15122
15123       graphic_info[graphic].anim_mode = anim_mode;
15124     }
15125   }
15126
15127   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15128   {
15129     InitRND(tape.random_seed);
15130     for (i = 0; i < num_random_calls; i++)
15131       RND(1);
15132   }
15133
15134   if (game.num_random_calls != num_random_calls)
15135   {
15136     Error(ERR_INFO, "number of random calls out of sync");
15137     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15138     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15139     Error(ERR_EXIT, "this should not happen -- please debug");
15140   }
15141 }
15142
15143 void FreeEngineSnapshotSingle(void)
15144 {
15145   FreeSnapshotSingle();
15146
15147   setString(&snapshot_level_identifier, NULL);
15148   snapshot_level_nr = -1;
15149 }
15150
15151 void FreeEngineSnapshotList(void)
15152 {
15153   FreeSnapshotList();
15154 }
15155
15156 static ListNode *SaveEngineSnapshotBuffers(void)
15157 {
15158   ListNode *buffers = NULL;
15159
15160   // copy some special values to a structure better suited for the snapshot
15161
15162   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15163     SaveEngineSnapshotValues_RND();
15164   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15165     SaveEngineSnapshotValues_EM();
15166   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15167     SaveEngineSnapshotValues_SP(&buffers);
15168   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15169     SaveEngineSnapshotValues_MM(&buffers);
15170
15171   // save values stored in special snapshot structure
15172
15173   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15174     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15175   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15176     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15177   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15178     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15179   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15180     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15181
15182   // save further RND engine values
15183
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15187
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15192
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15198
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15202
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15204
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15206
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15209
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15216   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15218   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15219   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15221   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15222   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15224   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15225   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15226   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15227   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15228
15229   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15230   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15231
15232   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15235
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15237   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15238
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15243   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15244
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15247
15248 #if 0
15249   ListNode *node = engine_snapshot_list_rnd;
15250   int num_bytes = 0;
15251
15252   while (node != NULL)
15253   {
15254     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15255
15256     node = node->next;
15257   }
15258
15259   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15260 #endif
15261
15262   return buffers;
15263 }
15264
15265 void SaveEngineSnapshotSingle(void)
15266 {
15267   ListNode *buffers = SaveEngineSnapshotBuffers();
15268
15269   // finally save all snapshot buffers to single snapshot
15270   SaveSnapshotSingle(buffers);
15271
15272   // save level identification information
15273   setString(&snapshot_level_identifier, leveldir_current->identifier);
15274   snapshot_level_nr = level_nr;
15275 }
15276
15277 boolean CheckSaveEngineSnapshotToList(void)
15278 {
15279   boolean save_snapshot =
15280     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15281      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15282       game.snapshot.changed_action) ||
15283      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15284       game.snapshot.collected_item));
15285
15286   game.snapshot.changed_action = FALSE;
15287   game.snapshot.collected_item = FALSE;
15288   game.snapshot.save_snapshot = save_snapshot;
15289
15290   return save_snapshot;
15291 }
15292
15293 void SaveEngineSnapshotToList(void)
15294 {
15295   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15296       tape.quick_resume)
15297     return;
15298
15299   ListNode *buffers = SaveEngineSnapshotBuffers();
15300
15301   // finally save all snapshot buffers to snapshot list
15302   SaveSnapshotToList(buffers);
15303 }
15304
15305 void SaveEngineSnapshotToListInitial(void)
15306 {
15307   FreeEngineSnapshotList();
15308
15309   SaveEngineSnapshotToList();
15310 }
15311
15312 static void LoadEngineSnapshotValues(void)
15313 {
15314   // restore special values from snapshot structure
15315
15316   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15317     LoadEngineSnapshotValues_RND();
15318   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15319     LoadEngineSnapshotValues_EM();
15320   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15321     LoadEngineSnapshotValues_SP();
15322   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15323     LoadEngineSnapshotValues_MM();
15324 }
15325
15326 void LoadEngineSnapshotSingle(void)
15327 {
15328   LoadSnapshotSingle();
15329
15330   LoadEngineSnapshotValues();
15331 }
15332
15333 static void LoadEngineSnapshot_Undo(int steps)
15334 {
15335   LoadSnapshotFromList_Older(steps);
15336
15337   LoadEngineSnapshotValues();
15338 }
15339
15340 static void LoadEngineSnapshot_Redo(int steps)
15341 {
15342   LoadSnapshotFromList_Newer(steps);
15343
15344   LoadEngineSnapshotValues();
15345 }
15346
15347 boolean CheckEngineSnapshotSingle(void)
15348 {
15349   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15350           snapshot_level_nr == level_nr);
15351 }
15352
15353 boolean CheckEngineSnapshotList(void)
15354 {
15355   return CheckSnapshotList();
15356 }
15357
15358
15359 // ---------- new game button stuff -------------------------------------------
15360
15361 static struct
15362 {
15363   int graphic;
15364   struct XY *pos;
15365   int gadget_id;
15366   boolean *setup_value;
15367   boolean allowed_on_tape;
15368   char *infotext;
15369 } gamebutton_info[NUM_GAME_BUTTONS] =
15370 {
15371   {
15372     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15373     GAME_CTRL_ID_STOP,                          NULL,
15374     TRUE,                                       "stop game"
15375   },
15376   {
15377     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15378     GAME_CTRL_ID_PAUSE,                         NULL,
15379     TRUE,                                       "pause game"
15380   },
15381   {
15382     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15383     GAME_CTRL_ID_PLAY,                          NULL,
15384     TRUE,                                       "play game"
15385   },
15386   {
15387     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15388     GAME_CTRL_ID_UNDO,                          NULL,
15389     TRUE,                                       "undo step"
15390   },
15391   {
15392     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15393     GAME_CTRL_ID_REDO,                          NULL,
15394     TRUE,                                       "redo step"
15395   },
15396   {
15397     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15398     GAME_CTRL_ID_SAVE,                          NULL,
15399     TRUE,                                       "save game"
15400   },
15401   {
15402     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15403     GAME_CTRL_ID_PAUSE2,                        NULL,
15404     TRUE,                                       "pause game"
15405   },
15406   {
15407     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15408     GAME_CTRL_ID_LOAD,                          NULL,
15409     TRUE,                                       "load game"
15410   },
15411   {
15412     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15413     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15414     FALSE,                                      "stop game"
15415   },
15416   {
15417     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15418     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15419     FALSE,                                      "pause game"
15420   },
15421   {
15422     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15423     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15424     FALSE,                                      "play game"
15425   },
15426   {
15427     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15428     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15429     TRUE,                                       "background music on/off"
15430   },
15431   {
15432     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15433     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15434     TRUE,                                       "sound loops on/off"
15435   },
15436   {
15437     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15438     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15439     TRUE,                                       "normal sounds on/off"
15440   },
15441   {
15442     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15443     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15444     FALSE,                                      "background music on/off"
15445   },
15446   {
15447     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15448     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15449     FALSE,                                      "sound loops on/off"
15450   },
15451   {
15452     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15453     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15454     FALSE,                                      "normal sounds on/off"
15455   }
15456 };
15457
15458 void CreateGameButtons(void)
15459 {
15460   int i;
15461
15462   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15463   {
15464     int graphic = gamebutton_info[i].graphic;
15465     struct GraphicInfo *gfx = &graphic_info[graphic];
15466     struct XY *pos = gamebutton_info[i].pos;
15467     struct GadgetInfo *gi;
15468     int button_type;
15469     boolean checked;
15470     unsigned int event_mask;
15471     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15472     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15473     int base_x = (on_tape ? VX : DX);
15474     int base_y = (on_tape ? VY : DY);
15475     int gd_x   = gfx->src_x;
15476     int gd_y   = gfx->src_y;
15477     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15478     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15479     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15480     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15481     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15482     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15483     int id = i;
15484
15485     if (gfx->bitmap == NULL)
15486     {
15487       game_gadget[id] = NULL;
15488
15489       continue;
15490     }
15491
15492     if (id == GAME_CTRL_ID_STOP ||
15493         id == GAME_CTRL_ID_PANEL_STOP ||
15494         id == GAME_CTRL_ID_PLAY ||
15495         id == GAME_CTRL_ID_PANEL_PLAY ||
15496         id == GAME_CTRL_ID_SAVE ||
15497         id == GAME_CTRL_ID_LOAD)
15498     {
15499       button_type = GD_TYPE_NORMAL_BUTTON;
15500       checked = FALSE;
15501       event_mask = GD_EVENT_RELEASED;
15502     }
15503     else if (id == GAME_CTRL_ID_UNDO ||
15504              id == GAME_CTRL_ID_REDO)
15505     {
15506       button_type = GD_TYPE_NORMAL_BUTTON;
15507       checked = FALSE;
15508       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15509     }
15510     else
15511     {
15512       button_type = GD_TYPE_CHECK_BUTTON;
15513       checked = (gamebutton_info[i].setup_value != NULL ?
15514                  *gamebutton_info[i].setup_value : FALSE);
15515       event_mask = GD_EVENT_PRESSED;
15516     }
15517
15518     gi = CreateGadget(GDI_CUSTOM_ID, id,
15519                       GDI_IMAGE_ID, graphic,
15520                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15521                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15522                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15523                       GDI_WIDTH, gfx->width,
15524                       GDI_HEIGHT, gfx->height,
15525                       GDI_TYPE, button_type,
15526                       GDI_STATE, GD_BUTTON_UNPRESSED,
15527                       GDI_CHECKED, checked,
15528                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15529                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15530                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15531                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15532                       GDI_DIRECT_DRAW, FALSE,
15533                       GDI_EVENT_MASK, event_mask,
15534                       GDI_CALLBACK_ACTION, HandleGameButtons,
15535                       GDI_END);
15536
15537     if (gi == NULL)
15538       Error(ERR_EXIT, "cannot create gadget");
15539
15540     game_gadget[id] = gi;
15541   }
15542 }
15543
15544 void FreeGameButtons(void)
15545 {
15546   int i;
15547
15548   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15549     FreeGadget(game_gadget[i]);
15550 }
15551
15552 static void UnmapGameButtonsAtSamePosition(int id)
15553 {
15554   int i;
15555
15556   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15557     if (i != id &&
15558         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15559         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15560       UnmapGadget(game_gadget[i]);
15561 }
15562
15563 static void UnmapGameButtonsAtSamePosition_All(void)
15564 {
15565   if (setup.show_snapshot_buttons)
15566   {
15567     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15568     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15569     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15570   }
15571   else
15572   {
15573     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15574     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15575     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15576
15577     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15578     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15579     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15580   }
15581 }
15582
15583 static void MapGameButtonsAtSamePosition(int id)
15584 {
15585   int i;
15586
15587   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15588     if (i != id &&
15589         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15590         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15591       MapGadget(game_gadget[i]);
15592
15593   UnmapGameButtonsAtSamePosition_All();
15594 }
15595
15596 void MapUndoRedoButtons(void)
15597 {
15598   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15599   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15600
15601   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15602   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15603
15604   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15605 }
15606
15607 void UnmapUndoRedoButtons(void)
15608 {
15609   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15610   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15611
15612   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15613   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15614
15615   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15616 }
15617
15618 static void MapGameButtonsExt(boolean on_tape)
15619 {
15620   int i;
15621
15622   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15623     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15624         i != GAME_CTRL_ID_UNDO &&
15625         i != GAME_CTRL_ID_REDO)
15626       MapGadget(game_gadget[i]);
15627
15628   UnmapGameButtonsAtSamePosition_All();
15629
15630   RedrawGameButtons();
15631 }
15632
15633 static void UnmapGameButtonsExt(boolean on_tape)
15634 {
15635   int i;
15636
15637   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15638     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15639       UnmapGadget(game_gadget[i]);
15640 }
15641
15642 static void RedrawGameButtonsExt(boolean on_tape)
15643 {
15644   int i;
15645
15646   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15647     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15648       RedrawGadget(game_gadget[i]);
15649
15650   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15651   redraw_mask &= ~REDRAW_ALL;
15652 }
15653
15654 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15655 {
15656   if (gi == NULL)
15657     return;
15658
15659   gi->checked = state;
15660 }
15661
15662 static void RedrawSoundButtonGadget(int id)
15663 {
15664   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15665              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15666              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15667              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15668              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15669              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15670              id);
15671
15672   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15673   RedrawGadget(game_gadget[id2]);
15674 }
15675
15676 void MapGameButtons(void)
15677 {
15678   MapGameButtonsExt(FALSE);
15679 }
15680
15681 void UnmapGameButtons(void)
15682 {
15683   UnmapGameButtonsExt(FALSE);
15684 }
15685
15686 void RedrawGameButtons(void)
15687 {
15688   RedrawGameButtonsExt(FALSE);
15689 }
15690
15691 void MapGameButtonsOnTape(void)
15692 {
15693   MapGameButtonsExt(TRUE);
15694 }
15695
15696 void UnmapGameButtonsOnTape(void)
15697 {
15698   UnmapGameButtonsExt(TRUE);
15699 }
15700
15701 void RedrawGameButtonsOnTape(void)
15702 {
15703   RedrawGameButtonsExt(TRUE);
15704 }
15705
15706 static void GameUndoRedoExt(void)
15707 {
15708   ClearPlayerAction();
15709
15710   tape.pausing = TRUE;
15711
15712   RedrawPlayfield();
15713   UpdateAndDisplayGameControlValues();
15714
15715   DrawCompleteVideoDisplay();
15716   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15717   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15718   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15719
15720   BackToFront();
15721 }
15722
15723 static void GameUndo(int steps)
15724 {
15725   if (!CheckEngineSnapshotList())
15726     return;
15727
15728   LoadEngineSnapshot_Undo(steps);
15729
15730   GameUndoRedoExt();
15731 }
15732
15733 static void GameRedo(int steps)
15734 {
15735   if (!CheckEngineSnapshotList())
15736     return;
15737
15738   LoadEngineSnapshot_Redo(steps);
15739
15740   GameUndoRedoExt();
15741 }
15742
15743 static void HandleGameButtonsExt(int id, int button)
15744 {
15745   static boolean game_undo_executed = FALSE;
15746   int steps = BUTTON_STEPSIZE(button);
15747   boolean handle_game_buttons =
15748     (game_status == GAME_MODE_PLAYING ||
15749      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15750
15751   if (!handle_game_buttons)
15752     return;
15753
15754   switch (id)
15755   {
15756     case GAME_CTRL_ID_STOP:
15757     case GAME_CTRL_ID_PANEL_STOP:
15758       if (game_status == GAME_MODE_MAIN)
15759         break;
15760
15761       if (tape.playing)
15762         TapeStop();
15763       else
15764         RequestQuitGame(TRUE);
15765
15766       break;
15767
15768     case GAME_CTRL_ID_PAUSE:
15769     case GAME_CTRL_ID_PAUSE2:
15770     case GAME_CTRL_ID_PANEL_PAUSE:
15771       if (network.enabled && game_status == GAME_MODE_PLAYING)
15772       {
15773         if (tape.pausing)
15774           SendToServer_ContinuePlaying();
15775         else
15776           SendToServer_PausePlaying();
15777       }
15778       else
15779         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15780
15781       game_undo_executed = FALSE;
15782
15783       break;
15784
15785     case GAME_CTRL_ID_PLAY:
15786     case GAME_CTRL_ID_PANEL_PLAY:
15787       if (game_status == GAME_MODE_MAIN)
15788       {
15789         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15790       }
15791       else if (tape.pausing)
15792       {
15793         if (network.enabled)
15794           SendToServer_ContinuePlaying();
15795         else
15796           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15797       }
15798       break;
15799
15800     case GAME_CTRL_ID_UNDO:
15801       // Important: When using "save snapshot when collecting an item" mode,
15802       // load last (current) snapshot for first "undo" after pressing "pause"
15803       // (else the last-but-one snapshot would be loaded, because the snapshot
15804       // pointer already points to the last snapshot when pressing "pause",
15805       // which is fine for "every step/move" mode, but not for "every collect")
15806       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15807           !game_undo_executed)
15808         steps--;
15809
15810       game_undo_executed = TRUE;
15811
15812       GameUndo(steps);
15813       break;
15814
15815     case GAME_CTRL_ID_REDO:
15816       GameRedo(steps);
15817       break;
15818
15819     case GAME_CTRL_ID_SAVE:
15820       TapeQuickSave();
15821       break;
15822
15823     case GAME_CTRL_ID_LOAD:
15824       TapeQuickLoad();
15825       break;
15826
15827     case SOUND_CTRL_ID_MUSIC:
15828     case SOUND_CTRL_ID_PANEL_MUSIC:
15829       if (setup.sound_music)
15830       { 
15831         setup.sound_music = FALSE;
15832
15833         FadeMusic();
15834       }
15835       else if (audio.music_available)
15836       { 
15837         setup.sound = setup.sound_music = TRUE;
15838
15839         SetAudioMode(setup.sound);
15840
15841         if (game_status == GAME_MODE_PLAYING)
15842           PlayLevelMusic();
15843       }
15844
15845       RedrawSoundButtonGadget(id);
15846
15847       break;
15848
15849     case SOUND_CTRL_ID_LOOPS:
15850     case SOUND_CTRL_ID_PANEL_LOOPS:
15851       if (setup.sound_loops)
15852         setup.sound_loops = FALSE;
15853       else if (audio.loops_available)
15854       {
15855         setup.sound = setup.sound_loops = TRUE;
15856
15857         SetAudioMode(setup.sound);
15858       }
15859
15860       RedrawSoundButtonGadget(id);
15861
15862       break;
15863
15864     case SOUND_CTRL_ID_SIMPLE:
15865     case SOUND_CTRL_ID_PANEL_SIMPLE:
15866       if (setup.sound_simple)
15867         setup.sound_simple = FALSE;
15868       else if (audio.sound_available)
15869       {
15870         setup.sound = setup.sound_simple = TRUE;
15871
15872         SetAudioMode(setup.sound);
15873       }
15874
15875       RedrawSoundButtonGadget(id);
15876
15877       break;
15878
15879     default:
15880       break;
15881   }
15882 }
15883
15884 static void HandleGameButtons(struct GadgetInfo *gi)
15885 {
15886   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15887 }
15888
15889 void HandleSoundButtonKeys(Key key)
15890 {
15891   if (key == setup.shortcut.sound_simple)
15892     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15893   else if (key == setup.shortcut.sound_loops)
15894     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15895   else if (key == setup.shortcut.sound_music)
15896     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15897 }