added checkbox to player settings in editor if first player solves level
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(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()
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()
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()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 void InitGameControlValues()
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 void UpdatePlayfieldElementCount()
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 void UpdateGameControlValues()
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 void DisplayGameControlValues()
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()
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 void UpdateGameDoorValues()
2771 {
2772   UpdateGameControlValues();
2773 }
2774
2775 void DrawGameDoorValues()
2776 {
2777   DisplayGameControlValues();
2778 }
2779
2780
2781 /*
2782   =============================================================================
2783   InitGameEngine()
2784   -----------------------------------------------------------------------------
2785   initialize game engine due to level / tape version number
2786   =============================================================================
2787 */
2788
2789 static void InitGameEngine()
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 int get_num_special_action(int element, int action_first, int action_last)
3247 {
3248   int num_special_action = 0;
3249   int i, j;
3250
3251   for (i = action_first; i <= action_last; i++)
3252   {
3253     boolean found = FALSE;
3254
3255     for (j = 0; j < NUM_DIRECTIONS; j++)
3256       if (el_act_dir2img(element, i, j) !=
3257           el_act_dir2img(element, ACTION_DEFAULT, j))
3258         found = TRUE;
3259
3260     if (found)
3261       num_special_action++;
3262     else
3263       break;
3264   }
3265
3266   return num_special_action;
3267 }
3268
3269
3270 /*
3271   =============================================================================
3272   InitGame()
3273   -----------------------------------------------------------------------------
3274   initialize and start new game
3275   =============================================================================
3276 */
3277
3278 #if DEBUG_INIT_PLAYER
3279 static void DebugPrintPlayerStatus(char *message)
3280 {
3281   int i;
3282
3283   if (!options.debug)
3284     return;
3285
3286   printf("%s:\n", message);
3287
3288   for (i = 0; i < MAX_PLAYERS; i++)
3289   {
3290     struct PlayerInfo *player = &stored_player[i];
3291
3292     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3293            i + 1,
3294            player->present,
3295            player->connected,
3296            player->connected_locally,
3297            player->connected_network,
3298            player->active);
3299
3300     if (local_player == player)
3301       printf(" (local player)");
3302
3303     printf("\n");
3304   }
3305 }
3306 #endif
3307
3308 void InitGame()
3309 {
3310   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3312   int fade_mask = REDRAW_FIELD;
3313
3314   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3315   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3316   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3317   int initial_move_dir = MV_DOWN;
3318   int i, j, x, y;
3319
3320   // required here to update video display before fading (FIX THIS)
3321   DrawMaskedBorder(REDRAW_DOOR_2);
3322
3323   if (!game.restart_level)
3324     CloseDoor(DOOR_CLOSE_1);
3325
3326   SetGameStatus(GAME_MODE_PLAYING);
3327
3328   if (level_editor_test_game)
3329     FadeSkipNextFadeIn();
3330   else
3331     FadeSetEnterScreen();
3332
3333   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3334     fade_mask = REDRAW_ALL;
3335
3336   FadeLevelSoundsAndMusic();
3337
3338   ExpireSoundLoops(TRUE);
3339
3340   FadeOut(fade_mask);
3341
3342   /* needed if different viewport properties defined for playing */
3343   ChangeViewportPropertiesIfNeeded();
3344
3345   ClearField();
3346
3347   DrawCompleteVideoDisplay();
3348
3349   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3350
3351   InitGameEngine();
3352   InitGameControlValues();
3353
3354   /* don't play tapes over network */
3355   network_playing = (network.enabled && !tape.playing);
3356
3357   for (i = 0; i < MAX_PLAYERS; i++)
3358   {
3359     struct PlayerInfo *player = &stored_player[i];
3360
3361     player->index_nr = i;
3362     player->index_bit = (1 << i);
3363     player->element_nr = EL_PLAYER_1 + i;
3364
3365     player->present = FALSE;
3366     player->active = FALSE;
3367     player->mapped = FALSE;
3368
3369     player->killed = FALSE;
3370     player->reanimated = FALSE;
3371
3372     player->action = 0;
3373     player->effective_action = 0;
3374     player->programmed_action = 0;
3375
3376     player->mouse_action.lx = 0;
3377     player->mouse_action.ly = 0;
3378     player->mouse_action.button = 0;
3379     player->mouse_action.button_hint = 0;
3380
3381     player->effective_mouse_action.lx = 0;
3382     player->effective_mouse_action.ly = 0;
3383     player->effective_mouse_action.button = 0;
3384     player->effective_mouse_action.button_hint = 0;
3385
3386     player->score = 0;
3387     player->score_final = 0;
3388
3389     player->health = MAX_HEALTH;
3390     player->health_final = MAX_HEALTH;
3391
3392     player->gems_still_needed = level.gems_needed;
3393     player->sokobanfields_still_needed = 0;
3394     player->lights_still_needed = 0;
3395     player->players_still_needed = 0;
3396     player->friends_still_needed = 0;
3397
3398     for (j = 0; j < MAX_NUM_KEYS; j++)
3399       player->key[j] = FALSE;
3400
3401     player->num_white_keys = 0;
3402
3403     player->dynabomb_count = 0;
3404     player->dynabomb_size = 1;
3405     player->dynabombs_left = 0;
3406     player->dynabomb_xl = FALSE;
3407
3408     player->MovDir = initial_move_dir;
3409     player->MovPos = 0;
3410     player->GfxPos = 0;
3411     player->GfxDir = initial_move_dir;
3412     player->GfxAction = ACTION_DEFAULT;
3413     player->Frame = 0;
3414     player->StepFrame = 0;
3415
3416     player->initial_element = player->element_nr;
3417     player->artwork_element =
3418       (level.use_artwork_element[i] ? level.artwork_element[i] :
3419        player->element_nr);
3420     player->use_murphy = FALSE;
3421
3422     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3423     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3424
3425     player->gravity = level.initial_player_gravity[i];
3426
3427     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3428
3429     player->actual_frame_counter = 0;
3430
3431     player->step_counter = 0;
3432
3433     player->last_move_dir = initial_move_dir;
3434
3435     player->is_active = FALSE;
3436
3437     player->is_waiting = FALSE;
3438     player->is_moving = FALSE;
3439     player->is_auto_moving = FALSE;
3440     player->is_digging = FALSE;
3441     player->is_snapping = FALSE;
3442     player->is_collecting = FALSE;
3443     player->is_pushing = FALSE;
3444     player->is_switching = FALSE;
3445     player->is_dropping = FALSE;
3446     player->is_dropping_pressed = FALSE;
3447
3448     player->is_bored = FALSE;
3449     player->is_sleeping = FALSE;
3450
3451     player->was_waiting = TRUE;
3452     player->was_moving = FALSE;
3453     player->was_snapping = FALSE;
3454     player->was_dropping = FALSE;
3455
3456     player->force_dropping = FALSE;
3457
3458     player->frame_counter_bored = -1;
3459     player->frame_counter_sleeping = -1;
3460
3461     player->anim_delay_counter = 0;
3462     player->post_delay_counter = 0;
3463
3464     player->dir_waiting = initial_move_dir;
3465     player->action_waiting = ACTION_DEFAULT;
3466     player->last_action_waiting = ACTION_DEFAULT;
3467     player->special_action_bored = ACTION_DEFAULT;
3468     player->special_action_sleeping = ACTION_DEFAULT;
3469
3470     player->switch_x = -1;
3471     player->switch_y = -1;
3472
3473     player->drop_x = -1;
3474     player->drop_y = -1;
3475
3476     player->show_envelope = 0;
3477
3478     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3479
3480     player->push_delay       = -1;      /* initialized when pushing starts */
3481     player->push_delay_value = game.initial_push_delay_value;
3482
3483     player->drop_delay = 0;
3484     player->drop_pressed_delay = 0;
3485
3486     player->last_jx = -1;
3487     player->last_jy = -1;
3488     player->jx = -1;
3489     player->jy = -1;
3490
3491     player->shield_normal_time_left = 0;
3492     player->shield_deadly_time_left = 0;
3493
3494     player->inventory_infinite_element = EL_UNDEFINED;
3495     player->inventory_size = 0;
3496
3497     if (level.use_initial_inventory[i])
3498     {
3499       for (j = 0; j < level.initial_inventory_size[i]; j++)
3500       {
3501         int element = level.initial_inventory_content[i][j];
3502         int collect_count = element_info[element].collect_count_initial;
3503         int k;
3504
3505         if (!IS_CUSTOM_ELEMENT(element))
3506           collect_count = 1;
3507
3508         if (collect_count == 0)
3509           player->inventory_infinite_element = element;
3510         else
3511           for (k = 0; k < collect_count; k++)
3512             if (player->inventory_size < MAX_INVENTORY_SIZE)
3513               player->inventory_element[player->inventory_size++] = element;
3514       }
3515     }
3516
3517     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3518     SnapField(player, 0, 0);
3519
3520     player->LevelSolved = FALSE;
3521     player->GameOver = FALSE;
3522
3523     player->LevelSolved_GameWon = FALSE;
3524     player->LevelSolved_GameEnd = FALSE;
3525     player->LevelSolved_PanelOff = FALSE;
3526     player->LevelSolved_SaveTape = FALSE;
3527     player->LevelSolved_SaveScore = FALSE;
3528
3529     player->LevelSolved_CountingTime = 0;
3530     player->LevelSolved_CountingScore = 0;
3531     player->LevelSolved_CountingHealth = 0;
3532
3533     map_player_action[i] = i;
3534   }
3535
3536   network_player_action_received = FALSE;
3537
3538   /* initial null action */
3539   if (network_playing)
3540     SendToServer_MovePlayer(MV_NONE);
3541
3542   ZX = ZY = -1;
3543   ExitX = ExitY = -1;
3544
3545   FrameCounter = 0;
3546   TimeFrames = 0;
3547   TimePlayed = 0;
3548   TimeLeft = level.time;
3549   TapeTime = 0;
3550
3551   ScreenMovDir = MV_NONE;
3552   ScreenMovPos = 0;
3553   ScreenGfxPos = 0;
3554
3555   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3556
3557   AllPlayersGone = FALSE;
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] = 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
4196   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4197     InitGameActions_MM();
4198
4199   SaveEngineSnapshotToListInitial();
4200
4201   if (!game.restart_level)
4202   {
4203     PlaySound(SND_GAME_STARTING);
4204
4205     if (setup.sound_music)
4206       PlayLevelMusic();
4207   }
4208 }
4209
4210 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4211                         int actual_player_x, int actual_player_y)
4212 {
4213   /* this is used for non-R'n'D game engines to update certain engine values */
4214
4215   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4216   {
4217     actual_player_x = correctLevelPosX_EM(actual_player_x);
4218     actual_player_y = correctLevelPosY_EM(actual_player_y);
4219   }
4220
4221   /* needed to determine if sounds are played within the visible screen area */
4222   scroll_x = actual_scroll_x;
4223   scroll_y = actual_scroll_y;
4224
4225   /* needed to get player position for "follow finger" playing input method */
4226   local_player->jx = actual_player_x;
4227   local_player->jy = actual_player_y;
4228 }
4229
4230 void InitMovDir(int x, int y)
4231 {
4232   int i, element = Feld[x][y];
4233   static int xy[4][2] =
4234   {
4235     {  0, +1 },
4236     { +1,  0 },
4237     {  0, -1 },
4238     { -1,  0 }
4239   };
4240   static int direction[3][4] =
4241   {
4242     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4243     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4244     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4245   };
4246
4247   switch (element)
4248   {
4249     case EL_BUG_RIGHT:
4250     case EL_BUG_UP:
4251     case EL_BUG_LEFT:
4252     case EL_BUG_DOWN:
4253       Feld[x][y] = EL_BUG;
4254       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4255       break;
4256
4257     case EL_SPACESHIP_RIGHT:
4258     case EL_SPACESHIP_UP:
4259     case EL_SPACESHIP_LEFT:
4260     case EL_SPACESHIP_DOWN:
4261       Feld[x][y] = EL_SPACESHIP;
4262       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4263       break;
4264
4265     case EL_BD_BUTTERFLY_RIGHT:
4266     case EL_BD_BUTTERFLY_UP:
4267     case EL_BD_BUTTERFLY_LEFT:
4268     case EL_BD_BUTTERFLY_DOWN:
4269       Feld[x][y] = EL_BD_BUTTERFLY;
4270       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4271       break;
4272
4273     case EL_BD_FIREFLY_RIGHT:
4274     case EL_BD_FIREFLY_UP:
4275     case EL_BD_FIREFLY_LEFT:
4276     case EL_BD_FIREFLY_DOWN:
4277       Feld[x][y] = EL_BD_FIREFLY;
4278       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4279       break;
4280
4281     case EL_PACMAN_RIGHT:
4282     case EL_PACMAN_UP:
4283     case EL_PACMAN_LEFT:
4284     case EL_PACMAN_DOWN:
4285       Feld[x][y] = EL_PACMAN;
4286       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4287       break;
4288
4289     case EL_YAMYAM_LEFT:
4290     case EL_YAMYAM_RIGHT:
4291     case EL_YAMYAM_UP:
4292     case EL_YAMYAM_DOWN:
4293       Feld[x][y] = EL_YAMYAM;
4294       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4295       break;
4296
4297     case EL_SP_SNIKSNAK:
4298       MovDir[x][y] = MV_UP;
4299       break;
4300
4301     case EL_SP_ELECTRON:
4302       MovDir[x][y] = MV_LEFT;
4303       break;
4304
4305     case EL_MOLE_LEFT:
4306     case EL_MOLE_RIGHT:
4307     case EL_MOLE_UP:
4308     case EL_MOLE_DOWN:
4309       Feld[x][y] = EL_MOLE;
4310       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4311       break;
4312
4313     default:
4314       if (IS_CUSTOM_ELEMENT(element))
4315       {
4316         struct ElementInfo *ei = &element_info[element];
4317         int move_direction_initial = ei->move_direction_initial;
4318         int move_pattern = ei->move_pattern;
4319
4320         if (move_direction_initial == MV_START_PREVIOUS)
4321         {
4322           if (MovDir[x][y] != MV_NONE)
4323             return;
4324
4325           move_direction_initial = MV_START_AUTOMATIC;
4326         }
4327
4328         if (move_direction_initial == MV_START_RANDOM)
4329           MovDir[x][y] = 1 << RND(4);
4330         else if (move_direction_initial & MV_ANY_DIRECTION)
4331           MovDir[x][y] = move_direction_initial;
4332         else if (move_pattern == MV_ALL_DIRECTIONS ||
4333                  move_pattern == MV_TURNING_LEFT ||
4334                  move_pattern == MV_TURNING_RIGHT ||
4335                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4336                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4337                  move_pattern == MV_TURNING_RANDOM)
4338           MovDir[x][y] = 1 << RND(4);
4339         else if (move_pattern == MV_HORIZONTAL)
4340           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4341         else if (move_pattern == MV_VERTICAL)
4342           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4343         else if (move_pattern & MV_ANY_DIRECTION)
4344           MovDir[x][y] = element_info[element].move_pattern;
4345         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4346                  move_pattern == MV_ALONG_RIGHT_SIDE)
4347         {
4348           /* use random direction as default start direction */
4349           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4350             MovDir[x][y] = 1 << RND(4);
4351
4352           for (i = 0; i < NUM_DIRECTIONS; i++)
4353           {
4354             int x1 = x + xy[i][0];
4355             int y1 = y + xy[i][1];
4356
4357             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4358             {
4359               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4360                 MovDir[x][y] = direction[0][i];
4361               else
4362                 MovDir[x][y] = direction[1][i];
4363
4364               break;
4365             }
4366           }
4367         }                
4368       }
4369       else
4370       {
4371         MovDir[x][y] = 1 << RND(4);
4372
4373         if (element != EL_BUG &&
4374             element != EL_SPACESHIP &&
4375             element != EL_BD_BUTTERFLY &&
4376             element != EL_BD_FIREFLY)
4377           break;
4378
4379         for (i = 0; i < NUM_DIRECTIONS; i++)
4380         {
4381           int x1 = x + xy[i][0];
4382           int y1 = y + xy[i][1];
4383
4384           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4385           {
4386             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4387             {
4388               MovDir[x][y] = direction[0][i];
4389               break;
4390             }
4391             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4392                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4393             {
4394               MovDir[x][y] = direction[1][i];
4395               break;
4396             }
4397           }
4398         }
4399       }
4400       break;
4401   }
4402
4403   GfxDir[x][y] = MovDir[x][y];
4404 }
4405
4406 void InitAmoebaNr(int x, int y)
4407 {
4408   int i;
4409   int group_nr = AmoebeNachbarNr(x, y);
4410
4411   if (group_nr == 0)
4412   {
4413     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4414     {
4415       if (AmoebaCnt[i] == 0)
4416       {
4417         group_nr = i;
4418         break;
4419       }
4420     }
4421   }
4422
4423   AmoebaNr[x][y] = group_nr;
4424   AmoebaCnt[group_nr]++;
4425   AmoebaCnt2[group_nr]++;
4426 }
4427
4428 static void PlayerWins(struct PlayerInfo *player)
4429 {
4430   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4431       local_player->players_still_needed > 0)
4432     return;
4433
4434   player->LevelSolved = TRUE;
4435   player->GameOver = TRUE;
4436
4437   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4438                          level.native_em_level->lev->score :
4439                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4440                          game_mm.score :
4441                          player->score);
4442   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4443                           MM_HEALTH(game_mm.laser_overload_value) :
4444                           player->health);
4445
4446   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4447                                       TimeLeft);
4448   player->LevelSolved_CountingScore = player->score_final;
4449   player->LevelSolved_CountingHealth = player->health_final;
4450 }
4451
4452 void GameWon()
4453 {
4454   static int time_count_steps;
4455   static int time, time_final;
4456   static int score, score_final;
4457   static int health, health_final;
4458   static int game_over_delay_1 = 0;
4459   static int game_over_delay_2 = 0;
4460   static int game_over_delay_3 = 0;
4461   int game_over_delay_value_1 = 50;
4462   int game_over_delay_value_2 = 25;
4463   int game_over_delay_value_3 = 50;
4464
4465   if (!local_player->LevelSolved_GameWon)
4466   {
4467     int i;
4468
4469     /* do not start end game actions before the player stops moving (to exit) */
4470     if (local_player->MovPos)
4471       return;
4472
4473     local_player->LevelSolved_GameWon = TRUE;
4474     local_player->LevelSolved_SaveTape = tape.recording;
4475     local_player->LevelSolved_SaveScore = !tape.playing;
4476
4477     if (!tape.playing)
4478     {
4479       LevelStats_incSolved(level_nr);
4480
4481       SaveLevelSetup_SeriesInfo();
4482     }
4483
4484     if (tape.auto_play)         /* tape might already be stopped here */
4485       tape.auto_play_level_solved = TRUE;
4486
4487     TapeStop();
4488
4489     game_over_delay_1 = 0;
4490     game_over_delay_2 = 0;
4491     game_over_delay_3 = game_over_delay_value_3;
4492
4493     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4494     score = score_final = local_player->score_final;
4495     health = health_final = local_player->health_final;
4496
4497     if (level.score[SC_TIME_BONUS] > 0)
4498     {
4499       if (TimeLeft > 0)
4500       {
4501         time_final = 0;
4502         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4503       }
4504       else if (game.no_time_limit && TimePlayed < 999)
4505       {
4506         time_final = 999;
4507         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4508       }
4509
4510       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4511
4512       game_over_delay_1 = game_over_delay_value_1;
4513
4514       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4515       {
4516         health_final = 0;
4517         score_final += health * level.score[SC_TIME_BONUS];
4518
4519         game_over_delay_2 = game_over_delay_value_2;
4520       }
4521
4522       local_player->score_final = score_final;
4523       local_player->health_final = health_final;
4524     }
4525
4526     if (level_editor_test_game)
4527     {
4528       time = time_final;
4529       score = score_final;
4530
4531       local_player->LevelSolved_CountingTime = time;
4532       local_player->LevelSolved_CountingScore = score;
4533
4534       game_panel_controls[GAME_PANEL_TIME].value = time;
4535       game_panel_controls[GAME_PANEL_SCORE].value = score;
4536
4537       DisplayGameControlValues();
4538     }
4539
4540     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4541     {
4542       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4543       {
4544         /* close exit door after last player */
4545         if ((AllPlayersGone &&
4546              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4547               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4548               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4549             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4550             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4551         {
4552           int element = Feld[ExitX][ExitY];
4553
4554           Feld[ExitX][ExitY] =
4555             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4556              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4557              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4558              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4559              EL_EM_STEEL_EXIT_CLOSING);
4560
4561           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4562         }
4563
4564         /* player disappears */
4565         DrawLevelField(ExitX, ExitY);
4566       }
4567
4568       for (i = 0; i < MAX_PLAYERS; i++)
4569       {
4570         struct PlayerInfo *player = &stored_player[i];
4571
4572         if (player->present)
4573         {
4574           RemovePlayer(player);
4575
4576           /* player disappears */
4577           DrawLevelField(player->jx, player->jy);
4578         }
4579       }
4580     }
4581
4582     PlaySound(SND_GAME_WINNING);
4583   }
4584
4585   if (game_over_delay_1 > 0)
4586   {
4587     game_over_delay_1--;
4588
4589     return;
4590   }
4591
4592   if (time != time_final)
4593   {
4594     int time_to_go = ABS(time_final - time);
4595     int time_count_dir = (time < time_final ? +1 : -1);
4596
4597     if (time_to_go < time_count_steps)
4598       time_count_steps = 1;
4599
4600     time  += time_count_steps * time_count_dir;
4601     score += time_count_steps * level.score[SC_TIME_BONUS];
4602
4603     local_player->LevelSolved_CountingTime = time;
4604     local_player->LevelSolved_CountingScore = score;
4605
4606     game_panel_controls[GAME_PANEL_TIME].value = time;
4607     game_panel_controls[GAME_PANEL_SCORE].value = score;
4608
4609     DisplayGameControlValues();
4610
4611     if (time == time_final)
4612       StopSound(SND_GAME_LEVELTIME_BONUS);
4613     else if (setup.sound_loops)
4614       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4615     else
4616       PlaySound(SND_GAME_LEVELTIME_BONUS);
4617
4618     return;
4619   }
4620
4621   if (game_over_delay_2 > 0)
4622   {
4623     game_over_delay_2--;
4624
4625     return;
4626   }
4627
4628   if (health != health_final)
4629   {
4630     int health_count_dir = (health < health_final ? +1 : -1);
4631
4632     health += health_count_dir;
4633     score  += level.score[SC_TIME_BONUS];
4634
4635     local_player->LevelSolved_CountingHealth = health;
4636     local_player->LevelSolved_CountingScore = score;
4637
4638     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4639     game_panel_controls[GAME_PANEL_SCORE].value = score;
4640
4641     DisplayGameControlValues();
4642
4643     if (health == health_final)
4644       StopSound(SND_GAME_LEVELTIME_BONUS);
4645     else if (setup.sound_loops)
4646       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4647     else
4648       PlaySound(SND_GAME_LEVELTIME_BONUS);
4649
4650     return;
4651   }
4652
4653   local_player->LevelSolved_PanelOff = TRUE;
4654
4655   if (game_over_delay_3 > 0)
4656   {
4657     game_over_delay_3--;
4658
4659     return;
4660   }
4661
4662   GameEnd();
4663 }
4664
4665 void GameEnd()
4666 {
4667   int hi_pos;
4668
4669   local_player->LevelSolved_GameEnd = TRUE;
4670
4671   if (local_player->LevelSolved_SaveTape)
4672   {
4673     /* make sure that request dialog to save tape does not open door again */
4674     if (!global.use_envelope_request)
4675       CloseDoor(DOOR_CLOSE_1);
4676
4677     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4678   }
4679
4680   /* if no tape is to be saved, close both doors simultaneously */
4681   CloseDoor(DOOR_CLOSE_ALL);
4682
4683   if (level_editor_test_game)
4684   {
4685     SetGameStatus(GAME_MODE_MAIN);
4686
4687     DrawMainMenu();
4688
4689     return;
4690   }
4691
4692   if (!local_player->LevelSolved_SaveScore)
4693   {
4694     SetGameStatus(GAME_MODE_MAIN);
4695
4696     DrawMainMenu();
4697
4698     return;
4699   }
4700
4701   if (level_nr == leveldir_current->handicap_level)
4702   {
4703     leveldir_current->handicap_level++;
4704
4705     SaveLevelSetup_SeriesInfo();
4706   }
4707
4708   if (setup.increment_levels &&
4709       level_nr < leveldir_current->last_level &&
4710       !network_playing)
4711   {
4712     level_nr++;         /* advance to next level */
4713     TapeErase();        /* start with empty tape */
4714
4715     if (setup.auto_play_next_level)
4716     {
4717       LoadLevel(level_nr);
4718
4719       SaveLevelSetup_SeriesInfo();
4720     }
4721   }
4722
4723   /* used instead of last "level_nr" (for network games) */
4724   hi_pos = NewHiScore(levelset.level_nr);
4725
4726   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4727   {
4728     SetGameStatus(GAME_MODE_SCORES);
4729
4730     DrawHallOfFame(levelset.level_nr, hi_pos);
4731   }
4732   else if (setup.auto_play_next_level && setup.increment_levels &&
4733            !network_playing)
4734   {
4735     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4736   }
4737   else
4738   {
4739     SetGameStatus(GAME_MODE_MAIN);
4740
4741     DrawMainMenu();
4742   }
4743 }
4744
4745 int NewHiScore(int level_nr)
4746 {
4747   int k, l;
4748   int position = -1;
4749   boolean one_score_entry_per_name = !program.many_scores_per_name;
4750
4751   LoadScore(level_nr);
4752
4753   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4754       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4755     return -1;
4756
4757   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4758   {
4759     if (local_player->score_final > highscore[k].Score)
4760     {
4761       /* player has made it to the hall of fame */
4762
4763       if (k < MAX_SCORE_ENTRIES - 1)
4764       {
4765         int m = MAX_SCORE_ENTRIES - 1;
4766
4767         if (one_score_entry_per_name)
4768         {
4769           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4770             if (strEqual(setup.player_name, highscore[l].Name))
4771               m = l;
4772
4773           if (m == k)   /* player's new highscore overwrites his old one */
4774             goto put_into_list;
4775         }
4776
4777         for (l = m; l > k; l--)
4778         {
4779           strcpy(highscore[l].Name, highscore[l - 1].Name);
4780           highscore[l].Score = highscore[l - 1].Score;
4781         }
4782       }
4783
4784       put_into_list:
4785
4786       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4787       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4788       highscore[k].Score = local_player->score_final; 
4789       position = k;
4790
4791       break;
4792     }
4793     else if (one_score_entry_per_name &&
4794              !strncmp(setup.player_name, highscore[k].Name,
4795                       MAX_PLAYER_NAME_LEN))
4796       break;    /* player already there with a higher score */
4797   }
4798
4799   if (position >= 0) 
4800     SaveScore(level_nr);
4801
4802   return position;
4803 }
4804
4805 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4806 {
4807   int element = Feld[x][y];
4808   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4809   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4810   int horiz_move = (dx != 0);
4811   int sign = (horiz_move ? dx : dy);
4812   int step = sign * element_info[element].move_stepsize;
4813
4814   /* special values for move stepsize for spring and things on conveyor belt */
4815   if (horiz_move)
4816   {
4817     if (CAN_FALL(element) &&
4818         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4819       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4820     else if (element == EL_SPRING)
4821       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4822   }
4823
4824   return step;
4825 }
4826
4827 inline static int getElementMoveStepsize(int x, int y)
4828 {
4829   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4830 }
4831
4832 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4833 {
4834   if (player->GfxAction != action || player->GfxDir != dir)
4835   {
4836     player->GfxAction = action;
4837     player->GfxDir = dir;
4838     player->Frame = 0;
4839     player->StepFrame = 0;
4840   }
4841 }
4842
4843 static void ResetGfxFrame(int x, int y)
4844 {
4845   // profiling showed that "autotest" spends 10~20% of its time in this function
4846   if (DrawingDeactivatedField())
4847     return;
4848
4849   int element = Feld[x][y];
4850   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4851
4852   if (graphic_info[graphic].anim_global_sync)
4853     GfxFrame[x][y] = FrameCounter;
4854   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4855     GfxFrame[x][y] = CustomValue[x][y];
4856   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4857     GfxFrame[x][y] = element_info[element].collect_score;
4858   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4859     GfxFrame[x][y] = ChangeDelay[x][y];
4860 }
4861
4862 static void ResetGfxAnimation(int x, int y)
4863 {
4864   GfxAction[x][y] = ACTION_DEFAULT;
4865   GfxDir[x][y] = MovDir[x][y];
4866   GfxFrame[x][y] = 0;
4867
4868   ResetGfxFrame(x, y);
4869 }
4870
4871 static void ResetRandomAnimationValue(int x, int y)
4872 {
4873   GfxRandom[x][y] = INIT_GFX_RANDOM();
4874 }
4875
4876 void InitMovingField(int x, int y, int direction)
4877 {
4878   int element = Feld[x][y];
4879   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4880   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4881   int newx = x + dx;
4882   int newy = y + dy;
4883   boolean is_moving_before, is_moving_after;
4884
4885   /* check if element was/is moving or being moved before/after mode change */
4886   is_moving_before = (WasJustMoving[x][y] != 0);
4887   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4888
4889   /* reset animation only for moving elements which change direction of moving
4890      or which just started or stopped moving
4891      (else CEs with property "can move" / "not moving" are reset each frame) */
4892   if (is_moving_before != is_moving_after ||
4893       direction != MovDir[x][y])
4894     ResetGfxAnimation(x, y);
4895
4896   MovDir[x][y] = direction;
4897   GfxDir[x][y] = direction;
4898
4899   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4900                      direction == MV_DOWN && CAN_FALL(element) ?
4901                      ACTION_FALLING : ACTION_MOVING);
4902
4903   /* this is needed for CEs with property "can move" / "not moving" */
4904
4905   if (is_moving_after)
4906   {
4907     if (Feld[newx][newy] == EL_EMPTY)
4908       Feld[newx][newy] = EL_BLOCKED;
4909
4910     MovDir[newx][newy] = MovDir[x][y];
4911
4912     CustomValue[newx][newy] = CustomValue[x][y];
4913
4914     GfxFrame[newx][newy] = GfxFrame[x][y];
4915     GfxRandom[newx][newy] = GfxRandom[x][y];
4916     GfxAction[newx][newy] = GfxAction[x][y];
4917     GfxDir[newx][newy] = GfxDir[x][y];
4918   }
4919 }
4920
4921 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4922 {
4923   int direction = MovDir[x][y];
4924   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4925   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4926
4927   *goes_to_x = newx;
4928   *goes_to_y = newy;
4929 }
4930
4931 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4932 {
4933   int oldx = x, oldy = y;
4934   int direction = MovDir[x][y];
4935
4936   if (direction == MV_LEFT)
4937     oldx++;
4938   else if (direction == MV_RIGHT)
4939     oldx--;
4940   else if (direction == MV_UP)
4941     oldy++;
4942   else if (direction == MV_DOWN)
4943     oldy--;
4944
4945   *comes_from_x = oldx;
4946   *comes_from_y = oldy;
4947 }
4948
4949 int MovingOrBlocked2Element(int x, int y)
4950 {
4951   int element = Feld[x][y];
4952
4953   if (element == EL_BLOCKED)
4954   {
4955     int oldx, oldy;
4956
4957     Blocked2Moving(x, y, &oldx, &oldy);
4958     return Feld[oldx][oldy];
4959   }
4960   else
4961     return element;
4962 }
4963
4964 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4965 {
4966   /* like MovingOrBlocked2Element(), but if element is moving
4967      and (x,y) is the field the moving element is just leaving,
4968      return EL_BLOCKED instead of the element value */
4969   int element = Feld[x][y];
4970
4971   if (IS_MOVING(x, y))
4972   {
4973     if (element == EL_BLOCKED)
4974     {
4975       int oldx, oldy;
4976
4977       Blocked2Moving(x, y, &oldx, &oldy);
4978       return Feld[oldx][oldy];
4979     }
4980     else
4981       return EL_BLOCKED;
4982   }
4983   else
4984     return element;
4985 }
4986
4987 static void RemoveField(int x, int y)
4988 {
4989   Feld[x][y] = EL_EMPTY;
4990
4991   MovPos[x][y] = 0;
4992   MovDir[x][y] = 0;
4993   MovDelay[x][y] = 0;
4994
4995   CustomValue[x][y] = 0;
4996
4997   AmoebaNr[x][y] = 0;
4998   ChangeDelay[x][y] = 0;
4999   ChangePage[x][y] = -1;
5000   Pushed[x][y] = FALSE;
5001
5002   GfxElement[x][y] = EL_UNDEFINED;
5003   GfxAction[x][y] = ACTION_DEFAULT;
5004   GfxDir[x][y] = MV_NONE;
5005 }
5006
5007 void RemoveMovingField(int x, int y)
5008 {
5009   int oldx = x, oldy = y, newx = x, newy = y;
5010   int element = Feld[x][y];
5011   int next_element = EL_UNDEFINED;
5012
5013   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5014     return;
5015
5016   if (IS_MOVING(x, y))
5017   {
5018     Moving2Blocked(x, y, &newx, &newy);
5019
5020     if (Feld[newx][newy] != EL_BLOCKED)
5021     {
5022       /* element is moving, but target field is not free (blocked), but
5023          already occupied by something different (example: acid pool);
5024          in this case, only remove the moving field, but not the target */
5025
5026       RemoveField(oldx, oldy);
5027
5028       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5029
5030       TEST_DrawLevelField(oldx, oldy);
5031
5032       return;
5033     }
5034   }
5035   else if (element == EL_BLOCKED)
5036   {
5037     Blocked2Moving(x, y, &oldx, &oldy);
5038     if (!IS_MOVING(oldx, oldy))
5039       return;
5040   }
5041
5042   if (element == EL_BLOCKED &&
5043       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5044        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5045        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5046        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5047        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5048        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5049     next_element = get_next_element(Feld[oldx][oldy]);
5050
5051   RemoveField(oldx, oldy);
5052   RemoveField(newx, newy);
5053
5054   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5055
5056   if (next_element != EL_UNDEFINED)
5057     Feld[oldx][oldy] = next_element;
5058
5059   TEST_DrawLevelField(oldx, oldy);
5060   TEST_DrawLevelField(newx, newy);
5061 }
5062
5063 void DrawDynamite(int x, int y)
5064 {
5065   int sx = SCREENX(x), sy = SCREENY(y);
5066   int graphic = el2img(Feld[x][y]);
5067   int frame;
5068
5069   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5070     return;
5071
5072   if (IS_WALKABLE_INSIDE(Back[x][y]))
5073     return;
5074
5075   if (Back[x][y])
5076     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5077   else if (Store[x][y])
5078     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5079
5080   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5081
5082   if (Back[x][y] || Store[x][y])
5083     DrawGraphicThruMask(sx, sy, graphic, frame);
5084   else
5085     DrawGraphic(sx, sy, graphic, frame);
5086 }
5087
5088 void CheckDynamite(int x, int y)
5089 {
5090   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5091   {
5092     MovDelay[x][y]--;
5093
5094     if (MovDelay[x][y] != 0)
5095     {
5096       DrawDynamite(x, y);
5097       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5098
5099       return;
5100     }
5101   }
5102
5103   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5104
5105   Bang(x, y);
5106 }
5107
5108 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5109 {
5110   boolean num_checked_players = 0;
5111   int i;
5112
5113   for (i = 0; i < MAX_PLAYERS; i++)
5114   {
5115     if (stored_player[i].active)
5116     {
5117       int sx = stored_player[i].jx;
5118       int sy = stored_player[i].jy;
5119
5120       if (num_checked_players == 0)
5121       {
5122         *sx1 = *sx2 = sx;
5123         *sy1 = *sy2 = sy;
5124       }
5125       else
5126       {
5127         *sx1 = MIN(*sx1, sx);
5128         *sy1 = MIN(*sy1, sy);
5129         *sx2 = MAX(*sx2, sx);
5130         *sy2 = MAX(*sy2, sy);
5131       }
5132
5133       num_checked_players++;
5134     }
5135   }
5136 }
5137
5138 static boolean checkIfAllPlayersFitToScreen_RND()
5139 {
5140   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5141
5142   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5143
5144   return (sx2 - sx1 < SCR_FIELDX &&
5145           sy2 - sy1 < SCR_FIELDY);
5146 }
5147
5148 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5149 {
5150   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5151
5152   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5153
5154   *sx = (sx1 + sx2) / 2;
5155   *sy = (sy1 + sy2) / 2;
5156 }
5157
5158 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5159                         boolean center_screen, boolean quick_relocation)
5160 {
5161   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5162   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5163   boolean no_delay = (tape.warp_forward);
5164   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5165   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5166   int new_scroll_x, new_scroll_y;
5167
5168   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5169   {
5170     /* case 1: quick relocation inside visible screen (without scrolling) */
5171
5172     RedrawPlayfield();
5173
5174     return;
5175   }
5176
5177   if (!level.shifted_relocation || center_screen)
5178   {
5179     /* relocation _with_ centering of screen */
5180
5181     new_scroll_x = SCROLL_POSITION_X(x);
5182     new_scroll_y = SCROLL_POSITION_Y(y);
5183   }
5184   else
5185   {
5186     /* relocation _without_ centering of screen */
5187
5188     int center_scroll_x = SCROLL_POSITION_X(old_x);
5189     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5190     int offset_x = x + (scroll_x - center_scroll_x);
5191     int offset_y = y + (scroll_y - center_scroll_y);
5192
5193     /* for new screen position, apply previous offset to center position */
5194     new_scroll_x = SCROLL_POSITION_X(offset_x);
5195     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5196   }
5197
5198   if (quick_relocation)
5199   {
5200     /* case 2: quick relocation (redraw without visible scrolling) */
5201
5202     scroll_x = new_scroll_x;
5203     scroll_y = new_scroll_y;
5204
5205     RedrawPlayfield();
5206
5207     return;
5208   }
5209
5210   /* case 3: visible relocation (with scrolling to new position) */
5211
5212   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5213
5214   SetVideoFrameDelay(wait_delay_value);
5215
5216   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5217   {
5218     int dx = 0, dy = 0;
5219     int fx = FX, fy = FY;
5220
5221     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5222     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5223
5224     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5225       break;
5226
5227     scroll_x -= dx;
5228     scroll_y -= dy;
5229
5230     fx += dx * TILEX / 2;
5231     fy += dy * TILEY / 2;
5232
5233     ScrollLevel(dx, dy);
5234     DrawAllPlayers();
5235
5236     /* scroll in two steps of half tile size to make things smoother */
5237     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5238
5239     /* scroll second step to align at full tile size */
5240     BlitScreenToBitmap(window);
5241   }
5242
5243   DrawAllPlayers();
5244   BackToFront();
5245
5246   SetVideoFrameDelay(frame_delay_value_old);
5247 }
5248
5249 void RelocatePlayer(int jx, int jy, int el_player_raw)
5250 {
5251   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5252   int player_nr = GET_PLAYER_NR(el_player);
5253   struct PlayerInfo *player = &stored_player[player_nr];
5254   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5255   boolean no_delay = (tape.warp_forward);
5256   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5257   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5258   int old_jx = player->jx;
5259   int old_jy = player->jy;
5260   int old_element = Feld[old_jx][old_jy];
5261   int element = Feld[jx][jy];
5262   boolean player_relocated = (old_jx != jx || old_jy != jy);
5263
5264   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5265   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5266   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5267   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5268   int leave_side_horiz = move_dir_horiz;
5269   int leave_side_vert  = move_dir_vert;
5270   int enter_side = enter_side_horiz | enter_side_vert;
5271   int leave_side = leave_side_horiz | leave_side_vert;
5272
5273   if (player->GameOver)         /* do not reanimate dead player */
5274     return;
5275
5276   if (!player_relocated)        /* no need to relocate the player */
5277     return;
5278
5279   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5280   {
5281     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5282     DrawLevelField(jx, jy);
5283   }
5284
5285   if (player->present)
5286   {
5287     while (player->MovPos)
5288     {
5289       ScrollPlayer(player, SCROLL_GO_ON);
5290       ScrollScreen(NULL, SCROLL_GO_ON);
5291
5292       AdvanceFrameAndPlayerCounters(player->index_nr);
5293
5294       DrawPlayer(player);
5295
5296       BackToFront_WithFrameDelay(wait_delay_value);
5297     }
5298
5299     DrawPlayer(player);         /* needed here only to cleanup last field */
5300     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5301
5302     player->is_moving = FALSE;
5303   }
5304
5305   if (IS_CUSTOM_ELEMENT(old_element))
5306     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5307                                CE_LEFT_BY_PLAYER,
5308                                player->index_bit, leave_side);
5309
5310   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5311                                       CE_PLAYER_LEAVES_X,
5312                                       player->index_bit, leave_side);
5313
5314   Feld[jx][jy] = el_player;
5315   InitPlayerField(jx, jy, el_player, TRUE);
5316
5317   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5318      possible that the relocation target field did not contain a player element,
5319      but a walkable element, to which the new player was relocated -- in this
5320      case, restore that (already initialized!) element on the player field */
5321   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5322   {
5323     Feld[jx][jy] = element;     /* restore previously existing element */
5324   }
5325
5326   /* only visually relocate centered player */
5327   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5328                      FALSE, level.instant_relocation);
5329
5330   TestIfPlayerTouchesBadThing(jx, jy);
5331   TestIfPlayerTouchesCustomElement(jx, jy);
5332
5333   if (IS_CUSTOM_ELEMENT(element))
5334     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5335                                player->index_bit, enter_side);
5336
5337   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5338                                       player->index_bit, enter_side);
5339
5340   if (player->is_switching)
5341   {
5342     /* ensure that relocation while still switching an element does not cause
5343        a new element to be treated as also switched directly after relocation
5344        (this is important for teleporter switches that teleport the player to
5345        a place where another teleporter switch is in the same direction, which
5346        would then incorrectly be treated as immediately switched before the
5347        direction key that caused the switch was released) */
5348
5349     player->switch_x += jx - old_jx;
5350     player->switch_y += jy - old_jy;
5351   }
5352 }
5353
5354 void Explode(int ex, int ey, int phase, int mode)
5355 {
5356   int x, y;
5357   int last_phase;
5358   int border_element;
5359
5360   /* !!! eliminate this variable !!! */
5361   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5362
5363   if (game.explosions_delayed)
5364   {
5365     ExplodeField[ex][ey] = mode;
5366     return;
5367   }
5368
5369   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5370   {
5371     int center_element = Feld[ex][ey];
5372     int artwork_element, explosion_element;     /* set these values later */
5373
5374     /* remove things displayed in background while burning dynamite */
5375     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5376       Back[ex][ey] = 0;
5377
5378     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5379     {
5380       /* put moving element to center field (and let it explode there) */
5381       center_element = MovingOrBlocked2Element(ex, ey);
5382       RemoveMovingField(ex, ey);
5383       Feld[ex][ey] = center_element;
5384     }
5385
5386     /* now "center_element" is finally determined -- set related values now */
5387     artwork_element = center_element;           /* for custom player artwork */
5388     explosion_element = center_element;         /* for custom player artwork */
5389
5390     if (IS_PLAYER(ex, ey))
5391     {
5392       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5393
5394       artwork_element = stored_player[player_nr].artwork_element;
5395
5396       if (level.use_explosion_element[player_nr])
5397       {
5398         explosion_element = level.explosion_element[player_nr];
5399         artwork_element = explosion_element;
5400       }
5401     }
5402
5403     if (mode == EX_TYPE_NORMAL ||
5404         mode == EX_TYPE_CENTER ||
5405         mode == EX_TYPE_CROSS)
5406       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5407
5408     last_phase = element_info[explosion_element].explosion_delay + 1;
5409
5410     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5411     {
5412       int xx = x - ex + 1;
5413       int yy = y - ey + 1;
5414       int element;
5415
5416       if (!IN_LEV_FIELD(x, y) ||
5417           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5418           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5419         continue;
5420
5421       element = Feld[x][y];
5422
5423       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5424       {
5425         element = MovingOrBlocked2Element(x, y);
5426
5427         if (!IS_EXPLOSION_PROOF(element))
5428           RemoveMovingField(x, y);
5429       }
5430
5431       /* indestructible elements can only explode in center (but not flames) */
5432       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5433                                            mode == EX_TYPE_BORDER)) ||
5434           element == EL_FLAMES)
5435         continue;
5436
5437       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5438          behaviour, for example when touching a yamyam that explodes to rocks
5439          with active deadly shield, a rock is created under the player !!! */
5440       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5441 #if 0
5442       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5443           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5444            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5445 #else
5446       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5447 #endif
5448       {
5449         if (IS_ACTIVE_BOMB(element))
5450         {
5451           /* re-activate things under the bomb like gate or penguin */
5452           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5453           Back[x][y] = 0;
5454         }
5455
5456         continue;
5457       }
5458
5459       /* save walkable background elements while explosion on same tile */
5460       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5461           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5462         Back[x][y] = element;
5463
5464       /* ignite explodable elements reached by other explosion */
5465       if (element == EL_EXPLOSION)
5466         element = Store2[x][y];
5467
5468       if (AmoebaNr[x][y] &&
5469           (element == EL_AMOEBA_FULL ||
5470            element == EL_BD_AMOEBA ||
5471            element == EL_AMOEBA_GROWING))
5472       {
5473         AmoebaCnt[AmoebaNr[x][y]]--;
5474         AmoebaCnt2[AmoebaNr[x][y]]--;
5475       }
5476
5477       RemoveField(x, y);
5478
5479       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5480       {
5481         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5482
5483         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5484
5485         if (PLAYERINFO(ex, ey)->use_murphy)
5486           Store[x][y] = EL_EMPTY;
5487       }
5488
5489       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5490          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5491       else if (ELEM_IS_PLAYER(center_element))
5492         Store[x][y] = EL_EMPTY;
5493       else if (center_element == EL_YAMYAM)
5494         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5495       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5496         Store[x][y] = element_info[center_element].content.e[xx][yy];
5497 #if 1
5498       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5499          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5500          otherwise) -- FIX THIS !!! */
5501       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5502         Store[x][y] = element_info[element].content.e[1][1];
5503 #else
5504       else if (!CAN_EXPLODE(element))
5505         Store[x][y] = element_info[element].content.e[1][1];
5506 #endif
5507       else
5508         Store[x][y] = EL_EMPTY;
5509
5510       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5511           center_element == EL_AMOEBA_TO_DIAMOND)
5512         Store2[x][y] = element;
5513
5514       Feld[x][y] = EL_EXPLOSION;
5515       GfxElement[x][y] = artwork_element;
5516
5517       ExplodePhase[x][y] = 1;
5518       ExplodeDelay[x][y] = last_phase;
5519
5520       Stop[x][y] = TRUE;
5521     }
5522
5523     if (center_element == EL_YAMYAM)
5524       game.yamyam_content_nr =
5525         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5526
5527     return;
5528   }
5529
5530   if (Stop[ex][ey])
5531     return;
5532
5533   x = ex;
5534   y = ey;
5535
5536   if (phase == 1)
5537     GfxFrame[x][y] = 0;         /* restart explosion animation */
5538
5539   last_phase = ExplodeDelay[x][y];
5540
5541   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5542
5543   /* this can happen if the player leaves an explosion just in time */
5544   if (GfxElement[x][y] == EL_UNDEFINED)
5545     GfxElement[x][y] = EL_EMPTY;
5546
5547   border_element = Store2[x][y];
5548   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5549     border_element = StorePlayer[x][y];
5550
5551   if (phase == element_info[border_element].ignition_delay ||
5552       phase == last_phase)
5553   {
5554     boolean border_explosion = FALSE;
5555
5556     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5557         !PLAYER_EXPLOSION_PROTECTED(x, y))
5558     {
5559       KillPlayerUnlessExplosionProtected(x, y);
5560       border_explosion = TRUE;
5561     }
5562     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5563     {
5564       Feld[x][y] = Store2[x][y];
5565       Store2[x][y] = 0;
5566       Bang(x, y);
5567       border_explosion = TRUE;
5568     }
5569     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5570     {
5571       AmoebeUmwandeln(x, y);
5572       Store2[x][y] = 0;
5573       border_explosion = TRUE;
5574     }
5575
5576     /* if an element just explodes due to another explosion (chain-reaction),
5577        do not immediately end the new explosion when it was the last frame of
5578        the explosion (as it would be done in the following "if"-statement!) */
5579     if (border_explosion && phase == last_phase)
5580       return;
5581   }
5582
5583   if (phase == last_phase)
5584   {
5585     int element;
5586
5587     element = Feld[x][y] = Store[x][y];
5588     Store[x][y] = Store2[x][y] = 0;
5589     GfxElement[x][y] = EL_UNDEFINED;
5590
5591     /* player can escape from explosions and might therefore be still alive */
5592     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5593         element <= EL_PLAYER_IS_EXPLODING_4)
5594     {
5595       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5596       int explosion_element = EL_PLAYER_1 + player_nr;
5597       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5598       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5599
5600       if (level.use_explosion_element[player_nr])
5601         explosion_element = level.explosion_element[player_nr];
5602
5603       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5604                     element_info[explosion_element].content.e[xx][yy]);
5605     }
5606
5607     /* restore probably existing indestructible background element */
5608     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5609       element = Feld[x][y] = Back[x][y];
5610     Back[x][y] = 0;
5611
5612     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5613     GfxDir[x][y] = MV_NONE;
5614     ChangeDelay[x][y] = 0;
5615     ChangePage[x][y] = -1;
5616
5617     CustomValue[x][y] = 0;
5618
5619     InitField_WithBug2(x, y, FALSE);
5620
5621     TEST_DrawLevelField(x, y);
5622
5623     TestIfElementTouchesCustomElement(x, y);
5624
5625     if (GFX_CRUMBLED(element))
5626       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5627
5628     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5629       StorePlayer[x][y] = 0;
5630
5631     if (ELEM_IS_PLAYER(element))
5632       RelocatePlayer(x, y, element);
5633   }
5634   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5635   {
5636     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5637     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5638
5639     if (phase == delay)
5640       TEST_DrawLevelFieldCrumbled(x, y);
5641
5642     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5643     {
5644       DrawLevelElement(x, y, Back[x][y]);
5645       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5646     }
5647     else if (IS_WALKABLE_UNDER(Back[x][y]))
5648     {
5649       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5650       DrawLevelElementThruMask(x, y, Back[x][y]);
5651     }
5652     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5653       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5654   }
5655 }
5656
5657 void DynaExplode(int ex, int ey)
5658 {
5659   int i, j;
5660   int dynabomb_element = Feld[ex][ey];
5661   int dynabomb_size = 1;
5662   boolean dynabomb_xl = FALSE;
5663   struct PlayerInfo *player;
5664   static int xy[4][2] =
5665   {
5666     { 0, -1 },
5667     { -1, 0 },
5668     { +1, 0 },
5669     { 0, +1 }
5670   };
5671
5672   if (IS_ACTIVE_BOMB(dynabomb_element))
5673   {
5674     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5675     dynabomb_size = player->dynabomb_size;
5676     dynabomb_xl = player->dynabomb_xl;
5677     player->dynabombs_left++;
5678   }
5679
5680   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5681
5682   for (i = 0; i < NUM_DIRECTIONS; i++)
5683   {
5684     for (j = 1; j <= dynabomb_size; j++)
5685     {
5686       int x = ex + j * xy[i][0];
5687       int y = ey + j * xy[i][1];
5688       int element;
5689
5690       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5691         break;
5692
5693       element = Feld[x][y];
5694
5695       /* do not restart explosions of fields with active bombs */
5696       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5697         continue;
5698
5699       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5700
5701       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5702           !IS_DIGGABLE(element) && !dynabomb_xl)
5703         break;
5704     }
5705   }
5706 }
5707
5708 void Bang(int x, int y)
5709 {
5710   int element = MovingOrBlocked2Element(x, y);
5711   int explosion_type = EX_TYPE_NORMAL;
5712
5713   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5714   {
5715     struct PlayerInfo *player = PLAYERINFO(x, y);
5716
5717     element = Feld[x][y] = player->initial_element;
5718
5719     if (level.use_explosion_element[player->index_nr])
5720     {
5721       int explosion_element = level.explosion_element[player->index_nr];
5722
5723       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5724         explosion_type = EX_TYPE_CROSS;
5725       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5726         explosion_type = EX_TYPE_CENTER;
5727     }
5728   }
5729
5730   switch (element)
5731   {
5732     case EL_BUG:
5733     case EL_SPACESHIP:
5734     case EL_BD_BUTTERFLY:
5735     case EL_BD_FIREFLY:
5736     case EL_YAMYAM:
5737     case EL_DARK_YAMYAM:
5738     case EL_ROBOT:
5739     case EL_PACMAN:
5740     case EL_MOLE:
5741       RaiseScoreElement(element);
5742       break;
5743
5744     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5745     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5746     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5747     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5748     case EL_DYNABOMB_INCREASE_NUMBER:
5749     case EL_DYNABOMB_INCREASE_SIZE:
5750     case EL_DYNABOMB_INCREASE_POWER:
5751       explosion_type = EX_TYPE_DYNA;
5752       break;
5753
5754     case EL_DC_LANDMINE:
5755       explosion_type = EX_TYPE_CENTER;
5756       break;
5757
5758     case EL_PENGUIN:
5759     case EL_LAMP:
5760     case EL_LAMP_ACTIVE:
5761     case EL_AMOEBA_TO_DIAMOND:
5762       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5763         explosion_type = EX_TYPE_CENTER;
5764       break;
5765
5766     default:
5767       if (element_info[element].explosion_type == EXPLODES_CROSS)
5768         explosion_type = EX_TYPE_CROSS;
5769       else if (element_info[element].explosion_type == EXPLODES_1X1)
5770         explosion_type = EX_TYPE_CENTER;
5771       break;
5772   }
5773
5774   if (explosion_type == EX_TYPE_DYNA)
5775     DynaExplode(x, y);
5776   else
5777     Explode(x, y, EX_PHASE_START, explosion_type);
5778
5779   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5780 }
5781
5782 void SplashAcid(int x, int y)
5783 {
5784   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5785       (!IN_LEV_FIELD(x - 1, y - 2) ||
5786        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5787     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5788
5789   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5790       (!IN_LEV_FIELD(x + 1, y - 2) ||
5791        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5792     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5793
5794   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5795 }
5796
5797 static void InitBeltMovement()
5798 {
5799   static int belt_base_element[4] =
5800   {
5801     EL_CONVEYOR_BELT_1_LEFT,
5802     EL_CONVEYOR_BELT_2_LEFT,
5803     EL_CONVEYOR_BELT_3_LEFT,
5804     EL_CONVEYOR_BELT_4_LEFT
5805   };
5806   static int belt_base_active_element[4] =
5807   {
5808     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5809     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5810     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5811     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5812   };
5813
5814   int x, y, i, j;
5815
5816   /* set frame order for belt animation graphic according to belt direction */
5817   for (i = 0; i < NUM_BELTS; i++)
5818   {
5819     int belt_nr = i;
5820
5821     for (j = 0; j < NUM_BELT_PARTS; j++)
5822     {
5823       int element = belt_base_active_element[belt_nr] + j;
5824       int graphic_1 = el2img(element);
5825       int graphic_2 = el2panelimg(element);
5826
5827       if (game.belt_dir[i] == MV_LEFT)
5828       {
5829         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5830         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5831       }
5832       else
5833       {
5834         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5835         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5836       }
5837     }
5838   }
5839
5840   SCAN_PLAYFIELD(x, y)
5841   {
5842     int element = Feld[x][y];
5843
5844     for (i = 0; i < NUM_BELTS; i++)
5845     {
5846       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5847       {
5848         int e_belt_nr = getBeltNrFromBeltElement(element);
5849         int belt_nr = i;
5850
5851         if (e_belt_nr == belt_nr)
5852         {
5853           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5854
5855           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5856         }
5857       }
5858     }
5859   }
5860 }
5861
5862 static void ToggleBeltSwitch(int x, int y)
5863 {
5864   static int belt_base_element[4] =
5865   {
5866     EL_CONVEYOR_BELT_1_LEFT,
5867     EL_CONVEYOR_BELT_2_LEFT,
5868     EL_CONVEYOR_BELT_3_LEFT,
5869     EL_CONVEYOR_BELT_4_LEFT
5870   };
5871   static int belt_base_active_element[4] =
5872   {
5873     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5874     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5875     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5876     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5877   };
5878   static int belt_base_switch_element[4] =
5879   {
5880     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5881     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5882     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5883     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5884   };
5885   static int belt_move_dir[4] =
5886   {
5887     MV_LEFT,
5888     MV_NONE,
5889     MV_RIGHT,
5890     MV_NONE,
5891   };
5892
5893   int element = Feld[x][y];
5894   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5895   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5896   int belt_dir = belt_move_dir[belt_dir_nr];
5897   int xx, yy, i;
5898
5899   if (!IS_BELT_SWITCH(element))
5900     return;
5901
5902   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5903   game.belt_dir[belt_nr] = belt_dir;
5904
5905   if (belt_dir_nr == 3)
5906     belt_dir_nr = 1;
5907
5908   /* set frame order for belt animation graphic according to belt direction */
5909   for (i = 0; i < NUM_BELT_PARTS; i++)
5910   {
5911     int element = belt_base_active_element[belt_nr] + i;
5912     int graphic_1 = el2img(element);
5913     int graphic_2 = el2panelimg(element);
5914
5915     if (belt_dir == MV_LEFT)
5916     {
5917       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5918       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5919     }
5920     else
5921     {
5922       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5923       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5924     }
5925   }
5926
5927   SCAN_PLAYFIELD(xx, yy)
5928   {
5929     int element = Feld[xx][yy];
5930
5931     if (IS_BELT_SWITCH(element))
5932     {
5933       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5934
5935       if (e_belt_nr == belt_nr)
5936       {
5937         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5938         TEST_DrawLevelField(xx, yy);
5939       }
5940     }
5941     else if (IS_BELT(element) && belt_dir != MV_NONE)
5942     {
5943       int e_belt_nr = getBeltNrFromBeltElement(element);
5944
5945       if (e_belt_nr == belt_nr)
5946       {
5947         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5948
5949         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5950         TEST_DrawLevelField(xx, yy);
5951       }
5952     }
5953     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5954     {
5955       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5956
5957       if (e_belt_nr == belt_nr)
5958       {
5959         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5960
5961         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5962         TEST_DrawLevelField(xx, yy);
5963       }
5964     }
5965   }
5966 }
5967
5968 static void ToggleSwitchgateSwitch(int x, int y)
5969 {
5970   int xx, yy;
5971
5972   game.switchgate_pos = !game.switchgate_pos;
5973
5974   SCAN_PLAYFIELD(xx, yy)
5975   {
5976     int element = Feld[xx][yy];
5977
5978     if (element == EL_SWITCHGATE_SWITCH_UP)
5979     {
5980       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5981       TEST_DrawLevelField(xx, yy);
5982     }
5983     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5984     {
5985       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5986       TEST_DrawLevelField(xx, yy);
5987     }
5988     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5989     {
5990       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5991       TEST_DrawLevelField(xx, yy);
5992     }
5993     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5994     {
5995       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5996       TEST_DrawLevelField(xx, yy);
5997     }
5998     else if (element == EL_SWITCHGATE_OPEN ||
5999              element == EL_SWITCHGATE_OPENING)
6000     {
6001       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6002
6003       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6004     }
6005     else if (element == EL_SWITCHGATE_CLOSED ||
6006              element == EL_SWITCHGATE_CLOSING)
6007     {
6008       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6009
6010       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6011     }
6012   }
6013 }
6014
6015 static int getInvisibleActiveFromInvisibleElement(int element)
6016 {
6017   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6018           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6019           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6020           element);
6021 }
6022
6023 static int getInvisibleFromInvisibleActiveElement(int element)
6024 {
6025   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6026           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6027           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6028           element);
6029 }
6030
6031 static void RedrawAllLightSwitchesAndInvisibleElements()
6032 {
6033   int x, y;
6034
6035   SCAN_PLAYFIELD(x, y)
6036   {
6037     int element = Feld[x][y];
6038
6039     if (element == EL_LIGHT_SWITCH &&
6040         game.light_time_left > 0)
6041     {
6042       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6043       TEST_DrawLevelField(x, y);
6044     }
6045     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6046              game.light_time_left == 0)
6047     {
6048       Feld[x][y] = EL_LIGHT_SWITCH;
6049       TEST_DrawLevelField(x, y);
6050     }
6051     else if (element == EL_EMC_DRIPPER &&
6052              game.light_time_left > 0)
6053     {
6054       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6055       TEST_DrawLevelField(x, y);
6056     }
6057     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6058              game.light_time_left == 0)
6059     {
6060       Feld[x][y] = EL_EMC_DRIPPER;
6061       TEST_DrawLevelField(x, y);
6062     }
6063     else if (element == EL_INVISIBLE_STEELWALL ||
6064              element == EL_INVISIBLE_WALL ||
6065              element == EL_INVISIBLE_SAND)
6066     {
6067       if (game.light_time_left > 0)
6068         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6069
6070       TEST_DrawLevelField(x, y);
6071
6072       /* uncrumble neighbour fields, if needed */
6073       if (element == EL_INVISIBLE_SAND)
6074         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6075     }
6076     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6077              element == EL_INVISIBLE_WALL_ACTIVE ||
6078              element == EL_INVISIBLE_SAND_ACTIVE)
6079     {
6080       if (game.light_time_left == 0)
6081         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6082
6083       TEST_DrawLevelField(x, y);
6084
6085       /* re-crumble neighbour fields, if needed */
6086       if (element == EL_INVISIBLE_SAND)
6087         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6088     }
6089   }
6090 }
6091
6092 static void RedrawAllInvisibleElementsForLenses()
6093 {
6094   int x, y;
6095
6096   SCAN_PLAYFIELD(x, y)
6097   {
6098     int element = Feld[x][y];
6099
6100     if (element == EL_EMC_DRIPPER &&
6101         game.lenses_time_left > 0)
6102     {
6103       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6104       TEST_DrawLevelField(x, y);
6105     }
6106     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6107              game.lenses_time_left == 0)
6108     {
6109       Feld[x][y] = EL_EMC_DRIPPER;
6110       TEST_DrawLevelField(x, y);
6111     }
6112     else if (element == EL_INVISIBLE_STEELWALL ||
6113              element == EL_INVISIBLE_WALL ||
6114              element == EL_INVISIBLE_SAND)
6115     {
6116       if (game.lenses_time_left > 0)
6117         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6118
6119       TEST_DrawLevelField(x, y);
6120
6121       /* uncrumble neighbour fields, if needed */
6122       if (element == EL_INVISIBLE_SAND)
6123         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6124     }
6125     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6126              element == EL_INVISIBLE_WALL_ACTIVE ||
6127              element == EL_INVISIBLE_SAND_ACTIVE)
6128     {
6129       if (game.lenses_time_left == 0)
6130         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6131
6132       TEST_DrawLevelField(x, y);
6133
6134       /* re-crumble neighbour fields, if needed */
6135       if (element == EL_INVISIBLE_SAND)
6136         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6137     }
6138   }
6139 }
6140
6141 static void RedrawAllInvisibleElementsForMagnifier()
6142 {
6143   int x, y;
6144
6145   SCAN_PLAYFIELD(x, y)
6146   {
6147     int element = Feld[x][y];
6148
6149     if (element == EL_EMC_FAKE_GRASS &&
6150         game.magnify_time_left > 0)
6151     {
6152       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6153       TEST_DrawLevelField(x, y);
6154     }
6155     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6156              game.magnify_time_left == 0)
6157     {
6158       Feld[x][y] = EL_EMC_FAKE_GRASS;
6159       TEST_DrawLevelField(x, y);
6160     }
6161     else if (IS_GATE_GRAY(element) &&
6162              game.magnify_time_left > 0)
6163     {
6164       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6165                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6166                     IS_EM_GATE_GRAY(element) ?
6167                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6168                     IS_EMC_GATE_GRAY(element) ?
6169                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6170                     IS_DC_GATE_GRAY(element) ?
6171                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6172                     element);
6173       TEST_DrawLevelField(x, y);
6174     }
6175     else if (IS_GATE_GRAY_ACTIVE(element) &&
6176              game.magnify_time_left == 0)
6177     {
6178       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6179                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6180                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6181                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6182                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6183                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6184                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6185                     EL_DC_GATE_WHITE_GRAY :
6186                     element);
6187       TEST_DrawLevelField(x, y);
6188     }
6189   }
6190 }
6191
6192 static void ToggleLightSwitch(int x, int y)
6193 {
6194   int element = Feld[x][y];
6195
6196   game.light_time_left =
6197     (element == EL_LIGHT_SWITCH ?
6198      level.time_light * FRAMES_PER_SECOND : 0);
6199
6200   RedrawAllLightSwitchesAndInvisibleElements();
6201 }
6202
6203 static void ActivateTimegateSwitch(int x, int y)
6204 {
6205   int xx, yy;
6206
6207   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6208
6209   SCAN_PLAYFIELD(xx, yy)
6210   {
6211     int element = Feld[xx][yy];
6212
6213     if (element == EL_TIMEGATE_CLOSED ||
6214         element == EL_TIMEGATE_CLOSING)
6215     {
6216       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6217       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6218     }
6219
6220     /*
6221     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6222     {
6223       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6224       TEST_DrawLevelField(xx, yy);
6225     }
6226     */
6227
6228   }
6229
6230   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6231                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6232 }
6233
6234 void Impact(int x, int y)
6235 {
6236   boolean last_line = (y == lev_fieldy - 1);
6237   boolean object_hit = FALSE;
6238   boolean impact = (last_line || object_hit);
6239   int element = Feld[x][y];
6240   int smashed = EL_STEELWALL;
6241
6242   if (!last_line)       /* check if element below was hit */
6243   {
6244     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6245       return;
6246
6247     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6248                                          MovDir[x][y + 1] != MV_DOWN ||
6249                                          MovPos[x][y + 1] <= TILEY / 2));
6250
6251     /* do not smash moving elements that left the smashed field in time */
6252     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6253         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6254       object_hit = FALSE;
6255
6256 #if USE_QUICKSAND_IMPACT_BUGFIX
6257     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6258     {
6259       RemoveMovingField(x, y + 1);
6260       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6261       Feld[x][y + 2] = EL_ROCK;
6262       TEST_DrawLevelField(x, y + 2);
6263
6264       object_hit = TRUE;
6265     }
6266
6267     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6268     {
6269       RemoveMovingField(x, y + 1);
6270       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6271       Feld[x][y + 2] = EL_ROCK;
6272       TEST_DrawLevelField(x, y + 2);
6273
6274       object_hit = TRUE;
6275     }
6276 #endif
6277
6278     if (object_hit)
6279       smashed = MovingOrBlocked2Element(x, y + 1);
6280
6281     impact = (last_line || object_hit);
6282   }
6283
6284   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6285   {
6286     SplashAcid(x, y + 1);
6287     return;
6288   }
6289
6290   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6291   /* only reset graphic animation if graphic really changes after impact */
6292   if (impact &&
6293       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6294   {
6295     ResetGfxAnimation(x, y);
6296     TEST_DrawLevelField(x, y);
6297   }
6298
6299   if (impact && CAN_EXPLODE_IMPACT(element))
6300   {
6301     Bang(x, y);
6302     return;
6303   }
6304   else if (impact && element == EL_PEARL &&
6305            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6306   {
6307     ResetGfxAnimation(x, y);
6308
6309     Feld[x][y] = EL_PEARL_BREAKING;
6310     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6311     return;
6312   }
6313   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6314   {
6315     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6316
6317     return;
6318   }
6319
6320   if (impact && element == EL_AMOEBA_DROP)
6321   {
6322     if (object_hit && IS_PLAYER(x, y + 1))
6323       KillPlayerUnlessEnemyProtected(x, y + 1);
6324     else if (object_hit && smashed == EL_PENGUIN)
6325       Bang(x, y + 1);
6326     else
6327     {
6328       Feld[x][y] = EL_AMOEBA_GROWING;
6329       Store[x][y] = EL_AMOEBA_WET;
6330
6331       ResetRandomAnimationValue(x, y);
6332     }
6333     return;
6334   }
6335
6336   if (object_hit)               /* check which object was hit */
6337   {
6338     if ((CAN_PASS_MAGIC_WALL(element) && 
6339          (smashed == EL_MAGIC_WALL ||
6340           smashed == EL_BD_MAGIC_WALL)) ||
6341         (CAN_PASS_DC_MAGIC_WALL(element) &&
6342          smashed == EL_DC_MAGIC_WALL))
6343     {
6344       int xx, yy;
6345       int activated_magic_wall =
6346         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6347          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6348          EL_DC_MAGIC_WALL_ACTIVE);
6349
6350       /* activate magic wall / mill */
6351       SCAN_PLAYFIELD(xx, yy)
6352       {
6353         if (Feld[xx][yy] == smashed)
6354           Feld[xx][yy] = activated_magic_wall;
6355       }
6356
6357       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6358       game.magic_wall_active = TRUE;
6359
6360       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6361                             SND_MAGIC_WALL_ACTIVATING :
6362                             smashed == EL_BD_MAGIC_WALL ?
6363                             SND_BD_MAGIC_WALL_ACTIVATING :
6364                             SND_DC_MAGIC_WALL_ACTIVATING));
6365     }
6366
6367     if (IS_PLAYER(x, y + 1))
6368     {
6369       if (CAN_SMASH_PLAYER(element))
6370       {
6371         KillPlayerUnlessEnemyProtected(x, y + 1);
6372         return;
6373       }
6374     }
6375     else if (smashed == EL_PENGUIN)
6376     {
6377       if (CAN_SMASH_PLAYER(element))
6378       {
6379         Bang(x, y + 1);
6380         return;
6381       }
6382     }
6383     else if (element == EL_BD_DIAMOND)
6384     {
6385       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6386       {
6387         Bang(x, y + 1);
6388         return;
6389       }
6390     }
6391     else if (((element == EL_SP_INFOTRON ||
6392                element == EL_SP_ZONK) &&
6393               (smashed == EL_SP_SNIKSNAK ||
6394                smashed == EL_SP_ELECTRON ||
6395                smashed == EL_SP_DISK_ORANGE)) ||
6396              (element == EL_SP_INFOTRON &&
6397               smashed == EL_SP_DISK_YELLOW))
6398     {
6399       Bang(x, y + 1);
6400       return;
6401     }
6402     else if (CAN_SMASH_EVERYTHING(element))
6403     {
6404       if (IS_CLASSIC_ENEMY(smashed) ||
6405           CAN_EXPLODE_SMASHED(smashed))
6406       {
6407         Bang(x, y + 1);
6408         return;
6409       }
6410       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6411       {
6412         if (smashed == EL_LAMP ||
6413             smashed == EL_LAMP_ACTIVE)
6414         {
6415           Bang(x, y + 1);
6416           return;
6417         }
6418         else if (smashed == EL_NUT)
6419         {
6420           Feld[x][y + 1] = EL_NUT_BREAKING;
6421           PlayLevelSound(x, y, SND_NUT_BREAKING);
6422           RaiseScoreElement(EL_NUT);
6423           return;
6424         }
6425         else if (smashed == EL_PEARL)
6426         {
6427           ResetGfxAnimation(x, y);
6428
6429           Feld[x][y + 1] = EL_PEARL_BREAKING;
6430           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6431           return;
6432         }
6433         else if (smashed == EL_DIAMOND)
6434         {
6435           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6436           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6437           return;
6438         }
6439         else if (IS_BELT_SWITCH(smashed))
6440         {
6441           ToggleBeltSwitch(x, y + 1);
6442         }
6443         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6444                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6445                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6446                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6447         {
6448           ToggleSwitchgateSwitch(x, y + 1);
6449         }
6450         else if (smashed == EL_LIGHT_SWITCH ||
6451                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6452         {
6453           ToggleLightSwitch(x, y + 1);
6454         }
6455         else
6456         {
6457           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6458
6459           CheckElementChangeBySide(x, y + 1, smashed, element,
6460                                    CE_SWITCHED, CH_SIDE_TOP);
6461           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6462                                             CH_SIDE_TOP);
6463         }
6464       }
6465       else
6466       {
6467         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6468       }
6469     }
6470   }
6471
6472   /* play sound of magic wall / mill */
6473   if (!last_line &&
6474       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6475        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6476        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6477   {
6478     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6479       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6480     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6481       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6482     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6483       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6484
6485     return;
6486   }
6487
6488   /* play sound of object that hits the ground */
6489   if (last_line || object_hit)
6490     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6491 }
6492
6493 inline static void TurnRoundExt(int x, int y)
6494 {
6495   static struct
6496   {
6497     int dx, dy;
6498   } move_xy[] =
6499   {
6500     {  0,  0 },
6501     { -1,  0 },
6502     { +1,  0 },
6503     {  0,  0 },
6504     {  0, -1 },
6505     {  0,  0 }, { 0, 0 }, { 0, 0 },
6506     {  0, +1 }
6507   };
6508   static struct
6509   {
6510     int left, right, back;
6511   } turn[] =
6512   {
6513     { 0,        0,              0        },
6514     { MV_DOWN,  MV_UP,          MV_RIGHT },
6515     { MV_UP,    MV_DOWN,        MV_LEFT  },
6516     { 0,        0,              0        },
6517     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6518     { 0,        0,              0        },
6519     { 0,        0,              0        },
6520     { 0,        0,              0        },
6521     { MV_RIGHT, MV_LEFT,        MV_UP    }
6522   };
6523
6524   int element = Feld[x][y];
6525   int move_pattern = element_info[element].move_pattern;
6526
6527   int old_move_dir = MovDir[x][y];
6528   int left_dir  = turn[old_move_dir].left;
6529   int right_dir = turn[old_move_dir].right;
6530   int back_dir  = turn[old_move_dir].back;
6531
6532   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6533   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6534   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6535   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6536
6537   int left_x  = x + left_dx,  left_y  = y + left_dy;
6538   int right_x = x + right_dx, right_y = y + right_dy;
6539   int move_x  = x + move_dx,  move_y  = y + move_dy;
6540
6541   int xx, yy;
6542
6543   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6544   {
6545     TestIfBadThingTouchesOtherBadThing(x, y);
6546
6547     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6548       MovDir[x][y] = right_dir;
6549     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6550       MovDir[x][y] = left_dir;
6551
6552     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6553       MovDelay[x][y] = 9;
6554     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6555       MovDelay[x][y] = 1;
6556   }
6557   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6558   {
6559     TestIfBadThingTouchesOtherBadThing(x, y);
6560
6561     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6562       MovDir[x][y] = left_dir;
6563     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6564       MovDir[x][y] = right_dir;
6565
6566     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6567       MovDelay[x][y] = 9;
6568     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6569       MovDelay[x][y] = 1;
6570   }
6571   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6572   {
6573     TestIfBadThingTouchesOtherBadThing(x, y);
6574
6575     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6576       MovDir[x][y] = left_dir;
6577     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6578       MovDir[x][y] = right_dir;
6579
6580     if (MovDir[x][y] != old_move_dir)
6581       MovDelay[x][y] = 9;
6582   }
6583   else if (element == EL_YAMYAM)
6584   {
6585     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6586     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6587
6588     if (can_turn_left && can_turn_right)
6589       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6590     else if (can_turn_left)
6591       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6592     else if (can_turn_right)
6593       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6594     else
6595       MovDir[x][y] = back_dir;
6596
6597     MovDelay[x][y] = 16 + 16 * RND(3);
6598   }
6599   else if (element == EL_DARK_YAMYAM)
6600   {
6601     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6602                                                          left_x, left_y);
6603     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6604                                                          right_x, right_y);
6605
6606     if (can_turn_left && can_turn_right)
6607       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6608     else if (can_turn_left)
6609       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6610     else if (can_turn_right)
6611       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6612     else
6613       MovDir[x][y] = back_dir;
6614
6615     MovDelay[x][y] = 16 + 16 * RND(3);
6616   }
6617   else if (element == EL_PACMAN)
6618   {
6619     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6620     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6621
6622     if (can_turn_left && can_turn_right)
6623       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6624     else if (can_turn_left)
6625       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6626     else if (can_turn_right)
6627       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6628     else
6629       MovDir[x][y] = back_dir;
6630
6631     MovDelay[x][y] = 6 + RND(40);
6632   }
6633   else if (element == EL_PIG)
6634   {
6635     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6636     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6637     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6638     boolean should_turn_left, should_turn_right, should_move_on;
6639     int rnd_value = 24;
6640     int rnd = RND(rnd_value);
6641
6642     should_turn_left = (can_turn_left &&
6643                         (!can_move_on ||
6644                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6645                                                    y + back_dy + left_dy)));
6646     should_turn_right = (can_turn_right &&
6647                          (!can_move_on ||
6648                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6649                                                     y + back_dy + right_dy)));
6650     should_move_on = (can_move_on &&
6651                       (!can_turn_left ||
6652                        !can_turn_right ||
6653                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6654                                                  y + move_dy + left_dy) ||
6655                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6656                                                  y + move_dy + right_dy)));
6657
6658     if (should_turn_left || should_turn_right || should_move_on)
6659     {
6660       if (should_turn_left && should_turn_right && should_move_on)
6661         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6662                         rnd < 2 * rnd_value / 3 ? right_dir :
6663                         old_move_dir);
6664       else if (should_turn_left && should_turn_right)
6665         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6666       else if (should_turn_left && should_move_on)
6667         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6668       else if (should_turn_right && should_move_on)
6669         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6670       else if (should_turn_left)
6671         MovDir[x][y] = left_dir;
6672       else if (should_turn_right)
6673         MovDir[x][y] = right_dir;
6674       else if (should_move_on)
6675         MovDir[x][y] = old_move_dir;
6676     }
6677     else if (can_move_on && rnd > rnd_value / 8)
6678       MovDir[x][y] = old_move_dir;
6679     else if (can_turn_left && can_turn_right)
6680       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6681     else if (can_turn_left && rnd > rnd_value / 8)
6682       MovDir[x][y] = left_dir;
6683     else if (can_turn_right && rnd > rnd_value/8)
6684       MovDir[x][y] = right_dir;
6685     else
6686       MovDir[x][y] = back_dir;
6687
6688     xx = x + move_xy[MovDir[x][y]].dx;
6689     yy = y + move_xy[MovDir[x][y]].dy;
6690
6691     if (!IN_LEV_FIELD(xx, yy) ||
6692         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6693       MovDir[x][y] = old_move_dir;
6694
6695     MovDelay[x][y] = 0;
6696   }
6697   else if (element == EL_DRAGON)
6698   {
6699     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6700     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6701     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6702     int rnd_value = 24;
6703     int rnd = RND(rnd_value);
6704
6705     if (can_move_on && rnd > rnd_value / 8)
6706       MovDir[x][y] = old_move_dir;
6707     else if (can_turn_left && can_turn_right)
6708       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6709     else if (can_turn_left && rnd > rnd_value / 8)
6710       MovDir[x][y] = left_dir;
6711     else if (can_turn_right && rnd > rnd_value / 8)
6712       MovDir[x][y] = right_dir;
6713     else
6714       MovDir[x][y] = back_dir;
6715
6716     xx = x + move_xy[MovDir[x][y]].dx;
6717     yy = y + move_xy[MovDir[x][y]].dy;
6718
6719     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6720       MovDir[x][y] = old_move_dir;
6721
6722     MovDelay[x][y] = 0;
6723   }
6724   else if (element == EL_MOLE)
6725   {
6726     boolean can_move_on =
6727       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6728                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6729                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6730     if (!can_move_on)
6731     {
6732       boolean can_turn_left =
6733         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6734                               IS_AMOEBOID(Feld[left_x][left_y])));
6735
6736       boolean can_turn_right =
6737         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6738                               IS_AMOEBOID(Feld[right_x][right_y])));
6739
6740       if (can_turn_left && can_turn_right)
6741         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6742       else if (can_turn_left)
6743         MovDir[x][y] = left_dir;
6744       else
6745         MovDir[x][y] = right_dir;
6746     }
6747
6748     if (MovDir[x][y] != old_move_dir)
6749       MovDelay[x][y] = 9;
6750   }
6751   else if (element == EL_BALLOON)
6752   {
6753     MovDir[x][y] = game.wind_direction;
6754     MovDelay[x][y] = 0;
6755   }
6756   else if (element == EL_SPRING)
6757   {
6758     if (MovDir[x][y] & MV_HORIZONTAL)
6759     {
6760       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6761           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6762       {
6763         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6764         ResetGfxAnimation(move_x, move_y);
6765         TEST_DrawLevelField(move_x, move_y);
6766
6767         MovDir[x][y] = back_dir;
6768       }
6769       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6770                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6771         MovDir[x][y] = MV_NONE;
6772     }
6773
6774     MovDelay[x][y] = 0;
6775   }
6776   else if (element == EL_ROBOT ||
6777            element == EL_SATELLITE ||
6778            element == EL_PENGUIN ||
6779            element == EL_EMC_ANDROID)
6780   {
6781     int attr_x = -1, attr_y = -1;
6782
6783     if (AllPlayersGone)
6784     {
6785       attr_x = ExitX;
6786       attr_y = ExitY;
6787     }
6788     else
6789     {
6790       int i;
6791
6792       for (i = 0; i < MAX_PLAYERS; i++)
6793       {
6794         struct PlayerInfo *player = &stored_player[i];
6795         int jx = player->jx, jy = player->jy;
6796
6797         if (!player->active)
6798           continue;
6799
6800         if (attr_x == -1 ||
6801             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6802         {
6803           attr_x = jx;
6804           attr_y = jy;
6805         }
6806       }
6807     }
6808
6809     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6810         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6811          game.engine_version < VERSION_IDENT(3,1,0,0)))
6812     {
6813       attr_x = ZX;
6814       attr_y = ZY;
6815     }
6816
6817     if (element == EL_PENGUIN)
6818     {
6819       int i;
6820       static int xy[4][2] =
6821       {
6822         { 0, -1 },
6823         { -1, 0 },
6824         { +1, 0 },
6825         { 0, +1 }
6826       };
6827
6828       for (i = 0; i < NUM_DIRECTIONS; i++)
6829       {
6830         int ex = x + xy[i][0];
6831         int ey = y + xy[i][1];
6832
6833         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6834                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6835                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6836                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6837         {
6838           attr_x = ex;
6839           attr_y = ey;
6840           break;
6841         }
6842       }
6843     }
6844
6845     MovDir[x][y] = MV_NONE;
6846     if (attr_x < x)
6847       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6848     else if (attr_x > x)
6849       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6850     if (attr_y < y)
6851       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6852     else if (attr_y > y)
6853       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6854
6855     if (element == EL_ROBOT)
6856     {
6857       int newx, newy;
6858
6859       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6860         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6861       Moving2Blocked(x, y, &newx, &newy);
6862
6863       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6864         MovDelay[x][y] = 8 + 8 * !RND(3);
6865       else
6866         MovDelay[x][y] = 16;
6867     }
6868     else if (element == EL_PENGUIN)
6869     {
6870       int newx, newy;
6871
6872       MovDelay[x][y] = 1;
6873
6874       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6875       {
6876         boolean first_horiz = RND(2);
6877         int new_move_dir = MovDir[x][y];
6878
6879         MovDir[x][y] =
6880           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6881         Moving2Blocked(x, y, &newx, &newy);
6882
6883         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6884           return;
6885
6886         MovDir[x][y] =
6887           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6888         Moving2Blocked(x, y, &newx, &newy);
6889
6890         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6891           return;
6892
6893         MovDir[x][y] = old_move_dir;
6894         return;
6895       }
6896     }
6897     else if (element == EL_SATELLITE)
6898     {
6899       int newx, newy;
6900
6901       MovDelay[x][y] = 1;
6902
6903       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6904       {
6905         boolean first_horiz = RND(2);
6906         int new_move_dir = MovDir[x][y];
6907
6908         MovDir[x][y] =
6909           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6910         Moving2Blocked(x, y, &newx, &newy);
6911
6912         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6913           return;
6914
6915         MovDir[x][y] =
6916           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6917         Moving2Blocked(x, y, &newx, &newy);
6918
6919         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6920           return;
6921
6922         MovDir[x][y] = old_move_dir;
6923         return;
6924       }
6925     }
6926     else if (element == EL_EMC_ANDROID)
6927     {
6928       static int check_pos[16] =
6929       {
6930         -1,             /*  0 => (invalid)          */
6931         7,              /*  1 => MV_LEFT            */
6932         3,              /*  2 => MV_RIGHT           */
6933         -1,             /*  3 => (invalid)          */
6934         1,              /*  4 =>            MV_UP   */
6935         0,              /*  5 => MV_LEFT  | MV_UP   */
6936         2,              /*  6 => MV_RIGHT | MV_UP   */
6937         -1,             /*  7 => (invalid)          */
6938         5,              /*  8 =>            MV_DOWN */
6939         6,              /*  9 => MV_LEFT  | MV_DOWN */
6940         4,              /* 10 => MV_RIGHT | MV_DOWN */
6941         -1,             /* 11 => (invalid)          */
6942         -1,             /* 12 => (invalid)          */
6943         -1,             /* 13 => (invalid)          */
6944         -1,             /* 14 => (invalid)          */
6945         -1,             /* 15 => (invalid)          */
6946       };
6947       static struct
6948       {
6949         int dx, dy;
6950         int dir;
6951       } check_xy[8] =
6952       {
6953         { -1, -1,       MV_LEFT  | MV_UP   },
6954         {  0, -1,                  MV_UP   },
6955         { +1, -1,       MV_RIGHT | MV_UP   },
6956         { +1,  0,       MV_RIGHT           },
6957         { +1, +1,       MV_RIGHT | MV_DOWN },
6958         {  0, +1,                  MV_DOWN },
6959         { -1, +1,       MV_LEFT  | MV_DOWN },
6960         { -1,  0,       MV_LEFT            },
6961       };
6962       int start_pos, check_order;
6963       boolean can_clone = FALSE;
6964       int i;
6965
6966       /* check if there is any free field around current position */
6967       for (i = 0; i < 8; i++)
6968       {
6969         int newx = x + check_xy[i].dx;
6970         int newy = y + check_xy[i].dy;
6971
6972         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6973         {
6974           can_clone = TRUE;
6975
6976           break;
6977         }
6978       }
6979
6980       if (can_clone)            /* randomly find an element to clone */
6981       {
6982         can_clone = FALSE;
6983
6984         start_pos = check_pos[RND(8)];
6985         check_order = (RND(2) ? -1 : +1);
6986
6987         for (i = 0; i < 8; i++)
6988         {
6989           int pos_raw = start_pos + i * check_order;
6990           int pos = (pos_raw + 8) % 8;
6991           int newx = x + check_xy[pos].dx;
6992           int newy = y + check_xy[pos].dy;
6993
6994           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6995           {
6996             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6997             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6998
6999             Store[x][y] = Feld[newx][newy];
7000
7001             can_clone = TRUE;
7002
7003             break;
7004           }
7005         }
7006       }
7007
7008       if (can_clone)            /* randomly find a direction to move */
7009       {
7010         can_clone = FALSE;
7011
7012         start_pos = check_pos[RND(8)];
7013         check_order = (RND(2) ? -1 : +1);
7014
7015         for (i = 0; i < 8; i++)
7016         {
7017           int pos_raw = start_pos + i * check_order;
7018           int pos = (pos_raw + 8) % 8;
7019           int newx = x + check_xy[pos].dx;
7020           int newy = y + check_xy[pos].dy;
7021           int new_move_dir = check_xy[pos].dir;
7022
7023           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7024           {
7025             MovDir[x][y] = new_move_dir;
7026             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7027
7028             can_clone = TRUE;
7029
7030             break;
7031           }
7032         }
7033       }
7034
7035       if (can_clone)            /* cloning and moving successful */
7036         return;
7037
7038       /* cannot clone -- try to move towards player */
7039
7040       start_pos = check_pos[MovDir[x][y] & 0x0f];
7041       check_order = (RND(2) ? -1 : +1);
7042
7043       for (i = 0; i < 3; i++)
7044       {
7045         /* first check start_pos, then previous/next or (next/previous) pos */
7046         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7047         int pos = (pos_raw + 8) % 8;
7048         int newx = x + check_xy[pos].dx;
7049         int newy = y + check_xy[pos].dy;
7050         int new_move_dir = check_xy[pos].dir;
7051
7052         if (IS_PLAYER(newx, newy))
7053           break;
7054
7055         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7056         {
7057           MovDir[x][y] = new_move_dir;
7058           MovDelay[x][y] = level.android_move_time * 8 + 1;
7059
7060           break;
7061         }
7062       }
7063     }
7064   }
7065   else if (move_pattern == MV_TURNING_LEFT ||
7066            move_pattern == MV_TURNING_RIGHT ||
7067            move_pattern == MV_TURNING_LEFT_RIGHT ||
7068            move_pattern == MV_TURNING_RIGHT_LEFT ||
7069            move_pattern == MV_TURNING_RANDOM ||
7070            move_pattern == MV_ALL_DIRECTIONS)
7071   {
7072     boolean can_turn_left =
7073       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7074     boolean can_turn_right =
7075       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7076
7077     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7078       return;
7079
7080     if (move_pattern == MV_TURNING_LEFT)
7081       MovDir[x][y] = left_dir;
7082     else if (move_pattern == MV_TURNING_RIGHT)
7083       MovDir[x][y] = right_dir;
7084     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7085       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7086     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7087       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7088     else if (move_pattern == MV_TURNING_RANDOM)
7089       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7090                       can_turn_right && !can_turn_left ? right_dir :
7091                       RND(2) ? left_dir : right_dir);
7092     else if (can_turn_left && can_turn_right)
7093       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7094     else if (can_turn_left)
7095       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7096     else if (can_turn_right)
7097       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7098     else
7099       MovDir[x][y] = back_dir;
7100
7101     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7102   }
7103   else if (move_pattern == MV_HORIZONTAL ||
7104            move_pattern == MV_VERTICAL)
7105   {
7106     if (move_pattern & old_move_dir)
7107       MovDir[x][y] = back_dir;
7108     else if (move_pattern == MV_HORIZONTAL)
7109       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7110     else if (move_pattern == MV_VERTICAL)
7111       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7112
7113     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7114   }
7115   else if (move_pattern & MV_ANY_DIRECTION)
7116   {
7117     MovDir[x][y] = move_pattern;
7118     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7119   }
7120   else if (move_pattern & MV_WIND_DIRECTION)
7121   {
7122     MovDir[x][y] = game.wind_direction;
7123     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7124   }
7125   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7126   {
7127     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7128       MovDir[x][y] = left_dir;
7129     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7130       MovDir[x][y] = right_dir;
7131
7132     if (MovDir[x][y] != old_move_dir)
7133       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7134   }
7135   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7136   {
7137     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7138       MovDir[x][y] = right_dir;
7139     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7140       MovDir[x][y] = left_dir;
7141
7142     if (MovDir[x][y] != old_move_dir)
7143       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7144   }
7145   else if (move_pattern == MV_TOWARDS_PLAYER ||
7146            move_pattern == MV_AWAY_FROM_PLAYER)
7147   {
7148     int attr_x = -1, attr_y = -1;
7149     int newx, newy;
7150     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7151
7152     if (AllPlayersGone)
7153     {
7154       attr_x = ExitX;
7155       attr_y = ExitY;
7156     }
7157     else
7158     {
7159       int i;
7160
7161       for (i = 0; i < MAX_PLAYERS; i++)
7162       {
7163         struct PlayerInfo *player = &stored_player[i];
7164         int jx = player->jx, jy = player->jy;
7165
7166         if (!player->active)
7167           continue;
7168
7169         if (attr_x == -1 ||
7170             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7171         {
7172           attr_x = jx;
7173           attr_y = jy;
7174         }
7175       }
7176     }
7177
7178     MovDir[x][y] = MV_NONE;
7179     if (attr_x < x)
7180       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7181     else if (attr_x > x)
7182       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7183     if (attr_y < y)
7184       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7185     else if (attr_y > y)
7186       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7187
7188     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7189
7190     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7191     {
7192       boolean first_horiz = RND(2);
7193       int new_move_dir = MovDir[x][y];
7194
7195       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7196       {
7197         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7198         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7199
7200         return;
7201       }
7202
7203       MovDir[x][y] =
7204         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7205       Moving2Blocked(x, y, &newx, &newy);
7206
7207       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7208         return;
7209
7210       MovDir[x][y] =
7211         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7212       Moving2Blocked(x, y, &newx, &newy);
7213
7214       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7215         return;
7216
7217       MovDir[x][y] = old_move_dir;
7218     }
7219   }
7220   else if (move_pattern == MV_WHEN_PUSHED ||
7221            move_pattern == MV_WHEN_DROPPED)
7222   {
7223     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7224       MovDir[x][y] = MV_NONE;
7225
7226     MovDelay[x][y] = 0;
7227   }
7228   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7229   {
7230     static int test_xy[7][2] =
7231     {
7232       { 0, -1 },
7233       { -1, 0 },
7234       { +1, 0 },
7235       { 0, +1 },
7236       { 0, -1 },
7237       { -1, 0 },
7238       { +1, 0 },
7239     };
7240     static int test_dir[7] =
7241     {
7242       MV_UP,
7243       MV_LEFT,
7244       MV_RIGHT,
7245       MV_DOWN,
7246       MV_UP,
7247       MV_LEFT,
7248       MV_RIGHT,
7249     };
7250     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7251     int move_preference = -1000000;     /* start with very low preference */
7252     int new_move_dir = MV_NONE;
7253     int start_test = RND(4);
7254     int i;
7255
7256     for (i = 0; i < NUM_DIRECTIONS; i++)
7257     {
7258       int move_dir = test_dir[start_test + i];
7259       int move_dir_preference;
7260
7261       xx = x + test_xy[start_test + i][0];
7262       yy = y + test_xy[start_test + i][1];
7263
7264       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7265           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7266       {
7267         new_move_dir = move_dir;
7268
7269         break;
7270       }
7271
7272       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7273         continue;
7274
7275       move_dir_preference = -1 * RunnerVisit[xx][yy];
7276       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7277         move_dir_preference = PlayerVisit[xx][yy];
7278
7279       if (move_dir_preference > move_preference)
7280       {
7281         /* prefer field that has not been visited for the longest time */
7282         move_preference = move_dir_preference;
7283         new_move_dir = move_dir;
7284       }
7285       else if (move_dir_preference == move_preference &&
7286                move_dir == old_move_dir)
7287       {
7288         /* prefer last direction when all directions are preferred equally */
7289         move_preference = move_dir_preference;
7290         new_move_dir = move_dir;
7291       }
7292     }
7293
7294     MovDir[x][y] = new_move_dir;
7295     if (old_move_dir != new_move_dir)
7296       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7297   }
7298 }
7299
7300 static void TurnRound(int x, int y)
7301 {
7302   int direction = MovDir[x][y];
7303
7304   TurnRoundExt(x, y);
7305
7306   GfxDir[x][y] = MovDir[x][y];
7307
7308   if (direction != MovDir[x][y])
7309     GfxFrame[x][y] = 0;
7310
7311   if (MovDelay[x][y])
7312     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7313
7314   ResetGfxFrame(x, y);
7315 }
7316
7317 static boolean JustBeingPushed(int x, int y)
7318 {
7319   int i;
7320
7321   for (i = 0; i < MAX_PLAYERS; i++)
7322   {
7323     struct PlayerInfo *player = &stored_player[i];
7324
7325     if (player->active && player->is_pushing && player->MovPos)
7326     {
7327       int next_jx = player->jx + (player->jx - player->last_jx);
7328       int next_jy = player->jy + (player->jy - player->last_jy);
7329
7330       if (x == next_jx && y == next_jy)
7331         return TRUE;
7332     }
7333   }
7334
7335   return FALSE;
7336 }
7337
7338 void StartMoving(int x, int y)
7339 {
7340   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7341   int element = Feld[x][y];
7342
7343   if (Stop[x][y])
7344     return;
7345
7346   if (MovDelay[x][y] == 0)
7347     GfxAction[x][y] = ACTION_DEFAULT;
7348
7349   if (CAN_FALL(element) && y < lev_fieldy - 1)
7350   {
7351     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7352         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7353       if (JustBeingPushed(x, y))
7354         return;
7355
7356     if (element == EL_QUICKSAND_FULL)
7357     {
7358       if (IS_FREE(x, y + 1))
7359       {
7360         InitMovingField(x, y, MV_DOWN);
7361         started_moving = TRUE;
7362
7363         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7364 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7365         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7366           Store[x][y] = EL_ROCK;
7367 #else
7368         Store[x][y] = EL_ROCK;
7369 #endif
7370
7371         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7372       }
7373       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7374       {
7375         if (!MovDelay[x][y])
7376         {
7377           MovDelay[x][y] = TILEY + 1;
7378
7379           ResetGfxAnimation(x, y);
7380           ResetGfxAnimation(x, y + 1);
7381         }
7382
7383         if (MovDelay[x][y])
7384         {
7385           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7386           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7387
7388           MovDelay[x][y]--;
7389           if (MovDelay[x][y])
7390             return;
7391         }
7392
7393         Feld[x][y] = EL_QUICKSAND_EMPTY;
7394         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7395         Store[x][y + 1] = Store[x][y];
7396         Store[x][y] = 0;
7397
7398         PlayLevelSoundAction(x, y, ACTION_FILLING);
7399       }
7400       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7401       {
7402         if (!MovDelay[x][y])
7403         {
7404           MovDelay[x][y] = TILEY + 1;
7405
7406           ResetGfxAnimation(x, y);
7407           ResetGfxAnimation(x, y + 1);
7408         }
7409
7410         if (MovDelay[x][y])
7411         {
7412           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7413           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7414
7415           MovDelay[x][y]--;
7416           if (MovDelay[x][y])
7417             return;
7418         }
7419
7420         Feld[x][y] = EL_QUICKSAND_EMPTY;
7421         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7422         Store[x][y + 1] = Store[x][y];
7423         Store[x][y] = 0;
7424
7425         PlayLevelSoundAction(x, y, ACTION_FILLING);
7426       }
7427     }
7428     else if (element == EL_QUICKSAND_FAST_FULL)
7429     {
7430       if (IS_FREE(x, y + 1))
7431       {
7432         InitMovingField(x, y, MV_DOWN);
7433         started_moving = TRUE;
7434
7435         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7436 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7437         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7438           Store[x][y] = EL_ROCK;
7439 #else
7440         Store[x][y] = EL_ROCK;
7441 #endif
7442
7443         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7444       }
7445       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7446       {
7447         if (!MovDelay[x][y])
7448         {
7449           MovDelay[x][y] = TILEY + 1;
7450
7451           ResetGfxAnimation(x, y);
7452           ResetGfxAnimation(x, y + 1);
7453         }
7454
7455         if (MovDelay[x][y])
7456         {
7457           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7458           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7459
7460           MovDelay[x][y]--;
7461           if (MovDelay[x][y])
7462             return;
7463         }
7464
7465         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7466         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7467         Store[x][y + 1] = Store[x][y];
7468         Store[x][y] = 0;
7469
7470         PlayLevelSoundAction(x, y, ACTION_FILLING);
7471       }
7472       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7473       {
7474         if (!MovDelay[x][y])
7475         {
7476           MovDelay[x][y] = TILEY + 1;
7477
7478           ResetGfxAnimation(x, y);
7479           ResetGfxAnimation(x, y + 1);
7480         }
7481
7482         if (MovDelay[x][y])
7483         {
7484           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7485           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7486
7487           MovDelay[x][y]--;
7488           if (MovDelay[x][y])
7489             return;
7490         }
7491
7492         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7493         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7494         Store[x][y + 1] = Store[x][y];
7495         Store[x][y] = 0;
7496
7497         PlayLevelSoundAction(x, y, ACTION_FILLING);
7498       }
7499     }
7500     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7501              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7502     {
7503       InitMovingField(x, y, MV_DOWN);
7504       started_moving = TRUE;
7505
7506       Feld[x][y] = EL_QUICKSAND_FILLING;
7507       Store[x][y] = element;
7508
7509       PlayLevelSoundAction(x, y, ACTION_FILLING);
7510     }
7511     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7512              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7513     {
7514       InitMovingField(x, y, MV_DOWN);
7515       started_moving = TRUE;
7516
7517       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7518       Store[x][y] = element;
7519
7520       PlayLevelSoundAction(x, y, ACTION_FILLING);
7521     }
7522     else if (element == EL_MAGIC_WALL_FULL)
7523     {
7524       if (IS_FREE(x, y + 1))
7525       {
7526         InitMovingField(x, y, MV_DOWN);
7527         started_moving = TRUE;
7528
7529         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7530         Store[x][y] = EL_CHANGED(Store[x][y]);
7531       }
7532       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7533       {
7534         if (!MovDelay[x][y])
7535           MovDelay[x][y] = TILEY / 4 + 1;
7536
7537         if (MovDelay[x][y])
7538         {
7539           MovDelay[x][y]--;
7540           if (MovDelay[x][y])
7541             return;
7542         }
7543
7544         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7545         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7546         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7547         Store[x][y] = 0;
7548       }
7549     }
7550     else if (element == EL_BD_MAGIC_WALL_FULL)
7551     {
7552       if (IS_FREE(x, y + 1))
7553       {
7554         InitMovingField(x, y, MV_DOWN);
7555         started_moving = TRUE;
7556
7557         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7558         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7559       }
7560       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7561       {
7562         if (!MovDelay[x][y])
7563           MovDelay[x][y] = TILEY / 4 + 1;
7564
7565         if (MovDelay[x][y])
7566         {
7567           MovDelay[x][y]--;
7568           if (MovDelay[x][y])
7569             return;
7570         }
7571
7572         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7573         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7574         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7575         Store[x][y] = 0;
7576       }
7577     }
7578     else if (element == EL_DC_MAGIC_WALL_FULL)
7579     {
7580       if (IS_FREE(x, y + 1))
7581       {
7582         InitMovingField(x, y, MV_DOWN);
7583         started_moving = TRUE;
7584
7585         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7586         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7587       }
7588       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7589       {
7590         if (!MovDelay[x][y])
7591           MovDelay[x][y] = TILEY / 4 + 1;
7592
7593         if (MovDelay[x][y])
7594         {
7595           MovDelay[x][y]--;
7596           if (MovDelay[x][y])
7597             return;
7598         }
7599
7600         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7601         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7602         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7603         Store[x][y] = 0;
7604       }
7605     }
7606     else if ((CAN_PASS_MAGIC_WALL(element) &&
7607               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7608                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7609              (CAN_PASS_DC_MAGIC_WALL(element) &&
7610               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7611
7612     {
7613       InitMovingField(x, y, MV_DOWN);
7614       started_moving = TRUE;
7615
7616       Feld[x][y] =
7617         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7618          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7619          EL_DC_MAGIC_WALL_FILLING);
7620       Store[x][y] = element;
7621     }
7622     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7623     {
7624       SplashAcid(x, y + 1);
7625
7626       InitMovingField(x, y, MV_DOWN);
7627       started_moving = TRUE;
7628
7629       Store[x][y] = EL_ACID;
7630     }
7631     else if (
7632              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7633               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7634              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7635               CAN_FALL(element) && WasJustFalling[x][y] &&
7636               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7637
7638              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7639               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7640               (Feld[x][y + 1] == EL_BLOCKED)))
7641     {
7642       /* this is needed for a special case not covered by calling "Impact()"
7643          from "ContinueMoving()": if an element moves to a tile directly below
7644          another element which was just falling on that tile (which was empty
7645          in the previous frame), the falling element above would just stop
7646          instead of smashing the element below (in previous version, the above
7647          element was just checked for "moving" instead of "falling", resulting
7648          in incorrect smashes caused by horizontal movement of the above
7649          element; also, the case of the player being the element to smash was
7650          simply not covered here... :-/ ) */
7651
7652       CheckCollision[x][y] = 0;
7653       CheckImpact[x][y] = 0;
7654
7655       Impact(x, y);
7656     }
7657     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7658     {
7659       if (MovDir[x][y] == MV_NONE)
7660       {
7661         InitMovingField(x, y, MV_DOWN);
7662         started_moving = TRUE;
7663       }
7664     }
7665     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7666     {
7667       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7668         MovDir[x][y] = MV_DOWN;
7669
7670       InitMovingField(x, y, MV_DOWN);
7671       started_moving = TRUE;
7672     }
7673     else if (element == EL_AMOEBA_DROP)
7674     {
7675       Feld[x][y] = EL_AMOEBA_GROWING;
7676       Store[x][y] = EL_AMOEBA_WET;
7677     }
7678     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7679               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7680              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7681              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7682     {
7683       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7684                                 (IS_FREE(x - 1, y + 1) ||
7685                                  Feld[x - 1][y + 1] == EL_ACID));
7686       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7687                                 (IS_FREE(x + 1, y + 1) ||
7688                                  Feld[x + 1][y + 1] == EL_ACID));
7689       boolean can_fall_any  = (can_fall_left || can_fall_right);
7690       boolean can_fall_both = (can_fall_left && can_fall_right);
7691       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7692
7693       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7694       {
7695         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7696           can_fall_right = FALSE;
7697         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7698           can_fall_left = FALSE;
7699         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7700           can_fall_right = FALSE;
7701         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7702           can_fall_left = FALSE;
7703
7704         can_fall_any  = (can_fall_left || can_fall_right);
7705         can_fall_both = FALSE;
7706       }
7707
7708       if (can_fall_both)
7709       {
7710         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7711           can_fall_right = FALSE;       /* slip down on left side */
7712         else
7713           can_fall_left = !(can_fall_right = RND(2));
7714
7715         can_fall_both = FALSE;
7716       }
7717
7718       if (can_fall_any)
7719       {
7720         /* if not determined otherwise, prefer left side for slipping down */
7721         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7722         started_moving = TRUE;
7723       }
7724     }
7725     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7726     {
7727       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7728       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7729       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7730       int belt_dir = game.belt_dir[belt_nr];
7731
7732       if ((belt_dir == MV_LEFT  && left_is_free) ||
7733           (belt_dir == MV_RIGHT && right_is_free))
7734       {
7735         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7736
7737         InitMovingField(x, y, belt_dir);
7738         started_moving = TRUE;
7739
7740         Pushed[x][y] = TRUE;
7741         Pushed[nextx][y] = TRUE;
7742
7743         GfxAction[x][y] = ACTION_DEFAULT;
7744       }
7745       else
7746       {
7747         MovDir[x][y] = 0;       /* if element was moving, stop it */
7748       }
7749     }
7750   }
7751
7752   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7753   if (CAN_MOVE(element) && !started_moving)
7754   {
7755     int move_pattern = element_info[element].move_pattern;
7756     int newx, newy;
7757
7758     Moving2Blocked(x, y, &newx, &newy);
7759
7760     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7761       return;
7762
7763     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7764         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7765     {
7766       WasJustMoving[x][y] = 0;
7767       CheckCollision[x][y] = 0;
7768
7769       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7770
7771       if (Feld[x][y] != element)        /* element has changed */
7772         return;
7773     }
7774
7775     if (!MovDelay[x][y])        /* start new movement phase */
7776     {
7777       /* all objects that can change their move direction after each step
7778          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7779
7780       if (element != EL_YAMYAM &&
7781           element != EL_DARK_YAMYAM &&
7782           element != EL_PACMAN &&
7783           !(move_pattern & MV_ANY_DIRECTION) &&
7784           move_pattern != MV_TURNING_LEFT &&
7785           move_pattern != MV_TURNING_RIGHT &&
7786           move_pattern != MV_TURNING_LEFT_RIGHT &&
7787           move_pattern != MV_TURNING_RIGHT_LEFT &&
7788           move_pattern != MV_TURNING_RANDOM)
7789       {
7790         TurnRound(x, y);
7791
7792         if (MovDelay[x][y] && (element == EL_BUG ||
7793                                element == EL_SPACESHIP ||
7794                                element == EL_SP_SNIKSNAK ||
7795                                element == EL_SP_ELECTRON ||
7796                                element == EL_MOLE))
7797           TEST_DrawLevelField(x, y);
7798       }
7799     }
7800
7801     if (MovDelay[x][y])         /* wait some time before next movement */
7802     {
7803       MovDelay[x][y]--;
7804
7805       if (element == EL_ROBOT ||
7806           element == EL_YAMYAM ||
7807           element == EL_DARK_YAMYAM)
7808       {
7809         DrawLevelElementAnimationIfNeeded(x, y, element);
7810         PlayLevelSoundAction(x, y, ACTION_WAITING);
7811       }
7812       else if (element == EL_SP_ELECTRON)
7813         DrawLevelElementAnimationIfNeeded(x, y, element);
7814       else if (element == EL_DRAGON)
7815       {
7816         int i;
7817         int dir = MovDir[x][y];
7818         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7819         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7820         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7821                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7822                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7823                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7824         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7825
7826         GfxAction[x][y] = ACTION_ATTACKING;
7827
7828         if (IS_PLAYER(x, y))
7829           DrawPlayerField(x, y);
7830         else
7831           TEST_DrawLevelField(x, y);
7832
7833         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7834
7835         for (i = 1; i <= 3; i++)
7836         {
7837           int xx = x + i * dx;
7838           int yy = y + i * dy;
7839           int sx = SCREENX(xx);
7840           int sy = SCREENY(yy);
7841           int flame_graphic = graphic + (i - 1);
7842
7843           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7844             break;
7845
7846           if (MovDelay[x][y])
7847           {
7848             int flamed = MovingOrBlocked2Element(xx, yy);
7849
7850             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7851               Bang(xx, yy);
7852             else
7853               RemoveMovingField(xx, yy);
7854
7855             ChangeDelay[xx][yy] = 0;
7856
7857             Feld[xx][yy] = EL_FLAMES;
7858
7859             if (IN_SCR_FIELD(sx, sy))
7860             {
7861               TEST_DrawLevelFieldCrumbled(xx, yy);
7862               DrawGraphic(sx, sy, flame_graphic, frame);
7863             }
7864           }
7865           else
7866           {
7867             if (Feld[xx][yy] == EL_FLAMES)
7868               Feld[xx][yy] = EL_EMPTY;
7869             TEST_DrawLevelField(xx, yy);
7870           }
7871         }
7872       }
7873
7874       if (MovDelay[x][y])       /* element still has to wait some time */
7875       {
7876         PlayLevelSoundAction(x, y, ACTION_WAITING);
7877
7878         return;
7879       }
7880     }
7881
7882     /* now make next step */
7883
7884     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7885
7886     if (DONT_COLLIDE_WITH(element) &&
7887         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7888         !PLAYER_ENEMY_PROTECTED(newx, newy))
7889     {
7890       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7891
7892       return;
7893     }
7894
7895     else if (CAN_MOVE_INTO_ACID(element) &&
7896              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7897              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7898              (MovDir[x][y] == MV_DOWN ||
7899               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7900     {
7901       SplashAcid(newx, newy);
7902       Store[x][y] = EL_ACID;
7903     }
7904     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7905     {
7906       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7907           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7908           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7909           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7910       {
7911         RemoveField(x, y);
7912         TEST_DrawLevelField(x, y);
7913
7914         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7915         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7916           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7917
7918         local_player->friends_still_needed--;
7919         if (!local_player->friends_still_needed &&
7920             !local_player->GameOver && AllPlayersGone)
7921           PlayerWins(local_player);
7922
7923         return;
7924       }
7925       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7926       {
7927         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7928           TEST_DrawLevelField(newx, newy);
7929         else
7930           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7931       }
7932       else if (!IS_FREE(newx, newy))
7933       {
7934         GfxAction[x][y] = ACTION_WAITING;
7935
7936         if (IS_PLAYER(x, y))
7937           DrawPlayerField(x, y);
7938         else
7939           TEST_DrawLevelField(x, y);
7940
7941         return;
7942       }
7943     }
7944     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7945     {
7946       if (IS_FOOD_PIG(Feld[newx][newy]))
7947       {
7948         if (IS_MOVING(newx, newy))
7949           RemoveMovingField(newx, newy);
7950         else
7951         {
7952           Feld[newx][newy] = EL_EMPTY;
7953           TEST_DrawLevelField(newx, newy);
7954         }
7955
7956         PlayLevelSound(x, y, SND_PIG_DIGGING);
7957       }
7958       else if (!IS_FREE(newx, newy))
7959       {
7960         if (IS_PLAYER(x, y))
7961           DrawPlayerField(x, y);
7962         else
7963           TEST_DrawLevelField(x, y);
7964
7965         return;
7966       }
7967     }
7968     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7969     {
7970       if (Store[x][y] != EL_EMPTY)
7971       {
7972         boolean can_clone = FALSE;
7973         int xx, yy;
7974
7975         /* check if element to clone is still there */
7976         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7977         {
7978           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7979           {
7980             can_clone = TRUE;
7981
7982             break;
7983           }
7984         }
7985
7986         /* cannot clone or target field not free anymore -- do not clone */
7987         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7988           Store[x][y] = EL_EMPTY;
7989       }
7990
7991       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7992       {
7993         if (IS_MV_DIAGONAL(MovDir[x][y]))
7994         {
7995           int diagonal_move_dir = MovDir[x][y];
7996           int stored = Store[x][y];
7997           int change_delay = 8;
7998           int graphic;
7999
8000           /* android is moving diagonally */
8001
8002           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8003
8004           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8005           GfxElement[x][y] = EL_EMC_ANDROID;
8006           GfxAction[x][y] = ACTION_SHRINKING;
8007           GfxDir[x][y] = diagonal_move_dir;
8008           ChangeDelay[x][y] = change_delay;
8009
8010           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8011                                    GfxDir[x][y]);
8012
8013           DrawLevelGraphicAnimation(x, y, graphic);
8014           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8015
8016           if (Feld[newx][newy] == EL_ACID)
8017           {
8018             SplashAcid(newx, newy);
8019
8020             return;
8021           }
8022
8023           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8024
8025           Store[newx][newy] = EL_EMC_ANDROID;
8026           GfxElement[newx][newy] = EL_EMC_ANDROID;
8027           GfxAction[newx][newy] = ACTION_GROWING;
8028           GfxDir[newx][newy] = diagonal_move_dir;
8029           ChangeDelay[newx][newy] = change_delay;
8030
8031           graphic = el_act_dir2img(GfxElement[newx][newy],
8032                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8033
8034           DrawLevelGraphicAnimation(newx, newy, graphic);
8035           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8036
8037           return;
8038         }
8039         else
8040         {
8041           Feld[newx][newy] = EL_EMPTY;
8042           TEST_DrawLevelField(newx, newy);
8043
8044           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8045         }
8046       }
8047       else if (!IS_FREE(newx, newy))
8048       {
8049         return;
8050       }
8051     }
8052     else if (IS_CUSTOM_ELEMENT(element) &&
8053              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8054     {
8055       if (!DigFieldByCE(newx, newy, element))
8056         return;
8057
8058       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8059       {
8060         RunnerVisit[x][y] = FrameCounter;
8061         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8062       }
8063     }
8064     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8065     {
8066       if (!IS_FREE(newx, newy))
8067       {
8068         if (IS_PLAYER(x, y))
8069           DrawPlayerField(x, y);
8070         else
8071           TEST_DrawLevelField(x, y);
8072
8073         return;
8074       }
8075       else
8076       {
8077         boolean wanna_flame = !RND(10);
8078         int dx = newx - x, dy = newy - y;
8079         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8080         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8081         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8082                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8083         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8084                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8085
8086         if ((wanna_flame ||
8087              IS_CLASSIC_ENEMY(element1) ||
8088              IS_CLASSIC_ENEMY(element2)) &&
8089             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8090             element1 != EL_FLAMES && element2 != EL_FLAMES)
8091         {
8092           ResetGfxAnimation(x, y);
8093           GfxAction[x][y] = ACTION_ATTACKING;
8094
8095           if (IS_PLAYER(x, y))
8096             DrawPlayerField(x, y);
8097           else
8098             TEST_DrawLevelField(x, y);
8099
8100           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8101
8102           MovDelay[x][y] = 50;
8103
8104           Feld[newx][newy] = EL_FLAMES;
8105           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8106             Feld[newx1][newy1] = EL_FLAMES;
8107           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8108             Feld[newx2][newy2] = EL_FLAMES;
8109
8110           return;
8111         }
8112       }
8113     }
8114     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8115              Feld[newx][newy] == EL_DIAMOND)
8116     {
8117       if (IS_MOVING(newx, newy))
8118         RemoveMovingField(newx, newy);
8119       else
8120       {
8121         Feld[newx][newy] = EL_EMPTY;
8122         TEST_DrawLevelField(newx, newy);
8123       }
8124
8125       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8126     }
8127     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8128              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8129     {
8130       if (AmoebaNr[newx][newy])
8131       {
8132         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8133         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8134             Feld[newx][newy] == EL_BD_AMOEBA)
8135           AmoebaCnt[AmoebaNr[newx][newy]]--;
8136       }
8137
8138       if (IS_MOVING(newx, newy))
8139       {
8140         RemoveMovingField(newx, newy);
8141       }
8142       else
8143       {
8144         Feld[newx][newy] = EL_EMPTY;
8145         TEST_DrawLevelField(newx, newy);
8146       }
8147
8148       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8149     }
8150     else if ((element == EL_PACMAN || element == EL_MOLE)
8151              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8152     {
8153       if (AmoebaNr[newx][newy])
8154       {
8155         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8156         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8157             Feld[newx][newy] == EL_BD_AMOEBA)
8158           AmoebaCnt[AmoebaNr[newx][newy]]--;
8159       }
8160
8161       if (element == EL_MOLE)
8162       {
8163         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8164         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8165
8166         ResetGfxAnimation(x, y);
8167         GfxAction[x][y] = ACTION_DIGGING;
8168         TEST_DrawLevelField(x, y);
8169
8170         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8171
8172         return;                         /* wait for shrinking amoeba */
8173       }
8174       else      /* element == EL_PACMAN */
8175       {
8176         Feld[newx][newy] = EL_EMPTY;
8177         TEST_DrawLevelField(newx, newy);
8178         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8179       }
8180     }
8181     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8182              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8183               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8184     {
8185       /* wait for shrinking amoeba to completely disappear */
8186       return;
8187     }
8188     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8189     {
8190       /* object was running against a wall */
8191
8192       TurnRound(x, y);
8193
8194       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8195         DrawLevelElementAnimation(x, y, element);
8196
8197       if (DONT_TOUCH(element))
8198         TestIfBadThingTouchesPlayer(x, y);
8199
8200       return;
8201     }
8202
8203     InitMovingField(x, y, MovDir[x][y]);
8204
8205     PlayLevelSoundAction(x, y, ACTION_MOVING);
8206   }
8207
8208   if (MovDir[x][y])
8209     ContinueMoving(x, y);
8210 }
8211
8212 void ContinueMoving(int x, int y)
8213 {
8214   int element = Feld[x][y];
8215   struct ElementInfo *ei = &element_info[element];
8216   int direction = MovDir[x][y];
8217   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8218   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8219   int newx = x + dx, newy = y + dy;
8220   int stored = Store[x][y];
8221   int stored_new = Store[newx][newy];
8222   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8223   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8224   boolean last_line = (newy == lev_fieldy - 1);
8225
8226   MovPos[x][y] += getElementMoveStepsize(x, y);
8227
8228   if (pushed_by_player) /* special case: moving object pushed by player */
8229     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8230
8231   if (ABS(MovPos[x][y]) < TILEX)
8232   {
8233     TEST_DrawLevelField(x, y);
8234
8235     return;     /* element is still moving */
8236   }
8237
8238   /* element reached destination field */
8239
8240   Feld[x][y] = EL_EMPTY;
8241   Feld[newx][newy] = element;
8242   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8243
8244   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8245   {
8246     element = Feld[newx][newy] = EL_ACID;
8247   }
8248   else if (element == EL_MOLE)
8249   {
8250     Feld[x][y] = EL_SAND;
8251
8252     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8253   }
8254   else if (element == EL_QUICKSAND_FILLING)
8255   {
8256     element = Feld[newx][newy] = get_next_element(element);
8257     Store[newx][newy] = Store[x][y];
8258   }
8259   else if (element == EL_QUICKSAND_EMPTYING)
8260   {
8261     Feld[x][y] = get_next_element(element);
8262     element = Feld[newx][newy] = Store[x][y];
8263   }
8264   else if (element == EL_QUICKSAND_FAST_FILLING)
8265   {
8266     element = Feld[newx][newy] = get_next_element(element);
8267     Store[newx][newy] = Store[x][y];
8268   }
8269   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8270   {
8271     Feld[x][y] = get_next_element(element);
8272     element = Feld[newx][newy] = Store[x][y];
8273   }
8274   else if (element == EL_MAGIC_WALL_FILLING)
8275   {
8276     element = Feld[newx][newy] = get_next_element(element);
8277     if (!game.magic_wall_active)
8278       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8279     Store[newx][newy] = Store[x][y];
8280   }
8281   else if (element == EL_MAGIC_WALL_EMPTYING)
8282   {
8283     Feld[x][y] = get_next_element(element);
8284     if (!game.magic_wall_active)
8285       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8286     element = Feld[newx][newy] = Store[x][y];
8287
8288     InitField(newx, newy, FALSE);
8289   }
8290   else if (element == EL_BD_MAGIC_WALL_FILLING)
8291   {
8292     element = Feld[newx][newy] = get_next_element(element);
8293     if (!game.magic_wall_active)
8294       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8295     Store[newx][newy] = Store[x][y];
8296   }
8297   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8298   {
8299     Feld[x][y] = get_next_element(element);
8300     if (!game.magic_wall_active)
8301       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8302     element = Feld[newx][newy] = Store[x][y];
8303
8304     InitField(newx, newy, FALSE);
8305   }
8306   else if (element == EL_DC_MAGIC_WALL_FILLING)
8307   {
8308     element = Feld[newx][newy] = get_next_element(element);
8309     if (!game.magic_wall_active)
8310       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8311     Store[newx][newy] = Store[x][y];
8312   }
8313   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8314   {
8315     Feld[x][y] = get_next_element(element);
8316     if (!game.magic_wall_active)
8317       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8318     element = Feld[newx][newy] = Store[x][y];
8319
8320     InitField(newx, newy, FALSE);
8321   }
8322   else if (element == EL_AMOEBA_DROPPING)
8323   {
8324     Feld[x][y] = get_next_element(element);
8325     element = Feld[newx][newy] = Store[x][y];
8326   }
8327   else if (element == EL_SOKOBAN_OBJECT)
8328   {
8329     if (Back[x][y])
8330       Feld[x][y] = Back[x][y];
8331
8332     if (Back[newx][newy])
8333       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8334
8335     Back[x][y] = Back[newx][newy] = 0;
8336   }
8337
8338   Store[x][y] = EL_EMPTY;
8339   MovPos[x][y] = 0;
8340   MovDir[x][y] = 0;
8341   MovDelay[x][y] = 0;
8342
8343   MovDelay[newx][newy] = 0;
8344
8345   if (CAN_CHANGE_OR_HAS_ACTION(element))
8346   {
8347     /* copy element change control values to new field */
8348     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8349     ChangePage[newx][newy]  = ChangePage[x][y];
8350     ChangeCount[newx][newy] = ChangeCount[x][y];
8351     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8352   }
8353
8354   CustomValue[newx][newy] = CustomValue[x][y];
8355
8356   ChangeDelay[x][y] = 0;
8357   ChangePage[x][y] = -1;
8358   ChangeCount[x][y] = 0;
8359   ChangeEvent[x][y] = -1;
8360
8361   CustomValue[x][y] = 0;
8362
8363   /* copy animation control values to new field */
8364   GfxFrame[newx][newy]  = GfxFrame[x][y];
8365   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8366   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8367   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8368
8369   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8370
8371   /* some elements can leave other elements behind after moving */
8372   if (ei->move_leave_element != EL_EMPTY &&
8373       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8374       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8375   {
8376     int move_leave_element = ei->move_leave_element;
8377
8378     /* this makes it possible to leave the removed element again */
8379     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8380       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8381
8382     Feld[x][y] = move_leave_element;
8383
8384     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8385       MovDir[x][y] = direction;
8386
8387     InitField(x, y, FALSE);
8388
8389     if (GFX_CRUMBLED(Feld[x][y]))
8390       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8391
8392     if (ELEM_IS_PLAYER(move_leave_element))
8393       RelocatePlayer(x, y, move_leave_element);
8394   }
8395
8396   /* do this after checking for left-behind element */
8397   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8398
8399   if (!CAN_MOVE(element) ||
8400       (CAN_FALL(element) && direction == MV_DOWN &&
8401        (element == EL_SPRING ||
8402         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8403         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8404     GfxDir[x][y] = MovDir[newx][newy] = 0;
8405
8406   TEST_DrawLevelField(x, y);
8407   TEST_DrawLevelField(newx, newy);
8408
8409   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8410
8411   /* prevent pushed element from moving on in pushed direction */
8412   if (pushed_by_player && CAN_MOVE(element) &&
8413       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8414       !(element_info[element].move_pattern & direction))
8415     TurnRound(newx, newy);
8416
8417   /* prevent elements on conveyor belt from moving on in last direction */
8418   if (pushed_by_conveyor && CAN_FALL(element) &&
8419       direction & MV_HORIZONTAL)
8420     MovDir[newx][newy] = 0;
8421
8422   if (!pushed_by_player)
8423   {
8424     int nextx = newx + dx, nexty = newy + dy;
8425     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8426
8427     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8428
8429     if (CAN_FALL(element) && direction == MV_DOWN)
8430       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8431
8432     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8433       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8434
8435     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8436       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8437   }
8438
8439   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8440   {
8441     TestIfBadThingTouchesPlayer(newx, newy);
8442     TestIfBadThingTouchesFriend(newx, newy);
8443
8444     if (!IS_CUSTOM_ELEMENT(element))
8445       TestIfBadThingTouchesOtherBadThing(newx, newy);
8446   }
8447   else if (element == EL_PENGUIN)
8448     TestIfFriendTouchesBadThing(newx, newy);
8449
8450   if (DONT_GET_HIT_BY(element))
8451   {
8452     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8453   }
8454
8455   /* give the player one last chance (one more frame) to move away */
8456   if (CAN_FALL(element) && direction == MV_DOWN &&
8457       (last_line || (!IS_FREE(x, newy + 1) &&
8458                      (!IS_PLAYER(x, newy + 1) ||
8459                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8460     Impact(x, newy);
8461
8462   if (pushed_by_player && !game.use_change_when_pushing_bug)
8463   {
8464     int push_side = MV_DIR_OPPOSITE(direction);
8465     struct PlayerInfo *player = PLAYERINFO(x, y);
8466
8467     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8468                                player->index_bit, push_side);
8469     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8470                                         player->index_bit, push_side);
8471   }
8472
8473   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8474     MovDelay[newx][newy] = 1;
8475
8476   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8477
8478   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8479   TestIfElementHitsCustomElement(newx, newy, direction);
8480   TestIfPlayerTouchesCustomElement(newx, newy);
8481   TestIfElementTouchesCustomElement(newx, newy);
8482
8483   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8484       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8485     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8486                              MV_DIR_OPPOSITE(direction));
8487 }
8488
8489 int AmoebeNachbarNr(int ax, int ay)
8490 {
8491   int i;
8492   int element = Feld[ax][ay];
8493   int group_nr = 0;
8494   static int xy[4][2] =
8495   {
8496     { 0, -1 },
8497     { -1, 0 },
8498     { +1, 0 },
8499     { 0, +1 }
8500   };
8501
8502   for (i = 0; i < NUM_DIRECTIONS; i++)
8503   {
8504     int x = ax + xy[i][0];
8505     int y = ay + xy[i][1];
8506
8507     if (!IN_LEV_FIELD(x, y))
8508       continue;
8509
8510     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8511       group_nr = AmoebaNr[x][y];
8512   }
8513
8514   return group_nr;
8515 }
8516
8517 void AmoebenVereinigen(int ax, int ay)
8518 {
8519   int i, x, y, xx, yy;
8520   int new_group_nr = AmoebaNr[ax][ay];
8521   static int xy[4][2] =
8522   {
8523     { 0, -1 },
8524     { -1, 0 },
8525     { +1, 0 },
8526     { 0, +1 }
8527   };
8528
8529   if (new_group_nr == 0)
8530     return;
8531
8532   for (i = 0; i < NUM_DIRECTIONS; i++)
8533   {
8534     x = ax + xy[i][0];
8535     y = ay + xy[i][1];
8536
8537     if (!IN_LEV_FIELD(x, y))
8538       continue;
8539
8540     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8541          Feld[x][y] == EL_BD_AMOEBA ||
8542          Feld[x][y] == EL_AMOEBA_DEAD) &&
8543         AmoebaNr[x][y] != new_group_nr)
8544     {
8545       int old_group_nr = AmoebaNr[x][y];
8546
8547       if (old_group_nr == 0)
8548         return;
8549
8550       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8551       AmoebaCnt[old_group_nr] = 0;
8552       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8553       AmoebaCnt2[old_group_nr] = 0;
8554
8555       SCAN_PLAYFIELD(xx, yy)
8556       {
8557         if (AmoebaNr[xx][yy] == old_group_nr)
8558           AmoebaNr[xx][yy] = new_group_nr;
8559       }
8560     }
8561   }
8562 }
8563
8564 void AmoebeUmwandeln(int ax, int ay)
8565 {
8566   int i, x, y;
8567
8568   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8569   {
8570     int group_nr = AmoebaNr[ax][ay];
8571
8572 #ifdef DEBUG
8573     if (group_nr == 0)
8574     {
8575       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8576       printf("AmoebeUmwandeln(): This should never happen!\n");
8577       return;
8578     }
8579 #endif
8580
8581     SCAN_PLAYFIELD(x, y)
8582     {
8583       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8584       {
8585         AmoebaNr[x][y] = 0;
8586         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8587       }
8588     }
8589
8590     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8591                             SND_AMOEBA_TURNING_TO_GEM :
8592                             SND_AMOEBA_TURNING_TO_ROCK));
8593     Bang(ax, ay);
8594   }
8595   else
8596   {
8597     static int xy[4][2] =
8598     {
8599       { 0, -1 },
8600       { -1, 0 },
8601       { +1, 0 },
8602       { 0, +1 }
8603     };
8604
8605     for (i = 0; i < NUM_DIRECTIONS; i++)
8606     {
8607       x = ax + xy[i][0];
8608       y = ay + xy[i][1];
8609
8610       if (!IN_LEV_FIELD(x, y))
8611         continue;
8612
8613       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8614       {
8615         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8616                               SND_AMOEBA_TURNING_TO_GEM :
8617                               SND_AMOEBA_TURNING_TO_ROCK));
8618         Bang(x, y);
8619       }
8620     }
8621   }
8622 }
8623
8624 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8625 {
8626   int x, y;
8627   int group_nr = AmoebaNr[ax][ay];
8628   boolean done = FALSE;
8629
8630 #ifdef DEBUG
8631   if (group_nr == 0)
8632   {
8633     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8634     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8635     return;
8636   }
8637 #endif
8638
8639   SCAN_PLAYFIELD(x, y)
8640   {
8641     if (AmoebaNr[x][y] == group_nr &&
8642         (Feld[x][y] == EL_AMOEBA_DEAD ||
8643          Feld[x][y] == EL_BD_AMOEBA ||
8644          Feld[x][y] == EL_AMOEBA_GROWING))
8645     {
8646       AmoebaNr[x][y] = 0;
8647       Feld[x][y] = new_element;
8648       InitField(x, y, FALSE);
8649       TEST_DrawLevelField(x, y);
8650       done = TRUE;
8651     }
8652   }
8653
8654   if (done)
8655     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8656                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8657                             SND_BD_AMOEBA_TURNING_TO_GEM));
8658 }
8659
8660 void AmoebeWaechst(int x, int y)
8661 {
8662   static unsigned int sound_delay = 0;
8663   static unsigned int sound_delay_value = 0;
8664
8665   if (!MovDelay[x][y])          /* start new growing cycle */
8666   {
8667     MovDelay[x][y] = 7;
8668
8669     if (DelayReached(&sound_delay, sound_delay_value))
8670     {
8671       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8672       sound_delay_value = 30;
8673     }
8674   }
8675
8676   if (MovDelay[x][y])           /* wait some time before growing bigger */
8677   {
8678     MovDelay[x][y]--;
8679     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8680     {
8681       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8682                                            6 - MovDelay[x][y]);
8683
8684       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8685     }
8686
8687     if (!MovDelay[x][y])
8688     {
8689       Feld[x][y] = Store[x][y];
8690       Store[x][y] = 0;
8691       TEST_DrawLevelField(x, y);
8692     }
8693   }
8694 }
8695
8696 void AmoebaDisappearing(int x, int y)
8697 {
8698   static unsigned int sound_delay = 0;
8699   static unsigned int sound_delay_value = 0;
8700
8701   if (!MovDelay[x][y])          /* start new shrinking cycle */
8702   {
8703     MovDelay[x][y] = 7;
8704
8705     if (DelayReached(&sound_delay, sound_delay_value))
8706       sound_delay_value = 30;
8707   }
8708
8709   if (MovDelay[x][y])           /* wait some time before shrinking */
8710   {
8711     MovDelay[x][y]--;
8712     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8713     {
8714       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8715                                            6 - MovDelay[x][y]);
8716
8717       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8718     }
8719
8720     if (!MovDelay[x][y])
8721     {
8722       Feld[x][y] = EL_EMPTY;
8723       TEST_DrawLevelField(x, y);
8724
8725       /* don't let mole enter this field in this cycle;
8726          (give priority to objects falling to this field from above) */
8727       Stop[x][y] = TRUE;
8728     }
8729   }
8730 }
8731
8732 void AmoebeAbleger(int ax, int ay)
8733 {
8734   int i;
8735   int element = Feld[ax][ay];
8736   int graphic = el2img(element);
8737   int newax = ax, neway = ay;
8738   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8739   static int xy[4][2] =
8740   {
8741     { 0, -1 },
8742     { -1, 0 },
8743     { +1, 0 },
8744     { 0, +1 }
8745   };
8746
8747   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8748   {
8749     Feld[ax][ay] = EL_AMOEBA_DEAD;
8750     TEST_DrawLevelField(ax, ay);
8751     return;
8752   }
8753
8754   if (IS_ANIMATED(graphic))
8755     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8756
8757   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8758     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8759
8760   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8761   {
8762     MovDelay[ax][ay]--;
8763     if (MovDelay[ax][ay])
8764       return;
8765   }
8766
8767   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8768   {
8769     int start = RND(4);
8770     int x = ax + xy[start][0];
8771     int y = ay + xy[start][1];
8772
8773     if (!IN_LEV_FIELD(x, y))
8774       return;
8775
8776     if (IS_FREE(x, y) ||
8777         CAN_GROW_INTO(Feld[x][y]) ||
8778         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8779         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8780     {
8781       newax = x;
8782       neway = y;
8783     }
8784
8785     if (newax == ax && neway == ay)
8786       return;
8787   }
8788   else                          /* normal or "filled" (BD style) amoeba */
8789   {
8790     int start = RND(4);
8791     boolean waiting_for_player = FALSE;
8792
8793     for (i = 0; i < NUM_DIRECTIONS; i++)
8794     {
8795       int j = (start + i) % 4;
8796       int x = ax + xy[j][0];
8797       int y = ay + xy[j][1];
8798
8799       if (!IN_LEV_FIELD(x, y))
8800         continue;
8801
8802       if (IS_FREE(x, y) ||
8803           CAN_GROW_INTO(Feld[x][y]) ||
8804           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8805           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8806       {
8807         newax = x;
8808         neway = y;
8809         break;
8810       }
8811       else if (IS_PLAYER(x, y))
8812         waiting_for_player = TRUE;
8813     }
8814
8815     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8816     {
8817       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8818       {
8819         Feld[ax][ay] = EL_AMOEBA_DEAD;
8820         TEST_DrawLevelField(ax, ay);
8821         AmoebaCnt[AmoebaNr[ax][ay]]--;
8822
8823         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8824         {
8825           if (element == EL_AMOEBA_FULL)
8826             AmoebeUmwandeln(ax, ay);
8827           else if (element == EL_BD_AMOEBA)
8828             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8829         }
8830       }
8831       return;
8832     }
8833     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8834     {
8835       /* amoeba gets larger by growing in some direction */
8836
8837       int new_group_nr = AmoebaNr[ax][ay];
8838
8839 #ifdef DEBUG
8840   if (new_group_nr == 0)
8841   {
8842     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8843     printf("AmoebeAbleger(): This should never happen!\n");
8844     return;
8845   }
8846 #endif
8847
8848       AmoebaNr[newax][neway] = new_group_nr;
8849       AmoebaCnt[new_group_nr]++;
8850       AmoebaCnt2[new_group_nr]++;
8851
8852       /* if amoeba touches other amoeba(s) after growing, unify them */
8853       AmoebenVereinigen(newax, neway);
8854
8855       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8856       {
8857         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8858         return;
8859       }
8860     }
8861   }
8862
8863   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8864       (neway == lev_fieldy - 1 && newax != ax))
8865   {
8866     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8867     Store[newax][neway] = element;
8868   }
8869   else if (neway == ay || element == EL_EMC_DRIPPER)
8870   {
8871     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8872
8873     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8874   }
8875   else
8876   {
8877     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8878     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8879     Store[ax][ay] = EL_AMOEBA_DROP;
8880     ContinueMoving(ax, ay);
8881     return;
8882   }
8883
8884   TEST_DrawLevelField(newax, neway);
8885 }
8886
8887 void Life(int ax, int ay)
8888 {
8889   int x1, y1, x2, y2;
8890   int life_time = 40;
8891   int element = Feld[ax][ay];
8892   int graphic = el2img(element);
8893   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8894                          level.biomaze);
8895   boolean changed = FALSE;
8896
8897   if (IS_ANIMATED(graphic))
8898     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8899
8900   if (Stop[ax][ay])
8901     return;
8902
8903   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8904     MovDelay[ax][ay] = life_time;
8905
8906   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8907   {
8908     MovDelay[ax][ay]--;
8909     if (MovDelay[ax][ay])
8910       return;
8911   }
8912
8913   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8914   {
8915     int xx = ax+x1, yy = ay+y1;
8916     int nachbarn = 0;
8917
8918     if (!IN_LEV_FIELD(xx, yy))
8919       continue;
8920
8921     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8922     {
8923       int x = xx+x2, y = yy+y2;
8924
8925       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8926         continue;
8927
8928       if (((Feld[x][y] == element ||
8929             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8930            !Stop[x][y]) ||
8931           (IS_FREE(x, y) && Stop[x][y]))
8932         nachbarn++;
8933     }
8934
8935     if (xx == ax && yy == ay)           /* field in the middle */
8936     {
8937       if (nachbarn < life_parameter[0] ||
8938           nachbarn > life_parameter[1])
8939       {
8940         Feld[xx][yy] = EL_EMPTY;
8941         if (!Stop[xx][yy])
8942           TEST_DrawLevelField(xx, yy);
8943         Stop[xx][yy] = TRUE;
8944         changed = TRUE;
8945       }
8946     }
8947     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8948     {                                   /* free border field */
8949       if (nachbarn >= life_parameter[2] &&
8950           nachbarn <= life_parameter[3])
8951       {
8952         Feld[xx][yy] = element;
8953         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8954         if (!Stop[xx][yy])
8955           TEST_DrawLevelField(xx, yy);
8956         Stop[xx][yy] = TRUE;
8957         changed = TRUE;
8958       }
8959     }
8960   }
8961
8962   if (changed)
8963     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8964                    SND_GAME_OF_LIFE_GROWING);
8965 }
8966
8967 static void InitRobotWheel(int x, int y)
8968 {
8969   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8970 }
8971
8972 static void RunRobotWheel(int x, int y)
8973 {
8974   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8975 }
8976
8977 static void StopRobotWheel(int x, int y)
8978 {
8979   if (ZX == x && ZY == y)
8980   {
8981     ZX = ZY = -1;
8982
8983     game.robot_wheel_active = FALSE;
8984   }
8985 }
8986
8987 static void InitTimegateWheel(int x, int y)
8988 {
8989   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8990 }
8991
8992 static void RunTimegateWheel(int x, int y)
8993 {
8994   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8995 }
8996
8997 static void InitMagicBallDelay(int x, int y)
8998 {
8999   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9000 }
9001
9002 static void ActivateMagicBall(int bx, int by)
9003 {
9004   int x, y;
9005
9006   if (level.ball_random)
9007   {
9008     int pos_border = RND(8);    /* select one of the eight border elements */
9009     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9010     int xx = pos_content % 3;
9011     int yy = pos_content / 3;
9012
9013     x = bx - 1 + xx;
9014     y = by - 1 + yy;
9015
9016     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9017       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9018   }
9019   else
9020   {
9021     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9022     {
9023       int xx = x - bx + 1;
9024       int yy = y - by + 1;
9025
9026       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9027         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9028     }
9029   }
9030
9031   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9032 }
9033
9034 void CheckExit(int x, int y)
9035 {
9036   if (local_player->gems_still_needed > 0 ||
9037       local_player->sokobanfields_still_needed > 0 ||
9038       local_player->lights_still_needed > 0)
9039   {
9040     int element = Feld[x][y];
9041     int graphic = el2img(element);
9042
9043     if (IS_ANIMATED(graphic))
9044       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9045
9046     return;
9047   }
9048
9049   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9050     return;
9051
9052   Feld[x][y] = EL_EXIT_OPENING;
9053
9054   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9055 }
9056
9057 void CheckExitEM(int x, int y)
9058 {
9059   if (local_player->gems_still_needed > 0 ||
9060       local_player->sokobanfields_still_needed > 0 ||
9061       local_player->lights_still_needed > 0)
9062   {
9063     int element = Feld[x][y];
9064     int graphic = el2img(element);
9065
9066     if (IS_ANIMATED(graphic))
9067       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9068
9069     return;
9070   }
9071
9072   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9073     return;
9074
9075   Feld[x][y] = EL_EM_EXIT_OPENING;
9076
9077   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9078 }
9079
9080 void CheckExitSteel(int x, int y)
9081 {
9082   if (local_player->gems_still_needed > 0 ||
9083       local_player->sokobanfields_still_needed > 0 ||
9084       local_player->lights_still_needed > 0)
9085   {
9086     int element = Feld[x][y];
9087     int graphic = el2img(element);
9088
9089     if (IS_ANIMATED(graphic))
9090       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9091
9092     return;
9093   }
9094
9095   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9096     return;
9097
9098   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9099
9100   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9101 }
9102
9103 void CheckExitSteelEM(int x, int y)
9104 {
9105   if (local_player->gems_still_needed > 0 ||
9106       local_player->sokobanfields_still_needed > 0 ||
9107       local_player->lights_still_needed > 0)
9108   {
9109     int element = Feld[x][y];
9110     int graphic = el2img(element);
9111
9112     if (IS_ANIMATED(graphic))
9113       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9114
9115     return;
9116   }
9117
9118   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9119     return;
9120
9121   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9122
9123   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9124 }
9125
9126 void CheckExitSP(int x, int y)
9127 {
9128   if (local_player->gems_still_needed > 0)
9129   {
9130     int element = Feld[x][y];
9131     int graphic = el2img(element);
9132
9133     if (IS_ANIMATED(graphic))
9134       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9135
9136     return;
9137   }
9138
9139   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9140     return;
9141
9142   Feld[x][y] = EL_SP_EXIT_OPENING;
9143
9144   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9145 }
9146
9147 static void CloseAllOpenTimegates()
9148 {
9149   int x, y;
9150
9151   SCAN_PLAYFIELD(x, y)
9152   {
9153     int element = Feld[x][y];
9154
9155     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9156     {
9157       Feld[x][y] = EL_TIMEGATE_CLOSING;
9158
9159       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9160     }
9161   }
9162 }
9163
9164 void DrawTwinkleOnField(int x, int y)
9165 {
9166   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9167     return;
9168
9169   if (Feld[x][y] == EL_BD_DIAMOND)
9170     return;
9171
9172   if (MovDelay[x][y] == 0)      /* next animation frame */
9173     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9174
9175   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9176   {
9177     MovDelay[x][y]--;
9178
9179     DrawLevelElementAnimation(x, y, Feld[x][y]);
9180
9181     if (MovDelay[x][y] != 0)
9182     {
9183       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9184                                            10 - MovDelay[x][y]);
9185
9186       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9187     }
9188   }
9189 }
9190
9191 void MauerWaechst(int x, int y)
9192 {
9193   int delay = 6;
9194
9195   if (!MovDelay[x][y])          /* next animation frame */
9196     MovDelay[x][y] = 3 * delay;
9197
9198   if (MovDelay[x][y])           /* wait some time before next frame */
9199   {
9200     MovDelay[x][y]--;
9201
9202     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9203     {
9204       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9205       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9206
9207       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9208     }
9209
9210     if (!MovDelay[x][y])
9211     {
9212       if (MovDir[x][y] == MV_LEFT)
9213       {
9214         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9215           TEST_DrawLevelField(x - 1, y);
9216       }
9217       else if (MovDir[x][y] == MV_RIGHT)
9218       {
9219         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9220           TEST_DrawLevelField(x + 1, y);
9221       }
9222       else if (MovDir[x][y] == MV_UP)
9223       {
9224         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9225           TEST_DrawLevelField(x, y - 1);
9226       }
9227       else
9228       {
9229         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9230           TEST_DrawLevelField(x, y + 1);
9231       }
9232
9233       Feld[x][y] = Store[x][y];
9234       Store[x][y] = 0;
9235       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9236       TEST_DrawLevelField(x, y);
9237     }
9238   }
9239 }
9240
9241 void MauerAbleger(int ax, int ay)
9242 {
9243   int element = Feld[ax][ay];
9244   int graphic = el2img(element);
9245   boolean oben_frei = FALSE, unten_frei = FALSE;
9246   boolean links_frei = FALSE, rechts_frei = FALSE;
9247   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9248   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9249   boolean new_wall = FALSE;
9250
9251   if (IS_ANIMATED(graphic))
9252     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9253
9254   if (!MovDelay[ax][ay])        /* start building new wall */
9255     MovDelay[ax][ay] = 6;
9256
9257   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9258   {
9259     MovDelay[ax][ay]--;
9260     if (MovDelay[ax][ay])
9261       return;
9262   }
9263
9264   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9265     oben_frei = TRUE;
9266   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9267     unten_frei = TRUE;
9268   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9269     links_frei = TRUE;
9270   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9271     rechts_frei = TRUE;
9272
9273   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9274       element == EL_EXPANDABLE_WALL_ANY)
9275   {
9276     if (oben_frei)
9277     {
9278       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9279       Store[ax][ay-1] = element;
9280       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9281       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9282         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9283                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9284       new_wall = TRUE;
9285     }
9286     if (unten_frei)
9287     {
9288       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9289       Store[ax][ay+1] = element;
9290       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9291       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9292         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9293                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9294       new_wall = TRUE;
9295     }
9296   }
9297
9298   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9299       element == EL_EXPANDABLE_WALL_ANY ||
9300       element == EL_EXPANDABLE_WALL ||
9301       element == EL_BD_EXPANDABLE_WALL)
9302   {
9303     if (links_frei)
9304     {
9305       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9306       Store[ax-1][ay] = element;
9307       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9308       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9309         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9310                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9311       new_wall = TRUE;
9312     }
9313
9314     if (rechts_frei)
9315     {
9316       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9317       Store[ax+1][ay] = element;
9318       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9319       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9320         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9321                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9322       new_wall = TRUE;
9323     }
9324   }
9325
9326   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9327     TEST_DrawLevelField(ax, ay);
9328
9329   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9330     oben_massiv = TRUE;
9331   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9332     unten_massiv = TRUE;
9333   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9334     links_massiv = TRUE;
9335   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9336     rechts_massiv = TRUE;
9337
9338   if (((oben_massiv && unten_massiv) ||
9339        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9340        element == EL_EXPANDABLE_WALL) &&
9341       ((links_massiv && rechts_massiv) ||
9342        element == EL_EXPANDABLE_WALL_VERTICAL))
9343     Feld[ax][ay] = EL_WALL;
9344
9345   if (new_wall)
9346     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9347 }
9348
9349 void MauerAblegerStahl(int ax, int ay)
9350 {
9351   int element = Feld[ax][ay];
9352   int graphic = el2img(element);
9353   boolean oben_frei = FALSE, unten_frei = FALSE;
9354   boolean links_frei = FALSE, rechts_frei = FALSE;
9355   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9356   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9357   boolean new_wall = FALSE;
9358
9359   if (IS_ANIMATED(graphic))
9360     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9361
9362   if (!MovDelay[ax][ay])        /* start building new wall */
9363     MovDelay[ax][ay] = 6;
9364
9365   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9366   {
9367     MovDelay[ax][ay]--;
9368     if (MovDelay[ax][ay])
9369       return;
9370   }
9371
9372   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9373     oben_frei = TRUE;
9374   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9375     unten_frei = TRUE;
9376   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9377     links_frei = TRUE;
9378   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9379     rechts_frei = TRUE;
9380
9381   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9382       element == EL_EXPANDABLE_STEELWALL_ANY)
9383   {
9384     if (oben_frei)
9385     {
9386       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9387       Store[ax][ay-1] = element;
9388       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9389       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9390         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9391                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9392       new_wall = TRUE;
9393     }
9394     if (unten_frei)
9395     {
9396       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9397       Store[ax][ay+1] = element;
9398       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9399       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9400         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9401                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9402       new_wall = TRUE;
9403     }
9404   }
9405
9406   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9407       element == EL_EXPANDABLE_STEELWALL_ANY)
9408   {
9409     if (links_frei)
9410     {
9411       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9412       Store[ax-1][ay] = element;
9413       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9414       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9415         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9416                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9417       new_wall = TRUE;
9418     }
9419
9420     if (rechts_frei)
9421     {
9422       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9423       Store[ax+1][ay] = element;
9424       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9425       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9426         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9427                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9428       new_wall = TRUE;
9429     }
9430   }
9431
9432   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9433     oben_massiv = TRUE;
9434   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9435     unten_massiv = TRUE;
9436   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9437     links_massiv = TRUE;
9438   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9439     rechts_massiv = TRUE;
9440
9441   if (((oben_massiv && unten_massiv) ||
9442        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9443       ((links_massiv && rechts_massiv) ||
9444        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9445     Feld[ax][ay] = EL_STEELWALL;
9446
9447   if (new_wall)
9448     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9449 }
9450
9451 void CheckForDragon(int x, int y)
9452 {
9453   int i, j;
9454   boolean dragon_found = FALSE;
9455   static int xy[4][2] =
9456   {
9457     { 0, -1 },
9458     { -1, 0 },
9459     { +1, 0 },
9460     { 0, +1 }
9461   };
9462
9463   for (i = 0; i < NUM_DIRECTIONS; i++)
9464   {
9465     for (j = 0; j < 4; j++)
9466     {
9467       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9468
9469       if (IN_LEV_FIELD(xx, yy) &&
9470           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9471       {
9472         if (Feld[xx][yy] == EL_DRAGON)
9473           dragon_found = TRUE;
9474       }
9475       else
9476         break;
9477     }
9478   }
9479
9480   if (!dragon_found)
9481   {
9482     for (i = 0; i < NUM_DIRECTIONS; i++)
9483     {
9484       for (j = 0; j < 3; j++)
9485       {
9486         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9487   
9488         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9489         {
9490           Feld[xx][yy] = EL_EMPTY;
9491           TEST_DrawLevelField(xx, yy);
9492         }
9493         else
9494           break;
9495       }
9496     }
9497   }
9498 }
9499
9500 static void InitBuggyBase(int x, int y)
9501 {
9502   int element = Feld[x][y];
9503   int activating_delay = FRAMES_PER_SECOND / 4;
9504
9505   ChangeDelay[x][y] =
9506     (element == EL_SP_BUGGY_BASE ?
9507      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9508      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9509      activating_delay :
9510      element == EL_SP_BUGGY_BASE_ACTIVE ?
9511      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9512 }
9513
9514 static void WarnBuggyBase(int x, int y)
9515 {
9516   int i;
9517   static int xy[4][2] =
9518   {
9519     { 0, -1 },
9520     { -1, 0 },
9521     { +1, 0 },
9522     { 0, +1 }
9523   };
9524
9525   for (i = 0; i < NUM_DIRECTIONS; i++)
9526   {
9527     int xx = x + xy[i][0];
9528     int yy = y + xy[i][1];
9529
9530     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9531     {
9532       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9533
9534       break;
9535     }
9536   }
9537 }
9538
9539 static void InitTrap(int x, int y)
9540 {
9541   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9542 }
9543
9544 static void ActivateTrap(int x, int y)
9545 {
9546   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9547 }
9548
9549 static void ChangeActiveTrap(int x, int y)
9550 {
9551   int graphic = IMG_TRAP_ACTIVE;
9552
9553   /* if new animation frame was drawn, correct crumbled sand border */
9554   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9555     TEST_DrawLevelFieldCrumbled(x, y);
9556 }
9557
9558 static int getSpecialActionElement(int element, int number, int base_element)
9559 {
9560   return (element != EL_EMPTY ? element :
9561           number != -1 ? base_element + number - 1 :
9562           EL_EMPTY);
9563 }
9564
9565 static int getModifiedActionNumber(int value_old, int operator, int operand,
9566                                    int value_min, int value_max)
9567 {
9568   int value_new = (operator == CA_MODE_SET      ? operand :
9569                    operator == CA_MODE_ADD      ? value_old + operand :
9570                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9571                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9572                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9573                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9574                    value_old);
9575
9576   return (value_new < value_min ? value_min :
9577           value_new > value_max ? value_max :
9578           value_new);
9579 }
9580
9581 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9582 {
9583   struct ElementInfo *ei = &element_info[element];
9584   struct ElementChangeInfo *change = &ei->change_page[page];
9585   int target_element = change->target_element;
9586   int action_type = change->action_type;
9587   int action_mode = change->action_mode;
9588   int action_arg = change->action_arg;
9589   int action_element = change->action_element;
9590   int i;
9591
9592   if (!change->has_action)
9593     return;
9594
9595   /* ---------- determine action paramater values -------------------------- */
9596
9597   int level_time_value =
9598     (level.time > 0 ? TimeLeft :
9599      TimePlayed);
9600
9601   int action_arg_element_raw =
9602     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9603      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9604      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9605      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9606      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9607      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9608      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9609      EL_EMPTY);
9610   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9611
9612   int action_arg_direction =
9613     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9614      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9615      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9616      change->actual_trigger_side :
9617      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9618      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9619      MV_NONE);
9620
9621   int action_arg_number_min =
9622     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9623      CA_ARG_MIN);
9624
9625   int action_arg_number_max =
9626     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9627      action_type == CA_SET_LEVEL_GEMS ? 999 :
9628      action_type == CA_SET_LEVEL_TIME ? 9999 :
9629      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9630      action_type == CA_SET_CE_VALUE ? 9999 :
9631      action_type == CA_SET_CE_SCORE ? 9999 :
9632      CA_ARG_MAX);
9633
9634   int action_arg_number_reset =
9635     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9636      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9637      action_type == CA_SET_LEVEL_TIME ? level.time :
9638      action_type == CA_SET_LEVEL_SCORE ? 0 :
9639      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9640      action_type == CA_SET_CE_SCORE ? 0 :
9641      0);
9642
9643   int action_arg_number =
9644     (action_arg <= CA_ARG_MAX ? action_arg :
9645      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9646      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9647      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9648      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9649      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9650      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9651      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9652      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9653      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9654      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9655      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9656      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9657      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9658      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9659      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9660      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9661      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9662      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9663      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9664      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9665      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9666      -1);
9667
9668   int action_arg_number_old =
9669     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9670      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9671      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9672      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9673      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9674      0);
9675
9676   int action_arg_number_new =
9677     getModifiedActionNumber(action_arg_number_old,
9678                             action_mode, action_arg_number,
9679                             action_arg_number_min, action_arg_number_max);
9680
9681   int trigger_player_bits =
9682     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9683      change->actual_trigger_player_bits : change->trigger_player);
9684
9685   int action_arg_player_bits =
9686     (action_arg >= CA_ARG_PLAYER_1 &&
9687      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9688      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9689      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9690      PLAYER_BITS_ANY);
9691
9692   /* ---------- execute action  -------------------------------------------- */
9693
9694   switch (action_type)
9695   {
9696     case CA_NO_ACTION:
9697     {
9698       return;
9699     }
9700
9701     /* ---------- level actions  ------------------------------------------- */
9702
9703     case CA_RESTART_LEVEL:
9704     {
9705       game.restart_level = TRUE;
9706
9707       break;
9708     }
9709
9710     case CA_SHOW_ENVELOPE:
9711     {
9712       int element = getSpecialActionElement(action_arg_element,
9713                                             action_arg_number, EL_ENVELOPE_1);
9714
9715       if (IS_ENVELOPE(element))
9716         local_player->show_envelope = element;
9717
9718       break;
9719     }
9720
9721     case CA_SET_LEVEL_TIME:
9722     {
9723       if (level.time > 0)       /* only modify limited time value */
9724       {
9725         TimeLeft = action_arg_number_new;
9726
9727         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9728
9729         DisplayGameControlValues();
9730
9731         if (!TimeLeft && setup.time_limit)
9732           for (i = 0; i < MAX_PLAYERS; i++)
9733             KillPlayer(&stored_player[i]);
9734       }
9735
9736       break;
9737     }
9738
9739     case CA_SET_LEVEL_SCORE:
9740     {
9741       local_player->score = action_arg_number_new;
9742
9743       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9744
9745       DisplayGameControlValues();
9746
9747       break;
9748     }
9749
9750     case CA_SET_LEVEL_GEMS:
9751     {
9752       local_player->gems_still_needed = action_arg_number_new;
9753
9754       game.snapshot.collected_item = TRUE;
9755
9756       game_panel_controls[GAME_PANEL_GEMS].value =
9757         local_player->gems_still_needed;
9758
9759       DisplayGameControlValues();
9760
9761       break;
9762     }
9763
9764     case CA_SET_LEVEL_WIND:
9765     {
9766       game.wind_direction = action_arg_direction;
9767
9768       break;
9769     }
9770
9771     case CA_SET_LEVEL_RANDOM_SEED:
9772     {
9773       /* ensure that setting a new random seed while playing is predictable */
9774       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9775
9776       break;
9777     }
9778
9779     /* ---------- player actions  ------------------------------------------ */
9780
9781     case CA_MOVE_PLAYER:
9782     {
9783       /* automatically move to the next field in specified direction */
9784       for (i = 0; i < MAX_PLAYERS; i++)
9785         if (trigger_player_bits & (1 << i))
9786           stored_player[i].programmed_action = action_arg_direction;
9787
9788       break;
9789     }
9790
9791     case CA_EXIT_PLAYER:
9792     {
9793       for (i = 0; i < MAX_PLAYERS; i++)
9794         if (action_arg_player_bits & (1 << i))
9795           ExitPlayer(&stored_player[i]);
9796
9797       if (AllPlayersGone)
9798         PlayerWins(local_player);
9799
9800       break;
9801     }
9802
9803     case CA_KILL_PLAYER:
9804     {
9805       for (i = 0; i < MAX_PLAYERS; i++)
9806         if (action_arg_player_bits & (1 << i))
9807           KillPlayer(&stored_player[i]);
9808
9809       break;
9810     }
9811
9812     case CA_SET_PLAYER_KEYS:
9813     {
9814       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9815       int element = getSpecialActionElement(action_arg_element,
9816                                             action_arg_number, EL_KEY_1);
9817
9818       if (IS_KEY(element))
9819       {
9820         for (i = 0; i < MAX_PLAYERS; i++)
9821         {
9822           if (trigger_player_bits & (1 << i))
9823           {
9824             stored_player[i].key[KEY_NR(element)] = key_state;
9825
9826             DrawGameDoorValues();
9827           }
9828         }
9829       }
9830
9831       break;
9832     }
9833
9834     case CA_SET_PLAYER_SPEED:
9835     {
9836       for (i = 0; i < MAX_PLAYERS; i++)
9837       {
9838         if (trigger_player_bits & (1 << i))
9839         {
9840           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9841
9842           if (action_arg == CA_ARG_SPEED_FASTER &&
9843               stored_player[i].cannot_move)
9844           {
9845             action_arg_number = STEPSIZE_VERY_SLOW;
9846           }
9847           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9848                    action_arg == CA_ARG_SPEED_FASTER)
9849           {
9850             action_arg_number = 2;
9851             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9852                            CA_MODE_MULTIPLY);
9853           }
9854           else if (action_arg == CA_ARG_NUMBER_RESET)
9855           {
9856             action_arg_number = level.initial_player_stepsize[i];
9857           }
9858
9859           move_stepsize =
9860             getModifiedActionNumber(move_stepsize,
9861                                     action_mode,
9862                                     action_arg_number,
9863                                     action_arg_number_min,
9864                                     action_arg_number_max);
9865
9866           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9867         }
9868       }
9869
9870       break;
9871     }
9872
9873     case CA_SET_PLAYER_SHIELD:
9874     {
9875       for (i = 0; i < MAX_PLAYERS; i++)
9876       {
9877         if (trigger_player_bits & (1 << i))
9878         {
9879           if (action_arg == CA_ARG_SHIELD_OFF)
9880           {
9881             stored_player[i].shield_normal_time_left = 0;
9882             stored_player[i].shield_deadly_time_left = 0;
9883           }
9884           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9885           {
9886             stored_player[i].shield_normal_time_left = 999999;
9887           }
9888           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9889           {
9890             stored_player[i].shield_normal_time_left = 999999;
9891             stored_player[i].shield_deadly_time_left = 999999;
9892           }
9893         }
9894       }
9895
9896       break;
9897     }
9898
9899     case CA_SET_PLAYER_GRAVITY:
9900     {
9901       for (i = 0; i < MAX_PLAYERS; i++)
9902       {
9903         if (trigger_player_bits & (1 << i))
9904         {
9905           stored_player[i].gravity =
9906             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9907              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9908              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9909              stored_player[i].gravity);
9910         }
9911       }
9912
9913       break;
9914     }
9915
9916     case CA_SET_PLAYER_ARTWORK:
9917     {
9918       for (i = 0; i < MAX_PLAYERS; i++)
9919       {
9920         if (trigger_player_bits & (1 << i))
9921         {
9922           int artwork_element = action_arg_element;
9923
9924           if (action_arg == CA_ARG_ELEMENT_RESET)
9925             artwork_element =
9926               (level.use_artwork_element[i] ? level.artwork_element[i] :
9927                stored_player[i].element_nr);
9928
9929           if (stored_player[i].artwork_element != artwork_element)
9930             stored_player[i].Frame = 0;
9931
9932           stored_player[i].artwork_element = artwork_element;
9933
9934           SetPlayerWaiting(&stored_player[i], FALSE);
9935
9936           /* set number of special actions for bored and sleeping animation */
9937           stored_player[i].num_special_action_bored =
9938             get_num_special_action(artwork_element,
9939                                    ACTION_BORING_1, ACTION_BORING_LAST);
9940           stored_player[i].num_special_action_sleeping =
9941             get_num_special_action(artwork_element,
9942                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9943         }
9944       }
9945
9946       break;
9947     }
9948
9949     case CA_SET_PLAYER_INVENTORY:
9950     {
9951       for (i = 0; i < MAX_PLAYERS; i++)
9952       {
9953         struct PlayerInfo *player = &stored_player[i];
9954         int j, k;
9955
9956         if (trigger_player_bits & (1 << i))
9957         {
9958           int inventory_element = action_arg_element;
9959
9960           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9961               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9962               action_arg == CA_ARG_ELEMENT_ACTION)
9963           {
9964             int element = inventory_element;
9965             int collect_count = element_info[element].collect_count_initial;
9966
9967             if (!IS_CUSTOM_ELEMENT(element))
9968               collect_count = 1;
9969
9970             if (collect_count == 0)
9971               player->inventory_infinite_element = element;
9972             else
9973               for (k = 0; k < collect_count; k++)
9974                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9975                   player->inventory_element[player->inventory_size++] =
9976                     element;
9977           }
9978           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9979                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9980                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9981           {
9982             if (player->inventory_infinite_element != EL_UNDEFINED &&
9983                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9984                                      action_arg_element_raw))
9985               player->inventory_infinite_element = EL_UNDEFINED;
9986
9987             for (k = 0, j = 0; j < player->inventory_size; j++)
9988             {
9989               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9990                                         action_arg_element_raw))
9991                 player->inventory_element[k++] = player->inventory_element[j];
9992             }
9993
9994             player->inventory_size = k;
9995           }
9996           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9997           {
9998             if (player->inventory_size > 0)
9999             {
10000               for (j = 0; j < player->inventory_size - 1; j++)
10001                 player->inventory_element[j] = player->inventory_element[j + 1];
10002
10003               player->inventory_size--;
10004             }
10005           }
10006           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10007           {
10008             if (player->inventory_size > 0)
10009               player->inventory_size--;
10010           }
10011           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10012           {
10013             player->inventory_infinite_element = EL_UNDEFINED;
10014             player->inventory_size = 0;
10015           }
10016           else if (action_arg == CA_ARG_INVENTORY_RESET)
10017           {
10018             player->inventory_infinite_element = EL_UNDEFINED;
10019             player->inventory_size = 0;
10020
10021             if (level.use_initial_inventory[i])
10022             {
10023               for (j = 0; j < level.initial_inventory_size[i]; j++)
10024               {
10025                 int element = level.initial_inventory_content[i][j];
10026                 int collect_count = element_info[element].collect_count_initial;
10027
10028                 if (!IS_CUSTOM_ELEMENT(element))
10029                   collect_count = 1;
10030
10031                 if (collect_count == 0)
10032                   player->inventory_infinite_element = element;
10033                 else
10034                   for (k = 0; k < collect_count; k++)
10035                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10036                       player->inventory_element[player->inventory_size++] =
10037                         element;
10038               }
10039             }
10040           }
10041         }
10042       }
10043
10044       break;
10045     }
10046
10047     /* ---------- CE actions  ---------------------------------------------- */
10048
10049     case CA_SET_CE_VALUE:
10050     {
10051       int last_ce_value = CustomValue[x][y];
10052
10053       CustomValue[x][y] = action_arg_number_new;
10054
10055       if (CustomValue[x][y] != last_ce_value)
10056       {
10057         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10058         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10059
10060         if (CustomValue[x][y] == 0)
10061         {
10062           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10063           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10064         }
10065       }
10066
10067       break;
10068     }
10069
10070     case CA_SET_CE_SCORE:
10071     {
10072       int last_ce_score = ei->collect_score;
10073
10074       ei->collect_score = action_arg_number_new;
10075
10076       if (ei->collect_score != last_ce_score)
10077       {
10078         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10079         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10080
10081         if (ei->collect_score == 0)
10082         {
10083           int xx, yy;
10084
10085           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10086           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10087
10088           /*
10089             This is a very special case that seems to be a mixture between
10090             CheckElementChange() and CheckTriggeredElementChange(): while
10091             the first one only affects single elements that are triggered
10092             directly, the second one affects multiple elements in the playfield
10093             that are triggered indirectly by another element. This is a third
10094             case: Changing the CE score always affects multiple identical CEs,
10095             so every affected CE must be checked, not only the single CE for
10096             which the CE score was changed in the first place (as every instance
10097             of that CE shares the same CE score, and therefore also can change)!
10098           */
10099           SCAN_PLAYFIELD(xx, yy)
10100           {
10101             if (Feld[xx][yy] == element)
10102               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10103                                  CE_SCORE_GETS_ZERO);
10104           }
10105         }
10106       }
10107
10108       break;
10109     }
10110
10111     case CA_SET_CE_ARTWORK:
10112     {
10113       int artwork_element = action_arg_element;
10114       boolean reset_frame = FALSE;
10115       int xx, yy;
10116
10117       if (action_arg == CA_ARG_ELEMENT_RESET)
10118         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10119                            element);
10120
10121       if (ei->gfx_element != artwork_element)
10122         reset_frame = TRUE;
10123
10124       ei->gfx_element = artwork_element;
10125
10126       SCAN_PLAYFIELD(xx, yy)
10127       {
10128         if (Feld[xx][yy] == element)
10129         {
10130           if (reset_frame)
10131           {
10132             ResetGfxAnimation(xx, yy);
10133             ResetRandomAnimationValue(xx, yy);
10134           }
10135
10136           TEST_DrawLevelField(xx, yy);
10137         }
10138       }
10139
10140       break;
10141     }
10142
10143     /* ---------- engine actions  ------------------------------------------ */
10144
10145     case CA_SET_ENGINE_SCAN_MODE:
10146     {
10147       InitPlayfieldScanMode(action_arg);
10148
10149       break;
10150     }
10151
10152     default:
10153       break;
10154   }
10155 }
10156
10157 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10158 {
10159   int old_element = Feld[x][y];
10160   int new_element = GetElementFromGroupElement(element);
10161   int previous_move_direction = MovDir[x][y];
10162   int last_ce_value = CustomValue[x][y];
10163   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10164   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10165   boolean add_player_onto_element = (new_element_is_player &&
10166                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10167                                      IS_WALKABLE(old_element));
10168
10169   if (!add_player_onto_element)
10170   {
10171     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10172       RemoveMovingField(x, y);
10173     else
10174       RemoveField(x, y);
10175
10176     Feld[x][y] = new_element;
10177
10178     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10179       MovDir[x][y] = previous_move_direction;
10180
10181     if (element_info[new_element].use_last_ce_value)
10182       CustomValue[x][y] = last_ce_value;
10183
10184     InitField_WithBug1(x, y, FALSE);
10185
10186     new_element = Feld[x][y];   /* element may have changed */
10187
10188     ResetGfxAnimation(x, y);
10189     ResetRandomAnimationValue(x, y);
10190
10191     TEST_DrawLevelField(x, y);
10192
10193     if (GFX_CRUMBLED(new_element))
10194       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10195   }
10196
10197   /* check if element under the player changes from accessible to unaccessible
10198      (needed for special case of dropping element which then changes) */
10199   /* (must be checked after creating new element for walkable group elements) */
10200   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10201       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10202   {
10203     Bang(x, y);
10204
10205     return;
10206   }
10207
10208   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10209   if (new_element_is_player)
10210     RelocatePlayer(x, y, new_element);
10211
10212   if (is_change)
10213     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10214
10215   TestIfBadThingTouchesPlayer(x, y);
10216   TestIfPlayerTouchesCustomElement(x, y);
10217   TestIfElementTouchesCustomElement(x, y);
10218 }
10219
10220 static void CreateField(int x, int y, int element)
10221 {
10222   CreateFieldExt(x, y, element, FALSE);
10223 }
10224
10225 static void CreateElementFromChange(int x, int y, int element)
10226 {
10227   element = GET_VALID_RUNTIME_ELEMENT(element);
10228
10229   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10230   {
10231     int old_element = Feld[x][y];
10232
10233     /* prevent changed element from moving in same engine frame
10234        unless both old and new element can either fall or move */
10235     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10236         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10237       Stop[x][y] = TRUE;
10238   }
10239
10240   CreateFieldExt(x, y, element, TRUE);
10241 }
10242
10243 static boolean ChangeElement(int x, int y, int element, int page)
10244 {
10245   struct ElementInfo *ei = &element_info[element];
10246   struct ElementChangeInfo *change = &ei->change_page[page];
10247   int ce_value = CustomValue[x][y];
10248   int ce_score = ei->collect_score;
10249   int target_element;
10250   int old_element = Feld[x][y];
10251
10252   /* always use default change event to prevent running into a loop */
10253   if (ChangeEvent[x][y] == -1)
10254     ChangeEvent[x][y] = CE_DELAY;
10255
10256   if (ChangeEvent[x][y] == CE_DELAY)
10257   {
10258     /* reset actual trigger element, trigger player and action element */
10259     change->actual_trigger_element = EL_EMPTY;
10260     change->actual_trigger_player = EL_EMPTY;
10261     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10262     change->actual_trigger_side = CH_SIDE_NONE;
10263     change->actual_trigger_ce_value = 0;
10264     change->actual_trigger_ce_score = 0;
10265   }
10266
10267   /* do not change elements more than a specified maximum number of changes */
10268   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10269     return FALSE;
10270
10271   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10272
10273   if (change->explode)
10274   {
10275     Bang(x, y);
10276
10277     return TRUE;
10278   }
10279
10280   if (change->use_target_content)
10281   {
10282     boolean complete_replace = TRUE;
10283     boolean can_replace[3][3];
10284     int xx, yy;
10285
10286     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10287     {
10288       boolean is_empty;
10289       boolean is_walkable;
10290       boolean is_diggable;
10291       boolean is_collectible;
10292       boolean is_removable;
10293       boolean is_destructible;
10294       int ex = x + xx - 1;
10295       int ey = y + yy - 1;
10296       int content_element = change->target_content.e[xx][yy];
10297       int e;
10298
10299       can_replace[xx][yy] = TRUE;
10300
10301       if (ex == x && ey == y)   /* do not check changing element itself */
10302         continue;
10303
10304       if (content_element == EL_EMPTY_SPACE)
10305       {
10306         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10307
10308         continue;
10309       }
10310
10311       if (!IN_LEV_FIELD(ex, ey))
10312       {
10313         can_replace[xx][yy] = FALSE;
10314         complete_replace = FALSE;
10315
10316         continue;
10317       }
10318
10319       e = Feld[ex][ey];
10320
10321       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10322         e = MovingOrBlocked2Element(ex, ey);
10323
10324       is_empty = (IS_FREE(ex, ey) ||
10325                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10326
10327       is_walkable     = (is_empty || IS_WALKABLE(e));
10328       is_diggable     = (is_empty || IS_DIGGABLE(e));
10329       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10330       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10331       is_removable    = (is_diggable || is_collectible);
10332
10333       can_replace[xx][yy] =
10334         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10335           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10336           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10337           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10338           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10339           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10340          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10341
10342       if (!can_replace[xx][yy])
10343         complete_replace = FALSE;
10344     }
10345
10346     if (!change->only_if_complete || complete_replace)
10347     {
10348       boolean something_has_changed = FALSE;
10349
10350       if (change->only_if_complete && change->use_random_replace &&
10351           RND(100) < change->random_percentage)
10352         return FALSE;
10353
10354       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10355       {
10356         int ex = x + xx - 1;
10357         int ey = y + yy - 1;
10358         int content_element;
10359
10360         if (can_replace[xx][yy] && (!change->use_random_replace ||
10361                                     RND(100) < change->random_percentage))
10362         {
10363           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10364             RemoveMovingField(ex, ey);
10365
10366           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10367
10368           content_element = change->target_content.e[xx][yy];
10369           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10370                                               ce_value, ce_score);
10371
10372           CreateElementFromChange(ex, ey, target_element);
10373
10374           something_has_changed = TRUE;
10375
10376           /* for symmetry reasons, freeze newly created border elements */
10377           if (ex != x || ey != y)
10378             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10379         }
10380       }
10381
10382       if (something_has_changed)
10383       {
10384         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10385         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10386       }
10387     }
10388   }
10389   else
10390   {
10391     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10392                                         ce_value, ce_score);
10393
10394     if (element == EL_DIAGONAL_GROWING ||
10395         element == EL_DIAGONAL_SHRINKING)
10396     {
10397       target_element = Store[x][y];
10398
10399       Store[x][y] = EL_EMPTY;
10400     }
10401
10402     CreateElementFromChange(x, y, target_element);
10403
10404     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10405     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10406   }
10407
10408   /* this uses direct change before indirect change */
10409   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10410
10411   return TRUE;
10412 }
10413
10414 static void HandleElementChange(int x, int y, int page)
10415 {
10416   int element = MovingOrBlocked2Element(x, y);
10417   struct ElementInfo *ei = &element_info[element];
10418   struct ElementChangeInfo *change = &ei->change_page[page];
10419   boolean handle_action_before_change = FALSE;
10420
10421 #ifdef DEBUG
10422   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10423       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10424   {
10425     printf("\n\n");
10426     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10427            x, y, element, element_info[element].token_name);
10428     printf("HandleElementChange(): This should never happen!\n");
10429     printf("\n\n");
10430   }
10431 #endif
10432
10433   /* this can happen with classic bombs on walkable, changing elements */
10434   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10435   {
10436     return;
10437   }
10438
10439   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10440   {
10441     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10442
10443     if (change->can_change)
10444     {
10445       /* !!! not clear why graphic animation should be reset at all here !!! */
10446       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10447       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10448
10449       /*
10450         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10451
10452         When using an animation frame delay of 1 (this only happens with
10453         "sp_zonk.moving.left/right" in the classic graphics), the default
10454         (non-moving) animation shows wrong animation frames (while the
10455         moving animation, like "sp_zonk.moving.left/right", is correct,
10456         so this graphical bug never shows up with the classic graphics).
10457         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10458         be drawn instead of the correct frames 0,1,2,3. This is caused by
10459         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10460         an element change: First when the change delay ("ChangeDelay[][]")
10461         counter has reached zero after decrementing, then a second time in
10462         the next frame (after "GfxFrame[][]" was already incremented) when
10463         "ChangeDelay[][]" is reset to the initial delay value again.
10464
10465         This causes frame 0 to be drawn twice, while the last frame won't
10466         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10467
10468         As some animations may already be cleverly designed around this bug
10469         (at least the "Snake Bite" snake tail animation does this), it cannot
10470         simply be fixed here without breaking such existing animations.
10471         Unfortunately, it cannot easily be detected if a graphics set was
10472         designed "before" or "after" the bug was fixed. As a workaround,
10473         a new graphics set option "game.graphics_engine_version" was added
10474         to be able to specify the game's major release version for which the
10475         graphics set was designed, which can then be used to decide if the
10476         bugfix should be used (version 4 and above) or not (version 3 or
10477         below, or if no version was specified at all, as with old sets).
10478
10479         (The wrong/fixed animation frames can be tested with the test level set
10480         "test_gfxframe" and level "000", which contains a specially prepared
10481         custom element at level position (x/y) == (11/9) which uses the zonk
10482         animation mentioned above. Using "game.graphics_engine_version: 4"
10483         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10484         This can also be seen from the debug output for this test element.)
10485       */
10486
10487       /* when a custom element is about to change (for example by change delay),
10488          do not reset graphic animation when the custom element is moving */
10489       if (game.graphics_engine_version < 4 &&
10490           !IS_MOVING(x, y))
10491       {
10492         ResetGfxAnimation(x, y);
10493         ResetRandomAnimationValue(x, y);
10494       }
10495
10496       if (change->pre_change_function)
10497         change->pre_change_function(x, y);
10498     }
10499   }
10500
10501   ChangeDelay[x][y]--;
10502
10503   if (ChangeDelay[x][y] != 0)           /* continue element change */
10504   {
10505     if (change->can_change)
10506     {
10507       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10508
10509       if (IS_ANIMATED(graphic))
10510         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10511
10512       if (change->change_function)
10513         change->change_function(x, y);
10514     }
10515   }
10516   else                                  /* finish element change */
10517   {
10518     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10519     {
10520       page = ChangePage[x][y];
10521       ChangePage[x][y] = -1;
10522
10523       change = &ei->change_page[page];
10524     }
10525
10526     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10527     {
10528       ChangeDelay[x][y] = 1;            /* try change after next move step */
10529       ChangePage[x][y] = page;          /* remember page to use for change */
10530
10531       return;
10532     }
10533
10534     /* special case: set new level random seed before changing element */
10535     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10536       handle_action_before_change = TRUE;
10537
10538     if (change->has_action && handle_action_before_change)
10539       ExecuteCustomElementAction(x, y, element, page);
10540
10541     if (change->can_change)
10542     {
10543       if (ChangeElement(x, y, element, page))
10544       {
10545         if (change->post_change_function)
10546           change->post_change_function(x, y);
10547       }
10548     }
10549
10550     if (change->has_action && !handle_action_before_change)
10551       ExecuteCustomElementAction(x, y, element, page);
10552   }
10553 }
10554
10555 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10556                                               int trigger_element,
10557                                               int trigger_event,
10558                                               int trigger_player,
10559                                               int trigger_side,
10560                                               int trigger_page)
10561 {
10562   boolean change_done_any = FALSE;
10563   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10564   int i;
10565
10566   if (!(trigger_events[trigger_element][trigger_event]))
10567     return FALSE;
10568
10569   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10570
10571   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10572   {
10573     int element = EL_CUSTOM_START + i;
10574     boolean change_done = FALSE;
10575     int p;
10576
10577     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10578         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10579       continue;
10580
10581     for (p = 0; p < element_info[element].num_change_pages; p++)
10582     {
10583       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10584
10585       if (change->can_change_or_has_action &&
10586           change->has_event[trigger_event] &&
10587           change->trigger_side & trigger_side &&
10588           change->trigger_player & trigger_player &&
10589           change->trigger_page & trigger_page_bits &&
10590           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10591       {
10592         change->actual_trigger_element = trigger_element;
10593         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10594         change->actual_trigger_player_bits = trigger_player;
10595         change->actual_trigger_side = trigger_side;
10596         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10597         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10598
10599         if ((change->can_change && !change_done) || change->has_action)
10600         {
10601           int x, y;
10602
10603           SCAN_PLAYFIELD(x, y)
10604           {
10605             if (Feld[x][y] == element)
10606             {
10607               if (change->can_change && !change_done)
10608               {
10609                 /* if element already changed in this frame, not only prevent
10610                    another element change (checked in ChangeElement()), but
10611                    also prevent additional element actions for this element */
10612
10613                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10614                     !level.use_action_after_change_bug)
10615                   continue;
10616
10617                 ChangeDelay[x][y] = 1;
10618                 ChangeEvent[x][y] = trigger_event;
10619
10620                 HandleElementChange(x, y, p);
10621               }
10622               else if (change->has_action)
10623               {
10624                 /* if element already changed in this frame, not only prevent
10625                    another element change (checked in ChangeElement()), but
10626                    also prevent additional element actions for this element */
10627
10628                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10629                     !level.use_action_after_change_bug)
10630                   continue;
10631
10632                 ExecuteCustomElementAction(x, y, element, p);
10633                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10634               }
10635             }
10636           }
10637
10638           if (change->can_change)
10639           {
10640             change_done = TRUE;
10641             change_done_any = TRUE;
10642           }
10643         }
10644       }
10645     }
10646   }
10647
10648   RECURSION_LOOP_DETECTION_END();
10649
10650   return change_done_any;
10651 }
10652
10653 static boolean CheckElementChangeExt(int x, int y,
10654                                      int element,
10655                                      int trigger_element,
10656                                      int trigger_event,
10657                                      int trigger_player,
10658                                      int trigger_side)
10659 {
10660   boolean change_done = FALSE;
10661   int p;
10662
10663   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10664       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10665     return FALSE;
10666
10667   if (Feld[x][y] == EL_BLOCKED)
10668   {
10669     Blocked2Moving(x, y, &x, &y);
10670     element = Feld[x][y];
10671   }
10672
10673   /* check if element has already changed or is about to change after moving */
10674   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10675        Feld[x][y] != element) ||
10676
10677       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10678        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10679         ChangePage[x][y] != -1)))
10680     return FALSE;
10681
10682   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10683
10684   for (p = 0; p < element_info[element].num_change_pages; p++)
10685   {
10686     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10687
10688     /* check trigger element for all events where the element that is checked
10689        for changing interacts with a directly adjacent element -- this is
10690        different to element changes that affect other elements to change on the
10691        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10692     boolean check_trigger_element =
10693       (trigger_event == CE_TOUCHING_X ||
10694        trigger_event == CE_HITTING_X ||
10695        trigger_event == CE_HIT_BY_X ||
10696        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10697
10698     if (change->can_change_or_has_action &&
10699         change->has_event[trigger_event] &&
10700         change->trigger_side & trigger_side &&
10701         change->trigger_player & trigger_player &&
10702         (!check_trigger_element ||
10703          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10704     {
10705       change->actual_trigger_element = trigger_element;
10706       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10707       change->actual_trigger_player_bits = trigger_player;
10708       change->actual_trigger_side = trigger_side;
10709       change->actual_trigger_ce_value = CustomValue[x][y];
10710       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10711
10712       /* special case: trigger element not at (x,y) position for some events */
10713       if (check_trigger_element)
10714       {
10715         static struct
10716         {
10717           int dx, dy;
10718         } move_xy[] =
10719           {
10720             {  0,  0 },
10721             { -1,  0 },
10722             { +1,  0 },
10723             {  0,  0 },
10724             {  0, -1 },
10725             {  0,  0 }, { 0, 0 }, { 0, 0 },
10726             {  0, +1 }
10727           };
10728
10729         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10730         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10731
10732         change->actual_trigger_ce_value = CustomValue[xx][yy];
10733         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10734       }
10735
10736       if (change->can_change && !change_done)
10737       {
10738         ChangeDelay[x][y] = 1;
10739         ChangeEvent[x][y] = trigger_event;
10740
10741         HandleElementChange(x, y, p);
10742
10743         change_done = TRUE;
10744       }
10745       else if (change->has_action)
10746       {
10747         ExecuteCustomElementAction(x, y, element, p);
10748         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10749       }
10750     }
10751   }
10752
10753   RECURSION_LOOP_DETECTION_END();
10754
10755   return change_done;
10756 }
10757
10758 static void PlayPlayerSound(struct PlayerInfo *player)
10759 {
10760   int jx = player->jx, jy = player->jy;
10761   int sound_element = player->artwork_element;
10762   int last_action = player->last_action_waiting;
10763   int action = player->action_waiting;
10764
10765   if (player->is_waiting)
10766   {
10767     if (action != last_action)
10768       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10769     else
10770       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10771   }
10772   else
10773   {
10774     if (action != last_action)
10775       StopSound(element_info[sound_element].sound[last_action]);
10776
10777     if (last_action == ACTION_SLEEPING)
10778       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10779   }
10780 }
10781
10782 static void PlayAllPlayersSound()
10783 {
10784   int i;
10785
10786   for (i = 0; i < MAX_PLAYERS; i++)
10787     if (stored_player[i].active)
10788       PlayPlayerSound(&stored_player[i]);
10789 }
10790
10791 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10792 {
10793   boolean last_waiting = player->is_waiting;
10794   int move_dir = player->MovDir;
10795
10796   player->dir_waiting = move_dir;
10797   player->last_action_waiting = player->action_waiting;
10798
10799   if (is_waiting)
10800   {
10801     if (!last_waiting)          /* not waiting -> waiting */
10802     {
10803       player->is_waiting = TRUE;
10804
10805       player->frame_counter_bored =
10806         FrameCounter +
10807         game.player_boring_delay_fixed +
10808         GetSimpleRandom(game.player_boring_delay_random);
10809       player->frame_counter_sleeping =
10810         FrameCounter +
10811         game.player_sleeping_delay_fixed +
10812         GetSimpleRandom(game.player_sleeping_delay_random);
10813
10814       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10815     }
10816
10817     if (game.player_sleeping_delay_fixed +
10818         game.player_sleeping_delay_random > 0 &&
10819         player->anim_delay_counter == 0 &&
10820         player->post_delay_counter == 0 &&
10821         FrameCounter >= player->frame_counter_sleeping)
10822       player->is_sleeping = TRUE;
10823     else if (game.player_boring_delay_fixed +
10824              game.player_boring_delay_random > 0 &&
10825              FrameCounter >= player->frame_counter_bored)
10826       player->is_bored = TRUE;
10827
10828     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10829                               player->is_bored ? ACTION_BORING :
10830                               ACTION_WAITING);
10831
10832     if (player->is_sleeping && player->use_murphy)
10833     {
10834       /* special case for sleeping Murphy when leaning against non-free tile */
10835
10836       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10837           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10838            !IS_MOVING(player->jx - 1, player->jy)))
10839         move_dir = MV_LEFT;
10840       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10841                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10842                 !IS_MOVING(player->jx + 1, player->jy)))
10843         move_dir = MV_RIGHT;
10844       else
10845         player->is_sleeping = FALSE;
10846
10847       player->dir_waiting = move_dir;
10848     }
10849
10850     if (player->is_sleeping)
10851     {
10852       if (player->num_special_action_sleeping > 0)
10853       {
10854         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10855         {
10856           int last_special_action = player->special_action_sleeping;
10857           int num_special_action = player->num_special_action_sleeping;
10858           int special_action =
10859             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10860              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10861              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10862              last_special_action + 1 : ACTION_SLEEPING);
10863           int special_graphic =
10864             el_act_dir2img(player->artwork_element, special_action, move_dir);
10865
10866           player->anim_delay_counter =
10867             graphic_info[special_graphic].anim_delay_fixed +
10868             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10869           player->post_delay_counter =
10870             graphic_info[special_graphic].post_delay_fixed +
10871             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10872
10873           player->special_action_sleeping = special_action;
10874         }
10875
10876         if (player->anim_delay_counter > 0)
10877         {
10878           player->action_waiting = player->special_action_sleeping;
10879           player->anim_delay_counter--;
10880         }
10881         else if (player->post_delay_counter > 0)
10882         {
10883           player->post_delay_counter--;
10884         }
10885       }
10886     }
10887     else if (player->is_bored)
10888     {
10889       if (player->num_special_action_bored > 0)
10890       {
10891         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10892         {
10893           int special_action =
10894             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10895           int special_graphic =
10896             el_act_dir2img(player->artwork_element, special_action, move_dir);
10897
10898           player->anim_delay_counter =
10899             graphic_info[special_graphic].anim_delay_fixed +
10900             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10901           player->post_delay_counter =
10902             graphic_info[special_graphic].post_delay_fixed +
10903             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10904
10905           player->special_action_bored = special_action;
10906         }
10907
10908         if (player->anim_delay_counter > 0)
10909         {
10910           player->action_waiting = player->special_action_bored;
10911           player->anim_delay_counter--;
10912         }
10913         else if (player->post_delay_counter > 0)
10914         {
10915           player->post_delay_counter--;
10916         }
10917       }
10918     }
10919   }
10920   else if (last_waiting)        /* waiting -> not waiting */
10921   {
10922     player->is_waiting = FALSE;
10923     player->is_bored = FALSE;
10924     player->is_sleeping = FALSE;
10925
10926     player->frame_counter_bored = -1;
10927     player->frame_counter_sleeping = -1;
10928
10929     player->anim_delay_counter = 0;
10930     player->post_delay_counter = 0;
10931
10932     player->dir_waiting = player->MovDir;
10933     player->action_waiting = ACTION_DEFAULT;
10934
10935     player->special_action_bored = ACTION_DEFAULT;
10936     player->special_action_sleeping = ACTION_DEFAULT;
10937   }
10938 }
10939
10940 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10941 {
10942   if ((!player->is_moving  && player->was_moving) ||
10943       (player->MovPos == 0 && player->was_moving) ||
10944       (player->is_snapping && !player->was_snapping) ||
10945       (player->is_dropping && !player->was_dropping))
10946   {
10947     if (!CheckSaveEngineSnapshotToList())
10948       return;
10949
10950     player->was_moving = FALSE;
10951     player->was_snapping = TRUE;
10952     player->was_dropping = TRUE;
10953   }
10954   else
10955   {
10956     if (player->is_moving)
10957       player->was_moving = TRUE;
10958
10959     if (!player->is_snapping)
10960       player->was_snapping = FALSE;
10961
10962     if (!player->is_dropping)
10963       player->was_dropping = FALSE;
10964   }
10965 }
10966
10967 static void CheckSingleStepMode(struct PlayerInfo *player)
10968 {
10969   if (tape.single_step && tape.recording && !tape.pausing)
10970   {
10971     /* as it is called "single step mode", just return to pause mode when the
10972        player stopped moving after one tile (or never starts moving at all) */
10973     if (!player->is_moving &&
10974         !player->is_pushing &&
10975         !player->is_dropping_pressed)
10976     {
10977       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10978       SnapField(player, 0, 0);                  /* stop snapping */
10979     }
10980   }
10981
10982   CheckSaveEngineSnapshot(player);
10983 }
10984
10985 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10986 {
10987   int left      = player_action & JOY_LEFT;
10988   int right     = player_action & JOY_RIGHT;
10989   int up        = player_action & JOY_UP;
10990   int down      = player_action & JOY_DOWN;
10991   int button1   = player_action & JOY_BUTTON_1;
10992   int button2   = player_action & JOY_BUTTON_2;
10993   int dx        = (left ? -1 : right ? 1 : 0);
10994   int dy        = (up   ? -1 : down  ? 1 : 0);
10995
10996   if (!player->active || tape.pausing)
10997     return 0;
10998
10999   if (player_action)
11000   {
11001     if (button1)
11002       SnapField(player, dx, dy);
11003     else
11004     {
11005       if (button2)
11006         DropElement(player);
11007
11008       MovePlayer(player, dx, dy);
11009     }
11010
11011     CheckSingleStepMode(player);
11012
11013     SetPlayerWaiting(player, FALSE);
11014
11015     return player_action;
11016   }
11017   else
11018   {
11019     /* no actions for this player (no input at player's configured device) */
11020
11021     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11022     SnapField(player, 0, 0);
11023     CheckGravityMovementWhenNotMoving(player);
11024
11025     if (player->MovPos == 0)
11026       SetPlayerWaiting(player, TRUE);
11027
11028     if (player->MovPos == 0)    /* needed for tape.playing */
11029       player->is_moving = FALSE;
11030
11031     player->is_dropping = FALSE;
11032     player->is_dropping_pressed = FALSE;
11033     player->drop_pressed_delay = 0;
11034
11035     CheckSingleStepMode(player);
11036
11037     return 0;
11038   }
11039 }
11040
11041 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11042                                          byte *tape_action)
11043 {
11044   if (!tape.use_mouse)
11045     return;
11046
11047   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11048   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11049   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11050 }
11051
11052 static void SetTapeActionFromMouseAction(byte *tape_action,
11053                                          struct MouseActionInfo *mouse_action)
11054 {
11055   if (!tape.use_mouse)
11056     return;
11057
11058   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11059   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11060   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11061 }
11062
11063 static void CheckLevelTime()
11064 {
11065   int i;
11066
11067   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11068   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11069   {
11070     if (level.native_em_level->lev->home == 0)  /* all players at home */
11071     {
11072       PlayerWins(local_player);
11073
11074       AllPlayersGone = TRUE;
11075
11076       level.native_em_level->lev->home = -1;
11077     }
11078
11079     if (level.native_em_level->ply[0]->alive == 0 &&
11080         level.native_em_level->ply[1]->alive == 0 &&
11081         level.native_em_level->ply[2]->alive == 0 &&
11082         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11083       AllPlayersGone = TRUE;
11084   }
11085   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11086   {
11087     if (game_sp.LevelSolved &&
11088         !game_sp.GameOver)                              /* game won */
11089     {
11090       PlayerWins(local_player);
11091
11092       game_sp.GameOver = TRUE;
11093
11094       AllPlayersGone = TRUE;
11095     }
11096
11097     if (game_sp.GameOver)                               /* game lost */
11098       AllPlayersGone = TRUE;
11099   }
11100   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11101   {
11102     if (game_mm.level_solved &&
11103         !game_mm.game_over)                             /* game won */
11104     {
11105       PlayerWins(local_player);
11106
11107       game_mm.game_over = TRUE;
11108
11109       AllPlayersGone = TRUE;
11110     }
11111
11112     if (game_mm.game_over)                              /* game lost */
11113       AllPlayersGone = TRUE;
11114   }
11115
11116   if (TimeFrames >= FRAMES_PER_SECOND)
11117   {
11118     TimeFrames = 0;
11119     TapeTime++;
11120
11121     for (i = 0; i < MAX_PLAYERS; i++)
11122     {
11123       struct PlayerInfo *player = &stored_player[i];
11124
11125       if (SHIELD_ON(player))
11126       {
11127         player->shield_normal_time_left--;
11128
11129         if (player->shield_deadly_time_left > 0)
11130           player->shield_deadly_time_left--;
11131       }
11132     }
11133
11134     if (!local_player->LevelSolved && !level.use_step_counter)
11135     {
11136       TimePlayed++;
11137
11138       if (TimeLeft > 0)
11139       {
11140         TimeLeft--;
11141
11142         if (TimeLeft <= 10 && setup.time_limit)
11143           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11144
11145         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11146            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11147
11148         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11149
11150         if (!TimeLeft && setup.time_limit)
11151         {
11152           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11153             level.native_em_level->lev->killed_out_of_time = TRUE;
11154           else
11155             for (i = 0; i < MAX_PLAYERS; i++)
11156               KillPlayer(&stored_player[i]);
11157         }
11158       }
11159       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11160       {
11161         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11162       }
11163
11164       level.native_em_level->lev->time =
11165         (game.no_time_limit ? TimePlayed : TimeLeft);
11166     }
11167
11168     if (tape.recording || tape.playing)
11169       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11170   }
11171
11172   if (tape.recording || tape.playing)
11173     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11174
11175   UpdateAndDisplayGameControlValues();
11176 }
11177
11178 void AdvanceFrameAndPlayerCounters(int player_nr)
11179 {
11180   int i;
11181
11182   /* advance frame counters (global frame counter and time frame counter) */
11183   FrameCounter++;
11184   TimeFrames++;
11185
11186   /* advance player counters (counters for move delay, move animation etc.) */
11187   for (i = 0; i < MAX_PLAYERS; i++)
11188   {
11189     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11190     int move_delay_value = stored_player[i].move_delay_value;
11191     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11192
11193     if (!advance_player_counters)       /* not all players may be affected */
11194       continue;
11195
11196     if (move_frames == 0)       /* less than one move per game frame */
11197     {
11198       int stepsize = TILEX / move_delay_value;
11199       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11200       int count = (stored_player[i].is_moving ?
11201                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11202
11203       if (count % delay == 0)
11204         move_frames = 1;
11205     }
11206
11207     stored_player[i].Frame += move_frames;
11208
11209     if (stored_player[i].MovPos != 0)
11210       stored_player[i].StepFrame += move_frames;
11211
11212     if (stored_player[i].move_delay > 0)
11213       stored_player[i].move_delay--;
11214
11215     /* due to bugs in previous versions, counter must count up, not down */
11216     if (stored_player[i].push_delay != -1)
11217       stored_player[i].push_delay++;
11218
11219     if (stored_player[i].drop_delay > 0)
11220       stored_player[i].drop_delay--;
11221
11222     if (stored_player[i].is_dropping_pressed)
11223       stored_player[i].drop_pressed_delay++;
11224   }
11225 }
11226
11227 void StartGameActions(boolean init_network_game, boolean record_tape,
11228                       int random_seed)
11229 {
11230   unsigned int new_random_seed = InitRND(random_seed);
11231
11232   if (record_tape)
11233     TapeStartRecording(new_random_seed);
11234
11235   if (init_network_game)
11236   {
11237     SendToServer_LevelFile();
11238     SendToServer_StartPlaying();
11239
11240     return;
11241   }
11242
11243   InitGame();
11244 }
11245
11246 void GameActionsExt()
11247 {
11248 #if 0
11249   static unsigned int game_frame_delay = 0;
11250 #endif
11251   unsigned int game_frame_delay_value;
11252   byte *recorded_player_action;
11253   byte summarized_player_action = 0;
11254   byte tape_action[MAX_PLAYERS];
11255   int i;
11256
11257   /* detect endless loops, caused by custom element programming */
11258   if (recursion_loop_detected && recursion_loop_depth == 0)
11259   {
11260     char *message = getStringCat3("Internal Error! Element ",
11261                                   EL_NAME(recursion_loop_element),
11262                                   " caused endless loop! Quit the game?");
11263
11264     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11265           EL_NAME(recursion_loop_element));
11266
11267     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11268
11269     recursion_loop_detected = FALSE;    /* if game should be continued */
11270
11271     free(message);
11272
11273     return;
11274   }
11275
11276   if (game.restart_level)
11277     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11278
11279   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11280   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11281   {
11282     if (level.native_em_level->lev->home == 0)  /* all players at home */
11283     {
11284       PlayerWins(local_player);
11285
11286       AllPlayersGone = TRUE;
11287
11288       level.native_em_level->lev->home = -1;
11289     }
11290
11291     if (level.native_em_level->ply[0]->alive == 0 &&
11292         level.native_em_level->ply[1]->alive == 0 &&
11293         level.native_em_level->ply[2]->alive == 0 &&
11294         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11295       AllPlayersGone = TRUE;
11296   }
11297   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11298   {
11299     if (game_sp.LevelSolved &&
11300         !game_sp.GameOver)                              /* game won */
11301     {
11302       PlayerWins(local_player);
11303
11304       game_sp.GameOver = TRUE;
11305
11306       AllPlayersGone = TRUE;
11307     }
11308
11309     if (game_sp.GameOver)                               /* game lost */
11310       AllPlayersGone = TRUE;
11311   }
11312   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11313   {
11314     if (game_mm.level_solved &&
11315         !game_mm.game_over)                             /* game won */
11316     {
11317       PlayerWins(local_player);
11318
11319       game_mm.game_over = TRUE;
11320
11321       AllPlayersGone = TRUE;
11322     }
11323
11324     if (game_mm.game_over)                              /* game lost */
11325       AllPlayersGone = TRUE;
11326   }
11327
11328   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11329     GameWon();
11330
11331   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11332     TapeStop();
11333
11334   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11335     return;
11336
11337   game_frame_delay_value =
11338     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11339
11340   if (tape.playing && tape.warp_forward && !tape.pausing)
11341     game_frame_delay_value = 0;
11342
11343   SetVideoFrameDelay(game_frame_delay_value);
11344
11345 #if 0
11346 #if 0
11347   /* ---------- main game synchronization point ---------- */
11348
11349   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11350
11351   printf("::: skip == %d\n", skip);
11352
11353 #else
11354   /* ---------- main game synchronization point ---------- */
11355
11356   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11357 #endif
11358 #endif
11359
11360   if (network_playing && !network_player_action_received)
11361   {
11362     /* try to get network player actions in time */
11363
11364     /* last chance to get network player actions without main loop delay */
11365     HandleNetworking();
11366
11367     /* game was quit by network peer */
11368     if (game_status != GAME_MODE_PLAYING)
11369       return;
11370
11371     if (!network_player_action_received)
11372       return;           /* failed to get network player actions in time */
11373
11374     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11375   }
11376
11377   if (tape.pausing)
11378     return;
11379
11380   /* at this point we know that we really continue executing the game */
11381
11382   network_player_action_received = FALSE;
11383
11384   /* when playing tape, read previously recorded player input from tape data */
11385   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11386
11387   local_player->effective_mouse_action = local_player->mouse_action;
11388
11389   if (recorded_player_action != NULL)
11390     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11391                                  recorded_player_action);
11392
11393   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11394   if (tape.pausing)
11395     return;
11396
11397   if (tape.set_centered_player)
11398   {
11399     game.centered_player_nr_next = tape.centered_player_nr_next;
11400     game.set_centered_player = TRUE;
11401   }
11402
11403   for (i = 0; i < MAX_PLAYERS; i++)
11404   {
11405     summarized_player_action |= stored_player[i].action;
11406
11407     if (!network_playing && (game.team_mode || tape.playing))
11408       stored_player[i].effective_action = stored_player[i].action;
11409   }
11410
11411   if (network_playing)
11412     SendToServer_MovePlayer(summarized_player_action);
11413
11414   // summarize all actions at local players mapped input device position
11415   // (this allows using different input devices in single player mode)
11416   if (!network.enabled && !game.team_mode)
11417     stored_player[map_player_action[local_player->index_nr]].effective_action =
11418       summarized_player_action;
11419
11420   if (tape.recording &&
11421       setup.team_mode &&
11422       setup.input_on_focus &&
11423       game.centered_player_nr != -1)
11424   {
11425     for (i = 0; i < MAX_PLAYERS; i++)
11426       stored_player[i].effective_action =
11427         (i == game.centered_player_nr ? summarized_player_action : 0);
11428   }
11429
11430   if (recorded_player_action != NULL)
11431     for (i = 0; i < MAX_PLAYERS; i++)
11432       stored_player[i].effective_action = recorded_player_action[i];
11433
11434   for (i = 0; i < MAX_PLAYERS; i++)
11435   {
11436     tape_action[i] = stored_player[i].effective_action;
11437
11438     /* (this may happen in the RND game engine if a player was not present on
11439        the playfield on level start, but appeared later from a custom element */
11440     if (setup.team_mode &&
11441         tape.recording &&
11442         tape_action[i] &&
11443         !tape.player_participates[i])
11444       tape.player_participates[i] = TRUE;
11445   }
11446
11447   SetTapeActionFromMouseAction(tape_action,
11448                                &local_player->effective_mouse_action);
11449
11450   /* only record actions from input devices, but not programmed actions */
11451   if (tape.recording)
11452     TapeRecordAction(tape_action);
11453
11454 #if USE_NEW_PLAYER_ASSIGNMENTS
11455   // !!! also map player actions in single player mode !!!
11456   // if (game.team_mode)
11457   if (1)
11458   {
11459     byte mapped_action[MAX_PLAYERS];
11460
11461 #if DEBUG_PLAYER_ACTIONS
11462     printf(":::");
11463     for (i = 0; i < MAX_PLAYERS; i++)
11464       printf(" %d, ", stored_player[i].effective_action);
11465 #endif
11466
11467     for (i = 0; i < MAX_PLAYERS; i++)
11468       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11469
11470     for (i = 0; i < MAX_PLAYERS; i++)
11471       stored_player[i].effective_action = mapped_action[i];
11472
11473 #if DEBUG_PLAYER_ACTIONS
11474     printf(" =>");
11475     for (i = 0; i < MAX_PLAYERS; i++)
11476       printf(" %d, ", stored_player[i].effective_action);
11477     printf("\n");
11478 #endif
11479   }
11480 #if DEBUG_PLAYER_ACTIONS
11481   else
11482   {
11483     printf(":::");
11484     for (i = 0; i < MAX_PLAYERS; i++)
11485       printf(" %d, ", stored_player[i].effective_action);
11486     printf("\n");
11487   }
11488 #endif
11489 #endif
11490
11491   for (i = 0; i < MAX_PLAYERS; i++)
11492   {
11493     // allow engine snapshot in case of changed movement attempt
11494     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11495         (stored_player[i].effective_action & KEY_MOTION))
11496       game.snapshot.changed_action = TRUE;
11497
11498     // allow engine snapshot in case of snapping/dropping attempt
11499     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11500         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11501       game.snapshot.changed_action = TRUE;
11502
11503     game.snapshot.last_action[i] = stored_player[i].effective_action;
11504   }
11505
11506   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11507   {
11508     GameActions_EM_Main();
11509   }
11510   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11511   {
11512     GameActions_SP_Main();
11513   }
11514   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11515   {
11516     GameActions_MM_Main();
11517   }
11518   else
11519   {
11520     GameActions_RND_Main();
11521   }
11522
11523   BlitScreenToBitmap(backbuffer);
11524
11525   CheckLevelTime();
11526
11527   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11528
11529   if (global.show_frames_per_second)
11530   {
11531     static unsigned int fps_counter = 0;
11532     static int fps_frames = 0;
11533     unsigned int fps_delay_ms = Counter() - fps_counter;
11534
11535     fps_frames++;
11536
11537     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11538     {
11539       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11540
11541       fps_frames = 0;
11542       fps_counter = Counter();
11543
11544       /* always draw FPS to screen after FPS value was updated */
11545       redraw_mask |= REDRAW_FPS;
11546     }
11547
11548     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11549     if (GetDrawDeactivationMask() == REDRAW_NONE)
11550       redraw_mask |= REDRAW_FPS;
11551   }
11552 }
11553
11554 static void GameActions_CheckSaveEngineSnapshot()
11555 {
11556   if (!game.snapshot.save_snapshot)
11557     return;
11558
11559   // clear flag for saving snapshot _before_ saving snapshot
11560   game.snapshot.save_snapshot = FALSE;
11561
11562   SaveEngineSnapshotToList();
11563 }
11564
11565 void GameActions()
11566 {
11567   GameActionsExt();
11568
11569   GameActions_CheckSaveEngineSnapshot();
11570 }
11571
11572 void GameActions_EM_Main()
11573 {
11574   byte effective_action[MAX_PLAYERS];
11575   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11576   int i;
11577
11578   for (i = 0; i < MAX_PLAYERS; i++)
11579     effective_action[i] = stored_player[i].effective_action;
11580
11581   GameActions_EM(effective_action, warp_mode);
11582 }
11583
11584 void GameActions_SP_Main()
11585 {
11586   byte effective_action[MAX_PLAYERS];
11587   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11588   int i;
11589
11590   for (i = 0; i < MAX_PLAYERS; i++)
11591     effective_action[i] = stored_player[i].effective_action;
11592
11593   GameActions_SP(effective_action, warp_mode);
11594
11595   for (i = 0; i < MAX_PLAYERS; i++)
11596   {
11597     if (stored_player[i].force_dropping)
11598       stored_player[i].action |= KEY_BUTTON_DROP;
11599
11600     stored_player[i].force_dropping = FALSE;
11601   }
11602 }
11603
11604 void GameActions_MM_Main()
11605 {
11606   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11607
11608   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11609 }
11610
11611 void GameActions_RND_Main()
11612 {
11613   GameActions_RND();
11614 }
11615
11616 void GameActions_RND()
11617 {
11618   int magic_wall_x = 0, magic_wall_y = 0;
11619   int i, x, y, element, graphic, last_gfx_frame;
11620
11621   InitPlayfieldScanModeVars();
11622
11623   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11624   {
11625     SCAN_PLAYFIELD(x, y)
11626     {
11627       ChangeCount[x][y] = 0;
11628       ChangeEvent[x][y] = -1;
11629     }
11630   }
11631
11632   if (game.set_centered_player)
11633   {
11634     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11635
11636     /* switching to "all players" only possible if all players fit to screen */
11637     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11638     {
11639       game.centered_player_nr_next = game.centered_player_nr;
11640       game.set_centered_player = FALSE;
11641     }
11642
11643     /* do not switch focus to non-existing (or non-active) player */
11644     if (game.centered_player_nr_next >= 0 &&
11645         !stored_player[game.centered_player_nr_next].active)
11646     {
11647       game.centered_player_nr_next = game.centered_player_nr;
11648       game.set_centered_player = FALSE;
11649     }
11650   }
11651
11652   if (game.set_centered_player &&
11653       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11654   {
11655     int sx, sy;
11656
11657     if (game.centered_player_nr_next == -1)
11658     {
11659       setScreenCenteredToAllPlayers(&sx, &sy);
11660     }
11661     else
11662     {
11663       sx = stored_player[game.centered_player_nr_next].jx;
11664       sy = stored_player[game.centered_player_nr_next].jy;
11665     }
11666
11667     game.centered_player_nr = game.centered_player_nr_next;
11668     game.set_centered_player = FALSE;
11669
11670     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11671     DrawGameDoorValues();
11672   }
11673
11674   for (i = 0; i < MAX_PLAYERS; i++)
11675   {
11676     int actual_player_action = stored_player[i].effective_action;
11677
11678 #if 1
11679     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11680        - rnd_equinox_tetrachloride 048
11681        - rnd_equinox_tetrachloride_ii 096
11682        - rnd_emanuel_schmieg 002
11683        - doctor_sloan_ww 001, 020
11684     */
11685     if (stored_player[i].MovPos == 0)
11686       CheckGravityMovement(&stored_player[i]);
11687 #endif
11688
11689     /* overwrite programmed action with tape action */
11690     if (stored_player[i].programmed_action)
11691       actual_player_action = stored_player[i].programmed_action;
11692
11693     PlayerActions(&stored_player[i], actual_player_action);
11694
11695     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11696   }
11697
11698   ScrollScreen(NULL, SCROLL_GO_ON);
11699
11700   /* for backwards compatibility, the following code emulates a fixed bug that
11701      occured when pushing elements (causing elements that just made their last
11702      pushing step to already (if possible) make their first falling step in the
11703      same game frame, which is bad); this code is also needed to use the famous
11704      "spring push bug" which is used in older levels and might be wanted to be
11705      used also in newer levels, but in this case the buggy pushing code is only
11706      affecting the "spring" element and no other elements */
11707
11708   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11709   {
11710     for (i = 0; i < MAX_PLAYERS; i++)
11711     {
11712       struct PlayerInfo *player = &stored_player[i];
11713       int x = player->jx;
11714       int y = player->jy;
11715
11716       if (player->active && player->is_pushing && player->is_moving &&
11717           IS_MOVING(x, y) &&
11718           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11719            Feld[x][y] == EL_SPRING))
11720       {
11721         ContinueMoving(x, y);
11722
11723         /* continue moving after pushing (this is actually a bug) */
11724         if (!IS_MOVING(x, y))
11725           Stop[x][y] = FALSE;
11726       }
11727     }
11728   }
11729
11730   SCAN_PLAYFIELD(x, y)
11731   {
11732     ChangeCount[x][y] = 0;
11733     ChangeEvent[x][y] = -1;
11734
11735     /* this must be handled before main playfield loop */
11736     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11737     {
11738       MovDelay[x][y]--;
11739       if (MovDelay[x][y] <= 0)
11740         RemoveField(x, y);
11741     }
11742
11743     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11744     {
11745       MovDelay[x][y]--;
11746       if (MovDelay[x][y] <= 0)
11747       {
11748         RemoveField(x, y);
11749         TEST_DrawLevelField(x, y);
11750
11751         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11752       }
11753     }
11754
11755 #if DEBUG
11756     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11757     {
11758       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11759       printf("GameActions(): This should never happen!\n");
11760
11761       ChangePage[x][y] = -1;
11762     }
11763 #endif
11764
11765     Stop[x][y] = FALSE;
11766     if (WasJustMoving[x][y] > 0)
11767       WasJustMoving[x][y]--;
11768     if (WasJustFalling[x][y] > 0)
11769       WasJustFalling[x][y]--;
11770     if (CheckCollision[x][y] > 0)
11771       CheckCollision[x][y]--;
11772     if (CheckImpact[x][y] > 0)
11773       CheckImpact[x][y]--;
11774
11775     GfxFrame[x][y]++;
11776
11777     /* reset finished pushing action (not done in ContinueMoving() to allow
11778        continuous pushing animation for elements with zero push delay) */
11779     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11780     {
11781       ResetGfxAnimation(x, y);
11782       TEST_DrawLevelField(x, y);
11783     }
11784
11785 #if DEBUG
11786     if (IS_BLOCKED(x, y))
11787     {
11788       int oldx, oldy;
11789
11790       Blocked2Moving(x, y, &oldx, &oldy);
11791       if (!IS_MOVING(oldx, oldy))
11792       {
11793         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11794         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11795         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11796         printf("GameActions(): This should never happen!\n");
11797       }
11798     }
11799 #endif
11800   }
11801
11802   SCAN_PLAYFIELD(x, y)
11803   {
11804     element = Feld[x][y];
11805     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11806     last_gfx_frame = GfxFrame[x][y];
11807
11808     ResetGfxFrame(x, y);
11809
11810     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11811       DrawLevelGraphicAnimation(x, y, graphic);
11812
11813     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11814         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11815       ResetRandomAnimationValue(x, y);
11816
11817     SetRandomAnimationValue(x, y);
11818
11819     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11820
11821     if (IS_INACTIVE(element))
11822     {
11823       if (IS_ANIMATED(graphic))
11824         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11825
11826       continue;
11827     }
11828
11829     /* this may take place after moving, so 'element' may have changed */
11830     if (IS_CHANGING(x, y) &&
11831         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11832     {
11833       int page = element_info[element].event_page_nr[CE_DELAY];
11834
11835       HandleElementChange(x, y, page);
11836
11837       element = Feld[x][y];
11838       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11839     }
11840
11841     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11842     {
11843       StartMoving(x, y);
11844
11845       element = Feld[x][y];
11846       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11847
11848       if (IS_ANIMATED(graphic) &&
11849           !IS_MOVING(x, y) &&
11850           !Stop[x][y])
11851         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11852
11853       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11854         TEST_DrawTwinkleOnField(x, y);
11855     }
11856     else if (element == EL_ACID)
11857     {
11858       if (!Stop[x][y])
11859         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11860     }
11861     else if ((element == EL_EXIT_OPEN ||
11862               element == EL_EM_EXIT_OPEN ||
11863               element == EL_SP_EXIT_OPEN ||
11864               element == EL_STEEL_EXIT_OPEN ||
11865               element == EL_EM_STEEL_EXIT_OPEN ||
11866               element == EL_SP_TERMINAL ||
11867               element == EL_SP_TERMINAL_ACTIVE ||
11868               element == EL_EXTRA_TIME ||
11869               element == EL_SHIELD_NORMAL ||
11870               element == EL_SHIELD_DEADLY) &&
11871              IS_ANIMATED(graphic))
11872       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11873     else if (IS_MOVING(x, y))
11874       ContinueMoving(x, y);
11875     else if (IS_ACTIVE_BOMB(element))
11876       CheckDynamite(x, y);
11877     else if (element == EL_AMOEBA_GROWING)
11878       AmoebeWaechst(x, y);
11879     else if (element == EL_AMOEBA_SHRINKING)
11880       AmoebaDisappearing(x, y);
11881
11882 #if !USE_NEW_AMOEBA_CODE
11883     else if (IS_AMOEBALIVE(element))
11884       AmoebeAbleger(x, y);
11885 #endif
11886
11887     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11888       Life(x, y);
11889     else if (element == EL_EXIT_CLOSED)
11890       CheckExit(x, y);
11891     else if (element == EL_EM_EXIT_CLOSED)
11892       CheckExitEM(x, y);
11893     else if (element == EL_STEEL_EXIT_CLOSED)
11894       CheckExitSteel(x, y);
11895     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11896       CheckExitSteelEM(x, y);
11897     else if (element == EL_SP_EXIT_CLOSED)
11898       CheckExitSP(x, y);
11899     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11900              element == EL_EXPANDABLE_STEELWALL_GROWING)
11901       MauerWaechst(x, y);
11902     else if (element == EL_EXPANDABLE_WALL ||
11903              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11904              element == EL_EXPANDABLE_WALL_VERTICAL ||
11905              element == EL_EXPANDABLE_WALL_ANY ||
11906              element == EL_BD_EXPANDABLE_WALL)
11907       MauerAbleger(x, y);
11908     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11909              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11910              element == EL_EXPANDABLE_STEELWALL_ANY)
11911       MauerAblegerStahl(x, y);
11912     else if (element == EL_FLAMES)
11913       CheckForDragon(x, y);
11914     else if (element == EL_EXPLOSION)
11915       ; /* drawing of correct explosion animation is handled separately */
11916     else if (element == EL_ELEMENT_SNAPPING ||
11917              element == EL_DIAGONAL_SHRINKING ||
11918              element == EL_DIAGONAL_GROWING)
11919     {
11920       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11921
11922       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11923     }
11924     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11925       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11926
11927     if (IS_BELT_ACTIVE(element))
11928       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11929
11930     if (game.magic_wall_active)
11931     {
11932       int jx = local_player->jx, jy = local_player->jy;
11933
11934       /* play the element sound at the position nearest to the player */
11935       if ((element == EL_MAGIC_WALL_FULL ||
11936            element == EL_MAGIC_WALL_ACTIVE ||
11937            element == EL_MAGIC_WALL_EMPTYING ||
11938            element == EL_BD_MAGIC_WALL_FULL ||
11939            element == EL_BD_MAGIC_WALL_ACTIVE ||
11940            element == EL_BD_MAGIC_WALL_EMPTYING ||
11941            element == EL_DC_MAGIC_WALL_FULL ||
11942            element == EL_DC_MAGIC_WALL_ACTIVE ||
11943            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11944           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11945       {
11946         magic_wall_x = x;
11947         magic_wall_y = y;
11948       }
11949     }
11950   }
11951
11952 #if USE_NEW_AMOEBA_CODE
11953   /* new experimental amoeba growth stuff */
11954   if (!(FrameCounter % 8))
11955   {
11956     static unsigned int random = 1684108901;
11957
11958     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11959     {
11960       x = RND(lev_fieldx);
11961       y = RND(lev_fieldy);
11962       element = Feld[x][y];
11963
11964       if (!IS_PLAYER(x,y) &&
11965           (element == EL_EMPTY ||
11966            CAN_GROW_INTO(element) ||
11967            element == EL_QUICKSAND_EMPTY ||
11968            element == EL_QUICKSAND_FAST_EMPTY ||
11969            element == EL_ACID_SPLASH_LEFT ||
11970            element == EL_ACID_SPLASH_RIGHT))
11971       {
11972         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11973             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11974             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11975             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11976           Feld[x][y] = EL_AMOEBA_DROP;
11977       }
11978
11979       random = random * 129 + 1;
11980     }
11981   }
11982 #endif
11983
11984   game.explosions_delayed = FALSE;
11985
11986   SCAN_PLAYFIELD(x, y)
11987   {
11988     element = Feld[x][y];
11989
11990     if (ExplodeField[x][y])
11991       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11992     else if (element == EL_EXPLOSION)
11993       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11994
11995     ExplodeField[x][y] = EX_TYPE_NONE;
11996   }
11997
11998   game.explosions_delayed = TRUE;
11999
12000   if (game.magic_wall_active)
12001   {
12002     if (!(game.magic_wall_time_left % 4))
12003     {
12004       int element = Feld[magic_wall_x][magic_wall_y];
12005
12006       if (element == EL_BD_MAGIC_WALL_FULL ||
12007           element == EL_BD_MAGIC_WALL_ACTIVE ||
12008           element == EL_BD_MAGIC_WALL_EMPTYING)
12009         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12010       else if (element == EL_DC_MAGIC_WALL_FULL ||
12011                element == EL_DC_MAGIC_WALL_ACTIVE ||
12012                element == EL_DC_MAGIC_WALL_EMPTYING)
12013         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12014       else
12015         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12016     }
12017
12018     if (game.magic_wall_time_left > 0)
12019     {
12020       game.magic_wall_time_left--;
12021
12022       if (!game.magic_wall_time_left)
12023       {
12024         SCAN_PLAYFIELD(x, y)
12025         {
12026           element = Feld[x][y];
12027
12028           if (element == EL_MAGIC_WALL_ACTIVE ||
12029               element == EL_MAGIC_WALL_FULL)
12030           {
12031             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12032             TEST_DrawLevelField(x, y);
12033           }
12034           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12035                    element == EL_BD_MAGIC_WALL_FULL)
12036           {
12037             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12038             TEST_DrawLevelField(x, y);
12039           }
12040           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12041                    element == EL_DC_MAGIC_WALL_FULL)
12042           {
12043             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12044             TEST_DrawLevelField(x, y);
12045           }
12046         }
12047
12048         game.magic_wall_active = FALSE;
12049       }
12050     }
12051   }
12052
12053   if (game.light_time_left > 0)
12054   {
12055     game.light_time_left--;
12056
12057     if (game.light_time_left == 0)
12058       RedrawAllLightSwitchesAndInvisibleElements();
12059   }
12060
12061   if (game.timegate_time_left > 0)
12062   {
12063     game.timegate_time_left--;
12064
12065     if (game.timegate_time_left == 0)
12066       CloseAllOpenTimegates();
12067   }
12068
12069   if (game.lenses_time_left > 0)
12070   {
12071     game.lenses_time_left--;
12072
12073     if (game.lenses_time_left == 0)
12074       RedrawAllInvisibleElementsForLenses();
12075   }
12076
12077   if (game.magnify_time_left > 0)
12078   {
12079     game.magnify_time_left--;
12080
12081     if (game.magnify_time_left == 0)
12082       RedrawAllInvisibleElementsForMagnifier();
12083   }
12084
12085   for (i = 0; i < MAX_PLAYERS; i++)
12086   {
12087     struct PlayerInfo *player = &stored_player[i];
12088
12089     if (SHIELD_ON(player))
12090     {
12091       if (player->shield_deadly_time_left)
12092         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12093       else if (player->shield_normal_time_left)
12094         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12095     }
12096   }
12097
12098 #if USE_DELAYED_GFX_REDRAW
12099   SCAN_PLAYFIELD(x, y)
12100   {
12101     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12102     {
12103       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12104          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12105
12106       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12107         DrawLevelField(x, y);
12108
12109       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12110         DrawLevelFieldCrumbled(x, y);
12111
12112       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12113         DrawLevelFieldCrumbledNeighbours(x, y);
12114
12115       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12116         DrawTwinkleOnField(x, y);
12117     }
12118
12119     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12120   }
12121 #endif
12122
12123   DrawAllPlayers();
12124   PlayAllPlayersSound();
12125
12126   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12127   {
12128     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12129
12130     local_player->show_envelope = 0;
12131   }
12132
12133   /* use random number generator in every frame to make it less predictable */
12134   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12135     RND(1);
12136 }
12137
12138 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12139 {
12140   int min_x = x, min_y = y, max_x = x, max_y = y;
12141   int i;
12142
12143   for (i = 0; i < MAX_PLAYERS; i++)
12144   {
12145     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12146
12147     if (!stored_player[i].active || &stored_player[i] == player)
12148       continue;
12149
12150     min_x = MIN(min_x, jx);
12151     min_y = MIN(min_y, jy);
12152     max_x = MAX(max_x, jx);
12153     max_y = MAX(max_y, jy);
12154   }
12155
12156   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12157 }
12158
12159 static boolean AllPlayersInVisibleScreen()
12160 {
12161   int i;
12162
12163   for (i = 0; i < MAX_PLAYERS; i++)
12164   {
12165     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12166
12167     if (!stored_player[i].active)
12168       continue;
12169
12170     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12171       return FALSE;
12172   }
12173
12174   return TRUE;
12175 }
12176
12177 void ScrollLevel(int dx, int dy)
12178 {
12179   int scroll_offset = 2 * TILEX_VAR;
12180   int x, y;
12181
12182   BlitBitmap(drawto_field, drawto_field,
12183              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12184              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12185              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12186              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12187              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12188              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12189
12190   if (dx != 0)
12191   {
12192     x = (dx == 1 ? BX1 : BX2);
12193     for (y = BY1; y <= BY2; y++)
12194       DrawScreenField(x, y);
12195   }
12196
12197   if (dy != 0)
12198   {
12199     y = (dy == 1 ? BY1 : BY2);
12200     for (x = BX1; x <= BX2; x++)
12201       DrawScreenField(x, y);
12202   }
12203
12204   redraw_mask |= REDRAW_FIELD;
12205 }
12206
12207 static boolean canFallDown(struct PlayerInfo *player)
12208 {
12209   int jx = player->jx, jy = player->jy;
12210
12211   return (IN_LEV_FIELD(jx, jy + 1) &&
12212           (IS_FREE(jx, jy + 1) ||
12213            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12214           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12215           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12216 }
12217
12218 static boolean canPassField(int x, int y, int move_dir)
12219 {
12220   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12221   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12222   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12223   int nextx = x + dx;
12224   int nexty = y + dy;
12225   int element = Feld[x][y];
12226
12227   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12228           !CAN_MOVE(element) &&
12229           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12230           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12231           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12232 }
12233
12234 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12235 {
12236   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12237   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12238   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12239   int newx = x + dx;
12240   int newy = y + dy;
12241
12242   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12243           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12244           (IS_DIGGABLE(Feld[newx][newy]) ||
12245            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12246            canPassField(newx, newy, move_dir)));
12247 }
12248
12249 static void CheckGravityMovement(struct PlayerInfo *player)
12250 {
12251   if (player->gravity && !player->programmed_action)
12252   {
12253     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12254     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12255     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12256     int jx = player->jx, jy = player->jy;
12257     boolean player_is_moving_to_valid_field =
12258       (!player_is_snapping &&
12259        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12260         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12261     boolean player_can_fall_down = canFallDown(player);
12262
12263     if (player_can_fall_down &&
12264         !player_is_moving_to_valid_field)
12265       player->programmed_action = MV_DOWN;
12266   }
12267 }
12268
12269 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12270 {
12271   return CheckGravityMovement(player);
12272
12273   if (player->gravity && !player->programmed_action)
12274   {
12275     int jx = player->jx, jy = player->jy;
12276     boolean field_under_player_is_free =
12277       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12278     boolean player_is_standing_on_valid_field =
12279       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12280        (IS_WALKABLE(Feld[jx][jy]) &&
12281         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12282
12283     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12284       player->programmed_action = MV_DOWN;
12285   }
12286 }
12287
12288 /*
12289   MovePlayerOneStep()
12290   -----------------------------------------------------------------------------
12291   dx, dy:               direction (non-diagonal) to try to move the player to
12292   real_dx, real_dy:     direction as read from input device (can be diagonal)
12293 */
12294
12295 boolean MovePlayerOneStep(struct PlayerInfo *player,
12296                           int dx, int dy, int real_dx, int real_dy)
12297 {
12298   int jx = player->jx, jy = player->jy;
12299   int new_jx = jx + dx, new_jy = jy + dy;
12300   int can_move;
12301   boolean player_can_move = !player->cannot_move;
12302
12303   if (!player->active || (!dx && !dy))
12304     return MP_NO_ACTION;
12305
12306   player->MovDir = (dx < 0 ? MV_LEFT :
12307                     dx > 0 ? MV_RIGHT :
12308                     dy < 0 ? MV_UP :
12309                     dy > 0 ? MV_DOWN :  MV_NONE);
12310
12311   if (!IN_LEV_FIELD(new_jx, new_jy))
12312     return MP_NO_ACTION;
12313
12314   if (!player_can_move)
12315   {
12316     if (player->MovPos == 0)
12317     {
12318       player->is_moving = FALSE;
12319       player->is_digging = FALSE;
12320       player->is_collecting = FALSE;
12321       player->is_snapping = FALSE;
12322       player->is_pushing = FALSE;
12323     }
12324   }
12325
12326   if (!network.enabled && game.centered_player_nr == -1 &&
12327       !AllPlayersInSight(player, new_jx, new_jy))
12328     return MP_NO_ACTION;
12329
12330   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12331   if (can_move != MP_MOVING)
12332     return can_move;
12333
12334   /* check if DigField() has caused relocation of the player */
12335   if (player->jx != jx || player->jy != jy)
12336     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12337
12338   StorePlayer[jx][jy] = 0;
12339   player->last_jx = jx;
12340   player->last_jy = jy;
12341   player->jx = new_jx;
12342   player->jy = new_jy;
12343   StorePlayer[new_jx][new_jy] = player->element_nr;
12344
12345   if (player->move_delay_value_next != -1)
12346   {
12347     player->move_delay_value = player->move_delay_value_next;
12348     player->move_delay_value_next = -1;
12349   }
12350
12351   player->MovPos =
12352     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12353
12354   player->step_counter++;
12355
12356   PlayerVisit[jx][jy] = FrameCounter;
12357
12358   player->is_moving = TRUE;
12359
12360 #if 1
12361   /* should better be called in MovePlayer(), but this breaks some tapes */
12362   ScrollPlayer(player, SCROLL_INIT);
12363 #endif
12364
12365   return MP_MOVING;
12366 }
12367
12368 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12369 {
12370   int jx = player->jx, jy = player->jy;
12371   int old_jx = jx, old_jy = jy;
12372   int moved = MP_NO_ACTION;
12373
12374   if (!player->active)
12375     return FALSE;
12376
12377   if (!dx && !dy)
12378   {
12379     if (player->MovPos == 0)
12380     {
12381       player->is_moving = FALSE;
12382       player->is_digging = FALSE;
12383       player->is_collecting = FALSE;
12384       player->is_snapping = FALSE;
12385       player->is_pushing = FALSE;
12386     }
12387
12388     return FALSE;
12389   }
12390
12391   if (player->move_delay > 0)
12392     return FALSE;
12393
12394   player->move_delay = -1;              /* set to "uninitialized" value */
12395
12396   /* store if player is automatically moved to next field */
12397   player->is_auto_moving = (player->programmed_action != MV_NONE);
12398
12399   /* remove the last programmed player action */
12400   player->programmed_action = 0;
12401
12402   if (player->MovPos)
12403   {
12404     /* should only happen if pre-1.2 tape recordings are played */
12405     /* this is only for backward compatibility */
12406
12407     int original_move_delay_value = player->move_delay_value;
12408
12409 #if DEBUG
12410     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12411            tape.counter);
12412 #endif
12413
12414     /* scroll remaining steps with finest movement resolution */
12415     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12416
12417     while (player->MovPos)
12418     {
12419       ScrollPlayer(player, SCROLL_GO_ON);
12420       ScrollScreen(NULL, SCROLL_GO_ON);
12421
12422       AdvanceFrameAndPlayerCounters(player->index_nr);
12423
12424       DrawAllPlayers();
12425       BackToFront_WithFrameDelay(0);
12426     }
12427
12428     player->move_delay_value = original_move_delay_value;
12429   }
12430
12431   player->is_active = FALSE;
12432
12433   if (player->last_move_dir & MV_HORIZONTAL)
12434   {
12435     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12436       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12437   }
12438   else
12439   {
12440     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12441       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12442   }
12443
12444   if (!moved && !player->is_active)
12445   {
12446     player->is_moving = FALSE;
12447     player->is_digging = FALSE;
12448     player->is_collecting = FALSE;
12449     player->is_snapping = FALSE;
12450     player->is_pushing = FALSE;
12451   }
12452
12453   jx = player->jx;
12454   jy = player->jy;
12455
12456   if (moved & MP_MOVING && !ScreenMovPos &&
12457       (player->index_nr == game.centered_player_nr ||
12458        game.centered_player_nr == -1))
12459   {
12460     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12461     int offset = game.scroll_delay_value;
12462
12463     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12464     {
12465       /* actual player has left the screen -- scroll in that direction */
12466       if (jx != old_jx)         /* player has moved horizontally */
12467         scroll_x += (jx - old_jx);
12468       else                      /* player has moved vertically */
12469         scroll_y += (jy - old_jy);
12470     }
12471     else
12472     {
12473       if (jx != old_jx)         /* player has moved horizontally */
12474       {
12475         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12476             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12477           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12478
12479         /* don't scroll over playfield boundaries */
12480         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12481           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12482
12483         /* don't scroll more than one field at a time */
12484         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12485
12486         /* don't scroll against the player's moving direction */
12487         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12488             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12489           scroll_x = old_scroll_x;
12490       }
12491       else                      /* player has moved vertically */
12492       {
12493         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12494             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12495           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12496
12497         /* don't scroll over playfield boundaries */
12498         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12499           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12500
12501         /* don't scroll more than one field at a time */
12502         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12503
12504         /* don't scroll against the player's moving direction */
12505         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12506             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12507           scroll_y = old_scroll_y;
12508       }
12509     }
12510
12511     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12512     {
12513       if (!network.enabled && game.centered_player_nr == -1 &&
12514           !AllPlayersInVisibleScreen())
12515       {
12516         scroll_x = old_scroll_x;
12517         scroll_y = old_scroll_y;
12518       }
12519       else
12520       {
12521         ScrollScreen(player, SCROLL_INIT);
12522         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12523       }
12524     }
12525   }
12526
12527   player->StepFrame = 0;
12528
12529   if (moved & MP_MOVING)
12530   {
12531     if (old_jx != jx && old_jy == jy)
12532       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12533     else if (old_jx == jx && old_jy != jy)
12534       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12535
12536     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12537
12538     player->last_move_dir = player->MovDir;
12539     player->is_moving = TRUE;
12540     player->is_snapping = FALSE;
12541     player->is_switching = FALSE;
12542     player->is_dropping = FALSE;
12543     player->is_dropping_pressed = FALSE;
12544     player->drop_pressed_delay = 0;
12545
12546 #if 0
12547     /* should better be called here than above, but this breaks some tapes */
12548     ScrollPlayer(player, SCROLL_INIT);
12549 #endif
12550   }
12551   else
12552   {
12553     CheckGravityMovementWhenNotMoving(player);
12554
12555     player->is_moving = FALSE;
12556
12557     /* at this point, the player is allowed to move, but cannot move right now
12558        (e.g. because of something blocking the way) -- ensure that the player
12559        is also allowed to move in the next frame (in old versions before 3.1.1,
12560        the player was forced to wait again for eight frames before next try) */
12561
12562     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12563       player->move_delay = 0;   /* allow direct movement in the next frame */
12564   }
12565
12566   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12567     player->move_delay = player->move_delay_value;
12568
12569   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12570   {
12571     TestIfPlayerTouchesBadThing(jx, jy);
12572     TestIfPlayerTouchesCustomElement(jx, jy);
12573   }
12574
12575   if (!player->active)
12576     RemovePlayer(player);
12577
12578   return moved;
12579 }
12580
12581 void ScrollPlayer(struct PlayerInfo *player, int mode)
12582 {
12583   int jx = player->jx, jy = player->jy;
12584   int last_jx = player->last_jx, last_jy = player->last_jy;
12585   int move_stepsize = TILEX / player->move_delay_value;
12586
12587   if (!player->active)
12588     return;
12589
12590   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12591     return;
12592
12593   if (mode == SCROLL_INIT)
12594   {
12595     player->actual_frame_counter = FrameCounter;
12596     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12597
12598     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12599         Feld[last_jx][last_jy] == EL_EMPTY)
12600     {
12601       int last_field_block_delay = 0;   /* start with no blocking at all */
12602       int block_delay_adjustment = player->block_delay_adjustment;
12603
12604       /* if player blocks last field, add delay for exactly one move */
12605       if (player->block_last_field)
12606       {
12607         last_field_block_delay += player->move_delay_value;
12608
12609         /* when blocking enabled, prevent moving up despite gravity */
12610         if (player->gravity && player->MovDir == MV_UP)
12611           block_delay_adjustment = -1;
12612       }
12613
12614       /* add block delay adjustment (also possible when not blocking) */
12615       last_field_block_delay += block_delay_adjustment;
12616
12617       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12618       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12619     }
12620
12621     if (player->MovPos != 0)    /* player has not yet reached destination */
12622       return;
12623   }
12624   else if (!FrameReached(&player->actual_frame_counter, 1))
12625     return;
12626
12627   if (player->MovPos != 0)
12628   {
12629     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12630     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12631
12632     /* before DrawPlayer() to draw correct player graphic for this case */
12633     if (player->MovPos == 0)
12634       CheckGravityMovement(player);
12635   }
12636
12637   if (player->MovPos == 0)      /* player reached destination field */
12638   {
12639     if (player->move_delay_reset_counter > 0)
12640     {
12641       player->move_delay_reset_counter--;
12642
12643       if (player->move_delay_reset_counter == 0)
12644       {
12645         /* continue with normal speed after quickly moving through gate */
12646         HALVE_PLAYER_SPEED(player);
12647
12648         /* be able to make the next move without delay */
12649         player->move_delay = 0;
12650       }
12651     }
12652
12653     player->last_jx = jx;
12654     player->last_jy = jy;
12655
12656     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12657         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12658         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12659         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12660         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12661         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12662         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12663         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12664     {
12665       ExitPlayer(player);
12666
12667       if ((local_player->friends_still_needed == 0 ||
12668            IS_SP_ELEMENT(Feld[jx][jy])) &&
12669           AllPlayersGone)
12670         PlayerWins(local_player);
12671     }
12672
12673     /* this breaks one level: "machine", level 000 */
12674     {
12675       int move_direction = player->MovDir;
12676       int enter_side = MV_DIR_OPPOSITE(move_direction);
12677       int leave_side = move_direction;
12678       int old_jx = last_jx;
12679       int old_jy = last_jy;
12680       int old_element = Feld[old_jx][old_jy];
12681       int new_element = Feld[jx][jy];
12682
12683       if (IS_CUSTOM_ELEMENT(old_element))
12684         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12685                                    CE_LEFT_BY_PLAYER,
12686                                    player->index_bit, leave_side);
12687
12688       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12689                                           CE_PLAYER_LEAVES_X,
12690                                           player->index_bit, leave_side);
12691
12692       if (IS_CUSTOM_ELEMENT(new_element))
12693         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12694                                    player->index_bit, enter_side);
12695
12696       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12697                                           CE_PLAYER_ENTERS_X,
12698                                           player->index_bit, enter_side);
12699
12700       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12701                                         CE_MOVE_OF_X, move_direction);
12702     }
12703
12704     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12705     {
12706       TestIfPlayerTouchesBadThing(jx, jy);
12707       TestIfPlayerTouchesCustomElement(jx, jy);
12708
12709       /* needed because pushed element has not yet reached its destination,
12710          so it would trigger a change event at its previous field location */
12711       if (!player->is_pushing)
12712         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12713
12714       if (!player->active)
12715         RemovePlayer(player);
12716     }
12717
12718     if (!local_player->LevelSolved && level.use_step_counter)
12719     {
12720       int i;
12721
12722       TimePlayed++;
12723
12724       if (TimeLeft > 0)
12725       {
12726         TimeLeft--;
12727
12728         if (TimeLeft <= 10 && setup.time_limit)
12729           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12730
12731         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12732
12733         DisplayGameControlValues();
12734
12735         if (!TimeLeft && setup.time_limit)
12736           for (i = 0; i < MAX_PLAYERS; i++)
12737             KillPlayer(&stored_player[i]);
12738       }
12739       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12740       {
12741         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12742
12743         DisplayGameControlValues();
12744       }
12745     }
12746
12747     if (tape.single_step && tape.recording && !tape.pausing &&
12748         !player->programmed_action)
12749       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12750
12751     if (!player->programmed_action)
12752       CheckSaveEngineSnapshot(player);
12753   }
12754 }
12755
12756 void ScrollScreen(struct PlayerInfo *player, int mode)
12757 {
12758   static unsigned int screen_frame_counter = 0;
12759
12760   if (mode == SCROLL_INIT)
12761   {
12762     /* set scrolling step size according to actual player's moving speed */
12763     ScrollStepSize = TILEX / player->move_delay_value;
12764
12765     screen_frame_counter = FrameCounter;
12766     ScreenMovDir = player->MovDir;
12767     ScreenMovPos = player->MovPos;
12768     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12769     return;
12770   }
12771   else if (!FrameReached(&screen_frame_counter, 1))
12772     return;
12773
12774   if (ScreenMovPos)
12775   {
12776     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12777     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12778     redraw_mask |= REDRAW_FIELD;
12779   }
12780   else
12781     ScreenMovDir = MV_NONE;
12782 }
12783
12784 void TestIfPlayerTouchesCustomElement(int x, int y)
12785 {
12786   static int xy[4][2] =
12787   {
12788     { 0, -1 },
12789     { -1, 0 },
12790     { +1, 0 },
12791     { 0, +1 }
12792   };
12793   static int trigger_sides[4][2] =
12794   {
12795     /* center side       border side */
12796     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12797     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12798     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12799     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12800   };
12801   static int touch_dir[4] =
12802   {
12803     MV_LEFT | MV_RIGHT,
12804     MV_UP   | MV_DOWN,
12805     MV_UP   | MV_DOWN,
12806     MV_LEFT | MV_RIGHT
12807   };
12808   int center_element = Feld[x][y];      /* should always be non-moving! */
12809   int i;
12810
12811   for (i = 0; i < NUM_DIRECTIONS; i++)
12812   {
12813     int xx = x + xy[i][0];
12814     int yy = y + xy[i][1];
12815     int center_side = trigger_sides[i][0];
12816     int border_side = trigger_sides[i][1];
12817     int border_element;
12818
12819     if (!IN_LEV_FIELD(xx, yy))
12820       continue;
12821
12822     if (IS_PLAYER(x, y))                /* player found at center element */
12823     {
12824       struct PlayerInfo *player = PLAYERINFO(x, y);
12825
12826       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12827         border_element = Feld[xx][yy];          /* may be moving! */
12828       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12829         border_element = Feld[xx][yy];
12830       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12831         border_element = MovingOrBlocked2Element(xx, yy);
12832       else
12833         continue;               /* center and border element do not touch */
12834
12835       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12836                                  player->index_bit, border_side);
12837       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12838                                           CE_PLAYER_TOUCHES_X,
12839                                           player->index_bit, border_side);
12840
12841       {
12842         /* use player element that is initially defined in the level playfield,
12843            not the player element that corresponds to the runtime player number
12844            (example: a level that contains EL_PLAYER_3 as the only player would
12845            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12846         int player_element = PLAYERINFO(x, y)->initial_element;
12847
12848         CheckElementChangeBySide(xx, yy, border_element, player_element,
12849                                  CE_TOUCHING_X, border_side);
12850       }
12851     }
12852     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12853     {
12854       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12855
12856       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12857       {
12858         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12859           continue;             /* center and border element do not touch */
12860       }
12861
12862       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12863                                  player->index_bit, center_side);
12864       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12865                                           CE_PLAYER_TOUCHES_X,
12866                                           player->index_bit, center_side);
12867
12868       {
12869         /* use player element that is initially defined in the level playfield,
12870            not the player element that corresponds to the runtime player number
12871            (example: a level that contains EL_PLAYER_3 as the only player would
12872            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12873         int player_element = PLAYERINFO(xx, yy)->initial_element;
12874
12875         CheckElementChangeBySide(x, y, center_element, player_element,
12876                                  CE_TOUCHING_X, center_side);
12877       }
12878
12879       break;
12880     }
12881   }
12882 }
12883
12884 void TestIfElementTouchesCustomElement(int x, int y)
12885 {
12886   static int xy[4][2] =
12887   {
12888     { 0, -1 },
12889     { -1, 0 },
12890     { +1, 0 },
12891     { 0, +1 }
12892   };
12893   static int trigger_sides[4][2] =
12894   {
12895     /* center side      border side */
12896     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12897     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12898     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12899     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12900   };
12901   static int touch_dir[4] =
12902   {
12903     MV_LEFT | MV_RIGHT,
12904     MV_UP   | MV_DOWN,
12905     MV_UP   | MV_DOWN,
12906     MV_LEFT | MV_RIGHT
12907   };
12908   boolean change_center_element = FALSE;
12909   int center_element = Feld[x][y];      /* should always be non-moving! */
12910   int border_element_old[NUM_DIRECTIONS];
12911   int i;
12912
12913   for (i = 0; i < NUM_DIRECTIONS; i++)
12914   {
12915     int xx = x + xy[i][0];
12916     int yy = y + xy[i][1];
12917     int border_element;
12918
12919     border_element_old[i] = -1;
12920
12921     if (!IN_LEV_FIELD(xx, yy))
12922       continue;
12923
12924     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12925       border_element = Feld[xx][yy];    /* may be moving! */
12926     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12927       border_element = Feld[xx][yy];
12928     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12929       border_element = MovingOrBlocked2Element(xx, yy);
12930     else
12931       continue;                 /* center and border element do not touch */
12932
12933     border_element_old[i] = border_element;
12934   }
12935
12936   for (i = 0; i < NUM_DIRECTIONS; i++)
12937   {
12938     int xx = x + xy[i][0];
12939     int yy = y + xy[i][1];
12940     int center_side = trigger_sides[i][0];
12941     int border_element = border_element_old[i];
12942
12943     if (border_element == -1)
12944       continue;
12945
12946     /* check for change of border element */
12947     CheckElementChangeBySide(xx, yy, border_element, center_element,
12948                              CE_TOUCHING_X, center_side);
12949
12950     /* (center element cannot be player, so we dont have to check this here) */
12951   }
12952
12953   for (i = 0; i < NUM_DIRECTIONS; i++)
12954   {
12955     int xx = x + xy[i][0];
12956     int yy = y + xy[i][1];
12957     int border_side = trigger_sides[i][1];
12958     int border_element = border_element_old[i];
12959
12960     if (border_element == -1)
12961       continue;
12962
12963     /* check for change of center element (but change it only once) */
12964     if (!change_center_element)
12965       change_center_element =
12966         CheckElementChangeBySide(x, y, center_element, border_element,
12967                                  CE_TOUCHING_X, border_side);
12968
12969     if (IS_PLAYER(xx, yy))
12970     {
12971       /* use player element that is initially defined in the level playfield,
12972          not the player element that corresponds to the runtime player number
12973          (example: a level that contains EL_PLAYER_3 as the only player would
12974          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12975       int player_element = PLAYERINFO(xx, yy)->initial_element;
12976
12977       CheckElementChangeBySide(x, y, center_element, player_element,
12978                                CE_TOUCHING_X, border_side);
12979     }
12980   }
12981 }
12982
12983 void TestIfElementHitsCustomElement(int x, int y, int direction)
12984 {
12985   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12986   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12987   int hitx = x + dx, hity = y + dy;
12988   int hitting_element = Feld[x][y];
12989   int touched_element;
12990
12991   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12992     return;
12993
12994   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12995                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12996
12997   if (IN_LEV_FIELD(hitx, hity))
12998   {
12999     int opposite_direction = MV_DIR_OPPOSITE(direction);
13000     int hitting_side = direction;
13001     int touched_side = opposite_direction;
13002     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13003                           MovDir[hitx][hity] != direction ||
13004                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13005
13006     object_hit = TRUE;
13007
13008     if (object_hit)
13009     {
13010       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13011                                CE_HITTING_X, touched_side);
13012
13013       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13014                                CE_HIT_BY_X, hitting_side);
13015
13016       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13017                                CE_HIT_BY_SOMETHING, opposite_direction);
13018
13019       if (IS_PLAYER(hitx, hity))
13020       {
13021         /* use player element that is initially defined in the level playfield,
13022            not the player element that corresponds to the runtime player number
13023            (example: a level that contains EL_PLAYER_3 as the only player would
13024            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13025         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13026
13027         CheckElementChangeBySide(x, y, hitting_element, player_element,
13028                                  CE_HITTING_X, touched_side);
13029       }
13030     }
13031   }
13032
13033   /* "hitting something" is also true when hitting the playfield border */
13034   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13035                            CE_HITTING_SOMETHING, direction);
13036 }
13037
13038 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13039 {
13040   int i, kill_x = -1, kill_y = -1;
13041
13042   int bad_element = -1;
13043   static int test_xy[4][2] =
13044   {
13045     { 0, -1 },
13046     { -1, 0 },
13047     { +1, 0 },
13048     { 0, +1 }
13049   };
13050   static int test_dir[4] =
13051   {
13052     MV_UP,
13053     MV_LEFT,
13054     MV_RIGHT,
13055     MV_DOWN
13056   };
13057
13058   for (i = 0; i < NUM_DIRECTIONS; i++)
13059   {
13060     int test_x, test_y, test_move_dir, test_element;
13061
13062     test_x = good_x + test_xy[i][0];
13063     test_y = good_y + test_xy[i][1];
13064
13065     if (!IN_LEV_FIELD(test_x, test_y))
13066       continue;
13067
13068     test_move_dir =
13069       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13070
13071     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13072
13073     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13074        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13075     */
13076     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13077         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13078     {
13079       kill_x = test_x;
13080       kill_y = test_y;
13081       bad_element = test_element;
13082
13083       break;
13084     }
13085   }
13086
13087   if (kill_x != -1 || kill_y != -1)
13088   {
13089     if (IS_PLAYER(good_x, good_y))
13090     {
13091       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13092
13093       if (player->shield_deadly_time_left > 0 &&
13094           !IS_INDESTRUCTIBLE(bad_element))
13095         Bang(kill_x, kill_y);
13096       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13097         KillPlayer(player);
13098     }
13099     else
13100       Bang(good_x, good_y);
13101   }
13102 }
13103
13104 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13105 {
13106   int i, kill_x = -1, kill_y = -1;
13107   int bad_element = Feld[bad_x][bad_y];
13108   static int test_xy[4][2] =
13109   {
13110     { 0, -1 },
13111     { -1, 0 },
13112     { +1, 0 },
13113     { 0, +1 }
13114   };
13115   static int touch_dir[4] =
13116   {
13117     MV_LEFT | MV_RIGHT,
13118     MV_UP   | MV_DOWN,
13119     MV_UP   | MV_DOWN,
13120     MV_LEFT | MV_RIGHT
13121   };
13122   static int test_dir[4] =
13123   {
13124     MV_UP,
13125     MV_LEFT,
13126     MV_RIGHT,
13127     MV_DOWN
13128   };
13129
13130   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13131     return;
13132
13133   for (i = 0; i < NUM_DIRECTIONS; i++)
13134   {
13135     int test_x, test_y, test_move_dir, test_element;
13136
13137     test_x = bad_x + test_xy[i][0];
13138     test_y = bad_y + test_xy[i][1];
13139
13140     if (!IN_LEV_FIELD(test_x, test_y))
13141       continue;
13142
13143     test_move_dir =
13144       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13145
13146     test_element = Feld[test_x][test_y];
13147
13148     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13149        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13150     */
13151     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13152         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13153     {
13154       /* good thing is player or penguin that does not move away */
13155       if (IS_PLAYER(test_x, test_y))
13156       {
13157         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13158
13159         if (bad_element == EL_ROBOT && player->is_moving)
13160           continue;     /* robot does not kill player if he is moving */
13161
13162         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13163         {
13164           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13165             continue;           /* center and border element do not touch */
13166         }
13167
13168         kill_x = test_x;
13169         kill_y = test_y;
13170
13171         break;
13172       }
13173       else if (test_element == EL_PENGUIN)
13174       {
13175         kill_x = test_x;
13176         kill_y = test_y;
13177
13178         break;
13179       }
13180     }
13181   }
13182
13183   if (kill_x != -1 || kill_y != -1)
13184   {
13185     if (IS_PLAYER(kill_x, kill_y))
13186     {
13187       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13188
13189       if (player->shield_deadly_time_left > 0 &&
13190           !IS_INDESTRUCTIBLE(bad_element))
13191         Bang(bad_x, bad_y);
13192       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13193         KillPlayer(player);
13194     }
13195     else
13196       Bang(kill_x, kill_y);
13197   }
13198 }
13199
13200 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13201 {
13202   int bad_element = Feld[bad_x][bad_y];
13203   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13204   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13205   int test_x = bad_x + dx, test_y = bad_y + dy;
13206   int test_move_dir, test_element;
13207   int kill_x = -1, kill_y = -1;
13208
13209   if (!IN_LEV_FIELD(test_x, test_y))
13210     return;
13211
13212   test_move_dir =
13213     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13214
13215   test_element = Feld[test_x][test_y];
13216
13217   if (test_move_dir != bad_move_dir)
13218   {
13219     /* good thing can be player or penguin that does not move away */
13220     if (IS_PLAYER(test_x, test_y))
13221     {
13222       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13223
13224       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13225          player as being hit when he is moving towards the bad thing, because
13226          the "get hit by" condition would be lost after the player stops) */
13227       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13228         return;         /* player moves away from bad thing */
13229
13230       kill_x = test_x;
13231       kill_y = test_y;
13232     }
13233     else if (test_element == EL_PENGUIN)
13234     {
13235       kill_x = test_x;
13236       kill_y = test_y;
13237     }
13238   }
13239
13240   if (kill_x != -1 || kill_y != -1)
13241   {
13242     if (IS_PLAYER(kill_x, kill_y))
13243     {
13244       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13245
13246       if (player->shield_deadly_time_left > 0 &&
13247           !IS_INDESTRUCTIBLE(bad_element))
13248         Bang(bad_x, bad_y);
13249       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13250         KillPlayer(player);
13251     }
13252     else
13253       Bang(kill_x, kill_y);
13254   }
13255 }
13256
13257 void TestIfPlayerTouchesBadThing(int x, int y)
13258 {
13259   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13260 }
13261
13262 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13263 {
13264   TestIfGoodThingHitsBadThing(x, y, move_dir);
13265 }
13266
13267 void TestIfBadThingTouchesPlayer(int x, int y)
13268 {
13269   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13270 }
13271
13272 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13273 {
13274   TestIfBadThingHitsGoodThing(x, y, move_dir);
13275 }
13276
13277 void TestIfFriendTouchesBadThing(int x, int y)
13278 {
13279   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13280 }
13281
13282 void TestIfBadThingTouchesFriend(int x, int y)
13283 {
13284   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13285 }
13286
13287 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13288 {
13289   int i, kill_x = bad_x, kill_y = bad_y;
13290   static int xy[4][2] =
13291   {
13292     { 0, -1 },
13293     { -1, 0 },
13294     { +1, 0 },
13295     { 0, +1 }
13296   };
13297
13298   for (i = 0; i < NUM_DIRECTIONS; i++)
13299   {
13300     int x, y, element;
13301
13302     x = bad_x + xy[i][0];
13303     y = bad_y + xy[i][1];
13304     if (!IN_LEV_FIELD(x, y))
13305       continue;
13306
13307     element = Feld[x][y];
13308     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13309         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13310     {
13311       kill_x = x;
13312       kill_y = y;
13313       break;
13314     }
13315   }
13316
13317   if (kill_x != bad_x || kill_y != bad_y)
13318     Bang(bad_x, bad_y);
13319 }
13320
13321 void KillPlayer(struct PlayerInfo *player)
13322 {
13323   int jx = player->jx, jy = player->jy;
13324
13325   if (!player->active)
13326     return;
13327
13328 #if 0
13329   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13330          player->killed, player->active, player->reanimated);
13331 #endif
13332
13333   /* the following code was introduced to prevent an infinite loop when calling
13334      -> Bang()
13335      -> CheckTriggeredElementChangeExt()
13336      -> ExecuteCustomElementAction()
13337      -> KillPlayer()
13338      -> (infinitely repeating the above sequence of function calls)
13339      which occurs when killing the player while having a CE with the setting
13340      "kill player X when explosion of <player X>"; the solution using a new
13341      field "player->killed" was chosen for backwards compatibility, although
13342      clever use of the fields "player->active" etc. would probably also work */
13343 #if 1
13344   if (player->killed)
13345     return;
13346 #endif
13347
13348   player->killed = TRUE;
13349
13350   /* remove accessible field at the player's position */
13351   Feld[jx][jy] = EL_EMPTY;
13352
13353   /* deactivate shield (else Bang()/Explode() would not work right) */
13354   player->shield_normal_time_left = 0;
13355   player->shield_deadly_time_left = 0;
13356
13357 #if 0
13358   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13359          player->killed, player->active, player->reanimated);
13360 #endif
13361
13362   Bang(jx, jy);
13363
13364 #if 0
13365   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13366          player->killed, player->active, player->reanimated);
13367 #endif
13368
13369   if (player->reanimated)       /* killed player may have been reanimated */
13370     player->killed = player->reanimated = FALSE;
13371   else
13372     BuryPlayer(player);
13373 }
13374
13375 static void KillPlayerUnlessEnemyProtected(int x, int y)
13376 {
13377   if (!PLAYER_ENEMY_PROTECTED(x, y))
13378     KillPlayer(PLAYERINFO(x, y));
13379 }
13380
13381 static void KillPlayerUnlessExplosionProtected(int x, int y)
13382 {
13383   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13384     KillPlayer(PLAYERINFO(x, y));
13385 }
13386
13387 void BuryPlayer(struct PlayerInfo *player)
13388 {
13389   int jx = player->jx, jy = player->jy;
13390
13391   if (!player->active)
13392     return;
13393
13394   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13395   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13396
13397   player->GameOver = TRUE;
13398   RemovePlayer(player);
13399 }
13400
13401 void RemovePlayer(struct PlayerInfo *player)
13402 {
13403   int jx = player->jx, jy = player->jy;
13404   int i, found = FALSE;
13405
13406   player->present = FALSE;
13407   player->active = FALSE;
13408
13409   if (!ExplodeField[jx][jy])
13410     StorePlayer[jx][jy] = 0;
13411
13412   if (player->is_moving)
13413     TEST_DrawLevelField(player->last_jx, player->last_jy);
13414
13415   for (i = 0; i < MAX_PLAYERS; i++)
13416     if (stored_player[i].active)
13417       found = TRUE;
13418
13419   if (!found)
13420     AllPlayersGone = TRUE;
13421
13422   ExitX = ZX = jx;
13423   ExitY = ZY = jy;
13424 }
13425
13426 void ExitPlayer(struct PlayerInfo *player)
13427 {
13428   DrawPlayer(player);   /* needed here only to cleanup last field */
13429   RemovePlayer(player);
13430
13431   if (local_player->players_still_needed > 0)
13432     local_player->players_still_needed--;
13433 }
13434
13435 static void setFieldForSnapping(int x, int y, int element, int direction)
13436 {
13437   struct ElementInfo *ei = &element_info[element];
13438   int direction_bit = MV_DIR_TO_BIT(direction);
13439   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13440   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13441                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13442
13443   Feld[x][y] = EL_ELEMENT_SNAPPING;
13444   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13445
13446   ResetGfxAnimation(x, y);
13447
13448   GfxElement[x][y] = element;
13449   GfxAction[x][y] = action;
13450   GfxDir[x][y] = direction;
13451   GfxFrame[x][y] = -1;
13452 }
13453
13454 /*
13455   =============================================================================
13456   checkDiagonalPushing()
13457   -----------------------------------------------------------------------------
13458   check if diagonal input device direction results in pushing of object
13459   (by checking if the alternative direction is walkable, diggable, ...)
13460   =============================================================================
13461 */
13462
13463 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13464                                     int x, int y, int real_dx, int real_dy)
13465 {
13466   int jx, jy, dx, dy, xx, yy;
13467
13468   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13469     return TRUE;
13470
13471   /* diagonal direction: check alternative direction */
13472   jx = player->jx;
13473   jy = player->jy;
13474   dx = x - jx;
13475   dy = y - jy;
13476   xx = jx + (dx == 0 ? real_dx : 0);
13477   yy = jy + (dy == 0 ? real_dy : 0);
13478
13479   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13480 }
13481
13482 /*
13483   =============================================================================
13484   DigField()
13485   -----------------------------------------------------------------------------
13486   x, y:                 field next to player (non-diagonal) to try to dig to
13487   real_dx, real_dy:     direction as read from input device (can be diagonal)
13488   =============================================================================
13489 */
13490
13491 static int DigField(struct PlayerInfo *player,
13492                     int oldx, int oldy, int x, int y,
13493                     int real_dx, int real_dy, int mode)
13494 {
13495   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13496   boolean player_was_pushing = player->is_pushing;
13497   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13498   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13499   int jx = oldx, jy = oldy;
13500   int dx = x - jx, dy = y - jy;
13501   int nextx = x + dx, nexty = y + dy;
13502   int move_direction = (dx == -1 ? MV_LEFT  :
13503                         dx == +1 ? MV_RIGHT :
13504                         dy == -1 ? MV_UP    :
13505                         dy == +1 ? MV_DOWN  : MV_NONE);
13506   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13507   int dig_side = MV_DIR_OPPOSITE(move_direction);
13508   int old_element = Feld[jx][jy];
13509   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13510   int collect_count;
13511
13512   if (is_player)                /* function can also be called by EL_PENGUIN */
13513   {
13514     if (player->MovPos == 0)
13515     {
13516       player->is_digging = FALSE;
13517       player->is_collecting = FALSE;
13518     }
13519
13520     if (player->MovPos == 0)    /* last pushing move finished */
13521       player->is_pushing = FALSE;
13522
13523     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13524     {
13525       player->is_switching = FALSE;
13526       player->push_delay = -1;
13527
13528       return MP_NO_ACTION;
13529     }
13530   }
13531
13532   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13533     old_element = Back[jx][jy];
13534
13535   /* in case of element dropped at player position, check background */
13536   else if (Back[jx][jy] != EL_EMPTY &&
13537            game.engine_version >= VERSION_IDENT(2,2,0,0))
13538     old_element = Back[jx][jy];
13539
13540   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13541     return MP_NO_ACTION;        /* field has no opening in this direction */
13542
13543   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13544     return MP_NO_ACTION;        /* field has no opening in this direction */
13545
13546   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13547   {
13548     SplashAcid(x, y);
13549
13550     Feld[jx][jy] = player->artwork_element;
13551     InitMovingField(jx, jy, MV_DOWN);
13552     Store[jx][jy] = EL_ACID;
13553     ContinueMoving(jx, jy);
13554     BuryPlayer(player);
13555
13556     return MP_DONT_RUN_INTO;
13557   }
13558
13559   if (player_can_move && DONT_RUN_INTO(element))
13560   {
13561     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13562
13563     return MP_DONT_RUN_INTO;
13564   }
13565
13566   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13567     return MP_NO_ACTION;
13568
13569   collect_count = element_info[element].collect_count_initial;
13570
13571   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13572     return MP_NO_ACTION;
13573
13574   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13575     player_can_move = player_can_move_or_snap;
13576
13577   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13578       game.engine_version >= VERSION_IDENT(2,2,0,0))
13579   {
13580     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13581                                player->index_bit, dig_side);
13582     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13583                                         player->index_bit, dig_side);
13584
13585     if (element == EL_DC_LANDMINE)
13586       Bang(x, y);
13587
13588     if (Feld[x][y] != element)          /* field changed by snapping */
13589       return MP_ACTION;
13590
13591     return MP_NO_ACTION;
13592   }
13593
13594   if (player->gravity && is_player && !player->is_auto_moving &&
13595       canFallDown(player) && move_direction != MV_DOWN &&
13596       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13597     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13598
13599   if (player_can_move &&
13600       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13601   {
13602     int sound_element = SND_ELEMENT(element);
13603     int sound_action = ACTION_WALKING;
13604
13605     if (IS_RND_GATE(element))
13606     {
13607       if (!player->key[RND_GATE_NR(element)])
13608         return MP_NO_ACTION;
13609     }
13610     else if (IS_RND_GATE_GRAY(element))
13611     {
13612       if (!player->key[RND_GATE_GRAY_NR(element)])
13613         return MP_NO_ACTION;
13614     }
13615     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13616     {
13617       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13618         return MP_NO_ACTION;
13619     }
13620     else if (element == EL_EXIT_OPEN ||
13621              element == EL_EM_EXIT_OPEN ||
13622              element == EL_EM_EXIT_OPENING ||
13623              element == EL_STEEL_EXIT_OPEN ||
13624              element == EL_EM_STEEL_EXIT_OPEN ||
13625              element == EL_EM_STEEL_EXIT_OPENING ||
13626              element == EL_SP_EXIT_OPEN ||
13627              element == EL_SP_EXIT_OPENING)
13628     {
13629       sound_action = ACTION_PASSING;    /* player is passing exit */
13630     }
13631     else if (element == EL_EMPTY)
13632     {
13633       sound_action = ACTION_MOVING;             /* nothing to walk on */
13634     }
13635
13636     /* play sound from background or player, whatever is available */
13637     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13638       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13639     else
13640       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13641   }
13642   else if (player_can_move &&
13643            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13644   {
13645     if (!ACCESS_FROM(element, opposite_direction))
13646       return MP_NO_ACTION;      /* field not accessible from this direction */
13647
13648     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13649       return MP_NO_ACTION;
13650
13651     if (IS_EM_GATE(element))
13652     {
13653       if (!player->key[EM_GATE_NR(element)])
13654         return MP_NO_ACTION;
13655     }
13656     else if (IS_EM_GATE_GRAY(element))
13657     {
13658       if (!player->key[EM_GATE_GRAY_NR(element)])
13659         return MP_NO_ACTION;
13660     }
13661     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13662     {
13663       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13664         return MP_NO_ACTION;
13665     }
13666     else if (IS_EMC_GATE(element))
13667     {
13668       if (!player->key[EMC_GATE_NR(element)])
13669         return MP_NO_ACTION;
13670     }
13671     else if (IS_EMC_GATE_GRAY(element))
13672     {
13673       if (!player->key[EMC_GATE_GRAY_NR(element)])
13674         return MP_NO_ACTION;
13675     }
13676     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13677     {
13678       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13679         return MP_NO_ACTION;
13680     }
13681     else if (element == EL_DC_GATE_WHITE ||
13682              element == EL_DC_GATE_WHITE_GRAY ||
13683              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13684     {
13685       if (player->num_white_keys == 0)
13686         return MP_NO_ACTION;
13687
13688       player->num_white_keys--;
13689     }
13690     else if (IS_SP_PORT(element))
13691     {
13692       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13693           element == EL_SP_GRAVITY_PORT_RIGHT ||
13694           element == EL_SP_GRAVITY_PORT_UP ||
13695           element == EL_SP_GRAVITY_PORT_DOWN)
13696         player->gravity = !player->gravity;
13697       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13698                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13699                element == EL_SP_GRAVITY_ON_PORT_UP ||
13700                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13701         player->gravity = TRUE;
13702       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13703                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13704                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13705                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13706         player->gravity = FALSE;
13707     }
13708
13709     /* automatically move to the next field with double speed */
13710     player->programmed_action = move_direction;
13711
13712     if (player->move_delay_reset_counter == 0)
13713     {
13714       player->move_delay_reset_counter = 2;     /* two double speed steps */
13715
13716       DOUBLE_PLAYER_SPEED(player);
13717     }
13718
13719     PlayLevelSoundAction(x, y, ACTION_PASSING);
13720   }
13721   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13722   {
13723     RemoveField(x, y);
13724
13725     if (mode != DF_SNAP)
13726     {
13727       GfxElement[x][y] = GFX_ELEMENT(element);
13728       player->is_digging = TRUE;
13729     }
13730
13731     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13732
13733     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13734                                         player->index_bit, dig_side);
13735
13736     if (mode == DF_SNAP)
13737     {
13738       if (level.block_snap_field)
13739         setFieldForSnapping(x, y, element, move_direction);
13740       else
13741         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13742
13743       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13744                                           player->index_bit, dig_side);
13745     }
13746   }
13747   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13748   {
13749     RemoveField(x, y);
13750
13751     if (is_player && mode != DF_SNAP)
13752     {
13753       GfxElement[x][y] = element;
13754       player->is_collecting = TRUE;
13755     }
13756
13757     if (element == EL_SPEED_PILL)
13758     {
13759       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13760     }
13761     else if (element == EL_EXTRA_TIME && level.time > 0)
13762     {
13763       TimeLeft += level.extra_time;
13764
13765       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13766
13767       DisplayGameControlValues();
13768     }
13769     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13770     {
13771       player->shield_normal_time_left += level.shield_normal_time;
13772       if (element == EL_SHIELD_DEADLY)
13773         player->shield_deadly_time_left += level.shield_deadly_time;
13774     }
13775     else if (element == EL_DYNAMITE ||
13776              element == EL_EM_DYNAMITE ||
13777              element == EL_SP_DISK_RED)
13778     {
13779       if (player->inventory_size < MAX_INVENTORY_SIZE)
13780         player->inventory_element[player->inventory_size++] = element;
13781
13782       DrawGameDoorValues();
13783     }
13784     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13785     {
13786       player->dynabomb_count++;
13787       player->dynabombs_left++;
13788     }
13789     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13790     {
13791       player->dynabomb_size++;
13792     }
13793     else if (element == EL_DYNABOMB_INCREASE_POWER)
13794     {
13795       player->dynabomb_xl = TRUE;
13796     }
13797     else if (IS_KEY(element))
13798     {
13799       player->key[KEY_NR(element)] = TRUE;
13800
13801       DrawGameDoorValues();
13802     }
13803     else if (element == EL_DC_KEY_WHITE)
13804     {
13805       player->num_white_keys++;
13806
13807       /* display white keys? */
13808       /* DrawGameDoorValues(); */
13809     }
13810     else if (IS_ENVELOPE(element))
13811     {
13812       player->show_envelope = element;
13813     }
13814     else if (element == EL_EMC_LENSES)
13815     {
13816       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13817
13818       RedrawAllInvisibleElementsForLenses();
13819     }
13820     else if (element == EL_EMC_MAGNIFIER)
13821     {
13822       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13823
13824       RedrawAllInvisibleElementsForMagnifier();
13825     }
13826     else if (IS_DROPPABLE(element) ||
13827              IS_THROWABLE(element))     /* can be collected and dropped */
13828     {
13829       int i;
13830
13831       if (collect_count == 0)
13832         player->inventory_infinite_element = element;
13833       else
13834         for (i = 0; i < collect_count; i++)
13835           if (player->inventory_size < MAX_INVENTORY_SIZE)
13836             player->inventory_element[player->inventory_size++] = element;
13837
13838       DrawGameDoorValues();
13839     }
13840     else if (collect_count > 0)
13841     {
13842       local_player->gems_still_needed -= collect_count;
13843       if (local_player->gems_still_needed < 0)
13844         local_player->gems_still_needed = 0;
13845
13846       game.snapshot.collected_item = TRUE;
13847
13848       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13849
13850       DisplayGameControlValues();
13851     }
13852
13853     RaiseScoreElement(element);
13854     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13855
13856     if (is_player)
13857       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13858                                           player->index_bit, dig_side);
13859
13860     if (mode == DF_SNAP)
13861     {
13862       if (level.block_snap_field)
13863         setFieldForSnapping(x, y, element, move_direction);
13864       else
13865         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13866
13867       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13868                                           player->index_bit, dig_side);
13869     }
13870   }
13871   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13872   {
13873     if (mode == DF_SNAP && element != EL_BD_ROCK)
13874       return MP_NO_ACTION;
13875
13876     if (CAN_FALL(element) && dy)
13877       return MP_NO_ACTION;
13878
13879     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13880         !(element == EL_SPRING && level.use_spring_bug))
13881       return MP_NO_ACTION;
13882
13883     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13884         ((move_direction & MV_VERTICAL &&
13885           ((element_info[element].move_pattern & MV_LEFT &&
13886             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13887            (element_info[element].move_pattern & MV_RIGHT &&
13888             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13889          (move_direction & MV_HORIZONTAL &&
13890           ((element_info[element].move_pattern & MV_UP &&
13891             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13892            (element_info[element].move_pattern & MV_DOWN &&
13893             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13894       return MP_NO_ACTION;
13895
13896     /* do not push elements already moving away faster than player */
13897     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13898         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13899       return MP_NO_ACTION;
13900
13901     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13902     {
13903       if (player->push_delay_value == -1 || !player_was_pushing)
13904         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13905     }
13906     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13907     {
13908       if (player->push_delay_value == -1)
13909         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13910     }
13911     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13912     {
13913       if (!player->is_pushing)
13914         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13915     }
13916
13917     player->is_pushing = TRUE;
13918     player->is_active = TRUE;
13919
13920     if (!(IN_LEV_FIELD(nextx, nexty) &&
13921           (IS_FREE(nextx, nexty) ||
13922            (IS_SB_ELEMENT(element) &&
13923             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13924            (IS_CUSTOM_ELEMENT(element) &&
13925             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13926       return MP_NO_ACTION;
13927
13928     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13929       return MP_NO_ACTION;
13930
13931     if (player->push_delay == -1)       /* new pushing; restart delay */
13932       player->push_delay = 0;
13933
13934     if (player->push_delay < player->push_delay_value &&
13935         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13936         element != EL_SPRING && element != EL_BALLOON)
13937     {
13938       /* make sure that there is no move delay before next try to push */
13939       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13940         player->move_delay = 0;
13941
13942       return MP_NO_ACTION;
13943     }
13944
13945     if (IS_CUSTOM_ELEMENT(element) &&
13946         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13947     {
13948       if (!DigFieldByCE(nextx, nexty, element))
13949         return MP_NO_ACTION;
13950     }
13951
13952     if (IS_SB_ELEMENT(element))
13953     {
13954       if (element == EL_SOKOBAN_FIELD_FULL)
13955       {
13956         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13957         local_player->sokobanfields_still_needed++;
13958       }
13959
13960       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13961       {
13962         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13963         local_player->sokobanfields_still_needed--;
13964       }
13965
13966       Feld[x][y] = EL_SOKOBAN_OBJECT;
13967
13968       if (Back[x][y] == Back[nextx][nexty])
13969         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13970       else if (Back[x][y] != 0)
13971         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13972                                     ACTION_EMPTYING);
13973       else
13974         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13975                                     ACTION_FILLING);
13976
13977       if (local_player->sokobanfields_still_needed == 0 &&
13978           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13979       {
13980         local_player->players_still_needed = 0;
13981
13982         PlayerWins(player);
13983
13984         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13985       }
13986     }
13987     else
13988       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13989
13990     InitMovingField(x, y, move_direction);
13991     GfxAction[x][y] = ACTION_PUSHING;
13992
13993     if (mode == DF_SNAP)
13994       ContinueMoving(x, y);
13995     else
13996       MovPos[x][y] = (dx != 0 ? dx : dy);
13997
13998     Pushed[x][y] = TRUE;
13999     Pushed[nextx][nexty] = TRUE;
14000
14001     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14002       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14003     else
14004       player->push_delay_value = -1;    /* get new value later */
14005
14006     /* check for element change _after_ element has been pushed */
14007     if (game.use_change_when_pushing_bug)
14008     {
14009       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14010                                  player->index_bit, dig_side);
14011       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14012                                           player->index_bit, dig_side);
14013     }
14014   }
14015   else if (IS_SWITCHABLE(element))
14016   {
14017     if (PLAYER_SWITCHING(player, x, y))
14018     {
14019       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14020                                           player->index_bit, dig_side);
14021
14022       return MP_ACTION;
14023     }
14024
14025     player->is_switching = TRUE;
14026     player->switch_x = x;
14027     player->switch_y = y;
14028
14029     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14030
14031     if (element == EL_ROBOT_WHEEL)
14032     {
14033       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14034       ZX = x;
14035       ZY = y;
14036
14037       game.robot_wheel_active = TRUE;
14038
14039       TEST_DrawLevelField(x, y);
14040     }
14041     else if (element == EL_SP_TERMINAL)
14042     {
14043       int xx, yy;
14044
14045       SCAN_PLAYFIELD(xx, yy)
14046       {
14047         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14048         {
14049           Bang(xx, yy);
14050         }
14051         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14052         {
14053           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14054
14055           ResetGfxAnimation(xx, yy);
14056           TEST_DrawLevelField(xx, yy);
14057         }
14058       }
14059     }
14060     else if (IS_BELT_SWITCH(element))
14061     {
14062       ToggleBeltSwitch(x, y);
14063     }
14064     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14065              element == EL_SWITCHGATE_SWITCH_DOWN ||
14066              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14067              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14068     {
14069       ToggleSwitchgateSwitch(x, y);
14070     }
14071     else if (element == EL_LIGHT_SWITCH ||
14072              element == EL_LIGHT_SWITCH_ACTIVE)
14073     {
14074       ToggleLightSwitch(x, y);
14075     }
14076     else if (element == EL_TIMEGATE_SWITCH ||
14077              element == EL_DC_TIMEGATE_SWITCH)
14078     {
14079       ActivateTimegateSwitch(x, y);
14080     }
14081     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14082              element == EL_BALLOON_SWITCH_RIGHT ||
14083              element == EL_BALLOON_SWITCH_UP    ||
14084              element == EL_BALLOON_SWITCH_DOWN  ||
14085              element == EL_BALLOON_SWITCH_NONE  ||
14086              element == EL_BALLOON_SWITCH_ANY)
14087     {
14088       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14089                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14090                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14091                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14092                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14093                              move_direction);
14094     }
14095     else if (element == EL_LAMP)
14096     {
14097       Feld[x][y] = EL_LAMP_ACTIVE;
14098       local_player->lights_still_needed--;
14099
14100       ResetGfxAnimation(x, y);
14101       TEST_DrawLevelField(x, y);
14102     }
14103     else if (element == EL_TIME_ORB_FULL)
14104     {
14105       Feld[x][y] = EL_TIME_ORB_EMPTY;
14106
14107       if (level.time > 0 || level.use_time_orb_bug)
14108       {
14109         TimeLeft += level.time_orb_time;
14110         game.no_time_limit = FALSE;
14111
14112         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14113
14114         DisplayGameControlValues();
14115       }
14116
14117       ResetGfxAnimation(x, y);
14118       TEST_DrawLevelField(x, y);
14119     }
14120     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14121              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14122     {
14123       int xx, yy;
14124
14125       game.ball_state = !game.ball_state;
14126
14127       SCAN_PLAYFIELD(xx, yy)
14128       {
14129         int e = Feld[xx][yy];
14130
14131         if (game.ball_state)
14132         {
14133           if (e == EL_EMC_MAGIC_BALL)
14134             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14135           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14136             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14137         }
14138         else
14139         {
14140           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14141             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14142           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14143             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14144         }
14145       }
14146     }
14147
14148     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14149                                         player->index_bit, dig_side);
14150
14151     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14152                                         player->index_bit, dig_side);
14153
14154     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14155                                         player->index_bit, dig_side);
14156
14157     return MP_ACTION;
14158   }
14159   else
14160   {
14161     if (!PLAYER_SWITCHING(player, x, y))
14162     {
14163       player->is_switching = TRUE;
14164       player->switch_x = x;
14165       player->switch_y = y;
14166
14167       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14168                                  player->index_bit, dig_side);
14169       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14170                                           player->index_bit, dig_side);
14171
14172       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14173                                  player->index_bit, dig_side);
14174       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14175                                           player->index_bit, dig_side);
14176     }
14177
14178     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14179                                player->index_bit, dig_side);
14180     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14181                                         player->index_bit, dig_side);
14182
14183     return MP_NO_ACTION;
14184   }
14185
14186   player->push_delay = -1;
14187
14188   if (is_player)                /* function can also be called by EL_PENGUIN */
14189   {
14190     if (Feld[x][y] != element)          /* really digged/collected something */
14191     {
14192       player->is_collecting = !player->is_digging;
14193       player->is_active = TRUE;
14194     }
14195   }
14196
14197   return MP_MOVING;
14198 }
14199
14200 static boolean DigFieldByCE(int x, int y, int digging_element)
14201 {
14202   int element = Feld[x][y];
14203
14204   if (!IS_FREE(x, y))
14205   {
14206     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14207                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14208                   ACTION_BREAKING);
14209
14210     /* no element can dig solid indestructible elements */
14211     if (IS_INDESTRUCTIBLE(element) &&
14212         !IS_DIGGABLE(element) &&
14213         !IS_COLLECTIBLE(element))
14214       return FALSE;
14215
14216     if (AmoebaNr[x][y] &&
14217         (element == EL_AMOEBA_FULL ||
14218          element == EL_BD_AMOEBA ||
14219          element == EL_AMOEBA_GROWING))
14220     {
14221       AmoebaCnt[AmoebaNr[x][y]]--;
14222       AmoebaCnt2[AmoebaNr[x][y]]--;
14223     }
14224
14225     if (IS_MOVING(x, y))
14226       RemoveMovingField(x, y);
14227     else
14228     {
14229       RemoveField(x, y);
14230       TEST_DrawLevelField(x, y);
14231     }
14232
14233     /* if digged element was about to explode, prevent the explosion */
14234     ExplodeField[x][y] = EX_TYPE_NONE;
14235
14236     PlayLevelSoundAction(x, y, action);
14237   }
14238
14239   Store[x][y] = EL_EMPTY;
14240
14241   /* this makes it possible to leave the removed element again */
14242   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14243     Store[x][y] = element;
14244
14245   return TRUE;
14246 }
14247
14248 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14249 {
14250   int jx = player->jx, jy = player->jy;
14251   int x = jx + dx, y = jy + dy;
14252   int snap_direction = (dx == -1 ? MV_LEFT  :
14253                         dx == +1 ? MV_RIGHT :
14254                         dy == -1 ? MV_UP    :
14255                         dy == +1 ? MV_DOWN  : MV_NONE);
14256   boolean can_continue_snapping = (level.continuous_snapping &&
14257                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14258
14259   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14260     return FALSE;
14261
14262   if (!player->active || !IN_LEV_FIELD(x, y))
14263     return FALSE;
14264
14265   if (dx && dy)
14266     return FALSE;
14267
14268   if (!dx && !dy)
14269   {
14270     if (player->MovPos == 0)
14271       player->is_pushing = FALSE;
14272
14273     player->is_snapping = FALSE;
14274
14275     if (player->MovPos == 0)
14276     {
14277       player->is_moving = FALSE;
14278       player->is_digging = FALSE;
14279       player->is_collecting = FALSE;
14280     }
14281
14282     return FALSE;
14283   }
14284
14285   /* prevent snapping with already pressed snap key when not allowed */
14286   if (player->is_snapping && !can_continue_snapping)
14287     return FALSE;
14288
14289   player->MovDir = snap_direction;
14290
14291   if (player->MovPos == 0)
14292   {
14293     player->is_moving = FALSE;
14294     player->is_digging = FALSE;
14295     player->is_collecting = FALSE;
14296   }
14297
14298   player->is_dropping = FALSE;
14299   player->is_dropping_pressed = FALSE;
14300   player->drop_pressed_delay = 0;
14301
14302   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14303     return FALSE;
14304
14305   player->is_snapping = TRUE;
14306   player->is_active = TRUE;
14307
14308   if (player->MovPos == 0)
14309   {
14310     player->is_moving = FALSE;
14311     player->is_digging = FALSE;
14312     player->is_collecting = FALSE;
14313   }
14314
14315   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14316     TEST_DrawLevelField(player->last_jx, player->last_jy);
14317
14318   TEST_DrawLevelField(x, y);
14319
14320   return TRUE;
14321 }
14322
14323 static boolean DropElement(struct PlayerInfo *player)
14324 {
14325   int old_element, new_element;
14326   int dropx = player->jx, dropy = player->jy;
14327   int drop_direction = player->MovDir;
14328   int drop_side = drop_direction;
14329   int drop_element = get_next_dropped_element(player);
14330
14331   /* do not drop an element on top of another element; when holding drop key
14332      pressed without moving, dropped element must move away before the next
14333      element can be dropped (this is especially important if the next element
14334      is dynamite, which can be placed on background for historical reasons) */
14335   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14336     return MP_ACTION;
14337
14338   if (IS_THROWABLE(drop_element))
14339   {
14340     dropx += GET_DX_FROM_DIR(drop_direction);
14341     dropy += GET_DY_FROM_DIR(drop_direction);
14342
14343     if (!IN_LEV_FIELD(dropx, dropy))
14344       return FALSE;
14345   }
14346
14347   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14348   new_element = drop_element;           /* default: no change when dropping */
14349
14350   /* check if player is active, not moving and ready to drop */
14351   if (!player->active || player->MovPos || player->drop_delay > 0)
14352     return FALSE;
14353
14354   /* check if player has anything that can be dropped */
14355   if (new_element == EL_UNDEFINED)
14356     return FALSE;
14357
14358   /* only set if player has anything that can be dropped */
14359   player->is_dropping_pressed = TRUE;
14360
14361   /* check if drop key was pressed long enough for EM style dynamite */
14362   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14363     return FALSE;
14364
14365   /* check if anything can be dropped at the current position */
14366   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14367     return FALSE;
14368
14369   /* collected custom elements can only be dropped on empty fields */
14370   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14371     return FALSE;
14372
14373   if (old_element != EL_EMPTY)
14374     Back[dropx][dropy] = old_element;   /* store old element on this field */
14375
14376   ResetGfxAnimation(dropx, dropy);
14377   ResetRandomAnimationValue(dropx, dropy);
14378
14379   if (player->inventory_size > 0 ||
14380       player->inventory_infinite_element != EL_UNDEFINED)
14381   {
14382     if (player->inventory_size > 0)
14383     {
14384       player->inventory_size--;
14385
14386       DrawGameDoorValues();
14387
14388       if (new_element == EL_DYNAMITE)
14389         new_element = EL_DYNAMITE_ACTIVE;
14390       else if (new_element == EL_EM_DYNAMITE)
14391         new_element = EL_EM_DYNAMITE_ACTIVE;
14392       else if (new_element == EL_SP_DISK_RED)
14393         new_element = EL_SP_DISK_RED_ACTIVE;
14394     }
14395
14396     Feld[dropx][dropy] = new_element;
14397
14398     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14399       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14400                           el2img(Feld[dropx][dropy]), 0);
14401
14402     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14403
14404     /* needed if previous element just changed to "empty" in the last frame */
14405     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14406
14407     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14408                                player->index_bit, drop_side);
14409     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14410                                         CE_PLAYER_DROPS_X,
14411                                         player->index_bit, drop_side);
14412
14413     TestIfElementTouchesCustomElement(dropx, dropy);
14414   }
14415   else          /* player is dropping a dyna bomb */
14416   {
14417     player->dynabombs_left--;
14418
14419     Feld[dropx][dropy] = new_element;
14420
14421     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14422       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14423                           el2img(Feld[dropx][dropy]), 0);
14424
14425     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14426   }
14427
14428   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14429     InitField_WithBug1(dropx, dropy, FALSE);
14430
14431   new_element = Feld[dropx][dropy];     /* element might have changed */
14432
14433   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14434       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14435   {
14436     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14437       MovDir[dropx][dropy] = drop_direction;
14438
14439     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14440
14441     /* do not cause impact style collision by dropping elements that can fall */
14442     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14443   }
14444
14445   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14446   player->is_dropping = TRUE;
14447
14448   player->drop_pressed_delay = 0;
14449   player->is_dropping_pressed = FALSE;
14450
14451   player->drop_x = dropx;
14452   player->drop_y = dropy;
14453
14454   return TRUE;
14455 }
14456
14457 /* ------------------------------------------------------------------------- */
14458 /* game sound playing functions                                              */
14459 /* ------------------------------------------------------------------------- */
14460
14461 static int *loop_sound_frame = NULL;
14462 static int *loop_sound_volume = NULL;
14463
14464 void InitPlayLevelSound()
14465 {
14466   int num_sounds = getSoundListSize();
14467
14468   checked_free(loop_sound_frame);
14469   checked_free(loop_sound_volume);
14470
14471   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14472   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14473 }
14474
14475 static void PlayLevelSound(int x, int y, int nr)
14476 {
14477   int sx = SCREENX(x), sy = SCREENY(y);
14478   int volume, stereo_position;
14479   int max_distance = 8;
14480   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14481
14482   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14483       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14484     return;
14485
14486   if (!IN_LEV_FIELD(x, y) ||
14487       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14488       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14489     return;
14490
14491   volume = SOUND_MAX_VOLUME;
14492
14493   if (!IN_SCR_FIELD(sx, sy))
14494   {
14495     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14496     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14497
14498     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14499   }
14500
14501   stereo_position = (SOUND_MAX_LEFT +
14502                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14503                      (SCR_FIELDX + 2 * max_distance));
14504
14505   if (IS_LOOP_SOUND(nr))
14506   {
14507     /* This assures that quieter loop sounds do not overwrite louder ones,
14508        while restarting sound volume comparison with each new game frame. */
14509
14510     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14511       return;
14512
14513     loop_sound_volume[nr] = volume;
14514     loop_sound_frame[nr] = FrameCounter;
14515   }
14516
14517   PlaySoundExt(nr, volume, stereo_position, type);
14518 }
14519
14520 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14521 {
14522   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14523                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14524                  y < LEVELY(BY1) ? LEVELY(BY1) :
14525                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14526                  sound_action);
14527 }
14528
14529 static void PlayLevelSoundAction(int x, int y, int action)
14530 {
14531   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14532 }
14533
14534 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14535 {
14536   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14537
14538   if (sound_effect != SND_UNDEFINED)
14539     PlayLevelSound(x, y, sound_effect);
14540 }
14541
14542 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14543                                               int action)
14544 {
14545   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14546
14547   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14548     PlayLevelSound(x, y, sound_effect);
14549 }
14550
14551 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14552 {
14553   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14554
14555   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14556     PlayLevelSound(x, y, sound_effect);
14557 }
14558
14559 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14560 {
14561   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14562
14563   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14564     StopSound(sound_effect);
14565 }
14566
14567 static int getLevelMusicNr()
14568 {
14569   if (levelset.music[level_nr] != MUS_UNDEFINED)
14570     return levelset.music[level_nr];            /* from config file */
14571   else
14572     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14573 }
14574
14575 static void FadeLevelSounds()
14576 {
14577   FadeSounds();
14578 }
14579
14580 static void FadeLevelMusic()
14581 {
14582   int music_nr = getLevelMusicNr();
14583   char *curr_music = getCurrentlyPlayingMusicFilename();
14584   char *next_music = getMusicInfoEntryFilename(music_nr);
14585
14586   if (!strEqual(curr_music, next_music))
14587     FadeMusic();
14588 }
14589
14590 void FadeLevelSoundsAndMusic()
14591 {
14592   FadeLevelSounds();
14593   FadeLevelMusic();
14594 }
14595
14596 static void PlayLevelMusic()
14597 {
14598   int music_nr = getLevelMusicNr();
14599   char *curr_music = getCurrentlyPlayingMusicFilename();
14600   char *next_music = getMusicInfoEntryFilename(music_nr);
14601
14602   if (!strEqual(curr_music, next_music))
14603     PlayMusicLoop(music_nr);
14604 }
14605
14606 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14607 {
14608   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14609   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14610   int x = xx - 1 - offset;
14611   int y = yy - 1 - offset;
14612
14613   switch (sample)
14614   {
14615     case SAMPLE_blank:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14617       break;
14618
14619     case SAMPLE_roll:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14621       break;
14622
14623     case SAMPLE_stone:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14625       break;
14626
14627     case SAMPLE_nut:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14629       break;
14630
14631     case SAMPLE_crack:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14633       break;
14634
14635     case SAMPLE_bug:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14637       break;
14638
14639     case SAMPLE_tank:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14641       break;
14642
14643     case SAMPLE_android_clone:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14645       break;
14646
14647     case SAMPLE_android_move:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14649       break;
14650
14651     case SAMPLE_spring:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14653       break;
14654
14655     case SAMPLE_slurp:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14657       break;
14658
14659     case SAMPLE_eater:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14661       break;
14662
14663     case SAMPLE_eater_eat:
14664       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14665       break;
14666
14667     case SAMPLE_alien:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14669       break;
14670
14671     case SAMPLE_collect:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14673       break;
14674
14675     case SAMPLE_diamond:
14676       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14677       break;
14678
14679     case SAMPLE_squash:
14680       /* !!! CHECK THIS !!! */
14681 #if 1
14682       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14683 #else
14684       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14685 #endif
14686       break;
14687
14688     case SAMPLE_wonderfall:
14689       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14690       break;
14691
14692     case SAMPLE_drip:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14694       break;
14695
14696     case SAMPLE_push:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14698       break;
14699
14700     case SAMPLE_dirt:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14702       break;
14703
14704     case SAMPLE_acid:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14706       break;
14707
14708     case SAMPLE_ball:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14710       break;
14711
14712     case SAMPLE_grow:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14714       break;
14715
14716     case SAMPLE_wonder:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14718       break;
14719
14720     case SAMPLE_door:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14722       break;
14723
14724     case SAMPLE_exit_open:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14726       break;
14727
14728     case SAMPLE_exit_leave:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14730       break;
14731
14732     case SAMPLE_dynamite:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14734       break;
14735
14736     case SAMPLE_tick:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14738       break;
14739
14740     case SAMPLE_press:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14742       break;
14743
14744     case SAMPLE_wheel:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14746       break;
14747
14748     case SAMPLE_boom:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14750       break;
14751
14752     case SAMPLE_die:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14754       break;
14755
14756     case SAMPLE_time:
14757       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14758       break;
14759
14760     default:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14762       break;
14763   }
14764 }
14765
14766 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14767 {
14768   int element = map_element_SP_to_RND(element_sp);
14769   int action = map_action_SP_to_RND(action_sp);
14770   int offset = (setup.sp_show_border_elements ? 0 : 1);
14771   int x = xx - offset;
14772   int y = yy - offset;
14773
14774   PlayLevelSoundElementAction(x, y, element, action);
14775 }
14776
14777 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14778 {
14779   int element = map_element_MM_to_RND(element_mm);
14780   int action = map_action_MM_to_RND(action_mm);
14781   int offset = 0;
14782   int x = xx - offset;
14783   int y = yy - offset;
14784
14785   if (!IS_MM_ELEMENT(element))
14786     element = EL_MM_DEFAULT;
14787
14788   PlayLevelSoundElementAction(x, y, element, action);
14789 }
14790
14791 void PlaySound_MM(int sound_mm)
14792 {
14793   int sound = map_sound_MM_to_RND(sound_mm);
14794
14795   if (sound == SND_UNDEFINED)
14796     return;
14797
14798   PlaySound(sound);
14799 }
14800
14801 void PlaySoundLoop_MM(int sound_mm)
14802 {
14803   int sound = map_sound_MM_to_RND(sound_mm);
14804
14805   if (sound == SND_UNDEFINED)
14806     return;
14807
14808   PlaySoundLoop(sound);
14809 }
14810
14811 void StopSound_MM(int sound_mm)
14812 {
14813   int sound = map_sound_MM_to_RND(sound_mm);
14814
14815   if (sound == SND_UNDEFINED)
14816     return;
14817
14818   StopSound(sound);
14819 }
14820
14821 void RaiseScore(int value)
14822 {
14823   local_player->score += value;
14824
14825   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14826
14827   DisplayGameControlValues();
14828 }
14829
14830 void RaiseScoreElement(int element)
14831 {
14832   switch (element)
14833   {
14834     case EL_EMERALD:
14835     case EL_BD_DIAMOND:
14836     case EL_EMERALD_YELLOW:
14837     case EL_EMERALD_RED:
14838     case EL_EMERALD_PURPLE:
14839     case EL_SP_INFOTRON:
14840       RaiseScore(level.score[SC_EMERALD]);
14841       break;
14842     case EL_DIAMOND:
14843       RaiseScore(level.score[SC_DIAMOND]);
14844       break;
14845     case EL_CRYSTAL:
14846       RaiseScore(level.score[SC_CRYSTAL]);
14847       break;
14848     case EL_PEARL:
14849       RaiseScore(level.score[SC_PEARL]);
14850       break;
14851     case EL_BUG:
14852     case EL_BD_BUTTERFLY:
14853     case EL_SP_ELECTRON:
14854       RaiseScore(level.score[SC_BUG]);
14855       break;
14856     case EL_SPACESHIP:
14857     case EL_BD_FIREFLY:
14858     case EL_SP_SNIKSNAK:
14859       RaiseScore(level.score[SC_SPACESHIP]);
14860       break;
14861     case EL_YAMYAM:
14862     case EL_DARK_YAMYAM:
14863       RaiseScore(level.score[SC_YAMYAM]);
14864       break;
14865     case EL_ROBOT:
14866       RaiseScore(level.score[SC_ROBOT]);
14867       break;
14868     case EL_PACMAN:
14869       RaiseScore(level.score[SC_PACMAN]);
14870       break;
14871     case EL_NUT:
14872       RaiseScore(level.score[SC_NUT]);
14873       break;
14874     case EL_DYNAMITE:
14875     case EL_EM_DYNAMITE:
14876     case EL_SP_DISK_RED:
14877     case EL_DYNABOMB_INCREASE_NUMBER:
14878     case EL_DYNABOMB_INCREASE_SIZE:
14879     case EL_DYNABOMB_INCREASE_POWER:
14880       RaiseScore(level.score[SC_DYNAMITE]);
14881       break;
14882     case EL_SHIELD_NORMAL:
14883     case EL_SHIELD_DEADLY:
14884       RaiseScore(level.score[SC_SHIELD]);
14885       break;
14886     case EL_EXTRA_TIME:
14887       RaiseScore(level.extra_time_score);
14888       break;
14889     case EL_KEY_1:
14890     case EL_KEY_2:
14891     case EL_KEY_3:
14892     case EL_KEY_4:
14893     case EL_EM_KEY_1:
14894     case EL_EM_KEY_2:
14895     case EL_EM_KEY_3:
14896     case EL_EM_KEY_4:
14897     case EL_EMC_KEY_5:
14898     case EL_EMC_KEY_6:
14899     case EL_EMC_KEY_7:
14900     case EL_EMC_KEY_8:
14901     case EL_DC_KEY_WHITE:
14902       RaiseScore(level.score[SC_KEY]);
14903       break;
14904     default:
14905       RaiseScore(element_info[element].collect_score);
14906       break;
14907   }
14908 }
14909
14910 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14911 {
14912   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14913   {
14914     /* closing door required in case of envelope style request dialogs */
14915     if (!skip_request)
14916       CloseDoor(DOOR_CLOSE_1);
14917
14918     if (network.enabled)
14919       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14920     else
14921     {
14922       if (quick_quit)
14923         FadeSkipNextFadeIn();
14924
14925       SetGameStatus(GAME_MODE_MAIN);
14926
14927       DrawMainMenu();
14928     }
14929   }
14930   else          /* continue playing the game */
14931   {
14932     if (tape.playing && tape.deactivate_display)
14933       TapeDeactivateDisplayOff(TRUE);
14934
14935     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14936
14937     if (tape.playing && tape.deactivate_display)
14938       TapeDeactivateDisplayOn();
14939   }
14940 }
14941
14942 void RequestQuitGame(boolean ask_if_really_quit)
14943 {
14944   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14945   boolean skip_request = AllPlayersGone || quick_quit;
14946
14947   RequestQuitGameExt(skip_request, quick_quit,
14948                      "Do you really want to quit the game?");
14949 }
14950
14951 void RequestRestartGame(char *message)
14952 {
14953   game.restart_game_message = NULL;
14954
14955   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14956   {
14957     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14958   }
14959   else
14960   {
14961     SetGameStatus(GAME_MODE_MAIN);
14962
14963     DrawMainMenu();
14964   }
14965 }
14966
14967
14968 /* ------------------------------------------------------------------------- */
14969 /* random generator functions                                                */
14970 /* ------------------------------------------------------------------------- */
14971
14972 unsigned int InitEngineRandom_RND(int seed)
14973 {
14974   game.num_random_calls = 0;
14975
14976   return InitEngineRandom(seed);
14977 }
14978
14979 unsigned int RND(int max)
14980 {
14981   if (max > 0)
14982   {
14983     game.num_random_calls++;
14984
14985     return GetEngineRandom(max);
14986   }
14987
14988   return 0;
14989 }
14990
14991
14992 /* ------------------------------------------------------------------------- */
14993 /* game engine snapshot handling functions                                   */
14994 /* ------------------------------------------------------------------------- */
14995
14996 struct EngineSnapshotInfo
14997 {
14998   /* runtime values for custom element collect score */
14999   int collect_score[NUM_CUSTOM_ELEMENTS];
15000
15001   /* runtime values for group element choice position */
15002   int choice_pos[NUM_GROUP_ELEMENTS];
15003
15004   /* runtime values for belt position animations */
15005   int belt_graphic[4][NUM_BELT_PARTS];
15006   int belt_anim_mode[4][NUM_BELT_PARTS];
15007 };
15008
15009 static struct EngineSnapshotInfo engine_snapshot_rnd;
15010 static char *snapshot_level_identifier = NULL;
15011 static int snapshot_level_nr = -1;
15012
15013 static void SaveEngineSnapshotValues_RND()
15014 {
15015   static int belt_base_active_element[4] =
15016   {
15017     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15018     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15019     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15020     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15021   };
15022   int i, j;
15023
15024   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15025   {
15026     int element = EL_CUSTOM_START + i;
15027
15028     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15029   }
15030
15031   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15032   {
15033     int element = EL_GROUP_START + i;
15034
15035     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15036   }
15037
15038   for (i = 0; i < 4; i++)
15039   {
15040     for (j = 0; j < NUM_BELT_PARTS; j++)
15041     {
15042       int element = belt_base_active_element[i] + j;
15043       int graphic = el2img(element);
15044       int anim_mode = graphic_info[graphic].anim_mode;
15045
15046       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15047       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15048     }
15049   }
15050 }
15051
15052 static void LoadEngineSnapshotValues_RND()
15053 {
15054   unsigned int num_random_calls = game.num_random_calls;
15055   int i, j;
15056
15057   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15058   {
15059     int element = EL_CUSTOM_START + i;
15060
15061     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15062   }
15063
15064   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15065   {
15066     int element = EL_GROUP_START + i;
15067
15068     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15069   }
15070
15071   for (i = 0; i < 4; i++)
15072   {
15073     for (j = 0; j < NUM_BELT_PARTS; j++)
15074     {
15075       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15076       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15077
15078       graphic_info[graphic].anim_mode = anim_mode;
15079     }
15080   }
15081
15082   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15083   {
15084     InitRND(tape.random_seed);
15085     for (i = 0; i < num_random_calls; i++)
15086       RND(1);
15087   }
15088
15089   if (game.num_random_calls != num_random_calls)
15090   {
15091     Error(ERR_INFO, "number of random calls out of sync");
15092     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15093     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15094     Error(ERR_EXIT, "this should not happen -- please debug");
15095   }
15096 }
15097
15098 void FreeEngineSnapshotSingle()
15099 {
15100   FreeSnapshotSingle();
15101
15102   setString(&snapshot_level_identifier, NULL);
15103   snapshot_level_nr = -1;
15104 }
15105
15106 void FreeEngineSnapshotList()
15107 {
15108   FreeSnapshotList();
15109 }
15110
15111 ListNode *SaveEngineSnapshotBuffers()
15112 {
15113   ListNode *buffers = NULL;
15114
15115   /* copy some special values to a structure better suited for the snapshot */
15116
15117   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15118     SaveEngineSnapshotValues_RND();
15119   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15120     SaveEngineSnapshotValues_EM();
15121   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15122     SaveEngineSnapshotValues_SP(&buffers);
15123   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15124     SaveEngineSnapshotValues_MM(&buffers);
15125
15126   /* save values stored in special snapshot structure */
15127
15128   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15129     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15131     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15132   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15133     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15134   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15135     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15136
15137   /* save further RND engine values */
15138
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15142
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15147
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15153
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15157
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15159
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15161
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15164
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15183
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15186
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15190
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15193
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15199
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15202
15203 #if 0
15204   ListNode *node = engine_snapshot_list_rnd;
15205   int num_bytes = 0;
15206
15207   while (node != NULL)
15208   {
15209     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15210
15211     node = node->next;
15212   }
15213
15214   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15215 #endif
15216
15217   return buffers;
15218 }
15219
15220 void SaveEngineSnapshotSingle()
15221 {
15222   ListNode *buffers = SaveEngineSnapshotBuffers();
15223
15224   /* finally save all snapshot buffers to single snapshot */
15225   SaveSnapshotSingle(buffers);
15226
15227   /* save level identification information */
15228   setString(&snapshot_level_identifier, leveldir_current->identifier);
15229   snapshot_level_nr = level_nr;
15230 }
15231
15232 boolean CheckSaveEngineSnapshotToList()
15233 {
15234   boolean save_snapshot =
15235     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15236      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15237       game.snapshot.changed_action) ||
15238      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15239       game.snapshot.collected_item));
15240
15241   game.snapshot.changed_action = FALSE;
15242   game.snapshot.collected_item = FALSE;
15243   game.snapshot.save_snapshot = save_snapshot;
15244
15245   return save_snapshot;
15246 }
15247
15248 void SaveEngineSnapshotToList()
15249 {
15250   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15251       tape.quick_resume)
15252     return;
15253
15254   ListNode *buffers = SaveEngineSnapshotBuffers();
15255
15256   /* finally save all snapshot buffers to snapshot list */
15257   SaveSnapshotToList(buffers);
15258 }
15259
15260 void SaveEngineSnapshotToListInitial()
15261 {
15262   FreeEngineSnapshotList();
15263
15264   SaveEngineSnapshotToList();
15265 }
15266
15267 void LoadEngineSnapshotValues()
15268 {
15269   /* restore special values from snapshot structure */
15270
15271   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15272     LoadEngineSnapshotValues_RND();
15273   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15274     LoadEngineSnapshotValues_EM();
15275   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15276     LoadEngineSnapshotValues_SP();
15277   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15278     LoadEngineSnapshotValues_MM();
15279 }
15280
15281 void LoadEngineSnapshotSingle()
15282 {
15283   LoadSnapshotSingle();
15284
15285   LoadEngineSnapshotValues();
15286 }
15287
15288 void LoadEngineSnapshot_Undo(int steps)
15289 {
15290   LoadSnapshotFromList_Older(steps);
15291
15292   LoadEngineSnapshotValues();
15293 }
15294
15295 void LoadEngineSnapshot_Redo(int steps)
15296 {
15297   LoadSnapshotFromList_Newer(steps);
15298
15299   LoadEngineSnapshotValues();
15300 }
15301
15302 boolean CheckEngineSnapshotSingle()
15303 {
15304   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15305           snapshot_level_nr == level_nr);
15306 }
15307
15308 boolean CheckEngineSnapshotList()
15309 {
15310   return CheckSnapshotList();
15311 }
15312
15313
15314 /* ---------- new game button stuff ---------------------------------------- */
15315
15316 static struct
15317 {
15318   int graphic;
15319   struct XY *pos;
15320   int gadget_id;
15321   boolean *setup_value;
15322   boolean allowed_on_tape;
15323   char *infotext;
15324 } gamebutton_info[NUM_GAME_BUTTONS] =
15325 {
15326   {
15327     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15328     GAME_CTRL_ID_STOP,                          NULL,
15329     TRUE,                                       "stop game"
15330   },
15331   {
15332     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15333     GAME_CTRL_ID_PAUSE,                         NULL,
15334     TRUE,                                       "pause game"
15335   },
15336   {
15337     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15338     GAME_CTRL_ID_PLAY,                          NULL,
15339     TRUE,                                       "play game"
15340   },
15341   {
15342     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15343     GAME_CTRL_ID_UNDO,                          NULL,
15344     TRUE,                                       "undo step"
15345   },
15346   {
15347     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15348     GAME_CTRL_ID_REDO,                          NULL,
15349     TRUE,                                       "redo step"
15350   },
15351   {
15352     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15353     GAME_CTRL_ID_SAVE,                          NULL,
15354     TRUE,                                       "save game"
15355   },
15356   {
15357     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15358     GAME_CTRL_ID_PAUSE2,                        NULL,
15359     TRUE,                                       "pause game"
15360   },
15361   {
15362     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15363     GAME_CTRL_ID_LOAD,                          NULL,
15364     TRUE,                                       "load game"
15365   },
15366   {
15367     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15368     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15369     FALSE,                                      "stop game"
15370   },
15371   {
15372     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15373     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15374     FALSE,                                      "pause game"
15375   },
15376   {
15377     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15378     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15379     FALSE,                                      "play game"
15380   },
15381   {
15382     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15383     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15384     TRUE,                                       "background music on/off"
15385   },
15386   {
15387     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15388     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15389     TRUE,                                       "sound loops on/off"
15390   },
15391   {
15392     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15393     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15394     TRUE,                                       "normal sounds on/off"
15395   },
15396   {
15397     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15398     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15399     FALSE,                                      "background music on/off"
15400   },
15401   {
15402     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15403     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15404     FALSE,                                      "sound loops on/off"
15405   },
15406   {
15407     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15408     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15409     FALSE,                                      "normal sounds on/off"
15410   }
15411 };
15412
15413 void CreateGameButtons()
15414 {
15415   int i;
15416
15417   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15418   {
15419     int graphic = gamebutton_info[i].graphic;
15420     struct GraphicInfo *gfx = &graphic_info[graphic];
15421     struct XY *pos = gamebutton_info[i].pos;
15422     struct GadgetInfo *gi;
15423     int button_type;
15424     boolean checked;
15425     unsigned int event_mask;
15426     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15427     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15428     int base_x = (on_tape ? VX : DX);
15429     int base_y = (on_tape ? VY : DY);
15430     int gd_x   = gfx->src_x;
15431     int gd_y   = gfx->src_y;
15432     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15433     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15434     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15435     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15436     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15437     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15438     int id = i;
15439
15440     if (gfx->bitmap == NULL)
15441     {
15442       game_gadget[id] = NULL;
15443
15444       continue;
15445     }
15446
15447     if (id == GAME_CTRL_ID_STOP ||
15448         id == GAME_CTRL_ID_PANEL_STOP ||
15449         id == GAME_CTRL_ID_PLAY ||
15450         id == GAME_CTRL_ID_PANEL_PLAY ||
15451         id == GAME_CTRL_ID_SAVE ||
15452         id == GAME_CTRL_ID_LOAD)
15453     {
15454       button_type = GD_TYPE_NORMAL_BUTTON;
15455       checked = FALSE;
15456       event_mask = GD_EVENT_RELEASED;
15457     }
15458     else if (id == GAME_CTRL_ID_UNDO ||
15459              id == GAME_CTRL_ID_REDO)
15460     {
15461       button_type = GD_TYPE_NORMAL_BUTTON;
15462       checked = FALSE;
15463       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15464     }
15465     else
15466     {
15467       button_type = GD_TYPE_CHECK_BUTTON;
15468       checked = (gamebutton_info[i].setup_value != NULL ?
15469                  *gamebutton_info[i].setup_value : FALSE);
15470       event_mask = GD_EVENT_PRESSED;
15471     }
15472
15473     gi = CreateGadget(GDI_CUSTOM_ID, id,
15474                       GDI_IMAGE_ID, graphic,
15475                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15476                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15477                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15478                       GDI_WIDTH, gfx->width,
15479                       GDI_HEIGHT, gfx->height,
15480                       GDI_TYPE, button_type,
15481                       GDI_STATE, GD_BUTTON_UNPRESSED,
15482                       GDI_CHECKED, checked,
15483                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15484                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15485                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15486                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15487                       GDI_DIRECT_DRAW, FALSE,
15488                       GDI_EVENT_MASK, event_mask,
15489                       GDI_CALLBACK_ACTION, HandleGameButtons,
15490                       GDI_END);
15491
15492     if (gi == NULL)
15493       Error(ERR_EXIT, "cannot create gadget");
15494
15495     game_gadget[id] = gi;
15496   }
15497 }
15498
15499 void FreeGameButtons()
15500 {
15501   int i;
15502
15503   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15504     FreeGadget(game_gadget[i]);
15505 }
15506
15507 static void UnmapGameButtonsAtSamePosition(int id)
15508 {
15509   int i;
15510
15511   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15512     if (i != id &&
15513         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15514         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15515       UnmapGadget(game_gadget[i]);
15516 }
15517
15518 static void UnmapGameButtonsAtSamePosition_All()
15519 {
15520   if (setup.show_snapshot_buttons)
15521   {
15522     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15523     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15524     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15525   }
15526   else
15527   {
15528     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15529     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15530     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15531
15532     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15533     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15534     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15535   }
15536 }
15537
15538 static void MapGameButtonsAtSamePosition(int id)
15539 {
15540   int i;
15541
15542   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15543     if (i != id &&
15544         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15545         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15546       MapGadget(game_gadget[i]);
15547
15548   UnmapGameButtonsAtSamePosition_All();
15549 }
15550
15551 void MapUndoRedoButtons()
15552 {
15553   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15554   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15555
15556   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15557   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15558
15559   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15560 }
15561
15562 void UnmapUndoRedoButtons()
15563 {
15564   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15565   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15566
15567   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15568   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15569
15570   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15571 }
15572
15573 void MapGameButtonsExt(boolean on_tape)
15574 {
15575   int i;
15576
15577   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15578     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15579         i != GAME_CTRL_ID_UNDO &&
15580         i != GAME_CTRL_ID_REDO)
15581       MapGadget(game_gadget[i]);
15582
15583   UnmapGameButtonsAtSamePosition_All();
15584
15585   RedrawGameButtons();
15586 }
15587
15588 void UnmapGameButtonsExt(boolean on_tape)
15589 {
15590   int i;
15591
15592   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15593     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15594       UnmapGadget(game_gadget[i]);
15595 }
15596
15597 void RedrawGameButtonsExt(boolean on_tape)
15598 {
15599   int i;
15600
15601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15602     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15603       RedrawGadget(game_gadget[i]);
15604
15605   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15606   redraw_mask &= ~REDRAW_ALL;
15607 }
15608
15609 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15610 {
15611   if (gi == NULL)
15612     return;
15613
15614   gi->checked = state;
15615 }
15616
15617 void RedrawSoundButtonGadget(int id)
15618 {
15619   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15620              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15621              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15622              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15623              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15624              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15625              id);
15626
15627   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15628   RedrawGadget(game_gadget[id2]);
15629 }
15630
15631 void MapGameButtons()
15632 {
15633   MapGameButtonsExt(FALSE);
15634 }
15635
15636 void UnmapGameButtons()
15637 {
15638   UnmapGameButtonsExt(FALSE);
15639 }
15640
15641 void RedrawGameButtons()
15642 {
15643   RedrawGameButtonsExt(FALSE);
15644 }
15645
15646 void MapGameButtonsOnTape()
15647 {
15648   MapGameButtonsExt(TRUE);
15649 }
15650
15651 void UnmapGameButtonsOnTape()
15652 {
15653   UnmapGameButtonsExt(TRUE);
15654 }
15655
15656 void RedrawGameButtonsOnTape()
15657 {
15658   RedrawGameButtonsExt(TRUE);
15659 }
15660
15661 void GameUndoRedoExt()
15662 {
15663   ClearPlayerAction();
15664
15665   tape.pausing = TRUE;
15666
15667   RedrawPlayfield();
15668   UpdateAndDisplayGameControlValues();
15669
15670   DrawCompleteVideoDisplay();
15671   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15672   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15673   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15674
15675   BackToFront();
15676 }
15677
15678 void GameUndo(int steps)
15679 {
15680   if (!CheckEngineSnapshotList())
15681     return;
15682
15683   LoadEngineSnapshot_Undo(steps);
15684
15685   GameUndoRedoExt();
15686 }
15687
15688 void GameRedo(int steps)
15689 {
15690   if (!CheckEngineSnapshotList())
15691     return;
15692
15693   LoadEngineSnapshot_Redo(steps);
15694
15695   GameUndoRedoExt();
15696 }
15697
15698 static void HandleGameButtonsExt(int id, int button)
15699 {
15700   static boolean game_undo_executed = FALSE;
15701   int steps = BUTTON_STEPSIZE(button);
15702   boolean handle_game_buttons =
15703     (game_status == GAME_MODE_PLAYING ||
15704      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15705
15706   if (!handle_game_buttons)
15707     return;
15708
15709   switch (id)
15710   {
15711     case GAME_CTRL_ID_STOP:
15712     case GAME_CTRL_ID_PANEL_STOP:
15713       if (game_status == GAME_MODE_MAIN)
15714         break;
15715
15716       if (tape.playing)
15717         TapeStop();
15718       else
15719         RequestQuitGame(TRUE);
15720
15721       break;
15722
15723     case GAME_CTRL_ID_PAUSE:
15724     case GAME_CTRL_ID_PAUSE2:
15725     case GAME_CTRL_ID_PANEL_PAUSE:
15726       if (network.enabled && game_status == GAME_MODE_PLAYING)
15727       {
15728         if (tape.pausing)
15729           SendToServer_ContinuePlaying();
15730         else
15731           SendToServer_PausePlaying();
15732       }
15733       else
15734         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15735
15736       game_undo_executed = FALSE;
15737
15738       break;
15739
15740     case GAME_CTRL_ID_PLAY:
15741     case GAME_CTRL_ID_PANEL_PLAY:
15742       if (game_status == GAME_MODE_MAIN)
15743       {
15744         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15745       }
15746       else if (tape.pausing)
15747       {
15748         if (network.enabled)
15749           SendToServer_ContinuePlaying();
15750         else
15751           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15752       }
15753       break;
15754
15755     case GAME_CTRL_ID_UNDO:
15756       // Important: When using "save snapshot when collecting an item" mode,
15757       // load last (current) snapshot for first "undo" after pressing "pause"
15758       // (else the last-but-one snapshot would be loaded, because the snapshot
15759       // pointer already points to the last snapshot when pressing "pause",
15760       // which is fine for "every step/move" mode, but not for "every collect")
15761       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15762           !game_undo_executed)
15763         steps--;
15764
15765       game_undo_executed = TRUE;
15766
15767       GameUndo(steps);
15768       break;
15769
15770     case GAME_CTRL_ID_REDO:
15771       GameRedo(steps);
15772       break;
15773
15774     case GAME_CTRL_ID_SAVE:
15775       TapeQuickSave();
15776       break;
15777
15778     case GAME_CTRL_ID_LOAD:
15779       TapeQuickLoad();
15780       break;
15781
15782     case SOUND_CTRL_ID_MUSIC:
15783     case SOUND_CTRL_ID_PANEL_MUSIC:
15784       if (setup.sound_music)
15785       { 
15786         setup.sound_music = FALSE;
15787
15788         FadeMusic();
15789       }
15790       else if (audio.music_available)
15791       { 
15792         setup.sound = setup.sound_music = TRUE;
15793
15794         SetAudioMode(setup.sound);
15795
15796         if (game_status == GAME_MODE_PLAYING)
15797           PlayLevelMusic();
15798       }
15799
15800       RedrawSoundButtonGadget(id);
15801
15802       break;
15803
15804     case SOUND_CTRL_ID_LOOPS:
15805     case SOUND_CTRL_ID_PANEL_LOOPS:
15806       if (setup.sound_loops)
15807         setup.sound_loops = FALSE;
15808       else if (audio.loops_available)
15809       {
15810         setup.sound = setup.sound_loops = TRUE;
15811
15812         SetAudioMode(setup.sound);
15813       }
15814
15815       RedrawSoundButtonGadget(id);
15816
15817       break;
15818
15819     case SOUND_CTRL_ID_SIMPLE:
15820     case SOUND_CTRL_ID_PANEL_SIMPLE:
15821       if (setup.sound_simple)
15822         setup.sound_simple = FALSE;
15823       else if (audio.sound_available)
15824       {
15825         setup.sound = setup.sound_simple = TRUE;
15826
15827         SetAudioMode(setup.sound);
15828       }
15829
15830       RedrawSoundButtonGadget(id);
15831
15832       break;
15833
15834     default:
15835       break;
15836   }
15837 }
15838
15839 static void HandleGameButtons(struct GadgetInfo *gi)
15840 {
15841   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15842 }
15843
15844 void HandleSoundButtonKeys(Key key)
15845 {
15846   if (key == setup.shortcut.sound_simple)
15847     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15848   else if (key == setup.shortcut.sound_loops)
15849     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15850   else if (key == setup.shortcut.sound_music)
15851     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15852 }