fixed bug in handling debug keys when compiled without symbol DEBUG defined
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     game.sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     game.sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (game.sokoban_fields_still_needed > 0)
1701     game.sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (game.sokoban_objects_still_needed > 0)
1707     game.sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       game.lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       game.friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                game.score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               game.gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      game.gems_still_needed > 0 ||
2243                      game.sokoban_fields_still_needed > 0 ||
2244                      game.sokoban_objects_still_needed > 0 ||
2245                      game.lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 game.health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     game.friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     game.sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     game.sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckFadeAll())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   FadeOut(fade_mask);
3369
3370   // needed if different viewport properties defined for playing
3371   ChangeViewportPropertiesIfNeeded();
3372
3373   ClearField();
3374
3375   DrawCompleteVideoDisplay();
3376
3377   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3378
3379   InitGameEngine();
3380   InitGameControlValues();
3381
3382   // don't play tapes over network
3383   network_playing = (network.enabled && !tape.playing);
3384
3385   for (i = 0; i < MAX_PLAYERS; i++)
3386   {
3387     struct PlayerInfo *player = &stored_player[i];
3388
3389     player->index_nr = i;
3390     player->index_bit = (1 << i);
3391     player->element_nr = EL_PLAYER_1 + i;
3392
3393     player->present = FALSE;
3394     player->active = FALSE;
3395     player->mapped = FALSE;
3396
3397     player->killed = FALSE;
3398     player->reanimated = FALSE;
3399     player->buried = FALSE;
3400
3401     player->action = 0;
3402     player->effective_action = 0;
3403     player->programmed_action = 0;
3404
3405     player->mouse_action.lx = 0;
3406     player->mouse_action.ly = 0;
3407     player->mouse_action.button = 0;
3408     player->mouse_action.button_hint = 0;
3409
3410     player->effective_mouse_action.lx = 0;
3411     player->effective_mouse_action.ly = 0;
3412     player->effective_mouse_action.button = 0;
3413     player->effective_mouse_action.button_hint = 0;
3414
3415     for (j = 0; j < MAX_NUM_KEYS; j++)
3416       player->key[j] = FALSE;
3417
3418     player->num_white_keys = 0;
3419
3420     player->dynabomb_count = 0;
3421     player->dynabomb_size = 1;
3422     player->dynabombs_left = 0;
3423     player->dynabomb_xl = FALSE;
3424
3425     player->MovDir = initial_move_dir;
3426     player->MovPos = 0;
3427     player->GfxPos = 0;
3428     player->GfxDir = initial_move_dir;
3429     player->GfxAction = ACTION_DEFAULT;
3430     player->Frame = 0;
3431     player->StepFrame = 0;
3432
3433     player->initial_element = player->element_nr;
3434     player->artwork_element =
3435       (level.use_artwork_element[i] ? level.artwork_element[i] :
3436        player->element_nr);
3437     player->use_murphy = FALSE;
3438
3439     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3440     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3441
3442     player->gravity = level.initial_player_gravity[i];
3443
3444     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3445
3446     player->actual_frame_counter = 0;
3447
3448     player->step_counter = 0;
3449
3450     player->last_move_dir = initial_move_dir;
3451
3452     player->is_active = FALSE;
3453
3454     player->is_waiting = FALSE;
3455     player->is_moving = FALSE;
3456     player->is_auto_moving = FALSE;
3457     player->is_digging = FALSE;
3458     player->is_snapping = FALSE;
3459     player->is_collecting = FALSE;
3460     player->is_pushing = FALSE;
3461     player->is_switching = FALSE;
3462     player->is_dropping = FALSE;
3463     player->is_dropping_pressed = FALSE;
3464
3465     player->is_bored = FALSE;
3466     player->is_sleeping = FALSE;
3467
3468     player->was_waiting = TRUE;
3469     player->was_moving = FALSE;
3470     player->was_snapping = FALSE;
3471     player->was_dropping = FALSE;
3472
3473     player->force_dropping = FALSE;
3474
3475     player->frame_counter_bored = -1;
3476     player->frame_counter_sleeping = -1;
3477
3478     player->anim_delay_counter = 0;
3479     player->post_delay_counter = 0;
3480
3481     player->dir_waiting = initial_move_dir;
3482     player->action_waiting = ACTION_DEFAULT;
3483     player->last_action_waiting = ACTION_DEFAULT;
3484     player->special_action_bored = ACTION_DEFAULT;
3485     player->special_action_sleeping = ACTION_DEFAULT;
3486
3487     player->switch_x = -1;
3488     player->switch_y = -1;
3489
3490     player->drop_x = -1;
3491     player->drop_y = -1;
3492
3493     player->show_envelope = 0;
3494
3495     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3496
3497     player->push_delay       = -1;      // initialized when pushing starts
3498     player->push_delay_value = game.initial_push_delay_value;
3499
3500     player->drop_delay = 0;
3501     player->drop_pressed_delay = 0;
3502
3503     player->last_jx = -1;
3504     player->last_jy = -1;
3505     player->jx = -1;
3506     player->jy = -1;
3507
3508     player->shield_normal_time_left = 0;
3509     player->shield_deadly_time_left = 0;
3510
3511     player->inventory_infinite_element = EL_UNDEFINED;
3512     player->inventory_size = 0;
3513
3514     if (level.use_initial_inventory[i])
3515     {
3516       for (j = 0; j < level.initial_inventory_size[i]; j++)
3517       {
3518         int element = level.initial_inventory_content[i][j];
3519         int collect_count = element_info[element].collect_count_initial;
3520         int k;
3521
3522         if (!IS_CUSTOM_ELEMENT(element))
3523           collect_count = 1;
3524
3525         if (collect_count == 0)
3526           player->inventory_infinite_element = element;
3527         else
3528           for (k = 0; k < collect_count; k++)
3529             if (player->inventory_size < MAX_INVENTORY_SIZE)
3530               player->inventory_element[player->inventory_size++] = element;
3531       }
3532     }
3533
3534     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3535     SnapField(player, 0, 0);
3536
3537     map_player_action[i] = i;
3538   }
3539
3540   network_player_action_received = FALSE;
3541
3542   // initial null action
3543   if (network_playing)
3544     SendToServer_MovePlayer(MV_NONE);
3545
3546   FrameCounter = 0;
3547   TimeFrames = 0;
3548   TimePlayed = 0;
3549   TimeLeft = level.time;
3550   TapeTime = 0;
3551
3552   ScreenMovDir = MV_NONE;
3553   ScreenMovPos = 0;
3554   ScreenGfxPos = 0;
3555
3556   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3557
3558   game.robot_wheel_x = -1;
3559   game.robot_wheel_y = -1;
3560
3561   game.exit_x = -1;
3562   game.exit_y = -1;
3563
3564   game.all_players_gone = FALSE;
3565
3566   game.LevelSolved = FALSE;
3567   game.GameOver = FALSE;
3568
3569   game.GamePlayed = !tape.playing;
3570
3571   game.LevelSolved_GameWon = FALSE;
3572   game.LevelSolved_GameEnd = FALSE;
3573   game.LevelSolved_SaveTape = FALSE;
3574   game.LevelSolved_SaveScore = FALSE;
3575
3576   game.LevelSolved_CountingTime = 0;
3577   game.LevelSolved_CountingScore = 0;
3578   game.LevelSolved_CountingHealth = 0;
3579
3580   game.panel.active = TRUE;
3581
3582   game.no_time_limit = (level.time == 0);
3583
3584   game.yamyam_content_nr = 0;
3585   game.robot_wheel_active = FALSE;
3586   game.magic_wall_active = FALSE;
3587   game.magic_wall_time_left = 0;
3588   game.light_time_left = 0;
3589   game.timegate_time_left = 0;
3590   game.switchgate_pos = 0;
3591   game.wind_direction = level.wind_direction_initial;
3592
3593   game.score = 0;
3594   game.score_final = 0;
3595
3596   game.health = MAX_HEALTH;
3597   game.health_final = MAX_HEALTH;
3598
3599   game.gems_still_needed = level.gems_needed;
3600   game.sokoban_fields_still_needed = 0;
3601   game.sokoban_objects_still_needed = 0;
3602   game.lights_still_needed = 0;
3603   game.players_still_needed = 0;
3604   game.friends_still_needed = 0;
3605
3606   game.lenses_time_left = 0;
3607   game.magnify_time_left = 0;
3608
3609   game.ball_state = level.ball_state_initial;
3610   game.ball_content_nr = 0;
3611
3612   game.explosions_delayed = TRUE;
3613
3614   game.envelope_active = FALSE;
3615
3616   for (i = 0; i < NUM_BELTS; i++)
3617   {
3618     game.belt_dir[i] = MV_NONE;
3619     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3620   }
3621
3622   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3623     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3624
3625 #if DEBUG_INIT_PLAYER
3626   DebugPrintPlayerStatus("Player status at level initialization");
3627 #endif
3628
3629   SCAN_PLAYFIELD(x, y)
3630   {
3631     Feld[x][y] = Last[x][y] = level.field[x][y];
3632     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3633     ChangeDelay[x][y] = 0;
3634     ChangePage[x][y] = -1;
3635     CustomValue[x][y] = 0;              // initialized in InitField()
3636     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3637     AmoebaNr[x][y] = 0;
3638     WasJustMoving[x][y] = 0;
3639     WasJustFalling[x][y] = 0;
3640     CheckCollision[x][y] = 0;
3641     CheckImpact[x][y] = 0;
3642     Stop[x][y] = FALSE;
3643     Pushed[x][y] = FALSE;
3644
3645     ChangeCount[x][y] = 0;
3646     ChangeEvent[x][y] = -1;
3647
3648     ExplodePhase[x][y] = 0;
3649     ExplodeDelay[x][y] = 0;
3650     ExplodeField[x][y] = EX_TYPE_NONE;
3651
3652     RunnerVisit[x][y] = 0;
3653     PlayerVisit[x][y] = 0;
3654
3655     GfxFrame[x][y] = 0;
3656     GfxRandom[x][y] = INIT_GFX_RANDOM();
3657     GfxElement[x][y] = EL_UNDEFINED;
3658     GfxAction[x][y] = ACTION_DEFAULT;
3659     GfxDir[x][y] = MV_NONE;
3660     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3661   }
3662
3663   SCAN_PLAYFIELD(x, y)
3664   {
3665     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3666       emulate_bd = FALSE;
3667     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3668       emulate_sb = FALSE;
3669     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3670       emulate_sp = FALSE;
3671
3672     InitField(x, y, TRUE);
3673
3674     ResetGfxAnimation(x, y);
3675   }
3676
3677   InitBeltMovement();
3678
3679   for (i = 0; i < MAX_PLAYERS; i++)
3680   {
3681     struct PlayerInfo *player = &stored_player[i];
3682
3683     // set number of special actions for bored and sleeping animation
3684     player->num_special_action_bored =
3685       get_num_special_action(player->artwork_element,
3686                              ACTION_BORING_1, ACTION_BORING_LAST);
3687     player->num_special_action_sleeping =
3688       get_num_special_action(player->artwork_element,
3689                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3690   }
3691
3692   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3693                     emulate_sb ? EMU_SOKOBAN :
3694                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3695
3696   // initialize type of slippery elements
3697   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3698   {
3699     if (!IS_CUSTOM_ELEMENT(i))
3700     {
3701       // default: elements slip down either to the left or right randomly
3702       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3703
3704       // SP style elements prefer to slip down on the left side
3705       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3706         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3707
3708       // BD style elements prefer to slip down on the left side
3709       if (game.emulation == EMU_BOULDERDASH)
3710         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3711     }
3712   }
3713
3714   // initialize explosion and ignition delay
3715   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3716   {
3717     if (!IS_CUSTOM_ELEMENT(i))
3718     {
3719       int num_phase = 8;
3720       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3721                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3722                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3723       int last_phase = (num_phase + 1) * delay;
3724       int half_phase = (num_phase / 2) * delay;
3725
3726       element_info[i].explosion_delay = last_phase - 1;
3727       element_info[i].ignition_delay = half_phase;
3728
3729       if (i == EL_BLACK_ORB)
3730         element_info[i].ignition_delay = 1;
3731     }
3732   }
3733
3734   // correct non-moving belts to start moving left
3735   for (i = 0; i < NUM_BELTS; i++)
3736     if (game.belt_dir[i] == MV_NONE)
3737       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3738
3739 #if USE_NEW_PLAYER_ASSIGNMENTS
3740   for (i = 0; i < MAX_PLAYERS; i++)
3741   {
3742     stored_player[i].connected = FALSE;
3743
3744     // in network game mode, the local player might not be the first player
3745     if (stored_player[i].connected_locally)
3746       local_player = &stored_player[i];
3747   }
3748
3749   if (!network.enabled)
3750     local_player->connected = TRUE;
3751
3752   if (tape.playing)
3753   {
3754     for (i = 0; i < MAX_PLAYERS; i++)
3755       stored_player[i].connected = tape.player_participates[i];
3756   }
3757   else if (network.enabled)
3758   {
3759     // add team mode players connected over the network (needed for correct
3760     // assignment of player figures from level to locally playing players)
3761
3762     for (i = 0; i < MAX_PLAYERS; i++)
3763       if (stored_player[i].connected_network)
3764         stored_player[i].connected = TRUE;
3765   }
3766   else if (game.team_mode)
3767   {
3768     // try to guess locally connected team mode players (needed for correct
3769     // assignment of player figures from level to locally playing players)
3770
3771     for (i = 0; i < MAX_PLAYERS; i++)
3772       if (setup.input[i].use_joystick ||
3773           setup.input[i].key.left != KSYM_UNDEFINED)
3774         stored_player[i].connected = TRUE;
3775   }
3776
3777 #if DEBUG_INIT_PLAYER
3778   DebugPrintPlayerStatus("Player status after level initialization");
3779 #endif
3780
3781 #if DEBUG_INIT_PLAYER
3782   if (options.debug)
3783     printf("Reassigning players ...\n");
3784 #endif
3785
3786   // check if any connected player was not found in playfield
3787   for (i = 0; i < MAX_PLAYERS; i++)
3788   {
3789     struct PlayerInfo *player = &stored_player[i];
3790
3791     if (player->connected && !player->present)
3792     {
3793       struct PlayerInfo *field_player = NULL;
3794
3795 #if DEBUG_INIT_PLAYER
3796       if (options.debug)
3797         printf("- looking for field player for player %d ...\n", i + 1);
3798 #endif
3799
3800       // assign first free player found that is present in the playfield
3801
3802       // first try: look for unmapped playfield player that is not connected
3803       for (j = 0; j < MAX_PLAYERS; j++)
3804         if (field_player == NULL &&
3805             stored_player[j].present &&
3806             !stored_player[j].mapped &&
3807             !stored_player[j].connected)
3808           field_player = &stored_player[j];
3809
3810       // second try: look for *any* unmapped playfield player
3811       for (j = 0; j < MAX_PLAYERS; j++)
3812         if (field_player == NULL &&
3813             stored_player[j].present &&
3814             !stored_player[j].mapped)
3815           field_player = &stored_player[j];
3816
3817       if (field_player != NULL)
3818       {
3819         int jx = field_player->jx, jy = field_player->jy;
3820
3821 #if DEBUG_INIT_PLAYER
3822         if (options.debug)
3823           printf("- found player %d\n", field_player->index_nr + 1);
3824 #endif
3825
3826         player->present = FALSE;
3827         player->active = FALSE;
3828
3829         field_player->present = TRUE;
3830         field_player->active = TRUE;
3831
3832         /*
3833         player->initial_element = field_player->initial_element;
3834         player->artwork_element = field_player->artwork_element;
3835
3836         player->block_last_field       = field_player->block_last_field;
3837         player->block_delay_adjustment = field_player->block_delay_adjustment;
3838         */
3839
3840         StorePlayer[jx][jy] = field_player->element_nr;
3841
3842         field_player->jx = field_player->last_jx = jx;
3843         field_player->jy = field_player->last_jy = jy;
3844
3845         if (local_player == player)
3846           local_player = field_player;
3847
3848         map_player_action[field_player->index_nr] = i;
3849
3850         field_player->mapped = TRUE;
3851
3852 #if DEBUG_INIT_PLAYER
3853         if (options.debug)
3854           printf("- map_player_action[%d] == %d\n",
3855                  field_player->index_nr + 1, i + 1);
3856 #endif
3857       }
3858     }
3859
3860     if (player->connected && player->present)
3861       player->mapped = TRUE;
3862   }
3863
3864 #if DEBUG_INIT_PLAYER
3865   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3866 #endif
3867
3868 #else
3869
3870   // check if any connected player was not found in playfield
3871   for (i = 0; i < MAX_PLAYERS; i++)
3872   {
3873     struct PlayerInfo *player = &stored_player[i];
3874
3875     if (player->connected && !player->present)
3876     {
3877       for (j = 0; j < MAX_PLAYERS; j++)
3878       {
3879         struct PlayerInfo *field_player = &stored_player[j];
3880         int jx = field_player->jx, jy = field_player->jy;
3881
3882         // assign first free player found that is present in the playfield
3883         if (field_player->present && !field_player->connected)
3884         {
3885           player->present = TRUE;
3886           player->active = TRUE;
3887
3888           field_player->present = FALSE;
3889           field_player->active = FALSE;
3890
3891           player->initial_element = field_player->initial_element;
3892           player->artwork_element = field_player->artwork_element;
3893
3894           player->block_last_field       = field_player->block_last_field;
3895           player->block_delay_adjustment = field_player->block_delay_adjustment;
3896
3897           StorePlayer[jx][jy] = player->element_nr;
3898
3899           player->jx = player->last_jx = jx;
3900           player->jy = player->last_jy = jy;
3901
3902           break;
3903         }
3904       }
3905     }
3906   }
3907 #endif
3908
3909 #if 0
3910   printf("::: local_player->present == %d\n", local_player->present);
3911 #endif
3912
3913   // set focus to local player for network games, else to all players
3914   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3915   game.centered_player_nr_next = game.centered_player_nr;
3916   game.set_centered_player = FALSE;
3917
3918   if (network_playing && tape.recording)
3919   {
3920     // store client dependent player focus when recording network games
3921     tape.centered_player_nr_next = game.centered_player_nr_next;
3922     tape.set_centered_player = TRUE;
3923   }
3924
3925   if (tape.playing)
3926   {
3927     // when playing a tape, eliminate all players who do not participate
3928
3929 #if USE_NEW_PLAYER_ASSIGNMENTS
3930
3931     if (!game.team_mode)
3932     {
3933       for (i = 0; i < MAX_PLAYERS; i++)
3934       {
3935         if (stored_player[i].active &&
3936             !tape.player_participates[map_player_action[i]])
3937         {
3938           struct PlayerInfo *player = &stored_player[i];
3939           int jx = player->jx, jy = player->jy;
3940
3941 #if DEBUG_INIT_PLAYER
3942           if (options.debug)
3943             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3944 #endif
3945
3946           player->active = FALSE;
3947           StorePlayer[jx][jy] = 0;
3948           Feld[jx][jy] = EL_EMPTY;
3949         }
3950       }
3951     }
3952
3953 #else
3954
3955     for (i = 0; i < MAX_PLAYERS; i++)
3956     {
3957       if (stored_player[i].active &&
3958           !tape.player_participates[i])
3959       {
3960         struct PlayerInfo *player = &stored_player[i];
3961         int jx = player->jx, jy = player->jy;
3962
3963         player->active = FALSE;
3964         StorePlayer[jx][jy] = 0;
3965         Feld[jx][jy] = EL_EMPTY;
3966       }
3967     }
3968 #endif
3969   }
3970   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3971   {
3972     // when in single player mode, eliminate all but the local player
3973
3974     for (i = 0; i < MAX_PLAYERS; i++)
3975     {
3976       struct PlayerInfo *player = &stored_player[i];
3977
3978       if (player->active && player != local_player)
3979       {
3980         int jx = player->jx, jy = player->jy;
3981
3982         player->active = FALSE;
3983         player->present = FALSE;
3984
3985         StorePlayer[jx][jy] = 0;
3986         Feld[jx][jy] = EL_EMPTY;
3987       }
3988     }
3989   }
3990
3991   for (i = 0; i < MAX_PLAYERS; i++)
3992     if (stored_player[i].active)
3993       game.players_still_needed++;
3994
3995   if (level.solved_by_one_player)
3996     game.players_still_needed = 1;
3997
3998   // when recording the game, store which players take part in the game
3999   if (tape.recording)
4000   {
4001 #if USE_NEW_PLAYER_ASSIGNMENTS
4002     for (i = 0; i < MAX_PLAYERS; i++)
4003       if (stored_player[i].connected)
4004         tape.player_participates[i] = TRUE;
4005 #else
4006     for (i = 0; i < MAX_PLAYERS; i++)
4007       if (stored_player[i].active)
4008         tape.player_participates[i] = TRUE;
4009 #endif
4010   }
4011
4012 #if DEBUG_INIT_PLAYER
4013   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4014 #endif
4015
4016   if (BorderElement == EL_EMPTY)
4017   {
4018     SBX_Left = 0;
4019     SBX_Right = lev_fieldx - SCR_FIELDX;
4020     SBY_Upper = 0;
4021     SBY_Lower = lev_fieldy - SCR_FIELDY;
4022   }
4023   else
4024   {
4025     SBX_Left = -1;
4026     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4027     SBY_Upper = -1;
4028     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4029   }
4030
4031   if (full_lev_fieldx <= SCR_FIELDX)
4032     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4033   if (full_lev_fieldy <= SCR_FIELDY)
4034     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4035
4036   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4037     SBX_Left--;
4038   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4039     SBY_Upper--;
4040
4041   // if local player not found, look for custom element that might create
4042   // the player (make some assumptions about the right custom element)
4043   if (!local_player->present)
4044   {
4045     int start_x = 0, start_y = 0;
4046     int found_rating = 0;
4047     int found_element = EL_UNDEFINED;
4048     int player_nr = local_player->index_nr;
4049
4050     SCAN_PLAYFIELD(x, y)
4051     {
4052       int element = Feld[x][y];
4053       int content;
4054       int xx, yy;
4055       boolean is_player;
4056
4057       if (level.use_start_element[player_nr] &&
4058           level.start_element[player_nr] == element &&
4059           found_rating < 4)
4060       {
4061         start_x = x;
4062         start_y = y;
4063
4064         found_rating = 4;
4065         found_element = element;
4066       }
4067
4068       if (!IS_CUSTOM_ELEMENT(element))
4069         continue;
4070
4071       if (CAN_CHANGE(element))
4072       {
4073         for (i = 0; i < element_info[element].num_change_pages; i++)
4074         {
4075           // check for player created from custom element as single target
4076           content = element_info[element].change_page[i].target_element;
4077           is_player = ELEM_IS_PLAYER(content);
4078
4079           if (is_player && (found_rating < 3 ||
4080                             (found_rating == 3 && element < found_element)))
4081           {
4082             start_x = x;
4083             start_y = y;
4084
4085             found_rating = 3;
4086             found_element = element;
4087           }
4088         }
4089       }
4090
4091       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4092       {
4093         // check for player created from custom element as explosion content
4094         content = element_info[element].content.e[xx][yy];
4095         is_player = ELEM_IS_PLAYER(content);
4096
4097         if (is_player && (found_rating < 2 ||
4098                           (found_rating == 2 && element < found_element)))
4099         {
4100           start_x = x + xx - 1;
4101           start_y = y + yy - 1;
4102
4103           found_rating = 2;
4104           found_element = element;
4105         }
4106
4107         if (!CAN_CHANGE(element))
4108           continue;
4109
4110         for (i = 0; i < element_info[element].num_change_pages; i++)
4111         {
4112           // check for player created from custom element as extended target
4113           content =
4114             element_info[element].change_page[i].target_content.e[xx][yy];
4115
4116           is_player = ELEM_IS_PLAYER(content);
4117
4118           if (is_player && (found_rating < 1 ||
4119                             (found_rating == 1 && element < found_element)))
4120           {
4121             start_x = x + xx - 1;
4122             start_y = y + yy - 1;
4123
4124             found_rating = 1;
4125             found_element = element;
4126           }
4127         }
4128       }
4129     }
4130
4131     scroll_x = SCROLL_POSITION_X(start_x);
4132     scroll_y = SCROLL_POSITION_Y(start_y);
4133   }
4134   else
4135   {
4136     scroll_x = SCROLL_POSITION_X(local_player->jx);
4137     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4138   }
4139
4140   // !!! FIX THIS (START) !!!
4141   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4142   {
4143     InitGameEngine_EM();
4144   }
4145   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4146   {
4147     InitGameEngine_SP();
4148   }
4149   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4150   {
4151     InitGameEngine_MM();
4152   }
4153   else
4154   {
4155     DrawLevel(REDRAW_FIELD);
4156     DrawAllPlayers();
4157
4158     // after drawing the level, correct some elements
4159     if (game.timegate_time_left == 0)
4160       CloseAllOpenTimegates();
4161   }
4162
4163   // blit playfield from scroll buffer to normal back buffer for fading in
4164   BlitScreenToBitmap(backbuffer);
4165   // !!! FIX THIS (END) !!!
4166
4167   DrawMaskedBorder(fade_mask);
4168
4169   FadeIn(fade_mask);
4170
4171 #if 1
4172   // full screen redraw is required at this point in the following cases:
4173   // - special editor door undrawn when game was started from level editor
4174   // - drawing area (playfield) was changed and has to be removed completely
4175   redraw_mask = REDRAW_ALL;
4176   BackToFront();
4177 #endif
4178
4179   if (!game.restart_level)
4180   {
4181     // copy default game door content to main double buffer
4182
4183     // !!! CHECK AGAIN !!!
4184     SetPanelBackground();
4185     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4186     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4187   }
4188
4189   SetPanelBackground();
4190   SetDrawBackgroundMask(REDRAW_DOOR_1);
4191
4192   UpdateAndDisplayGameControlValues();
4193
4194   if (!game.restart_level)
4195   {
4196     UnmapGameButtons();
4197     UnmapTapeButtons();
4198
4199     FreeGameButtons();
4200     CreateGameButtons();
4201
4202     MapGameButtons();
4203     MapTapeButtons();
4204
4205     // copy actual game door content to door double buffer for OpenDoor()
4206     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4207
4208     OpenDoor(DOOR_OPEN_ALL);
4209
4210     KeyboardAutoRepeatOffUnlessAutoplay();
4211
4212 #if DEBUG_INIT_PLAYER
4213     DebugPrintPlayerStatus("Player status (final)");
4214 #endif
4215   }
4216
4217   UnmapAllGadgets();
4218
4219   MapGameButtons();
4220   MapTapeButtons();
4221
4222   if (!game.restart_level && !tape.playing)
4223   {
4224     LevelStats_incPlayed(level_nr);
4225
4226     SaveLevelSetup_SeriesInfo();
4227   }
4228
4229   game.restart_level = FALSE;
4230   game.restart_game_message = NULL;
4231   game.request_active = FALSE;
4232
4233   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4234     InitGameActions_MM();
4235
4236   SaveEngineSnapshotToListInitial();
4237
4238   if (!game.restart_level)
4239   {
4240     PlaySound(SND_GAME_STARTING);
4241
4242     if (setup.sound_music)
4243       PlayLevelMusic();
4244   }
4245 }
4246
4247 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4248                         int actual_player_x, int actual_player_y)
4249 {
4250   // this is used for non-R'n'D game engines to update certain engine values
4251
4252   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4253   {
4254     actual_player_x = correctLevelPosX_EM(actual_player_x);
4255     actual_player_y = correctLevelPosY_EM(actual_player_y);
4256   }
4257
4258   // needed to determine if sounds are played within the visible screen area
4259   scroll_x = actual_scroll_x;
4260   scroll_y = actual_scroll_y;
4261
4262   // needed to get player position for "follow finger" playing input method
4263   local_player->jx = actual_player_x;
4264   local_player->jy = actual_player_y;
4265 }
4266
4267 void InitMovDir(int x, int y)
4268 {
4269   int i, element = Feld[x][y];
4270   static int xy[4][2] =
4271   {
4272     {  0, +1 },
4273     { +1,  0 },
4274     {  0, -1 },
4275     { -1,  0 }
4276   };
4277   static int direction[3][4] =
4278   {
4279     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4280     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4281     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4282   };
4283
4284   switch (element)
4285   {
4286     case EL_BUG_RIGHT:
4287     case EL_BUG_UP:
4288     case EL_BUG_LEFT:
4289     case EL_BUG_DOWN:
4290       Feld[x][y] = EL_BUG;
4291       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4292       break;
4293
4294     case EL_SPACESHIP_RIGHT:
4295     case EL_SPACESHIP_UP:
4296     case EL_SPACESHIP_LEFT:
4297     case EL_SPACESHIP_DOWN:
4298       Feld[x][y] = EL_SPACESHIP;
4299       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4300       break;
4301
4302     case EL_BD_BUTTERFLY_RIGHT:
4303     case EL_BD_BUTTERFLY_UP:
4304     case EL_BD_BUTTERFLY_LEFT:
4305     case EL_BD_BUTTERFLY_DOWN:
4306       Feld[x][y] = EL_BD_BUTTERFLY;
4307       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4308       break;
4309
4310     case EL_BD_FIREFLY_RIGHT:
4311     case EL_BD_FIREFLY_UP:
4312     case EL_BD_FIREFLY_LEFT:
4313     case EL_BD_FIREFLY_DOWN:
4314       Feld[x][y] = EL_BD_FIREFLY;
4315       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4316       break;
4317
4318     case EL_PACMAN_RIGHT:
4319     case EL_PACMAN_UP:
4320     case EL_PACMAN_LEFT:
4321     case EL_PACMAN_DOWN:
4322       Feld[x][y] = EL_PACMAN;
4323       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4324       break;
4325
4326     case EL_YAMYAM_LEFT:
4327     case EL_YAMYAM_RIGHT:
4328     case EL_YAMYAM_UP:
4329     case EL_YAMYAM_DOWN:
4330       Feld[x][y] = EL_YAMYAM;
4331       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4332       break;
4333
4334     case EL_SP_SNIKSNAK:
4335       MovDir[x][y] = MV_UP;
4336       break;
4337
4338     case EL_SP_ELECTRON:
4339       MovDir[x][y] = MV_LEFT;
4340       break;
4341
4342     case EL_MOLE_LEFT:
4343     case EL_MOLE_RIGHT:
4344     case EL_MOLE_UP:
4345     case EL_MOLE_DOWN:
4346       Feld[x][y] = EL_MOLE;
4347       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4348       break;
4349
4350     default:
4351       if (IS_CUSTOM_ELEMENT(element))
4352       {
4353         struct ElementInfo *ei = &element_info[element];
4354         int move_direction_initial = ei->move_direction_initial;
4355         int move_pattern = ei->move_pattern;
4356
4357         if (move_direction_initial == MV_START_PREVIOUS)
4358         {
4359           if (MovDir[x][y] != MV_NONE)
4360             return;
4361
4362           move_direction_initial = MV_START_AUTOMATIC;
4363         }
4364
4365         if (move_direction_initial == MV_START_RANDOM)
4366           MovDir[x][y] = 1 << RND(4);
4367         else if (move_direction_initial & MV_ANY_DIRECTION)
4368           MovDir[x][y] = move_direction_initial;
4369         else if (move_pattern == MV_ALL_DIRECTIONS ||
4370                  move_pattern == MV_TURNING_LEFT ||
4371                  move_pattern == MV_TURNING_RIGHT ||
4372                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4373                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4374                  move_pattern == MV_TURNING_RANDOM)
4375           MovDir[x][y] = 1 << RND(4);
4376         else if (move_pattern == MV_HORIZONTAL)
4377           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4378         else if (move_pattern == MV_VERTICAL)
4379           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4380         else if (move_pattern & MV_ANY_DIRECTION)
4381           MovDir[x][y] = element_info[element].move_pattern;
4382         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4383                  move_pattern == MV_ALONG_RIGHT_SIDE)
4384         {
4385           // use random direction as default start direction
4386           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4387             MovDir[x][y] = 1 << RND(4);
4388
4389           for (i = 0; i < NUM_DIRECTIONS; i++)
4390           {
4391             int x1 = x + xy[i][0];
4392             int y1 = y + xy[i][1];
4393
4394             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4395             {
4396               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4397                 MovDir[x][y] = direction[0][i];
4398               else
4399                 MovDir[x][y] = direction[1][i];
4400
4401               break;
4402             }
4403           }
4404         }                
4405       }
4406       else
4407       {
4408         MovDir[x][y] = 1 << RND(4);
4409
4410         if (element != EL_BUG &&
4411             element != EL_SPACESHIP &&
4412             element != EL_BD_BUTTERFLY &&
4413             element != EL_BD_FIREFLY)
4414           break;
4415
4416         for (i = 0; i < NUM_DIRECTIONS; i++)
4417         {
4418           int x1 = x + xy[i][0];
4419           int y1 = y + xy[i][1];
4420
4421           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4422           {
4423             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4424             {
4425               MovDir[x][y] = direction[0][i];
4426               break;
4427             }
4428             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4429                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4430             {
4431               MovDir[x][y] = direction[1][i];
4432               break;
4433             }
4434           }
4435         }
4436       }
4437       break;
4438   }
4439
4440   GfxDir[x][y] = MovDir[x][y];
4441 }
4442
4443 void InitAmoebaNr(int x, int y)
4444 {
4445   int i;
4446   int group_nr = AmoebeNachbarNr(x, y);
4447
4448   if (group_nr == 0)
4449   {
4450     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4451     {
4452       if (AmoebaCnt[i] == 0)
4453       {
4454         group_nr = i;
4455         break;
4456       }
4457     }
4458   }
4459
4460   AmoebaNr[x][y] = group_nr;
4461   AmoebaCnt[group_nr]++;
4462   AmoebaCnt2[group_nr]++;
4463 }
4464
4465 static void LevelSolved(void)
4466 {
4467   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4468       game.players_still_needed > 0)
4469     return;
4470
4471   game.LevelSolved = TRUE;
4472   game.GameOver = TRUE;
4473
4474   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4475                       level.native_em_level->lev->score :
4476                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4477                       game_mm.score :
4478                       game.score);
4479   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4480                        MM_HEALTH(game_mm.laser_overload_value) :
4481                        game.health);
4482
4483   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4484   game.LevelSolved_CountingScore = game.score_final;
4485   game.LevelSolved_CountingHealth = game.health_final;
4486 }
4487
4488 void GameWon(void)
4489 {
4490   static int time_count_steps;
4491   static int time, time_final;
4492   static int score, score_final;
4493   static int health, health_final;
4494   static int game_over_delay_1 = 0;
4495   static int game_over_delay_2 = 0;
4496   static int game_over_delay_3 = 0;
4497   int game_over_delay_value_1 = 50;
4498   int game_over_delay_value_2 = 25;
4499   int game_over_delay_value_3 = 50;
4500
4501   if (!game.LevelSolved_GameWon)
4502   {
4503     int i;
4504
4505     // do not start end game actions before the player stops moving (to exit)
4506     if (local_player->active && local_player->MovPos)
4507       return;
4508
4509     game.LevelSolved_GameWon = TRUE;
4510     game.LevelSolved_SaveTape = tape.recording;
4511     game.LevelSolved_SaveScore = !tape.playing;
4512
4513     if (!tape.playing)
4514     {
4515       LevelStats_incSolved(level_nr);
4516
4517       SaveLevelSetup_SeriesInfo();
4518     }
4519
4520     if (tape.auto_play)         // tape might already be stopped here
4521       tape.auto_play_level_solved = TRUE;
4522
4523     TapeStop();
4524
4525     game_over_delay_1 = 0;
4526     game_over_delay_2 = 0;
4527     game_over_delay_3 = game_over_delay_value_3;
4528
4529     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4530     score = score_final = game.score_final;
4531     health = health_final = game.health_final;
4532
4533     if (level.score[SC_TIME_BONUS] > 0)
4534     {
4535       if (TimeLeft > 0)
4536       {
4537         time_final = 0;
4538         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4539       }
4540       else if (game.no_time_limit && TimePlayed < 999)
4541       {
4542         time_final = 999;
4543         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4544       }
4545
4546       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4547
4548       game_over_delay_1 = game_over_delay_value_1;
4549
4550       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4551       {
4552         health_final = 0;
4553         score_final += health * level.score[SC_TIME_BONUS];
4554
4555         game_over_delay_2 = game_over_delay_value_2;
4556       }
4557
4558       game.score_final = score_final;
4559       game.health_final = health_final;
4560     }
4561
4562     if (level_editor_test_game)
4563     {
4564       time = time_final;
4565       score = score_final;
4566
4567       game.LevelSolved_CountingTime = time;
4568       game.LevelSolved_CountingScore = score;
4569
4570       game_panel_controls[GAME_PANEL_TIME].value = time;
4571       game_panel_controls[GAME_PANEL_SCORE].value = score;
4572
4573       DisplayGameControlValues();
4574     }
4575
4576     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4577     {
4578       // check if last player has left the level
4579       if (game.exit_x >= 0 &&
4580           game.exit_y >= 0)
4581       {
4582         int x = game.exit_x;
4583         int y = game.exit_y;
4584         int element = Feld[x][y];
4585
4586         // close exit door after last player
4587         if ((game.all_players_gone &&
4588              (element == EL_EXIT_OPEN ||
4589               element == EL_SP_EXIT_OPEN ||
4590               element == EL_STEEL_EXIT_OPEN)) ||
4591             element == EL_EM_EXIT_OPEN ||
4592             element == EL_EM_STEEL_EXIT_OPEN)
4593         {
4594
4595           Feld[x][y] =
4596             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4597              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4598              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4599              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4600              EL_EM_STEEL_EXIT_CLOSING);
4601
4602           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4603         }
4604
4605         // player disappears
4606         DrawLevelField(x, y);
4607       }
4608
4609       for (i = 0; i < MAX_PLAYERS; i++)
4610       {
4611         struct PlayerInfo *player = &stored_player[i];
4612
4613         if (player->present)
4614         {
4615           RemovePlayer(player);
4616
4617           // player disappears
4618           DrawLevelField(player->jx, player->jy);
4619         }
4620       }
4621     }
4622
4623     PlaySound(SND_GAME_WINNING);
4624   }
4625
4626   if (game_over_delay_1 > 0)
4627   {
4628     game_over_delay_1--;
4629
4630     return;
4631   }
4632
4633   if (time != time_final)
4634   {
4635     int time_to_go = ABS(time_final - time);
4636     int time_count_dir = (time < time_final ? +1 : -1);
4637
4638     if (time_to_go < time_count_steps)
4639       time_count_steps = 1;
4640
4641     time  += time_count_steps * time_count_dir;
4642     score += time_count_steps * level.score[SC_TIME_BONUS];
4643
4644     game.LevelSolved_CountingTime = time;
4645     game.LevelSolved_CountingScore = score;
4646
4647     game_panel_controls[GAME_PANEL_TIME].value = time;
4648     game_panel_controls[GAME_PANEL_SCORE].value = score;
4649
4650     DisplayGameControlValues();
4651
4652     if (time == time_final)
4653       StopSound(SND_GAME_LEVELTIME_BONUS);
4654     else if (setup.sound_loops)
4655       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4656     else
4657       PlaySound(SND_GAME_LEVELTIME_BONUS);
4658
4659     return;
4660   }
4661
4662   if (game_over_delay_2 > 0)
4663   {
4664     game_over_delay_2--;
4665
4666     return;
4667   }
4668
4669   if (health != health_final)
4670   {
4671     int health_count_dir = (health < health_final ? +1 : -1);
4672
4673     health += health_count_dir;
4674     score  += level.score[SC_TIME_BONUS];
4675
4676     game.LevelSolved_CountingHealth = health;
4677     game.LevelSolved_CountingScore = score;
4678
4679     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4680     game_panel_controls[GAME_PANEL_SCORE].value = score;
4681
4682     DisplayGameControlValues();
4683
4684     if (health == health_final)
4685       StopSound(SND_GAME_LEVELTIME_BONUS);
4686     else if (setup.sound_loops)
4687       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4688     else
4689       PlaySound(SND_GAME_LEVELTIME_BONUS);
4690
4691     return;
4692   }
4693
4694   game.panel.active = FALSE;
4695
4696   if (game_over_delay_3 > 0)
4697   {
4698     game_over_delay_3--;
4699
4700     return;
4701   }
4702
4703   GameEnd();
4704 }
4705
4706 void GameEnd(void)
4707 {
4708   // used instead of "level_nr" (needed for network games)
4709   int last_level_nr = levelset.level_nr;
4710   int hi_pos;
4711
4712   game.LevelSolved_GameEnd = TRUE;
4713
4714   if (game.LevelSolved_SaveTape)
4715   {
4716     // make sure that request dialog to save tape does not open door again
4717     if (!global.use_envelope_request)
4718       CloseDoor(DOOR_CLOSE_1);
4719
4720     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4721   }
4722
4723   // if no tape is to be saved, close both doors simultaneously
4724   CloseDoor(DOOR_CLOSE_ALL);
4725
4726   if (level_editor_test_game)
4727   {
4728     SetGameStatus(GAME_MODE_MAIN);
4729
4730     DrawMainMenu();
4731
4732     return;
4733   }
4734
4735   if (!game.LevelSolved_SaveScore)
4736   {
4737     SetGameStatus(GAME_MODE_MAIN);
4738
4739     DrawMainMenu();
4740
4741     return;
4742   }
4743
4744   if (level_nr == leveldir_current->handicap_level)
4745   {
4746     leveldir_current->handicap_level++;
4747
4748     SaveLevelSetup_SeriesInfo();
4749   }
4750
4751   if (setup.increment_levels &&
4752       level_nr < leveldir_current->last_level &&
4753       !network_playing)
4754   {
4755     level_nr++;         // advance to next level
4756     TapeErase();        // start with empty tape
4757
4758     if (setup.auto_play_next_level)
4759     {
4760       LoadLevel(level_nr);
4761
4762       SaveLevelSetup_SeriesInfo();
4763     }
4764   }
4765
4766   hi_pos = NewHiScore(last_level_nr);
4767
4768   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4769   {
4770     SetGameStatus(GAME_MODE_SCORES);
4771
4772     DrawHallOfFame(last_level_nr, hi_pos);
4773   }
4774   else if (setup.auto_play_next_level && setup.increment_levels &&
4775            last_level_nr < leveldir_current->last_level &&
4776            !network_playing)
4777   {
4778     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4779   }
4780   else
4781   {
4782     SetGameStatus(GAME_MODE_MAIN);
4783
4784     DrawMainMenu();
4785   }
4786 }
4787
4788 int NewHiScore(int level_nr)
4789 {
4790   int k, l;
4791   int position = -1;
4792   boolean one_score_entry_per_name = !program.many_scores_per_name;
4793
4794   LoadScore(level_nr);
4795
4796   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4797       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4798     return -1;
4799
4800   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4801   {
4802     if (game.score_final > highscore[k].Score)
4803     {
4804       // player has made it to the hall of fame
4805
4806       if (k < MAX_SCORE_ENTRIES - 1)
4807       {
4808         int m = MAX_SCORE_ENTRIES - 1;
4809
4810         if (one_score_entry_per_name)
4811         {
4812           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4813             if (strEqual(setup.player_name, highscore[l].Name))
4814               m = l;
4815
4816           if (m == k)   // player's new highscore overwrites his old one
4817             goto put_into_list;
4818         }
4819
4820         for (l = m; l > k; l--)
4821         {
4822           strcpy(highscore[l].Name, highscore[l - 1].Name);
4823           highscore[l].Score = highscore[l - 1].Score;
4824         }
4825       }
4826
4827       put_into_list:
4828
4829       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4830       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4831       highscore[k].Score = game.score_final;
4832       position = k;
4833
4834       break;
4835     }
4836     else if (one_score_entry_per_name &&
4837              !strncmp(setup.player_name, highscore[k].Name,
4838                       MAX_PLAYER_NAME_LEN))
4839       break;    // player already there with a higher score
4840   }
4841
4842   if (position >= 0) 
4843     SaveScore(level_nr);
4844
4845   return position;
4846 }
4847
4848 static int getElementMoveStepsizeExt(int x, int y, int direction)
4849 {
4850   int element = Feld[x][y];
4851   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4852   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4853   int horiz_move = (dx != 0);
4854   int sign = (horiz_move ? dx : dy);
4855   int step = sign * element_info[element].move_stepsize;
4856
4857   // special values for move stepsize for spring and things on conveyor belt
4858   if (horiz_move)
4859   {
4860     if (CAN_FALL(element) &&
4861         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4862       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4863     else if (element == EL_SPRING)
4864       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4865   }
4866
4867   return step;
4868 }
4869
4870 static int getElementMoveStepsize(int x, int y)
4871 {
4872   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4873 }
4874
4875 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4876 {
4877   if (player->GfxAction != action || player->GfxDir != dir)
4878   {
4879     player->GfxAction = action;
4880     player->GfxDir = dir;
4881     player->Frame = 0;
4882     player->StepFrame = 0;
4883   }
4884 }
4885
4886 static void ResetGfxFrame(int x, int y)
4887 {
4888   // profiling showed that "autotest" spends 10~20% of its time in this function
4889   if (DrawingDeactivatedField())
4890     return;
4891
4892   int element = Feld[x][y];
4893   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4894
4895   if (graphic_info[graphic].anim_global_sync)
4896     GfxFrame[x][y] = FrameCounter;
4897   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4898     GfxFrame[x][y] = CustomValue[x][y];
4899   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4900     GfxFrame[x][y] = element_info[element].collect_score;
4901   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4902     GfxFrame[x][y] = ChangeDelay[x][y];
4903 }
4904
4905 static void ResetGfxAnimation(int x, int y)
4906 {
4907   GfxAction[x][y] = ACTION_DEFAULT;
4908   GfxDir[x][y] = MovDir[x][y];
4909   GfxFrame[x][y] = 0;
4910
4911   ResetGfxFrame(x, y);
4912 }
4913
4914 static void ResetRandomAnimationValue(int x, int y)
4915 {
4916   GfxRandom[x][y] = INIT_GFX_RANDOM();
4917 }
4918
4919 static void InitMovingField(int x, int y, int direction)
4920 {
4921   int element = Feld[x][y];
4922   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4923   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4924   int newx = x + dx;
4925   int newy = y + dy;
4926   boolean is_moving_before, is_moving_after;
4927
4928   // check if element was/is moving or being moved before/after mode change
4929   is_moving_before = (WasJustMoving[x][y] != 0);
4930   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4931
4932   // reset animation only for moving elements which change direction of moving
4933   // or which just started or stopped moving
4934   // (else CEs with property "can move" / "not moving" are reset each frame)
4935   if (is_moving_before != is_moving_after ||
4936       direction != MovDir[x][y])
4937     ResetGfxAnimation(x, y);
4938
4939   MovDir[x][y] = direction;
4940   GfxDir[x][y] = direction;
4941
4942   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4943                      direction == MV_DOWN && CAN_FALL(element) ?
4944                      ACTION_FALLING : ACTION_MOVING);
4945
4946   // this is needed for CEs with property "can move" / "not moving"
4947
4948   if (is_moving_after)
4949   {
4950     if (Feld[newx][newy] == EL_EMPTY)
4951       Feld[newx][newy] = EL_BLOCKED;
4952
4953     MovDir[newx][newy] = MovDir[x][y];
4954
4955     CustomValue[newx][newy] = CustomValue[x][y];
4956
4957     GfxFrame[newx][newy] = GfxFrame[x][y];
4958     GfxRandom[newx][newy] = GfxRandom[x][y];
4959     GfxAction[newx][newy] = GfxAction[x][y];
4960     GfxDir[newx][newy] = GfxDir[x][y];
4961   }
4962 }
4963
4964 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4965 {
4966   int direction = MovDir[x][y];
4967   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4968   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4969
4970   *goes_to_x = newx;
4971   *goes_to_y = newy;
4972 }
4973
4974 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4975 {
4976   int oldx = x, oldy = y;
4977   int direction = MovDir[x][y];
4978
4979   if (direction == MV_LEFT)
4980     oldx++;
4981   else if (direction == MV_RIGHT)
4982     oldx--;
4983   else if (direction == MV_UP)
4984     oldy++;
4985   else if (direction == MV_DOWN)
4986     oldy--;
4987
4988   *comes_from_x = oldx;
4989   *comes_from_y = oldy;
4990 }
4991
4992 static int MovingOrBlocked2Element(int x, int y)
4993 {
4994   int element = Feld[x][y];
4995
4996   if (element == EL_BLOCKED)
4997   {
4998     int oldx, oldy;
4999
5000     Blocked2Moving(x, y, &oldx, &oldy);
5001     return Feld[oldx][oldy];
5002   }
5003   else
5004     return element;
5005 }
5006
5007 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5008 {
5009   // like MovingOrBlocked2Element(), but if element is moving
5010   // and (x,y) is the field the moving element is just leaving,
5011   // return EL_BLOCKED instead of the element value
5012   int element = Feld[x][y];
5013
5014   if (IS_MOVING(x, y))
5015   {
5016     if (element == EL_BLOCKED)
5017     {
5018       int oldx, oldy;
5019
5020       Blocked2Moving(x, y, &oldx, &oldy);
5021       return Feld[oldx][oldy];
5022     }
5023     else
5024       return EL_BLOCKED;
5025   }
5026   else
5027     return element;
5028 }
5029
5030 static void RemoveField(int x, int y)
5031 {
5032   Feld[x][y] = EL_EMPTY;
5033
5034   MovPos[x][y] = 0;
5035   MovDir[x][y] = 0;
5036   MovDelay[x][y] = 0;
5037
5038   CustomValue[x][y] = 0;
5039
5040   AmoebaNr[x][y] = 0;
5041   ChangeDelay[x][y] = 0;
5042   ChangePage[x][y] = -1;
5043   Pushed[x][y] = FALSE;
5044
5045   GfxElement[x][y] = EL_UNDEFINED;
5046   GfxAction[x][y] = ACTION_DEFAULT;
5047   GfxDir[x][y] = MV_NONE;
5048 }
5049
5050 static void RemoveMovingField(int x, int y)
5051 {
5052   int oldx = x, oldy = y, newx = x, newy = y;
5053   int element = Feld[x][y];
5054   int next_element = EL_UNDEFINED;
5055
5056   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5057     return;
5058
5059   if (IS_MOVING(x, y))
5060   {
5061     Moving2Blocked(x, y, &newx, &newy);
5062
5063     if (Feld[newx][newy] != EL_BLOCKED)
5064     {
5065       // element is moving, but target field is not free (blocked), but
5066       // already occupied by something different (example: acid pool);
5067       // in this case, only remove the moving field, but not the target
5068
5069       RemoveField(oldx, oldy);
5070
5071       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5072
5073       TEST_DrawLevelField(oldx, oldy);
5074
5075       return;
5076     }
5077   }
5078   else if (element == EL_BLOCKED)
5079   {
5080     Blocked2Moving(x, y, &oldx, &oldy);
5081     if (!IS_MOVING(oldx, oldy))
5082       return;
5083   }
5084
5085   if (element == EL_BLOCKED &&
5086       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5087        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5088        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5089        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5090        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5091        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5092     next_element = get_next_element(Feld[oldx][oldy]);
5093
5094   RemoveField(oldx, oldy);
5095   RemoveField(newx, newy);
5096
5097   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5098
5099   if (next_element != EL_UNDEFINED)
5100     Feld[oldx][oldy] = next_element;
5101
5102   TEST_DrawLevelField(oldx, oldy);
5103   TEST_DrawLevelField(newx, newy);
5104 }
5105
5106 void DrawDynamite(int x, int y)
5107 {
5108   int sx = SCREENX(x), sy = SCREENY(y);
5109   int graphic = el2img(Feld[x][y]);
5110   int frame;
5111
5112   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5113     return;
5114
5115   if (IS_WALKABLE_INSIDE(Back[x][y]))
5116     return;
5117
5118   if (Back[x][y])
5119     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5120   else if (Store[x][y])
5121     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5122
5123   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5124
5125   if (Back[x][y] || Store[x][y])
5126     DrawGraphicThruMask(sx, sy, graphic, frame);
5127   else
5128     DrawGraphic(sx, sy, graphic, frame);
5129 }
5130
5131 static void CheckDynamite(int x, int y)
5132 {
5133   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5134   {
5135     MovDelay[x][y]--;
5136
5137     if (MovDelay[x][y] != 0)
5138     {
5139       DrawDynamite(x, y);
5140       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5141
5142       return;
5143     }
5144   }
5145
5146   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5147
5148   Bang(x, y);
5149 }
5150
5151 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5152 {
5153   boolean num_checked_players = 0;
5154   int i;
5155
5156   for (i = 0; i < MAX_PLAYERS; i++)
5157   {
5158     if (stored_player[i].active)
5159     {
5160       int sx = stored_player[i].jx;
5161       int sy = stored_player[i].jy;
5162
5163       if (num_checked_players == 0)
5164       {
5165         *sx1 = *sx2 = sx;
5166         *sy1 = *sy2 = sy;
5167       }
5168       else
5169       {
5170         *sx1 = MIN(*sx1, sx);
5171         *sy1 = MIN(*sy1, sy);
5172         *sx2 = MAX(*sx2, sx);
5173         *sy2 = MAX(*sy2, sy);
5174       }
5175
5176       num_checked_players++;
5177     }
5178   }
5179 }
5180
5181 static boolean checkIfAllPlayersFitToScreen_RND(void)
5182 {
5183   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5184
5185   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5186
5187   return (sx2 - sx1 < SCR_FIELDX &&
5188           sy2 - sy1 < SCR_FIELDY);
5189 }
5190
5191 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5192 {
5193   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5194
5195   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5196
5197   *sx = (sx1 + sx2) / 2;
5198   *sy = (sy1 + sy2) / 2;
5199 }
5200
5201 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5202                                boolean center_screen, boolean quick_relocation)
5203 {
5204   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5205   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5206   boolean no_delay = (tape.warp_forward);
5207   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5208   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5209   int new_scroll_x, new_scroll_y;
5210
5211   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5212   {
5213     // case 1: quick relocation inside visible screen (without scrolling)
5214
5215     RedrawPlayfield();
5216
5217     return;
5218   }
5219
5220   if (!level.shifted_relocation || center_screen)
5221   {
5222     // relocation _with_ centering of screen
5223
5224     new_scroll_x = SCROLL_POSITION_X(x);
5225     new_scroll_y = SCROLL_POSITION_Y(y);
5226   }
5227   else
5228   {
5229     // relocation _without_ centering of screen
5230
5231     int center_scroll_x = SCROLL_POSITION_X(old_x);
5232     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5233     int offset_x = x + (scroll_x - center_scroll_x);
5234     int offset_y = y + (scroll_y - center_scroll_y);
5235
5236     // for new screen position, apply previous offset to center position
5237     new_scroll_x = SCROLL_POSITION_X(offset_x);
5238     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5239   }
5240
5241   if (quick_relocation)
5242   {
5243     // case 2: quick relocation (redraw without visible scrolling)
5244
5245     scroll_x = new_scroll_x;
5246     scroll_y = new_scroll_y;
5247
5248     RedrawPlayfield();
5249
5250     return;
5251   }
5252
5253   // case 3: visible relocation (with scrolling to new position)
5254
5255   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5256
5257   SetVideoFrameDelay(wait_delay_value);
5258
5259   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5260   {
5261     int dx = 0, dy = 0;
5262     int fx = FX, fy = FY;
5263
5264     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5265     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5266
5267     if (dx == 0 && dy == 0)             // no scrolling needed at all
5268       break;
5269
5270     scroll_x -= dx;
5271     scroll_y -= dy;
5272
5273     fx += dx * TILEX / 2;
5274     fy += dy * TILEY / 2;
5275
5276     ScrollLevel(dx, dy);
5277     DrawAllPlayers();
5278
5279     // scroll in two steps of half tile size to make things smoother
5280     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5281
5282     // scroll second step to align at full tile size
5283     BlitScreenToBitmap(window);
5284   }
5285
5286   DrawAllPlayers();
5287   BackToFront();
5288
5289   SetVideoFrameDelay(frame_delay_value_old);
5290 }
5291
5292 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5293 {
5294   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5295   int player_nr = GET_PLAYER_NR(el_player);
5296   struct PlayerInfo *player = &stored_player[player_nr];
5297   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5298   boolean no_delay = (tape.warp_forward);
5299   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5300   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5301   int old_jx = player->jx;
5302   int old_jy = player->jy;
5303   int old_element = Feld[old_jx][old_jy];
5304   int element = Feld[jx][jy];
5305   boolean player_relocated = (old_jx != jx || old_jy != jy);
5306
5307   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5308   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5309   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5310   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5311   int leave_side_horiz = move_dir_horiz;
5312   int leave_side_vert  = move_dir_vert;
5313   int enter_side = enter_side_horiz | enter_side_vert;
5314   int leave_side = leave_side_horiz | leave_side_vert;
5315
5316   if (player->buried)           // do not reanimate dead player
5317     return;
5318
5319   if (!player_relocated)        // no need to relocate the player
5320     return;
5321
5322   if (IS_PLAYER(jx, jy))        // player already placed at new position
5323   {
5324     RemoveField(jx, jy);        // temporarily remove newly placed player
5325     DrawLevelField(jx, jy);
5326   }
5327
5328   if (player->present)
5329   {
5330     while (player->MovPos)
5331     {
5332       ScrollPlayer(player, SCROLL_GO_ON);
5333       ScrollScreen(NULL, SCROLL_GO_ON);
5334
5335       AdvanceFrameAndPlayerCounters(player->index_nr);
5336
5337       DrawPlayer(player);
5338
5339       BackToFront_WithFrameDelay(wait_delay_value);
5340     }
5341
5342     DrawPlayer(player);         // needed here only to cleanup last field
5343     DrawLevelField(player->jx, player->jy);     // remove player graphic
5344
5345     player->is_moving = FALSE;
5346   }
5347
5348   if (IS_CUSTOM_ELEMENT(old_element))
5349     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5350                                CE_LEFT_BY_PLAYER,
5351                                player->index_bit, leave_side);
5352
5353   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5354                                       CE_PLAYER_LEAVES_X,
5355                                       player->index_bit, leave_side);
5356
5357   Feld[jx][jy] = el_player;
5358   InitPlayerField(jx, jy, el_player, TRUE);
5359
5360   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5361      possible that the relocation target field did not contain a player element,
5362      but a walkable element, to which the new player was relocated -- in this
5363      case, restore that (already initialized!) element on the player field */
5364   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5365   {
5366     Feld[jx][jy] = element;     // restore previously existing element
5367   }
5368
5369   // only visually relocate centered player
5370   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5371                      FALSE, level.instant_relocation);
5372
5373   TestIfPlayerTouchesBadThing(jx, jy);
5374   TestIfPlayerTouchesCustomElement(jx, jy);
5375
5376   if (IS_CUSTOM_ELEMENT(element))
5377     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5378                                player->index_bit, enter_side);
5379
5380   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5381                                       player->index_bit, enter_side);
5382
5383   if (player->is_switching)
5384   {
5385     /* ensure that relocation while still switching an element does not cause
5386        a new element to be treated as also switched directly after relocation
5387        (this is important for teleporter switches that teleport the player to
5388        a place where another teleporter switch is in the same direction, which
5389        would then incorrectly be treated as immediately switched before the
5390        direction key that caused the switch was released) */
5391
5392     player->switch_x += jx - old_jx;
5393     player->switch_y += jy - old_jy;
5394   }
5395 }
5396
5397 static void Explode(int ex, int ey, int phase, int mode)
5398 {
5399   int x, y;
5400   int last_phase;
5401   int border_element;
5402
5403   // !!! eliminate this variable !!!
5404   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5405
5406   if (game.explosions_delayed)
5407   {
5408     ExplodeField[ex][ey] = mode;
5409     return;
5410   }
5411
5412   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5413   {
5414     int center_element = Feld[ex][ey];
5415     int artwork_element, explosion_element;     // set these values later
5416
5417     // remove things displayed in background while burning dynamite
5418     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5419       Back[ex][ey] = 0;
5420
5421     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5422     {
5423       // put moving element to center field (and let it explode there)
5424       center_element = MovingOrBlocked2Element(ex, ey);
5425       RemoveMovingField(ex, ey);
5426       Feld[ex][ey] = center_element;
5427     }
5428
5429     // now "center_element" is finally determined -- set related values now
5430     artwork_element = center_element;           // for custom player artwork
5431     explosion_element = center_element;         // for custom player artwork
5432
5433     if (IS_PLAYER(ex, ey))
5434     {
5435       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5436
5437       artwork_element = stored_player[player_nr].artwork_element;
5438
5439       if (level.use_explosion_element[player_nr])
5440       {
5441         explosion_element = level.explosion_element[player_nr];
5442         artwork_element = explosion_element;
5443       }
5444     }
5445
5446     if (mode == EX_TYPE_NORMAL ||
5447         mode == EX_TYPE_CENTER ||
5448         mode == EX_TYPE_CROSS)
5449       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5450
5451     last_phase = element_info[explosion_element].explosion_delay + 1;
5452
5453     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5454     {
5455       int xx = x - ex + 1;
5456       int yy = y - ey + 1;
5457       int element;
5458
5459       if (!IN_LEV_FIELD(x, y) ||
5460           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5461           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5462         continue;
5463
5464       element = Feld[x][y];
5465
5466       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5467       {
5468         element = MovingOrBlocked2Element(x, y);
5469
5470         if (!IS_EXPLOSION_PROOF(element))
5471           RemoveMovingField(x, y);
5472       }
5473
5474       // indestructible elements can only explode in center (but not flames)
5475       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5476                                            mode == EX_TYPE_BORDER)) ||
5477           element == EL_FLAMES)
5478         continue;
5479
5480       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5481          behaviour, for example when touching a yamyam that explodes to rocks
5482          with active deadly shield, a rock is created under the player !!! */
5483       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5484 #if 0
5485       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5486           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5487            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5488 #else
5489       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5490 #endif
5491       {
5492         if (IS_ACTIVE_BOMB(element))
5493         {
5494           // re-activate things under the bomb like gate or penguin
5495           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5496           Back[x][y] = 0;
5497         }
5498
5499         continue;
5500       }
5501
5502       // save walkable background elements while explosion on same tile
5503       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5504           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5505         Back[x][y] = element;
5506
5507       // ignite explodable elements reached by other explosion
5508       if (element == EL_EXPLOSION)
5509         element = Store2[x][y];
5510
5511       if (AmoebaNr[x][y] &&
5512           (element == EL_AMOEBA_FULL ||
5513            element == EL_BD_AMOEBA ||
5514            element == EL_AMOEBA_GROWING))
5515       {
5516         AmoebaCnt[AmoebaNr[x][y]]--;
5517         AmoebaCnt2[AmoebaNr[x][y]]--;
5518       }
5519
5520       RemoveField(x, y);
5521
5522       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5523       {
5524         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5525
5526         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5527
5528         if (PLAYERINFO(ex, ey)->use_murphy)
5529           Store[x][y] = EL_EMPTY;
5530       }
5531
5532       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5533       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5534       else if (ELEM_IS_PLAYER(center_element))
5535         Store[x][y] = EL_EMPTY;
5536       else if (center_element == EL_YAMYAM)
5537         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5538       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5539         Store[x][y] = element_info[center_element].content.e[xx][yy];
5540 #if 1
5541       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5542       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5543       // otherwise) -- FIX THIS !!!
5544       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5545         Store[x][y] = element_info[element].content.e[1][1];
5546 #else
5547       else if (!CAN_EXPLODE(element))
5548         Store[x][y] = element_info[element].content.e[1][1];
5549 #endif
5550       else
5551         Store[x][y] = EL_EMPTY;
5552
5553       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5554           center_element == EL_AMOEBA_TO_DIAMOND)
5555         Store2[x][y] = element;
5556
5557       Feld[x][y] = EL_EXPLOSION;
5558       GfxElement[x][y] = artwork_element;
5559
5560       ExplodePhase[x][y] = 1;
5561       ExplodeDelay[x][y] = last_phase;
5562
5563       Stop[x][y] = TRUE;
5564     }
5565
5566     if (center_element == EL_YAMYAM)
5567       game.yamyam_content_nr =
5568         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5569
5570     return;
5571   }
5572
5573   if (Stop[ex][ey])
5574     return;
5575
5576   x = ex;
5577   y = ey;
5578
5579   if (phase == 1)
5580     GfxFrame[x][y] = 0;         // restart explosion animation
5581
5582   last_phase = ExplodeDelay[x][y];
5583
5584   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5585
5586   // this can happen if the player leaves an explosion just in time
5587   if (GfxElement[x][y] == EL_UNDEFINED)
5588     GfxElement[x][y] = EL_EMPTY;
5589
5590   border_element = Store2[x][y];
5591   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5592     border_element = StorePlayer[x][y];
5593
5594   if (phase == element_info[border_element].ignition_delay ||
5595       phase == last_phase)
5596   {
5597     boolean border_explosion = FALSE;
5598
5599     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5600         !PLAYER_EXPLOSION_PROTECTED(x, y))
5601     {
5602       KillPlayerUnlessExplosionProtected(x, y);
5603       border_explosion = TRUE;
5604     }
5605     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5606     {
5607       Feld[x][y] = Store2[x][y];
5608       Store2[x][y] = 0;
5609       Bang(x, y);
5610       border_explosion = TRUE;
5611     }
5612     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5613     {
5614       AmoebeUmwandeln(x, y);
5615       Store2[x][y] = 0;
5616       border_explosion = TRUE;
5617     }
5618
5619     // if an element just explodes due to another explosion (chain-reaction),
5620     // do not immediately end the new explosion when it was the last frame of
5621     // the explosion (as it would be done in the following "if"-statement!)
5622     if (border_explosion && phase == last_phase)
5623       return;
5624   }
5625
5626   if (phase == last_phase)
5627   {
5628     int element;
5629
5630     element = Feld[x][y] = Store[x][y];
5631     Store[x][y] = Store2[x][y] = 0;
5632     GfxElement[x][y] = EL_UNDEFINED;
5633
5634     // player can escape from explosions and might therefore be still alive
5635     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5636         element <= EL_PLAYER_IS_EXPLODING_4)
5637     {
5638       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5639       int explosion_element = EL_PLAYER_1 + player_nr;
5640       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5641       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5642
5643       if (level.use_explosion_element[player_nr])
5644         explosion_element = level.explosion_element[player_nr];
5645
5646       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5647                     element_info[explosion_element].content.e[xx][yy]);
5648     }
5649
5650     // restore probably existing indestructible background element
5651     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5652       element = Feld[x][y] = Back[x][y];
5653     Back[x][y] = 0;
5654
5655     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5656     GfxDir[x][y] = MV_NONE;
5657     ChangeDelay[x][y] = 0;
5658     ChangePage[x][y] = -1;
5659
5660     CustomValue[x][y] = 0;
5661
5662     InitField_WithBug2(x, y, FALSE);
5663
5664     TEST_DrawLevelField(x, y);
5665
5666     TestIfElementTouchesCustomElement(x, y);
5667
5668     if (GFX_CRUMBLED(element))
5669       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5670
5671     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5672       StorePlayer[x][y] = 0;
5673
5674     if (ELEM_IS_PLAYER(element))
5675       RelocatePlayer(x, y, element);
5676   }
5677   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5678   {
5679     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5680     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5681
5682     if (phase == delay)
5683       TEST_DrawLevelFieldCrumbled(x, y);
5684
5685     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5686     {
5687       DrawLevelElement(x, y, Back[x][y]);
5688       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5689     }
5690     else if (IS_WALKABLE_UNDER(Back[x][y]))
5691     {
5692       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5693       DrawLevelElementThruMask(x, y, Back[x][y]);
5694     }
5695     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5696       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5697   }
5698 }
5699
5700 static void DynaExplode(int ex, int ey)
5701 {
5702   int i, j;
5703   int dynabomb_element = Feld[ex][ey];
5704   int dynabomb_size = 1;
5705   boolean dynabomb_xl = FALSE;
5706   struct PlayerInfo *player;
5707   static int xy[4][2] =
5708   {
5709     { 0, -1 },
5710     { -1, 0 },
5711     { +1, 0 },
5712     { 0, +1 }
5713   };
5714
5715   if (IS_ACTIVE_BOMB(dynabomb_element))
5716   {
5717     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5718     dynabomb_size = player->dynabomb_size;
5719     dynabomb_xl = player->dynabomb_xl;
5720     player->dynabombs_left++;
5721   }
5722
5723   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5724
5725   for (i = 0; i < NUM_DIRECTIONS; i++)
5726   {
5727     for (j = 1; j <= dynabomb_size; j++)
5728     {
5729       int x = ex + j * xy[i][0];
5730       int y = ey + j * xy[i][1];
5731       int element;
5732
5733       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5734         break;
5735
5736       element = Feld[x][y];
5737
5738       // do not restart explosions of fields with active bombs
5739       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5740         continue;
5741
5742       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5743
5744       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5745           !IS_DIGGABLE(element) && !dynabomb_xl)
5746         break;
5747     }
5748   }
5749 }
5750
5751 void Bang(int x, int y)
5752 {
5753   int element = MovingOrBlocked2Element(x, y);
5754   int explosion_type = EX_TYPE_NORMAL;
5755
5756   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5757   {
5758     struct PlayerInfo *player = PLAYERINFO(x, y);
5759
5760     element = Feld[x][y] = player->initial_element;
5761
5762     if (level.use_explosion_element[player->index_nr])
5763     {
5764       int explosion_element = level.explosion_element[player->index_nr];
5765
5766       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5767         explosion_type = EX_TYPE_CROSS;
5768       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5769         explosion_type = EX_TYPE_CENTER;
5770     }
5771   }
5772
5773   switch (element)
5774   {
5775     case EL_BUG:
5776     case EL_SPACESHIP:
5777     case EL_BD_BUTTERFLY:
5778     case EL_BD_FIREFLY:
5779     case EL_YAMYAM:
5780     case EL_DARK_YAMYAM:
5781     case EL_ROBOT:
5782     case EL_PACMAN:
5783     case EL_MOLE:
5784       RaiseScoreElement(element);
5785       break;
5786
5787     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5788     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5789     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5790     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5791     case EL_DYNABOMB_INCREASE_NUMBER:
5792     case EL_DYNABOMB_INCREASE_SIZE:
5793     case EL_DYNABOMB_INCREASE_POWER:
5794       explosion_type = EX_TYPE_DYNA;
5795       break;
5796
5797     case EL_DC_LANDMINE:
5798       explosion_type = EX_TYPE_CENTER;
5799       break;
5800
5801     case EL_PENGUIN:
5802     case EL_LAMP:
5803     case EL_LAMP_ACTIVE:
5804     case EL_AMOEBA_TO_DIAMOND:
5805       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5806         explosion_type = EX_TYPE_CENTER;
5807       break;
5808
5809     default:
5810       if (element_info[element].explosion_type == EXPLODES_CROSS)
5811         explosion_type = EX_TYPE_CROSS;
5812       else if (element_info[element].explosion_type == EXPLODES_1X1)
5813         explosion_type = EX_TYPE_CENTER;
5814       break;
5815   }
5816
5817   if (explosion_type == EX_TYPE_DYNA)
5818     DynaExplode(x, y);
5819   else
5820     Explode(x, y, EX_PHASE_START, explosion_type);
5821
5822   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5823 }
5824
5825 static void SplashAcid(int x, int y)
5826 {
5827   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5828       (!IN_LEV_FIELD(x - 1, y - 2) ||
5829        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5830     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5831
5832   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5833       (!IN_LEV_FIELD(x + 1, y - 2) ||
5834        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5835     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5836
5837   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5838 }
5839
5840 static void InitBeltMovement(void)
5841 {
5842   static int belt_base_element[4] =
5843   {
5844     EL_CONVEYOR_BELT_1_LEFT,
5845     EL_CONVEYOR_BELT_2_LEFT,
5846     EL_CONVEYOR_BELT_3_LEFT,
5847     EL_CONVEYOR_BELT_4_LEFT
5848   };
5849   static int belt_base_active_element[4] =
5850   {
5851     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5852     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5853     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5854     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5855   };
5856
5857   int x, y, i, j;
5858
5859   // set frame order for belt animation graphic according to belt direction
5860   for (i = 0; i < NUM_BELTS; i++)
5861   {
5862     int belt_nr = i;
5863
5864     for (j = 0; j < NUM_BELT_PARTS; j++)
5865     {
5866       int element = belt_base_active_element[belt_nr] + j;
5867       int graphic_1 = el2img(element);
5868       int graphic_2 = el2panelimg(element);
5869
5870       if (game.belt_dir[i] == MV_LEFT)
5871       {
5872         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5873         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5874       }
5875       else
5876       {
5877         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5878         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5879       }
5880     }
5881   }
5882
5883   SCAN_PLAYFIELD(x, y)
5884   {
5885     int element = Feld[x][y];
5886
5887     for (i = 0; i < NUM_BELTS; i++)
5888     {
5889       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5890       {
5891         int e_belt_nr = getBeltNrFromBeltElement(element);
5892         int belt_nr = i;
5893
5894         if (e_belt_nr == belt_nr)
5895         {
5896           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5897
5898           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5899         }
5900       }
5901     }
5902   }
5903 }
5904
5905 static void ToggleBeltSwitch(int x, int y)
5906 {
5907   static int belt_base_element[4] =
5908   {
5909     EL_CONVEYOR_BELT_1_LEFT,
5910     EL_CONVEYOR_BELT_2_LEFT,
5911     EL_CONVEYOR_BELT_3_LEFT,
5912     EL_CONVEYOR_BELT_4_LEFT
5913   };
5914   static int belt_base_active_element[4] =
5915   {
5916     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5917     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5918     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5919     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5920   };
5921   static int belt_base_switch_element[4] =
5922   {
5923     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5924     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5925     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5926     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5927   };
5928   static int belt_move_dir[4] =
5929   {
5930     MV_LEFT,
5931     MV_NONE,
5932     MV_RIGHT,
5933     MV_NONE,
5934   };
5935
5936   int element = Feld[x][y];
5937   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5938   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5939   int belt_dir = belt_move_dir[belt_dir_nr];
5940   int xx, yy, i;
5941
5942   if (!IS_BELT_SWITCH(element))
5943     return;
5944
5945   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5946   game.belt_dir[belt_nr] = belt_dir;
5947
5948   if (belt_dir_nr == 3)
5949     belt_dir_nr = 1;
5950
5951   // set frame order for belt animation graphic according to belt direction
5952   for (i = 0; i < NUM_BELT_PARTS; i++)
5953   {
5954     int element = belt_base_active_element[belt_nr] + i;
5955     int graphic_1 = el2img(element);
5956     int graphic_2 = el2panelimg(element);
5957
5958     if (belt_dir == MV_LEFT)
5959     {
5960       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5961       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5962     }
5963     else
5964     {
5965       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5966       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5967     }
5968   }
5969
5970   SCAN_PLAYFIELD(xx, yy)
5971   {
5972     int element = Feld[xx][yy];
5973
5974     if (IS_BELT_SWITCH(element))
5975     {
5976       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5977
5978       if (e_belt_nr == belt_nr)
5979       {
5980         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5981         TEST_DrawLevelField(xx, yy);
5982       }
5983     }
5984     else if (IS_BELT(element) && belt_dir != MV_NONE)
5985     {
5986       int e_belt_nr = getBeltNrFromBeltElement(element);
5987
5988       if (e_belt_nr == belt_nr)
5989       {
5990         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5991
5992         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5993         TEST_DrawLevelField(xx, yy);
5994       }
5995     }
5996     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5997     {
5998       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5999
6000       if (e_belt_nr == belt_nr)
6001       {
6002         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6003
6004         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6005         TEST_DrawLevelField(xx, yy);
6006       }
6007     }
6008   }
6009 }
6010
6011 static void ToggleSwitchgateSwitch(int x, int y)
6012 {
6013   int xx, yy;
6014
6015   game.switchgate_pos = !game.switchgate_pos;
6016
6017   SCAN_PLAYFIELD(xx, yy)
6018   {
6019     int element = Feld[xx][yy];
6020
6021     if (element == EL_SWITCHGATE_SWITCH_UP)
6022     {
6023       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6024       TEST_DrawLevelField(xx, yy);
6025     }
6026     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6027     {
6028       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6029       TEST_DrawLevelField(xx, yy);
6030     }
6031     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6032     {
6033       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6034       TEST_DrawLevelField(xx, yy);
6035     }
6036     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6037     {
6038       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6039       TEST_DrawLevelField(xx, yy);
6040     }
6041     else if (element == EL_SWITCHGATE_OPEN ||
6042              element == EL_SWITCHGATE_OPENING)
6043     {
6044       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6045
6046       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6047     }
6048     else if (element == EL_SWITCHGATE_CLOSED ||
6049              element == EL_SWITCHGATE_CLOSING)
6050     {
6051       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6052
6053       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6054     }
6055   }
6056 }
6057
6058 static int getInvisibleActiveFromInvisibleElement(int element)
6059 {
6060   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6061           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6062           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6063           element);
6064 }
6065
6066 static int getInvisibleFromInvisibleActiveElement(int element)
6067 {
6068   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6069           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6070           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6071           element);
6072 }
6073
6074 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6075 {
6076   int x, y;
6077
6078   SCAN_PLAYFIELD(x, y)
6079   {
6080     int element = Feld[x][y];
6081
6082     if (element == EL_LIGHT_SWITCH &&
6083         game.light_time_left > 0)
6084     {
6085       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6086       TEST_DrawLevelField(x, y);
6087     }
6088     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6089              game.light_time_left == 0)
6090     {
6091       Feld[x][y] = EL_LIGHT_SWITCH;
6092       TEST_DrawLevelField(x, y);
6093     }
6094     else if (element == EL_EMC_DRIPPER &&
6095              game.light_time_left > 0)
6096     {
6097       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6098       TEST_DrawLevelField(x, y);
6099     }
6100     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6101              game.light_time_left == 0)
6102     {
6103       Feld[x][y] = EL_EMC_DRIPPER;
6104       TEST_DrawLevelField(x, y);
6105     }
6106     else if (element == EL_INVISIBLE_STEELWALL ||
6107              element == EL_INVISIBLE_WALL ||
6108              element == EL_INVISIBLE_SAND)
6109     {
6110       if (game.light_time_left > 0)
6111         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6112
6113       TEST_DrawLevelField(x, y);
6114
6115       // uncrumble neighbour fields, if needed
6116       if (element == EL_INVISIBLE_SAND)
6117         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6118     }
6119     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6120              element == EL_INVISIBLE_WALL_ACTIVE ||
6121              element == EL_INVISIBLE_SAND_ACTIVE)
6122     {
6123       if (game.light_time_left == 0)
6124         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6125
6126       TEST_DrawLevelField(x, y);
6127
6128       // re-crumble neighbour fields, if needed
6129       if (element == EL_INVISIBLE_SAND)
6130         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6131     }
6132   }
6133 }
6134
6135 static void RedrawAllInvisibleElementsForLenses(void)
6136 {
6137   int x, y;
6138
6139   SCAN_PLAYFIELD(x, y)
6140   {
6141     int element = Feld[x][y];
6142
6143     if (element == EL_EMC_DRIPPER &&
6144         game.lenses_time_left > 0)
6145     {
6146       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6147       TEST_DrawLevelField(x, y);
6148     }
6149     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6150              game.lenses_time_left == 0)
6151     {
6152       Feld[x][y] = EL_EMC_DRIPPER;
6153       TEST_DrawLevelField(x, y);
6154     }
6155     else if (element == EL_INVISIBLE_STEELWALL ||
6156              element == EL_INVISIBLE_WALL ||
6157              element == EL_INVISIBLE_SAND)
6158     {
6159       if (game.lenses_time_left > 0)
6160         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6161
6162       TEST_DrawLevelField(x, y);
6163
6164       // uncrumble neighbour fields, if needed
6165       if (element == EL_INVISIBLE_SAND)
6166         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6167     }
6168     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6169              element == EL_INVISIBLE_WALL_ACTIVE ||
6170              element == EL_INVISIBLE_SAND_ACTIVE)
6171     {
6172       if (game.lenses_time_left == 0)
6173         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6174
6175       TEST_DrawLevelField(x, y);
6176
6177       // re-crumble neighbour fields, if needed
6178       if (element == EL_INVISIBLE_SAND)
6179         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6180     }
6181   }
6182 }
6183
6184 static void RedrawAllInvisibleElementsForMagnifier(void)
6185 {
6186   int x, y;
6187
6188   SCAN_PLAYFIELD(x, y)
6189   {
6190     int element = Feld[x][y];
6191
6192     if (element == EL_EMC_FAKE_GRASS &&
6193         game.magnify_time_left > 0)
6194     {
6195       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6196       TEST_DrawLevelField(x, y);
6197     }
6198     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6199              game.magnify_time_left == 0)
6200     {
6201       Feld[x][y] = EL_EMC_FAKE_GRASS;
6202       TEST_DrawLevelField(x, y);
6203     }
6204     else if (IS_GATE_GRAY(element) &&
6205              game.magnify_time_left > 0)
6206     {
6207       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6208                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6209                     IS_EM_GATE_GRAY(element) ?
6210                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6211                     IS_EMC_GATE_GRAY(element) ?
6212                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6213                     IS_DC_GATE_GRAY(element) ?
6214                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6215                     element);
6216       TEST_DrawLevelField(x, y);
6217     }
6218     else if (IS_GATE_GRAY_ACTIVE(element) &&
6219              game.magnify_time_left == 0)
6220     {
6221       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6222                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6223                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6224                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6225                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6226                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6227                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6228                     EL_DC_GATE_WHITE_GRAY :
6229                     element);
6230       TEST_DrawLevelField(x, y);
6231     }
6232   }
6233 }
6234
6235 static void ToggleLightSwitch(int x, int y)
6236 {
6237   int element = Feld[x][y];
6238
6239   game.light_time_left =
6240     (element == EL_LIGHT_SWITCH ?
6241      level.time_light * FRAMES_PER_SECOND : 0);
6242
6243   RedrawAllLightSwitchesAndInvisibleElements();
6244 }
6245
6246 static void ActivateTimegateSwitch(int x, int y)
6247 {
6248   int xx, yy;
6249
6250   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6251
6252   SCAN_PLAYFIELD(xx, yy)
6253   {
6254     int element = Feld[xx][yy];
6255
6256     if (element == EL_TIMEGATE_CLOSED ||
6257         element == EL_TIMEGATE_CLOSING)
6258     {
6259       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6260       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6261     }
6262
6263     /*
6264     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6265     {
6266       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6267       TEST_DrawLevelField(xx, yy);
6268     }
6269     */
6270
6271   }
6272
6273   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6274                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6275 }
6276
6277 static void Impact(int x, int y)
6278 {
6279   boolean last_line = (y == lev_fieldy - 1);
6280   boolean object_hit = FALSE;
6281   boolean impact = (last_line || object_hit);
6282   int element = Feld[x][y];
6283   int smashed = EL_STEELWALL;
6284
6285   if (!last_line)       // check if element below was hit
6286   {
6287     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6288       return;
6289
6290     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6291                                          MovDir[x][y + 1] != MV_DOWN ||
6292                                          MovPos[x][y + 1] <= TILEY / 2));
6293
6294     // do not smash moving elements that left the smashed field in time
6295     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6296         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6297       object_hit = FALSE;
6298
6299 #if USE_QUICKSAND_IMPACT_BUGFIX
6300     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6301     {
6302       RemoveMovingField(x, y + 1);
6303       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6304       Feld[x][y + 2] = EL_ROCK;
6305       TEST_DrawLevelField(x, y + 2);
6306
6307       object_hit = TRUE;
6308     }
6309
6310     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6311     {
6312       RemoveMovingField(x, y + 1);
6313       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6314       Feld[x][y + 2] = EL_ROCK;
6315       TEST_DrawLevelField(x, y + 2);
6316
6317       object_hit = TRUE;
6318     }
6319 #endif
6320
6321     if (object_hit)
6322       smashed = MovingOrBlocked2Element(x, y + 1);
6323
6324     impact = (last_line || object_hit);
6325   }
6326
6327   if (!last_line && smashed == EL_ACID) // element falls into acid
6328   {
6329     SplashAcid(x, y + 1);
6330     return;
6331   }
6332
6333   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6334   // only reset graphic animation if graphic really changes after impact
6335   if (impact &&
6336       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6337   {
6338     ResetGfxAnimation(x, y);
6339     TEST_DrawLevelField(x, y);
6340   }
6341
6342   if (impact && CAN_EXPLODE_IMPACT(element))
6343   {
6344     Bang(x, y);
6345     return;
6346   }
6347   else if (impact && element == EL_PEARL &&
6348            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6349   {
6350     ResetGfxAnimation(x, y);
6351
6352     Feld[x][y] = EL_PEARL_BREAKING;
6353     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6354     return;
6355   }
6356   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6357   {
6358     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6359
6360     return;
6361   }
6362
6363   if (impact && element == EL_AMOEBA_DROP)
6364   {
6365     if (object_hit && IS_PLAYER(x, y + 1))
6366       KillPlayerUnlessEnemyProtected(x, y + 1);
6367     else if (object_hit && smashed == EL_PENGUIN)
6368       Bang(x, y + 1);
6369     else
6370     {
6371       Feld[x][y] = EL_AMOEBA_GROWING;
6372       Store[x][y] = EL_AMOEBA_WET;
6373
6374       ResetRandomAnimationValue(x, y);
6375     }
6376     return;
6377   }
6378
6379   if (object_hit)               // check which object was hit
6380   {
6381     if ((CAN_PASS_MAGIC_WALL(element) && 
6382          (smashed == EL_MAGIC_WALL ||
6383           smashed == EL_BD_MAGIC_WALL)) ||
6384         (CAN_PASS_DC_MAGIC_WALL(element) &&
6385          smashed == EL_DC_MAGIC_WALL))
6386     {
6387       int xx, yy;
6388       int activated_magic_wall =
6389         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6390          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6391          EL_DC_MAGIC_WALL_ACTIVE);
6392
6393       // activate magic wall / mill
6394       SCAN_PLAYFIELD(xx, yy)
6395       {
6396         if (Feld[xx][yy] == smashed)
6397           Feld[xx][yy] = activated_magic_wall;
6398       }
6399
6400       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6401       game.magic_wall_active = TRUE;
6402
6403       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6404                             SND_MAGIC_WALL_ACTIVATING :
6405                             smashed == EL_BD_MAGIC_WALL ?
6406                             SND_BD_MAGIC_WALL_ACTIVATING :
6407                             SND_DC_MAGIC_WALL_ACTIVATING));
6408     }
6409
6410     if (IS_PLAYER(x, y + 1))
6411     {
6412       if (CAN_SMASH_PLAYER(element))
6413       {
6414         KillPlayerUnlessEnemyProtected(x, y + 1);
6415         return;
6416       }
6417     }
6418     else if (smashed == EL_PENGUIN)
6419     {
6420       if (CAN_SMASH_PLAYER(element))
6421       {
6422         Bang(x, y + 1);
6423         return;
6424       }
6425     }
6426     else if (element == EL_BD_DIAMOND)
6427     {
6428       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6429       {
6430         Bang(x, y + 1);
6431         return;
6432       }
6433     }
6434     else if (((element == EL_SP_INFOTRON ||
6435                element == EL_SP_ZONK) &&
6436               (smashed == EL_SP_SNIKSNAK ||
6437                smashed == EL_SP_ELECTRON ||
6438                smashed == EL_SP_DISK_ORANGE)) ||
6439              (element == EL_SP_INFOTRON &&
6440               smashed == EL_SP_DISK_YELLOW))
6441     {
6442       Bang(x, y + 1);
6443       return;
6444     }
6445     else if (CAN_SMASH_EVERYTHING(element))
6446     {
6447       if (IS_CLASSIC_ENEMY(smashed) ||
6448           CAN_EXPLODE_SMASHED(smashed))
6449       {
6450         Bang(x, y + 1);
6451         return;
6452       }
6453       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6454       {
6455         if (smashed == EL_LAMP ||
6456             smashed == EL_LAMP_ACTIVE)
6457         {
6458           Bang(x, y + 1);
6459           return;
6460         }
6461         else if (smashed == EL_NUT)
6462         {
6463           Feld[x][y + 1] = EL_NUT_BREAKING;
6464           PlayLevelSound(x, y, SND_NUT_BREAKING);
6465           RaiseScoreElement(EL_NUT);
6466           return;
6467         }
6468         else if (smashed == EL_PEARL)
6469         {
6470           ResetGfxAnimation(x, y);
6471
6472           Feld[x][y + 1] = EL_PEARL_BREAKING;
6473           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6474           return;
6475         }
6476         else if (smashed == EL_DIAMOND)
6477         {
6478           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6479           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6480           return;
6481         }
6482         else if (IS_BELT_SWITCH(smashed))
6483         {
6484           ToggleBeltSwitch(x, y + 1);
6485         }
6486         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6487                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6488                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6489                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6490         {
6491           ToggleSwitchgateSwitch(x, y + 1);
6492         }
6493         else if (smashed == EL_LIGHT_SWITCH ||
6494                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6495         {
6496           ToggleLightSwitch(x, y + 1);
6497         }
6498         else
6499         {
6500           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6501
6502           CheckElementChangeBySide(x, y + 1, smashed, element,
6503                                    CE_SWITCHED, CH_SIDE_TOP);
6504           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6505                                             CH_SIDE_TOP);
6506         }
6507       }
6508       else
6509       {
6510         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6511       }
6512     }
6513   }
6514
6515   // play sound of magic wall / mill
6516   if (!last_line &&
6517       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6518        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6519        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6520   {
6521     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6522       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6523     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6524       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6525     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6526       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6527
6528     return;
6529   }
6530
6531   // play sound of object that hits the ground
6532   if (last_line || object_hit)
6533     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6534 }
6535
6536 static void TurnRoundExt(int x, int y)
6537 {
6538   static struct
6539   {
6540     int dx, dy;
6541   } move_xy[] =
6542   {
6543     {  0,  0 },
6544     { -1,  0 },
6545     { +1,  0 },
6546     {  0,  0 },
6547     {  0, -1 },
6548     {  0,  0 }, { 0, 0 }, { 0, 0 },
6549     {  0, +1 }
6550   };
6551   static struct
6552   {
6553     int left, right, back;
6554   } turn[] =
6555   {
6556     { 0,        0,              0        },
6557     { MV_DOWN,  MV_UP,          MV_RIGHT },
6558     { MV_UP,    MV_DOWN,        MV_LEFT  },
6559     { 0,        0,              0        },
6560     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6561     { 0,        0,              0        },
6562     { 0,        0,              0        },
6563     { 0,        0,              0        },
6564     { MV_RIGHT, MV_LEFT,        MV_UP    }
6565   };
6566
6567   int element = Feld[x][y];
6568   int move_pattern = element_info[element].move_pattern;
6569
6570   int old_move_dir = MovDir[x][y];
6571   int left_dir  = turn[old_move_dir].left;
6572   int right_dir = turn[old_move_dir].right;
6573   int back_dir  = turn[old_move_dir].back;
6574
6575   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6576   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6577   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6578   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6579
6580   int left_x  = x + left_dx,  left_y  = y + left_dy;
6581   int right_x = x + right_dx, right_y = y + right_dy;
6582   int move_x  = x + move_dx,  move_y  = y + move_dy;
6583
6584   int xx, yy;
6585
6586   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6587   {
6588     TestIfBadThingTouchesOtherBadThing(x, y);
6589
6590     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6591       MovDir[x][y] = right_dir;
6592     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6593       MovDir[x][y] = left_dir;
6594
6595     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6596       MovDelay[x][y] = 9;
6597     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6598       MovDelay[x][y] = 1;
6599   }
6600   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6601   {
6602     TestIfBadThingTouchesOtherBadThing(x, y);
6603
6604     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6605       MovDir[x][y] = left_dir;
6606     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6607       MovDir[x][y] = right_dir;
6608
6609     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6610       MovDelay[x][y] = 9;
6611     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6612       MovDelay[x][y] = 1;
6613   }
6614   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6615   {
6616     TestIfBadThingTouchesOtherBadThing(x, y);
6617
6618     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6619       MovDir[x][y] = left_dir;
6620     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6621       MovDir[x][y] = right_dir;
6622
6623     if (MovDir[x][y] != old_move_dir)
6624       MovDelay[x][y] = 9;
6625   }
6626   else if (element == EL_YAMYAM)
6627   {
6628     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6629     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6630
6631     if (can_turn_left && can_turn_right)
6632       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6633     else if (can_turn_left)
6634       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6635     else if (can_turn_right)
6636       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6637     else
6638       MovDir[x][y] = back_dir;
6639
6640     MovDelay[x][y] = 16 + 16 * RND(3);
6641   }
6642   else if (element == EL_DARK_YAMYAM)
6643   {
6644     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6645                                                          left_x, left_y);
6646     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6647                                                          right_x, right_y);
6648
6649     if (can_turn_left && can_turn_right)
6650       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6651     else if (can_turn_left)
6652       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6653     else if (can_turn_right)
6654       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6655     else
6656       MovDir[x][y] = back_dir;
6657
6658     MovDelay[x][y] = 16 + 16 * RND(3);
6659   }
6660   else if (element == EL_PACMAN)
6661   {
6662     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6663     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6664
6665     if (can_turn_left && can_turn_right)
6666       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6667     else if (can_turn_left)
6668       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6669     else if (can_turn_right)
6670       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6671     else
6672       MovDir[x][y] = back_dir;
6673
6674     MovDelay[x][y] = 6 + RND(40);
6675   }
6676   else if (element == EL_PIG)
6677   {
6678     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6679     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6680     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6681     boolean should_turn_left, should_turn_right, should_move_on;
6682     int rnd_value = 24;
6683     int rnd = RND(rnd_value);
6684
6685     should_turn_left = (can_turn_left &&
6686                         (!can_move_on ||
6687                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6688                                                    y + back_dy + left_dy)));
6689     should_turn_right = (can_turn_right &&
6690                          (!can_move_on ||
6691                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6692                                                     y + back_dy + right_dy)));
6693     should_move_on = (can_move_on &&
6694                       (!can_turn_left ||
6695                        !can_turn_right ||
6696                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6697                                                  y + move_dy + left_dy) ||
6698                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6699                                                  y + move_dy + right_dy)));
6700
6701     if (should_turn_left || should_turn_right || should_move_on)
6702     {
6703       if (should_turn_left && should_turn_right && should_move_on)
6704         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6705                         rnd < 2 * rnd_value / 3 ? right_dir :
6706                         old_move_dir);
6707       else if (should_turn_left && should_turn_right)
6708         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6709       else if (should_turn_left && should_move_on)
6710         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6711       else if (should_turn_right && should_move_on)
6712         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6713       else if (should_turn_left)
6714         MovDir[x][y] = left_dir;
6715       else if (should_turn_right)
6716         MovDir[x][y] = right_dir;
6717       else if (should_move_on)
6718         MovDir[x][y] = old_move_dir;
6719     }
6720     else if (can_move_on && rnd > rnd_value / 8)
6721       MovDir[x][y] = old_move_dir;
6722     else if (can_turn_left && can_turn_right)
6723       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6724     else if (can_turn_left && rnd > rnd_value / 8)
6725       MovDir[x][y] = left_dir;
6726     else if (can_turn_right && rnd > rnd_value/8)
6727       MovDir[x][y] = right_dir;
6728     else
6729       MovDir[x][y] = back_dir;
6730
6731     xx = x + move_xy[MovDir[x][y]].dx;
6732     yy = y + move_xy[MovDir[x][y]].dy;
6733
6734     if (!IN_LEV_FIELD(xx, yy) ||
6735         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6736       MovDir[x][y] = old_move_dir;
6737
6738     MovDelay[x][y] = 0;
6739   }
6740   else if (element == EL_DRAGON)
6741   {
6742     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6743     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6744     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6745     int rnd_value = 24;
6746     int rnd = RND(rnd_value);
6747
6748     if (can_move_on && rnd > rnd_value / 8)
6749       MovDir[x][y] = old_move_dir;
6750     else if (can_turn_left && can_turn_right)
6751       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6752     else if (can_turn_left && rnd > rnd_value / 8)
6753       MovDir[x][y] = left_dir;
6754     else if (can_turn_right && rnd > rnd_value / 8)
6755       MovDir[x][y] = right_dir;
6756     else
6757       MovDir[x][y] = back_dir;
6758
6759     xx = x + move_xy[MovDir[x][y]].dx;
6760     yy = y + move_xy[MovDir[x][y]].dy;
6761
6762     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6763       MovDir[x][y] = old_move_dir;
6764
6765     MovDelay[x][y] = 0;
6766   }
6767   else if (element == EL_MOLE)
6768   {
6769     boolean can_move_on =
6770       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6771                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6772                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6773     if (!can_move_on)
6774     {
6775       boolean can_turn_left =
6776         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6777                               IS_AMOEBOID(Feld[left_x][left_y])));
6778
6779       boolean can_turn_right =
6780         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6781                               IS_AMOEBOID(Feld[right_x][right_y])));
6782
6783       if (can_turn_left && can_turn_right)
6784         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6785       else if (can_turn_left)
6786         MovDir[x][y] = left_dir;
6787       else
6788         MovDir[x][y] = right_dir;
6789     }
6790
6791     if (MovDir[x][y] != old_move_dir)
6792       MovDelay[x][y] = 9;
6793   }
6794   else if (element == EL_BALLOON)
6795   {
6796     MovDir[x][y] = game.wind_direction;
6797     MovDelay[x][y] = 0;
6798   }
6799   else if (element == EL_SPRING)
6800   {
6801     if (MovDir[x][y] & MV_HORIZONTAL)
6802     {
6803       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6804           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6805       {
6806         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6807         ResetGfxAnimation(move_x, move_y);
6808         TEST_DrawLevelField(move_x, move_y);
6809
6810         MovDir[x][y] = back_dir;
6811       }
6812       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6813                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6814         MovDir[x][y] = MV_NONE;
6815     }
6816
6817     MovDelay[x][y] = 0;
6818   }
6819   else if (element == EL_ROBOT ||
6820            element == EL_SATELLITE ||
6821            element == EL_PENGUIN ||
6822            element == EL_EMC_ANDROID)
6823   {
6824     int attr_x = -1, attr_y = -1;
6825
6826     if (game.all_players_gone)
6827     {
6828       attr_x = game.exit_x;
6829       attr_y = game.exit_y;
6830     }
6831     else
6832     {
6833       int i;
6834
6835       for (i = 0; i < MAX_PLAYERS; i++)
6836       {
6837         struct PlayerInfo *player = &stored_player[i];
6838         int jx = player->jx, jy = player->jy;
6839
6840         if (!player->active)
6841           continue;
6842
6843         if (attr_x == -1 ||
6844             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6845         {
6846           attr_x = jx;
6847           attr_y = jy;
6848         }
6849       }
6850     }
6851
6852     if (element == EL_ROBOT &&
6853         game.robot_wheel_x >= 0 &&
6854         game.robot_wheel_y >= 0 &&
6855         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6856          game.engine_version < VERSION_IDENT(3,1,0,0)))
6857     {
6858       attr_x = game.robot_wheel_x;
6859       attr_y = game.robot_wheel_y;
6860     }
6861
6862     if (element == EL_PENGUIN)
6863     {
6864       int i;
6865       static int xy[4][2] =
6866       {
6867         { 0, -1 },
6868         { -1, 0 },
6869         { +1, 0 },
6870         { 0, +1 }
6871       };
6872
6873       for (i = 0; i < NUM_DIRECTIONS; i++)
6874       {
6875         int ex = x + xy[i][0];
6876         int ey = y + xy[i][1];
6877
6878         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6879                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6880                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6881                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6882         {
6883           attr_x = ex;
6884           attr_y = ey;
6885           break;
6886         }
6887       }
6888     }
6889
6890     MovDir[x][y] = MV_NONE;
6891     if (attr_x < x)
6892       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6893     else if (attr_x > x)
6894       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6895     if (attr_y < y)
6896       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6897     else if (attr_y > y)
6898       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6899
6900     if (element == EL_ROBOT)
6901     {
6902       int newx, newy;
6903
6904       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6905         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6906       Moving2Blocked(x, y, &newx, &newy);
6907
6908       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6909         MovDelay[x][y] = 8 + 8 * !RND(3);
6910       else
6911         MovDelay[x][y] = 16;
6912     }
6913     else if (element == EL_PENGUIN)
6914     {
6915       int newx, newy;
6916
6917       MovDelay[x][y] = 1;
6918
6919       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6920       {
6921         boolean first_horiz = RND(2);
6922         int new_move_dir = MovDir[x][y];
6923
6924         MovDir[x][y] =
6925           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6926         Moving2Blocked(x, y, &newx, &newy);
6927
6928         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6929           return;
6930
6931         MovDir[x][y] =
6932           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6933         Moving2Blocked(x, y, &newx, &newy);
6934
6935         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6936           return;
6937
6938         MovDir[x][y] = old_move_dir;
6939         return;
6940       }
6941     }
6942     else if (element == EL_SATELLITE)
6943     {
6944       int newx, newy;
6945
6946       MovDelay[x][y] = 1;
6947
6948       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6949       {
6950         boolean first_horiz = RND(2);
6951         int new_move_dir = MovDir[x][y];
6952
6953         MovDir[x][y] =
6954           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6955         Moving2Blocked(x, y, &newx, &newy);
6956
6957         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6958           return;
6959
6960         MovDir[x][y] =
6961           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6962         Moving2Blocked(x, y, &newx, &newy);
6963
6964         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6965           return;
6966
6967         MovDir[x][y] = old_move_dir;
6968         return;
6969       }
6970     }
6971     else if (element == EL_EMC_ANDROID)
6972     {
6973       static int check_pos[16] =
6974       {
6975         -1,             //  0 => (invalid)
6976         7,              //  1 => MV_LEFT
6977         3,              //  2 => MV_RIGHT
6978         -1,             //  3 => (invalid)
6979         1,              //  4 =>            MV_UP
6980         0,              //  5 => MV_LEFT  | MV_UP
6981         2,              //  6 => MV_RIGHT | MV_UP
6982         -1,             //  7 => (invalid)
6983         5,              //  8 =>            MV_DOWN
6984         6,              //  9 => MV_LEFT  | MV_DOWN
6985         4,              // 10 => MV_RIGHT | MV_DOWN
6986         -1,             // 11 => (invalid)
6987         -1,             // 12 => (invalid)
6988         -1,             // 13 => (invalid)
6989         -1,             // 14 => (invalid)
6990         -1,             // 15 => (invalid)
6991       };
6992       static struct
6993       {
6994         int dx, dy;
6995         int dir;
6996       } check_xy[8] =
6997       {
6998         { -1, -1,       MV_LEFT  | MV_UP   },
6999         {  0, -1,                  MV_UP   },
7000         { +1, -1,       MV_RIGHT | MV_UP   },
7001         { +1,  0,       MV_RIGHT           },
7002         { +1, +1,       MV_RIGHT | MV_DOWN },
7003         {  0, +1,                  MV_DOWN },
7004         { -1, +1,       MV_LEFT  | MV_DOWN },
7005         { -1,  0,       MV_LEFT            },
7006       };
7007       int start_pos, check_order;
7008       boolean can_clone = FALSE;
7009       int i;
7010
7011       // check if there is any free field around current position
7012       for (i = 0; i < 8; i++)
7013       {
7014         int newx = x + check_xy[i].dx;
7015         int newy = y + check_xy[i].dy;
7016
7017         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7018         {
7019           can_clone = TRUE;
7020
7021           break;
7022         }
7023       }
7024
7025       if (can_clone)            // randomly find an element to clone
7026       {
7027         can_clone = FALSE;
7028
7029         start_pos = check_pos[RND(8)];
7030         check_order = (RND(2) ? -1 : +1);
7031
7032         for (i = 0; i < 8; i++)
7033         {
7034           int pos_raw = start_pos + i * check_order;
7035           int pos = (pos_raw + 8) % 8;
7036           int newx = x + check_xy[pos].dx;
7037           int newy = y + check_xy[pos].dy;
7038
7039           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7040           {
7041             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7042             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7043
7044             Store[x][y] = Feld[newx][newy];
7045
7046             can_clone = TRUE;
7047
7048             break;
7049           }
7050         }
7051       }
7052
7053       if (can_clone)            // randomly find a direction to move
7054       {
7055         can_clone = FALSE;
7056
7057         start_pos = check_pos[RND(8)];
7058         check_order = (RND(2) ? -1 : +1);
7059
7060         for (i = 0; i < 8; i++)
7061         {
7062           int pos_raw = start_pos + i * check_order;
7063           int pos = (pos_raw + 8) % 8;
7064           int newx = x + check_xy[pos].dx;
7065           int newy = y + check_xy[pos].dy;
7066           int new_move_dir = check_xy[pos].dir;
7067
7068           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7069           {
7070             MovDir[x][y] = new_move_dir;
7071             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7072
7073             can_clone = TRUE;
7074
7075             break;
7076           }
7077         }
7078       }
7079
7080       if (can_clone)            // cloning and moving successful
7081         return;
7082
7083       // cannot clone -- try to move towards player
7084
7085       start_pos = check_pos[MovDir[x][y] & 0x0f];
7086       check_order = (RND(2) ? -1 : +1);
7087
7088       for (i = 0; i < 3; i++)
7089       {
7090         // first check start_pos, then previous/next or (next/previous) pos
7091         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7092         int pos = (pos_raw + 8) % 8;
7093         int newx = x + check_xy[pos].dx;
7094         int newy = y + check_xy[pos].dy;
7095         int new_move_dir = check_xy[pos].dir;
7096
7097         if (IS_PLAYER(newx, newy))
7098           break;
7099
7100         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7101         {
7102           MovDir[x][y] = new_move_dir;
7103           MovDelay[x][y] = level.android_move_time * 8 + 1;
7104
7105           break;
7106         }
7107       }
7108     }
7109   }
7110   else if (move_pattern == MV_TURNING_LEFT ||
7111            move_pattern == MV_TURNING_RIGHT ||
7112            move_pattern == MV_TURNING_LEFT_RIGHT ||
7113            move_pattern == MV_TURNING_RIGHT_LEFT ||
7114            move_pattern == MV_TURNING_RANDOM ||
7115            move_pattern == MV_ALL_DIRECTIONS)
7116   {
7117     boolean can_turn_left =
7118       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7119     boolean can_turn_right =
7120       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7121
7122     if (element_info[element].move_stepsize == 0)       // "not moving"
7123       return;
7124
7125     if (move_pattern == MV_TURNING_LEFT)
7126       MovDir[x][y] = left_dir;
7127     else if (move_pattern == MV_TURNING_RIGHT)
7128       MovDir[x][y] = right_dir;
7129     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7130       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7131     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7132       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7133     else if (move_pattern == MV_TURNING_RANDOM)
7134       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7135                       can_turn_right && !can_turn_left ? right_dir :
7136                       RND(2) ? left_dir : right_dir);
7137     else if (can_turn_left && can_turn_right)
7138       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7139     else if (can_turn_left)
7140       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7141     else if (can_turn_right)
7142       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7143     else
7144       MovDir[x][y] = back_dir;
7145
7146     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7147   }
7148   else if (move_pattern == MV_HORIZONTAL ||
7149            move_pattern == MV_VERTICAL)
7150   {
7151     if (move_pattern & old_move_dir)
7152       MovDir[x][y] = back_dir;
7153     else if (move_pattern == MV_HORIZONTAL)
7154       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7155     else if (move_pattern == MV_VERTICAL)
7156       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7157
7158     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7159   }
7160   else if (move_pattern & MV_ANY_DIRECTION)
7161   {
7162     MovDir[x][y] = move_pattern;
7163     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7164   }
7165   else if (move_pattern & MV_WIND_DIRECTION)
7166   {
7167     MovDir[x][y] = game.wind_direction;
7168     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7169   }
7170   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7171   {
7172     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7173       MovDir[x][y] = left_dir;
7174     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7175       MovDir[x][y] = right_dir;
7176
7177     if (MovDir[x][y] != old_move_dir)
7178       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7179   }
7180   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7181   {
7182     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7183       MovDir[x][y] = right_dir;
7184     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7185       MovDir[x][y] = left_dir;
7186
7187     if (MovDir[x][y] != old_move_dir)
7188       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7189   }
7190   else if (move_pattern == MV_TOWARDS_PLAYER ||
7191            move_pattern == MV_AWAY_FROM_PLAYER)
7192   {
7193     int attr_x = -1, attr_y = -1;
7194     int newx, newy;
7195     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7196
7197     if (game.all_players_gone)
7198     {
7199       attr_x = game.exit_x;
7200       attr_y = game.exit_y;
7201     }
7202     else
7203     {
7204       int i;
7205
7206       for (i = 0; i < MAX_PLAYERS; i++)
7207       {
7208         struct PlayerInfo *player = &stored_player[i];
7209         int jx = player->jx, jy = player->jy;
7210
7211         if (!player->active)
7212           continue;
7213
7214         if (attr_x == -1 ||
7215             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7216         {
7217           attr_x = jx;
7218           attr_y = jy;
7219         }
7220       }
7221     }
7222
7223     MovDir[x][y] = MV_NONE;
7224     if (attr_x < x)
7225       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7226     else if (attr_x > x)
7227       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7228     if (attr_y < y)
7229       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7230     else if (attr_y > y)
7231       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7232
7233     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7234
7235     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7236     {
7237       boolean first_horiz = RND(2);
7238       int new_move_dir = MovDir[x][y];
7239
7240       if (element_info[element].move_stepsize == 0)     // "not moving"
7241       {
7242         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7243         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7244
7245         return;
7246       }
7247
7248       MovDir[x][y] =
7249         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7250       Moving2Blocked(x, y, &newx, &newy);
7251
7252       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7253         return;
7254
7255       MovDir[x][y] =
7256         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7257       Moving2Blocked(x, y, &newx, &newy);
7258
7259       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7260         return;
7261
7262       MovDir[x][y] = old_move_dir;
7263     }
7264   }
7265   else if (move_pattern == MV_WHEN_PUSHED ||
7266            move_pattern == MV_WHEN_DROPPED)
7267   {
7268     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7269       MovDir[x][y] = MV_NONE;
7270
7271     MovDelay[x][y] = 0;
7272   }
7273   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7274   {
7275     static int test_xy[7][2] =
7276     {
7277       { 0, -1 },
7278       { -1, 0 },
7279       { +1, 0 },
7280       { 0, +1 },
7281       { 0, -1 },
7282       { -1, 0 },
7283       { +1, 0 },
7284     };
7285     static int test_dir[7] =
7286     {
7287       MV_UP,
7288       MV_LEFT,
7289       MV_RIGHT,
7290       MV_DOWN,
7291       MV_UP,
7292       MV_LEFT,
7293       MV_RIGHT,
7294     };
7295     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7296     int move_preference = -1000000;     // start with very low preference
7297     int new_move_dir = MV_NONE;
7298     int start_test = RND(4);
7299     int i;
7300
7301     for (i = 0; i < NUM_DIRECTIONS; i++)
7302     {
7303       int move_dir = test_dir[start_test + i];
7304       int move_dir_preference;
7305
7306       xx = x + test_xy[start_test + i][0];
7307       yy = y + test_xy[start_test + i][1];
7308
7309       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7310           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7311       {
7312         new_move_dir = move_dir;
7313
7314         break;
7315       }
7316
7317       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7318         continue;
7319
7320       move_dir_preference = -1 * RunnerVisit[xx][yy];
7321       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7322         move_dir_preference = PlayerVisit[xx][yy];
7323
7324       if (move_dir_preference > move_preference)
7325       {
7326         // prefer field that has not been visited for the longest time
7327         move_preference = move_dir_preference;
7328         new_move_dir = move_dir;
7329       }
7330       else if (move_dir_preference == move_preference &&
7331                move_dir == old_move_dir)
7332       {
7333         // prefer last direction when all directions are preferred equally
7334         move_preference = move_dir_preference;
7335         new_move_dir = move_dir;
7336       }
7337     }
7338
7339     MovDir[x][y] = new_move_dir;
7340     if (old_move_dir != new_move_dir)
7341       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7342   }
7343 }
7344
7345 static void TurnRound(int x, int y)
7346 {
7347   int direction = MovDir[x][y];
7348
7349   TurnRoundExt(x, y);
7350
7351   GfxDir[x][y] = MovDir[x][y];
7352
7353   if (direction != MovDir[x][y])
7354     GfxFrame[x][y] = 0;
7355
7356   if (MovDelay[x][y])
7357     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7358
7359   ResetGfxFrame(x, y);
7360 }
7361
7362 static boolean JustBeingPushed(int x, int y)
7363 {
7364   int i;
7365
7366   for (i = 0; i < MAX_PLAYERS; i++)
7367   {
7368     struct PlayerInfo *player = &stored_player[i];
7369
7370     if (player->active && player->is_pushing && player->MovPos)
7371     {
7372       int next_jx = player->jx + (player->jx - player->last_jx);
7373       int next_jy = player->jy + (player->jy - player->last_jy);
7374
7375       if (x == next_jx && y == next_jy)
7376         return TRUE;
7377     }
7378   }
7379
7380   return FALSE;
7381 }
7382
7383 static void StartMoving(int x, int y)
7384 {
7385   boolean started_moving = FALSE;       // some elements can fall _and_ move
7386   int element = Feld[x][y];
7387
7388   if (Stop[x][y])
7389     return;
7390
7391   if (MovDelay[x][y] == 0)
7392     GfxAction[x][y] = ACTION_DEFAULT;
7393
7394   if (CAN_FALL(element) && y < lev_fieldy - 1)
7395   {
7396     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7397         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7398       if (JustBeingPushed(x, y))
7399         return;
7400
7401     if (element == EL_QUICKSAND_FULL)
7402     {
7403       if (IS_FREE(x, y + 1))
7404       {
7405         InitMovingField(x, y, MV_DOWN);
7406         started_moving = TRUE;
7407
7408         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7409 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7410         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7411           Store[x][y] = EL_ROCK;
7412 #else
7413         Store[x][y] = EL_ROCK;
7414 #endif
7415
7416         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7417       }
7418       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7419       {
7420         if (!MovDelay[x][y])
7421         {
7422           MovDelay[x][y] = TILEY + 1;
7423
7424           ResetGfxAnimation(x, y);
7425           ResetGfxAnimation(x, y + 1);
7426         }
7427
7428         if (MovDelay[x][y])
7429         {
7430           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7431           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7432
7433           MovDelay[x][y]--;
7434           if (MovDelay[x][y])
7435             return;
7436         }
7437
7438         Feld[x][y] = EL_QUICKSAND_EMPTY;
7439         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7440         Store[x][y + 1] = Store[x][y];
7441         Store[x][y] = 0;
7442
7443         PlayLevelSoundAction(x, y, ACTION_FILLING);
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_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_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     }
7473     else if (element == EL_QUICKSAND_FAST_FULL)
7474     {
7475       if (IS_FREE(x, y + 1))
7476       {
7477         InitMovingField(x, y, MV_DOWN);
7478         started_moving = TRUE;
7479
7480         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7481 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7482         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7483           Store[x][y] = EL_ROCK;
7484 #else
7485         Store[x][y] = EL_ROCK;
7486 #endif
7487
7488         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7489       }
7490       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7491       {
7492         if (!MovDelay[x][y])
7493         {
7494           MovDelay[x][y] = TILEY + 1;
7495
7496           ResetGfxAnimation(x, y);
7497           ResetGfxAnimation(x, y + 1);
7498         }
7499
7500         if (MovDelay[x][y])
7501         {
7502           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7503           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7504
7505           MovDelay[x][y]--;
7506           if (MovDelay[x][y])
7507             return;
7508         }
7509
7510         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7511         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7512         Store[x][y + 1] = Store[x][y];
7513         Store[x][y] = 0;
7514
7515         PlayLevelSoundAction(x, y, ACTION_FILLING);
7516       }
7517       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7518       {
7519         if (!MovDelay[x][y])
7520         {
7521           MovDelay[x][y] = TILEY + 1;
7522
7523           ResetGfxAnimation(x, y);
7524           ResetGfxAnimation(x, y + 1);
7525         }
7526
7527         if (MovDelay[x][y])
7528         {
7529           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7530           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7531
7532           MovDelay[x][y]--;
7533           if (MovDelay[x][y])
7534             return;
7535         }
7536
7537         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7538         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7539         Store[x][y + 1] = Store[x][y];
7540         Store[x][y] = 0;
7541
7542         PlayLevelSoundAction(x, y, ACTION_FILLING);
7543       }
7544     }
7545     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7546              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7547     {
7548       InitMovingField(x, y, MV_DOWN);
7549       started_moving = TRUE;
7550
7551       Feld[x][y] = EL_QUICKSAND_FILLING;
7552       Store[x][y] = element;
7553
7554       PlayLevelSoundAction(x, y, ACTION_FILLING);
7555     }
7556     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7557              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7558     {
7559       InitMovingField(x, y, MV_DOWN);
7560       started_moving = TRUE;
7561
7562       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7563       Store[x][y] = element;
7564
7565       PlayLevelSoundAction(x, y, ACTION_FILLING);
7566     }
7567     else if (element == EL_MAGIC_WALL_FULL)
7568     {
7569       if (IS_FREE(x, y + 1))
7570       {
7571         InitMovingField(x, y, MV_DOWN);
7572         started_moving = TRUE;
7573
7574         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7575         Store[x][y] = EL_CHANGED(Store[x][y]);
7576       }
7577       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7578       {
7579         if (!MovDelay[x][y])
7580           MovDelay[x][y] = TILEY / 4 + 1;
7581
7582         if (MovDelay[x][y])
7583         {
7584           MovDelay[x][y]--;
7585           if (MovDelay[x][y])
7586             return;
7587         }
7588
7589         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7590         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7591         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7592         Store[x][y] = 0;
7593       }
7594     }
7595     else if (element == EL_BD_MAGIC_WALL_FULL)
7596     {
7597       if (IS_FREE(x, y + 1))
7598       {
7599         InitMovingField(x, y, MV_DOWN);
7600         started_moving = TRUE;
7601
7602         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7603         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7604       }
7605       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7606       {
7607         if (!MovDelay[x][y])
7608           MovDelay[x][y] = TILEY / 4 + 1;
7609
7610         if (MovDelay[x][y])
7611         {
7612           MovDelay[x][y]--;
7613           if (MovDelay[x][y])
7614             return;
7615         }
7616
7617         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7618         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7619         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7620         Store[x][y] = 0;
7621       }
7622     }
7623     else if (element == EL_DC_MAGIC_WALL_FULL)
7624     {
7625       if (IS_FREE(x, y + 1))
7626       {
7627         InitMovingField(x, y, MV_DOWN);
7628         started_moving = TRUE;
7629
7630         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7631         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7632       }
7633       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7634       {
7635         if (!MovDelay[x][y])
7636           MovDelay[x][y] = TILEY / 4 + 1;
7637
7638         if (MovDelay[x][y])
7639         {
7640           MovDelay[x][y]--;
7641           if (MovDelay[x][y])
7642             return;
7643         }
7644
7645         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7646         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7647         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7648         Store[x][y] = 0;
7649       }
7650     }
7651     else if ((CAN_PASS_MAGIC_WALL(element) &&
7652               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7653                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7654              (CAN_PASS_DC_MAGIC_WALL(element) &&
7655               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7656
7657     {
7658       InitMovingField(x, y, MV_DOWN);
7659       started_moving = TRUE;
7660
7661       Feld[x][y] =
7662         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7663          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7664          EL_DC_MAGIC_WALL_FILLING);
7665       Store[x][y] = element;
7666     }
7667     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7668     {
7669       SplashAcid(x, y + 1);
7670
7671       InitMovingField(x, y, MV_DOWN);
7672       started_moving = TRUE;
7673
7674       Store[x][y] = EL_ACID;
7675     }
7676     else if (
7677              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7678               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7679              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7680               CAN_FALL(element) && WasJustFalling[x][y] &&
7681               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7682
7683              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7684               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7685               (Feld[x][y + 1] == EL_BLOCKED)))
7686     {
7687       /* this is needed for a special case not covered by calling "Impact()"
7688          from "ContinueMoving()": if an element moves to a tile directly below
7689          another element which was just falling on that tile (which was empty
7690          in the previous frame), the falling element above would just stop
7691          instead of smashing the element below (in previous version, the above
7692          element was just checked for "moving" instead of "falling", resulting
7693          in incorrect smashes caused by horizontal movement of the above
7694          element; also, the case of the player being the element to smash was
7695          simply not covered here... :-/ ) */
7696
7697       CheckCollision[x][y] = 0;
7698       CheckImpact[x][y] = 0;
7699
7700       Impact(x, y);
7701     }
7702     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7703     {
7704       if (MovDir[x][y] == MV_NONE)
7705       {
7706         InitMovingField(x, y, MV_DOWN);
7707         started_moving = TRUE;
7708       }
7709     }
7710     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7711     {
7712       if (WasJustFalling[x][y]) // prevent animation from being restarted
7713         MovDir[x][y] = MV_DOWN;
7714
7715       InitMovingField(x, y, MV_DOWN);
7716       started_moving = TRUE;
7717     }
7718     else if (element == EL_AMOEBA_DROP)
7719     {
7720       Feld[x][y] = EL_AMOEBA_GROWING;
7721       Store[x][y] = EL_AMOEBA_WET;
7722     }
7723     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7724               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7725              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7726              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7727     {
7728       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7729                                 (IS_FREE(x - 1, y + 1) ||
7730                                  Feld[x - 1][y + 1] == EL_ACID));
7731       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7732                                 (IS_FREE(x + 1, y + 1) ||
7733                                  Feld[x + 1][y + 1] == EL_ACID));
7734       boolean can_fall_any  = (can_fall_left || can_fall_right);
7735       boolean can_fall_both = (can_fall_left && can_fall_right);
7736       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7737
7738       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7739       {
7740         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7741           can_fall_right = FALSE;
7742         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7743           can_fall_left = FALSE;
7744         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7745           can_fall_right = FALSE;
7746         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7747           can_fall_left = FALSE;
7748
7749         can_fall_any  = (can_fall_left || can_fall_right);
7750         can_fall_both = FALSE;
7751       }
7752
7753       if (can_fall_both)
7754       {
7755         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7756           can_fall_right = FALSE;       // slip down on left side
7757         else
7758           can_fall_left = !(can_fall_right = RND(2));
7759
7760         can_fall_both = FALSE;
7761       }
7762
7763       if (can_fall_any)
7764       {
7765         // if not determined otherwise, prefer left side for slipping down
7766         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7767         started_moving = TRUE;
7768       }
7769     }
7770     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7771     {
7772       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7773       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7774       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7775       int belt_dir = game.belt_dir[belt_nr];
7776
7777       if ((belt_dir == MV_LEFT  && left_is_free) ||
7778           (belt_dir == MV_RIGHT && right_is_free))
7779       {
7780         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7781
7782         InitMovingField(x, y, belt_dir);
7783         started_moving = TRUE;
7784
7785         Pushed[x][y] = TRUE;
7786         Pushed[nextx][y] = TRUE;
7787
7788         GfxAction[x][y] = ACTION_DEFAULT;
7789       }
7790       else
7791       {
7792         MovDir[x][y] = 0;       // if element was moving, stop it
7793       }
7794     }
7795   }
7796
7797   // not "else if" because of elements that can fall and move (EL_SPRING)
7798   if (CAN_MOVE(element) && !started_moving)
7799   {
7800     int move_pattern = element_info[element].move_pattern;
7801     int newx, newy;
7802
7803     Moving2Blocked(x, y, &newx, &newy);
7804
7805     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7806       return;
7807
7808     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7809         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7810     {
7811       WasJustMoving[x][y] = 0;
7812       CheckCollision[x][y] = 0;
7813
7814       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7815
7816       if (Feld[x][y] != element)        // element has changed
7817         return;
7818     }
7819
7820     if (!MovDelay[x][y])        // start new movement phase
7821     {
7822       // all objects that can change their move direction after each step
7823       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7824
7825       if (element != EL_YAMYAM &&
7826           element != EL_DARK_YAMYAM &&
7827           element != EL_PACMAN &&
7828           !(move_pattern & MV_ANY_DIRECTION) &&
7829           move_pattern != MV_TURNING_LEFT &&
7830           move_pattern != MV_TURNING_RIGHT &&
7831           move_pattern != MV_TURNING_LEFT_RIGHT &&
7832           move_pattern != MV_TURNING_RIGHT_LEFT &&
7833           move_pattern != MV_TURNING_RANDOM)
7834       {
7835         TurnRound(x, y);
7836
7837         if (MovDelay[x][y] && (element == EL_BUG ||
7838                                element == EL_SPACESHIP ||
7839                                element == EL_SP_SNIKSNAK ||
7840                                element == EL_SP_ELECTRON ||
7841                                element == EL_MOLE))
7842           TEST_DrawLevelField(x, y);
7843       }
7844     }
7845
7846     if (MovDelay[x][y])         // wait some time before next movement
7847     {
7848       MovDelay[x][y]--;
7849
7850       if (element == EL_ROBOT ||
7851           element == EL_YAMYAM ||
7852           element == EL_DARK_YAMYAM)
7853       {
7854         DrawLevelElementAnimationIfNeeded(x, y, element);
7855         PlayLevelSoundAction(x, y, ACTION_WAITING);
7856       }
7857       else if (element == EL_SP_ELECTRON)
7858         DrawLevelElementAnimationIfNeeded(x, y, element);
7859       else if (element == EL_DRAGON)
7860       {
7861         int i;
7862         int dir = MovDir[x][y];
7863         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7864         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7865         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7866                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7867                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7868                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7869         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7870
7871         GfxAction[x][y] = ACTION_ATTACKING;
7872
7873         if (IS_PLAYER(x, y))
7874           DrawPlayerField(x, y);
7875         else
7876           TEST_DrawLevelField(x, y);
7877
7878         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7879
7880         for (i = 1; i <= 3; i++)
7881         {
7882           int xx = x + i * dx;
7883           int yy = y + i * dy;
7884           int sx = SCREENX(xx);
7885           int sy = SCREENY(yy);
7886           int flame_graphic = graphic + (i - 1);
7887
7888           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7889             break;
7890
7891           if (MovDelay[x][y])
7892           {
7893             int flamed = MovingOrBlocked2Element(xx, yy);
7894
7895             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7896               Bang(xx, yy);
7897             else
7898               RemoveMovingField(xx, yy);
7899
7900             ChangeDelay[xx][yy] = 0;
7901
7902             Feld[xx][yy] = EL_FLAMES;
7903
7904             if (IN_SCR_FIELD(sx, sy))
7905             {
7906               TEST_DrawLevelFieldCrumbled(xx, yy);
7907               DrawGraphic(sx, sy, flame_graphic, frame);
7908             }
7909           }
7910           else
7911           {
7912             if (Feld[xx][yy] == EL_FLAMES)
7913               Feld[xx][yy] = EL_EMPTY;
7914             TEST_DrawLevelField(xx, yy);
7915           }
7916         }
7917       }
7918
7919       if (MovDelay[x][y])       // element still has to wait some time
7920       {
7921         PlayLevelSoundAction(x, y, ACTION_WAITING);
7922
7923         return;
7924       }
7925     }
7926
7927     // now make next step
7928
7929     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7930
7931     if (DONT_COLLIDE_WITH(element) &&
7932         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7933         !PLAYER_ENEMY_PROTECTED(newx, newy))
7934     {
7935       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7936
7937       return;
7938     }
7939
7940     else if (CAN_MOVE_INTO_ACID(element) &&
7941              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7942              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7943              (MovDir[x][y] == MV_DOWN ||
7944               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7945     {
7946       SplashAcid(newx, newy);
7947       Store[x][y] = EL_ACID;
7948     }
7949     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7950     {
7951       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7952           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7953           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7954           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7955       {
7956         RemoveField(x, y);
7957         TEST_DrawLevelField(x, y);
7958
7959         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7960         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7961           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7962
7963         game.friends_still_needed--;
7964         if (!game.friends_still_needed &&
7965             !game.GameOver &&
7966             game.all_players_gone)
7967           LevelSolved();
7968
7969         return;
7970       }
7971       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7972       {
7973         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7974           TEST_DrawLevelField(newx, newy);
7975         else
7976           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7977       }
7978       else if (!IS_FREE(newx, newy))
7979       {
7980         GfxAction[x][y] = ACTION_WAITING;
7981
7982         if (IS_PLAYER(x, y))
7983           DrawPlayerField(x, y);
7984         else
7985           TEST_DrawLevelField(x, y);
7986
7987         return;
7988       }
7989     }
7990     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7991     {
7992       if (IS_FOOD_PIG(Feld[newx][newy]))
7993       {
7994         if (IS_MOVING(newx, newy))
7995           RemoveMovingField(newx, newy);
7996         else
7997         {
7998           Feld[newx][newy] = EL_EMPTY;
7999           TEST_DrawLevelField(newx, newy);
8000         }
8001
8002         PlayLevelSound(x, y, SND_PIG_DIGGING);
8003       }
8004       else if (!IS_FREE(newx, newy))
8005       {
8006         if (IS_PLAYER(x, y))
8007           DrawPlayerField(x, y);
8008         else
8009           TEST_DrawLevelField(x, y);
8010
8011         return;
8012       }
8013     }
8014     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8015     {
8016       if (Store[x][y] != EL_EMPTY)
8017       {
8018         boolean can_clone = FALSE;
8019         int xx, yy;
8020
8021         // check if element to clone is still there
8022         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8023         {
8024           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8025           {
8026             can_clone = TRUE;
8027
8028             break;
8029           }
8030         }
8031
8032         // cannot clone or target field not free anymore -- do not clone
8033         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8034           Store[x][y] = EL_EMPTY;
8035       }
8036
8037       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8038       {
8039         if (IS_MV_DIAGONAL(MovDir[x][y]))
8040         {
8041           int diagonal_move_dir = MovDir[x][y];
8042           int stored = Store[x][y];
8043           int change_delay = 8;
8044           int graphic;
8045
8046           // android is moving diagonally
8047
8048           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8049
8050           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8051           GfxElement[x][y] = EL_EMC_ANDROID;
8052           GfxAction[x][y] = ACTION_SHRINKING;
8053           GfxDir[x][y] = diagonal_move_dir;
8054           ChangeDelay[x][y] = change_delay;
8055
8056           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8057                                    GfxDir[x][y]);
8058
8059           DrawLevelGraphicAnimation(x, y, graphic);
8060           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8061
8062           if (Feld[newx][newy] == EL_ACID)
8063           {
8064             SplashAcid(newx, newy);
8065
8066             return;
8067           }
8068
8069           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8070
8071           Store[newx][newy] = EL_EMC_ANDROID;
8072           GfxElement[newx][newy] = EL_EMC_ANDROID;
8073           GfxAction[newx][newy] = ACTION_GROWING;
8074           GfxDir[newx][newy] = diagonal_move_dir;
8075           ChangeDelay[newx][newy] = change_delay;
8076
8077           graphic = el_act_dir2img(GfxElement[newx][newy],
8078                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8079
8080           DrawLevelGraphicAnimation(newx, newy, graphic);
8081           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8082
8083           return;
8084         }
8085         else
8086         {
8087           Feld[newx][newy] = EL_EMPTY;
8088           TEST_DrawLevelField(newx, newy);
8089
8090           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8091         }
8092       }
8093       else if (!IS_FREE(newx, newy))
8094       {
8095         return;
8096       }
8097     }
8098     else if (IS_CUSTOM_ELEMENT(element) &&
8099              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8100     {
8101       if (!DigFieldByCE(newx, newy, element))
8102         return;
8103
8104       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8105       {
8106         RunnerVisit[x][y] = FrameCounter;
8107         PlayerVisit[x][y] /= 8;         // expire player visit path
8108       }
8109     }
8110     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8111     {
8112       if (!IS_FREE(newx, newy))
8113       {
8114         if (IS_PLAYER(x, y))
8115           DrawPlayerField(x, y);
8116         else
8117           TEST_DrawLevelField(x, y);
8118
8119         return;
8120       }
8121       else
8122       {
8123         boolean wanna_flame = !RND(10);
8124         int dx = newx - x, dy = newy - y;
8125         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8126         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8127         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8128                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8129         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8130                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8131
8132         if ((wanna_flame ||
8133              IS_CLASSIC_ENEMY(element1) ||
8134              IS_CLASSIC_ENEMY(element2)) &&
8135             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8136             element1 != EL_FLAMES && element2 != EL_FLAMES)
8137         {
8138           ResetGfxAnimation(x, y);
8139           GfxAction[x][y] = ACTION_ATTACKING;
8140
8141           if (IS_PLAYER(x, y))
8142             DrawPlayerField(x, y);
8143           else
8144             TEST_DrawLevelField(x, y);
8145
8146           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8147
8148           MovDelay[x][y] = 50;
8149
8150           Feld[newx][newy] = EL_FLAMES;
8151           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8152             Feld[newx1][newy1] = EL_FLAMES;
8153           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8154             Feld[newx2][newy2] = EL_FLAMES;
8155
8156           return;
8157         }
8158       }
8159     }
8160     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8161              Feld[newx][newy] == EL_DIAMOND)
8162     {
8163       if (IS_MOVING(newx, newy))
8164         RemoveMovingField(newx, newy);
8165       else
8166       {
8167         Feld[newx][newy] = EL_EMPTY;
8168         TEST_DrawLevelField(newx, newy);
8169       }
8170
8171       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8172     }
8173     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8174              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8175     {
8176       if (AmoebaNr[newx][newy])
8177       {
8178         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8179         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8180             Feld[newx][newy] == EL_BD_AMOEBA)
8181           AmoebaCnt[AmoebaNr[newx][newy]]--;
8182       }
8183
8184       if (IS_MOVING(newx, newy))
8185       {
8186         RemoveMovingField(newx, newy);
8187       }
8188       else
8189       {
8190         Feld[newx][newy] = EL_EMPTY;
8191         TEST_DrawLevelField(newx, newy);
8192       }
8193
8194       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8195     }
8196     else if ((element == EL_PACMAN || element == EL_MOLE)
8197              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8198     {
8199       if (AmoebaNr[newx][newy])
8200       {
8201         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8202         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8203             Feld[newx][newy] == EL_BD_AMOEBA)
8204           AmoebaCnt[AmoebaNr[newx][newy]]--;
8205       }
8206
8207       if (element == EL_MOLE)
8208       {
8209         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8210         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8211
8212         ResetGfxAnimation(x, y);
8213         GfxAction[x][y] = ACTION_DIGGING;
8214         TEST_DrawLevelField(x, y);
8215
8216         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8217
8218         return;                         // wait for shrinking amoeba
8219       }
8220       else      // element == EL_PACMAN
8221       {
8222         Feld[newx][newy] = EL_EMPTY;
8223         TEST_DrawLevelField(newx, newy);
8224         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8225       }
8226     }
8227     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8228              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8229               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8230     {
8231       // wait for shrinking amoeba to completely disappear
8232       return;
8233     }
8234     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8235     {
8236       // object was running against a wall
8237
8238       TurnRound(x, y);
8239
8240       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8241         DrawLevelElementAnimation(x, y, element);
8242
8243       if (DONT_TOUCH(element))
8244         TestIfBadThingTouchesPlayer(x, y);
8245
8246       return;
8247     }
8248
8249     InitMovingField(x, y, MovDir[x][y]);
8250
8251     PlayLevelSoundAction(x, y, ACTION_MOVING);
8252   }
8253
8254   if (MovDir[x][y])
8255     ContinueMoving(x, y);
8256 }
8257
8258 void ContinueMoving(int x, int y)
8259 {
8260   int element = Feld[x][y];
8261   struct ElementInfo *ei = &element_info[element];
8262   int direction = MovDir[x][y];
8263   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8264   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8265   int newx = x + dx, newy = y + dy;
8266   int stored = Store[x][y];
8267   int stored_new = Store[newx][newy];
8268   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8269   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8270   boolean last_line = (newy == lev_fieldy - 1);
8271
8272   MovPos[x][y] += getElementMoveStepsize(x, y);
8273
8274   if (pushed_by_player) // special case: moving object pushed by player
8275     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8276
8277   if (ABS(MovPos[x][y]) < TILEX)
8278   {
8279     TEST_DrawLevelField(x, y);
8280
8281     return;     // element is still moving
8282   }
8283
8284   // element reached destination field
8285
8286   Feld[x][y] = EL_EMPTY;
8287   Feld[newx][newy] = element;
8288   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8289
8290   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8291   {
8292     element = Feld[newx][newy] = EL_ACID;
8293   }
8294   else if (element == EL_MOLE)
8295   {
8296     Feld[x][y] = EL_SAND;
8297
8298     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8299   }
8300   else if (element == EL_QUICKSAND_FILLING)
8301   {
8302     element = Feld[newx][newy] = get_next_element(element);
8303     Store[newx][newy] = Store[x][y];
8304   }
8305   else if (element == EL_QUICKSAND_EMPTYING)
8306   {
8307     Feld[x][y] = get_next_element(element);
8308     element = Feld[newx][newy] = Store[x][y];
8309   }
8310   else if (element == EL_QUICKSAND_FAST_FILLING)
8311   {
8312     element = Feld[newx][newy] = get_next_element(element);
8313     Store[newx][newy] = Store[x][y];
8314   }
8315   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8316   {
8317     Feld[x][y] = get_next_element(element);
8318     element = Feld[newx][newy] = Store[x][y];
8319   }
8320   else if (element == EL_MAGIC_WALL_FILLING)
8321   {
8322     element = Feld[newx][newy] = get_next_element(element);
8323     if (!game.magic_wall_active)
8324       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8325     Store[newx][newy] = Store[x][y];
8326   }
8327   else if (element == EL_MAGIC_WALL_EMPTYING)
8328   {
8329     Feld[x][y] = get_next_element(element);
8330     if (!game.magic_wall_active)
8331       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8332     element = Feld[newx][newy] = Store[x][y];
8333
8334     InitField(newx, newy, FALSE);
8335   }
8336   else if (element == EL_BD_MAGIC_WALL_FILLING)
8337   {
8338     element = Feld[newx][newy] = get_next_element(element);
8339     if (!game.magic_wall_active)
8340       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8341     Store[newx][newy] = Store[x][y];
8342   }
8343   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8344   {
8345     Feld[x][y] = get_next_element(element);
8346     if (!game.magic_wall_active)
8347       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8348     element = Feld[newx][newy] = Store[x][y];
8349
8350     InitField(newx, newy, FALSE);
8351   }
8352   else if (element == EL_DC_MAGIC_WALL_FILLING)
8353   {
8354     element = Feld[newx][newy] = get_next_element(element);
8355     if (!game.magic_wall_active)
8356       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8357     Store[newx][newy] = Store[x][y];
8358   }
8359   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8360   {
8361     Feld[x][y] = get_next_element(element);
8362     if (!game.magic_wall_active)
8363       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8364     element = Feld[newx][newy] = Store[x][y];
8365
8366     InitField(newx, newy, FALSE);
8367   }
8368   else if (element == EL_AMOEBA_DROPPING)
8369   {
8370     Feld[x][y] = get_next_element(element);
8371     element = Feld[newx][newy] = Store[x][y];
8372   }
8373   else if (element == EL_SOKOBAN_OBJECT)
8374   {
8375     if (Back[x][y])
8376       Feld[x][y] = Back[x][y];
8377
8378     if (Back[newx][newy])
8379       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8380
8381     Back[x][y] = Back[newx][newy] = 0;
8382   }
8383
8384   Store[x][y] = EL_EMPTY;
8385   MovPos[x][y] = 0;
8386   MovDir[x][y] = 0;
8387   MovDelay[x][y] = 0;
8388
8389   MovDelay[newx][newy] = 0;
8390
8391   if (CAN_CHANGE_OR_HAS_ACTION(element))
8392   {
8393     // copy element change control values to new field
8394     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8395     ChangePage[newx][newy]  = ChangePage[x][y];
8396     ChangeCount[newx][newy] = ChangeCount[x][y];
8397     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8398   }
8399
8400   CustomValue[newx][newy] = CustomValue[x][y];
8401
8402   ChangeDelay[x][y] = 0;
8403   ChangePage[x][y] = -1;
8404   ChangeCount[x][y] = 0;
8405   ChangeEvent[x][y] = -1;
8406
8407   CustomValue[x][y] = 0;
8408
8409   // copy animation control values to new field
8410   GfxFrame[newx][newy]  = GfxFrame[x][y];
8411   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8412   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8413   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8414
8415   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8416
8417   // some elements can leave other elements behind after moving
8418   if (ei->move_leave_element != EL_EMPTY &&
8419       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8420       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8421   {
8422     int move_leave_element = ei->move_leave_element;
8423
8424     // this makes it possible to leave the removed element again
8425     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8426       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8427
8428     Feld[x][y] = move_leave_element;
8429
8430     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8431       MovDir[x][y] = direction;
8432
8433     InitField(x, y, FALSE);
8434
8435     if (GFX_CRUMBLED(Feld[x][y]))
8436       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8437
8438     if (ELEM_IS_PLAYER(move_leave_element))
8439       RelocatePlayer(x, y, move_leave_element);
8440   }
8441
8442   // do this after checking for left-behind element
8443   ResetGfxAnimation(x, y);      // reset animation values for old field
8444
8445   if (!CAN_MOVE(element) ||
8446       (CAN_FALL(element) && direction == MV_DOWN &&
8447        (element == EL_SPRING ||
8448         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8449         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8450     GfxDir[x][y] = MovDir[newx][newy] = 0;
8451
8452   TEST_DrawLevelField(x, y);
8453   TEST_DrawLevelField(newx, newy);
8454
8455   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8456
8457   // prevent pushed element from moving on in pushed direction
8458   if (pushed_by_player && CAN_MOVE(element) &&
8459       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8460       !(element_info[element].move_pattern & direction))
8461     TurnRound(newx, newy);
8462
8463   // prevent elements on conveyor belt from moving on in last direction
8464   if (pushed_by_conveyor && CAN_FALL(element) &&
8465       direction & MV_HORIZONTAL)
8466     MovDir[newx][newy] = 0;
8467
8468   if (!pushed_by_player)
8469   {
8470     int nextx = newx + dx, nexty = newy + dy;
8471     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8472
8473     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8474
8475     if (CAN_FALL(element) && direction == MV_DOWN)
8476       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8477
8478     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8479       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8480
8481     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8482       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8483   }
8484
8485   if (DONT_TOUCH(element))      // object may be nasty to player or others
8486   {
8487     TestIfBadThingTouchesPlayer(newx, newy);
8488     TestIfBadThingTouchesFriend(newx, newy);
8489
8490     if (!IS_CUSTOM_ELEMENT(element))
8491       TestIfBadThingTouchesOtherBadThing(newx, newy);
8492   }
8493   else if (element == EL_PENGUIN)
8494     TestIfFriendTouchesBadThing(newx, newy);
8495
8496   if (DONT_GET_HIT_BY(element))
8497   {
8498     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8499   }
8500
8501   // give the player one last chance (one more frame) to move away
8502   if (CAN_FALL(element) && direction == MV_DOWN &&
8503       (last_line || (!IS_FREE(x, newy + 1) &&
8504                      (!IS_PLAYER(x, newy + 1) ||
8505                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8506     Impact(x, newy);
8507
8508   if (pushed_by_player && !game.use_change_when_pushing_bug)
8509   {
8510     int push_side = MV_DIR_OPPOSITE(direction);
8511     struct PlayerInfo *player = PLAYERINFO(x, y);
8512
8513     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8514                                player->index_bit, push_side);
8515     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8516                                         player->index_bit, push_side);
8517   }
8518
8519   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8520     MovDelay[newx][newy] = 1;
8521
8522   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8523
8524   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8525   TestIfElementHitsCustomElement(newx, newy, direction);
8526   TestIfPlayerTouchesCustomElement(newx, newy);
8527   TestIfElementTouchesCustomElement(newx, newy);
8528
8529   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8530       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8531     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8532                              MV_DIR_OPPOSITE(direction));
8533 }
8534
8535 int AmoebeNachbarNr(int ax, int ay)
8536 {
8537   int i;
8538   int element = Feld[ax][ay];
8539   int group_nr = 0;
8540   static int xy[4][2] =
8541   {
8542     { 0, -1 },
8543     { -1, 0 },
8544     { +1, 0 },
8545     { 0, +1 }
8546   };
8547
8548   for (i = 0; i < NUM_DIRECTIONS; i++)
8549   {
8550     int x = ax + xy[i][0];
8551     int y = ay + xy[i][1];
8552
8553     if (!IN_LEV_FIELD(x, y))
8554       continue;
8555
8556     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8557       group_nr = AmoebaNr[x][y];
8558   }
8559
8560   return group_nr;
8561 }
8562
8563 static void AmoebenVereinigen(int ax, int ay)
8564 {
8565   int i, x, y, xx, yy;
8566   int new_group_nr = AmoebaNr[ax][ay];
8567   static int xy[4][2] =
8568   {
8569     { 0, -1 },
8570     { -1, 0 },
8571     { +1, 0 },
8572     { 0, +1 }
8573   };
8574
8575   if (new_group_nr == 0)
8576     return;
8577
8578   for (i = 0; i < NUM_DIRECTIONS; i++)
8579   {
8580     x = ax + xy[i][0];
8581     y = ay + xy[i][1];
8582
8583     if (!IN_LEV_FIELD(x, y))
8584       continue;
8585
8586     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8587          Feld[x][y] == EL_BD_AMOEBA ||
8588          Feld[x][y] == EL_AMOEBA_DEAD) &&
8589         AmoebaNr[x][y] != new_group_nr)
8590     {
8591       int old_group_nr = AmoebaNr[x][y];
8592
8593       if (old_group_nr == 0)
8594         return;
8595
8596       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8597       AmoebaCnt[old_group_nr] = 0;
8598       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8599       AmoebaCnt2[old_group_nr] = 0;
8600
8601       SCAN_PLAYFIELD(xx, yy)
8602       {
8603         if (AmoebaNr[xx][yy] == old_group_nr)
8604           AmoebaNr[xx][yy] = new_group_nr;
8605       }
8606     }
8607   }
8608 }
8609
8610 void AmoebeUmwandeln(int ax, int ay)
8611 {
8612   int i, x, y;
8613
8614   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8615   {
8616     int group_nr = AmoebaNr[ax][ay];
8617
8618 #ifdef DEBUG
8619     if (group_nr == 0)
8620     {
8621       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8622       printf("AmoebeUmwandeln(): This should never happen!\n");
8623       return;
8624     }
8625 #endif
8626
8627     SCAN_PLAYFIELD(x, y)
8628     {
8629       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8630       {
8631         AmoebaNr[x][y] = 0;
8632         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8633       }
8634     }
8635
8636     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8637                             SND_AMOEBA_TURNING_TO_GEM :
8638                             SND_AMOEBA_TURNING_TO_ROCK));
8639     Bang(ax, ay);
8640   }
8641   else
8642   {
8643     static int xy[4][2] =
8644     {
8645       { 0, -1 },
8646       { -1, 0 },
8647       { +1, 0 },
8648       { 0, +1 }
8649     };
8650
8651     for (i = 0; i < NUM_DIRECTIONS; i++)
8652     {
8653       x = ax + xy[i][0];
8654       y = ay + xy[i][1];
8655
8656       if (!IN_LEV_FIELD(x, y))
8657         continue;
8658
8659       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8660       {
8661         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8662                               SND_AMOEBA_TURNING_TO_GEM :
8663                               SND_AMOEBA_TURNING_TO_ROCK));
8664         Bang(x, y);
8665       }
8666     }
8667   }
8668 }
8669
8670 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8671 {
8672   int x, y;
8673   int group_nr = AmoebaNr[ax][ay];
8674   boolean done = FALSE;
8675
8676 #ifdef DEBUG
8677   if (group_nr == 0)
8678   {
8679     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8680     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8681     return;
8682   }
8683 #endif
8684
8685   SCAN_PLAYFIELD(x, y)
8686   {
8687     if (AmoebaNr[x][y] == group_nr &&
8688         (Feld[x][y] == EL_AMOEBA_DEAD ||
8689          Feld[x][y] == EL_BD_AMOEBA ||
8690          Feld[x][y] == EL_AMOEBA_GROWING))
8691     {
8692       AmoebaNr[x][y] = 0;
8693       Feld[x][y] = new_element;
8694       InitField(x, y, FALSE);
8695       TEST_DrawLevelField(x, y);
8696       done = TRUE;
8697     }
8698   }
8699
8700   if (done)
8701     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8702                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8703                             SND_BD_AMOEBA_TURNING_TO_GEM));
8704 }
8705
8706 static void AmoebeWaechst(int x, int y)
8707 {
8708   static unsigned int sound_delay = 0;
8709   static unsigned int sound_delay_value = 0;
8710
8711   if (!MovDelay[x][y])          // start new growing cycle
8712   {
8713     MovDelay[x][y] = 7;
8714
8715     if (DelayReached(&sound_delay, sound_delay_value))
8716     {
8717       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8718       sound_delay_value = 30;
8719     }
8720   }
8721
8722   if (MovDelay[x][y])           // wait some time before growing bigger
8723   {
8724     MovDelay[x][y]--;
8725     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8726     {
8727       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8728                                            6 - MovDelay[x][y]);
8729
8730       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8731     }
8732
8733     if (!MovDelay[x][y])
8734     {
8735       Feld[x][y] = Store[x][y];
8736       Store[x][y] = 0;
8737       TEST_DrawLevelField(x, y);
8738     }
8739   }
8740 }
8741
8742 static void AmoebaDisappearing(int x, int y)
8743 {
8744   static unsigned int sound_delay = 0;
8745   static unsigned int sound_delay_value = 0;
8746
8747   if (!MovDelay[x][y])          // start new shrinking cycle
8748   {
8749     MovDelay[x][y] = 7;
8750
8751     if (DelayReached(&sound_delay, sound_delay_value))
8752       sound_delay_value = 30;
8753   }
8754
8755   if (MovDelay[x][y])           // wait some time before shrinking
8756   {
8757     MovDelay[x][y]--;
8758     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8759     {
8760       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8761                                            6 - MovDelay[x][y]);
8762
8763       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8764     }
8765
8766     if (!MovDelay[x][y])
8767     {
8768       Feld[x][y] = EL_EMPTY;
8769       TEST_DrawLevelField(x, y);
8770
8771       // don't let mole enter this field in this cycle;
8772       // (give priority to objects falling to this field from above)
8773       Stop[x][y] = TRUE;
8774     }
8775   }
8776 }
8777
8778 static void AmoebeAbleger(int ax, int ay)
8779 {
8780   int i;
8781   int element = Feld[ax][ay];
8782   int graphic = el2img(element);
8783   int newax = ax, neway = ay;
8784   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8785   static int xy[4][2] =
8786   {
8787     { 0, -1 },
8788     { -1, 0 },
8789     { +1, 0 },
8790     { 0, +1 }
8791   };
8792
8793   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8794   {
8795     Feld[ax][ay] = EL_AMOEBA_DEAD;
8796     TEST_DrawLevelField(ax, ay);
8797     return;
8798   }
8799
8800   if (IS_ANIMATED(graphic))
8801     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8802
8803   if (!MovDelay[ax][ay])        // start making new amoeba field
8804     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8805
8806   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8807   {
8808     MovDelay[ax][ay]--;
8809     if (MovDelay[ax][ay])
8810       return;
8811   }
8812
8813   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8814   {
8815     int start = RND(4);
8816     int x = ax + xy[start][0];
8817     int y = ay + xy[start][1];
8818
8819     if (!IN_LEV_FIELD(x, y))
8820       return;
8821
8822     if (IS_FREE(x, y) ||
8823         CAN_GROW_INTO(Feld[x][y]) ||
8824         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8825         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8826     {
8827       newax = x;
8828       neway = y;
8829     }
8830
8831     if (newax == ax && neway == ay)
8832       return;
8833   }
8834   else                          // normal or "filled" (BD style) amoeba
8835   {
8836     int start = RND(4);
8837     boolean waiting_for_player = FALSE;
8838
8839     for (i = 0; i < NUM_DIRECTIONS; i++)
8840     {
8841       int j = (start + i) % 4;
8842       int x = ax + xy[j][0];
8843       int y = ay + xy[j][1];
8844
8845       if (!IN_LEV_FIELD(x, y))
8846         continue;
8847
8848       if (IS_FREE(x, y) ||
8849           CAN_GROW_INTO(Feld[x][y]) ||
8850           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8851           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8852       {
8853         newax = x;
8854         neway = y;
8855         break;
8856       }
8857       else if (IS_PLAYER(x, y))
8858         waiting_for_player = TRUE;
8859     }
8860
8861     if (newax == ax && neway == ay)             // amoeba cannot grow
8862     {
8863       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8864       {
8865         Feld[ax][ay] = EL_AMOEBA_DEAD;
8866         TEST_DrawLevelField(ax, ay);
8867         AmoebaCnt[AmoebaNr[ax][ay]]--;
8868
8869         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8870         {
8871           if (element == EL_AMOEBA_FULL)
8872             AmoebeUmwandeln(ax, ay);
8873           else if (element == EL_BD_AMOEBA)
8874             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8875         }
8876       }
8877       return;
8878     }
8879     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8880     {
8881       // amoeba gets larger by growing in some direction
8882
8883       int new_group_nr = AmoebaNr[ax][ay];
8884
8885 #ifdef DEBUG
8886   if (new_group_nr == 0)
8887   {
8888     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8889     printf("AmoebeAbleger(): This should never happen!\n");
8890     return;
8891   }
8892 #endif
8893
8894       AmoebaNr[newax][neway] = new_group_nr;
8895       AmoebaCnt[new_group_nr]++;
8896       AmoebaCnt2[new_group_nr]++;
8897
8898       // if amoeba touches other amoeba(s) after growing, unify them
8899       AmoebenVereinigen(newax, neway);
8900
8901       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8902       {
8903         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8904         return;
8905       }
8906     }
8907   }
8908
8909   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8910       (neway == lev_fieldy - 1 && newax != ax))
8911   {
8912     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8913     Store[newax][neway] = element;
8914   }
8915   else if (neway == ay || element == EL_EMC_DRIPPER)
8916   {
8917     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8918
8919     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8920   }
8921   else
8922   {
8923     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8924     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8925     Store[ax][ay] = EL_AMOEBA_DROP;
8926     ContinueMoving(ax, ay);
8927     return;
8928   }
8929
8930   TEST_DrawLevelField(newax, neway);
8931 }
8932
8933 static void Life(int ax, int ay)
8934 {
8935   int x1, y1, x2, y2;
8936   int life_time = 40;
8937   int element = Feld[ax][ay];
8938   int graphic = el2img(element);
8939   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8940                          level.biomaze);
8941   boolean changed = FALSE;
8942
8943   if (IS_ANIMATED(graphic))
8944     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8945
8946   if (Stop[ax][ay])
8947     return;
8948
8949   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8950     MovDelay[ax][ay] = life_time;
8951
8952   if (MovDelay[ax][ay])         // wait some time before next cycle
8953   {
8954     MovDelay[ax][ay]--;
8955     if (MovDelay[ax][ay])
8956       return;
8957   }
8958
8959   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8960   {
8961     int xx = ax+x1, yy = ay+y1;
8962     int old_element = Feld[xx][yy];
8963     int num_neighbours = 0;
8964
8965     if (!IN_LEV_FIELD(xx, yy))
8966       continue;
8967
8968     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8969     {
8970       int x = xx+x2, y = yy+y2;
8971
8972       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8973         continue;
8974
8975       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8976       boolean is_neighbour = FALSE;
8977
8978       if (level.use_life_bugs)
8979         is_neighbour =
8980           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8981            (IS_FREE(x, y)                             &&  Stop[x][y]));
8982       else
8983         is_neighbour =
8984           (Last[x][y] == element || is_player_cell);
8985
8986       if (is_neighbour)
8987         num_neighbours++;
8988     }
8989
8990     boolean is_free = FALSE;
8991
8992     if (level.use_life_bugs)
8993       is_free = (IS_FREE(xx, yy));
8994     else
8995       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8996
8997     if (xx == ax && yy == ay)           // field in the middle
8998     {
8999       if (num_neighbours < life_parameter[0] ||
9000           num_neighbours > life_parameter[1])
9001       {
9002         Feld[xx][yy] = EL_EMPTY;
9003         if (Feld[xx][yy] != old_element)
9004           TEST_DrawLevelField(xx, yy);
9005         Stop[xx][yy] = TRUE;
9006         changed = TRUE;
9007       }
9008     }
9009     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9010     {                                   // free border field
9011       if (num_neighbours >= life_parameter[2] &&
9012           num_neighbours <= life_parameter[3])
9013       {
9014         Feld[xx][yy] = element;
9015         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9016         if (Feld[xx][yy] != old_element)
9017           TEST_DrawLevelField(xx, yy);
9018         Stop[xx][yy] = TRUE;
9019         changed = TRUE;
9020       }
9021     }
9022   }
9023
9024   if (changed)
9025     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9026                    SND_GAME_OF_LIFE_GROWING);
9027 }
9028
9029 static void InitRobotWheel(int x, int y)
9030 {
9031   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9032 }
9033
9034 static void RunRobotWheel(int x, int y)
9035 {
9036   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9037 }
9038
9039 static void StopRobotWheel(int x, int y)
9040 {
9041   if (game.robot_wheel_x == x &&
9042       game.robot_wheel_y == y)
9043   {
9044     game.robot_wheel_x = -1;
9045     game.robot_wheel_y = -1;
9046     game.robot_wheel_active = FALSE;
9047   }
9048 }
9049
9050 static void InitTimegateWheel(int x, int y)
9051 {
9052   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9053 }
9054
9055 static void RunTimegateWheel(int x, int y)
9056 {
9057   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9058 }
9059
9060 static void InitMagicBallDelay(int x, int y)
9061 {
9062   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9063 }
9064
9065 static void ActivateMagicBall(int bx, int by)
9066 {
9067   int x, y;
9068
9069   if (level.ball_random)
9070   {
9071     int pos_border = RND(8);    // select one of the eight border elements
9072     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9073     int xx = pos_content % 3;
9074     int yy = pos_content / 3;
9075
9076     x = bx - 1 + xx;
9077     y = by - 1 + yy;
9078
9079     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9080       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9081   }
9082   else
9083   {
9084     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9085     {
9086       int xx = x - bx + 1;
9087       int yy = y - by + 1;
9088
9089       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9090         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9091     }
9092   }
9093
9094   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9095 }
9096
9097 static void CheckExit(int x, int y)
9098 {
9099   if (game.gems_still_needed > 0 ||
9100       game.sokoban_fields_still_needed > 0 ||
9101       game.sokoban_objects_still_needed > 0 ||
9102       game.lights_still_needed > 0)
9103   {
9104     int element = Feld[x][y];
9105     int graphic = el2img(element);
9106
9107     if (IS_ANIMATED(graphic))
9108       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9109
9110     return;
9111   }
9112
9113   // do not re-open exit door closed after last player
9114   if (game.all_players_gone)
9115     return;
9116
9117   Feld[x][y] = EL_EXIT_OPENING;
9118
9119   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9120 }
9121
9122 static void CheckExitEM(int x, int y)
9123 {
9124   if (game.gems_still_needed > 0 ||
9125       game.sokoban_fields_still_needed > 0 ||
9126       game.sokoban_objects_still_needed > 0 ||
9127       game.lights_still_needed > 0)
9128   {
9129     int element = Feld[x][y];
9130     int graphic = el2img(element);
9131
9132     if (IS_ANIMATED(graphic))
9133       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9134
9135     return;
9136   }
9137
9138   // do not re-open exit door closed after last player
9139   if (game.all_players_gone)
9140     return;
9141
9142   Feld[x][y] = EL_EM_EXIT_OPENING;
9143
9144   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9145 }
9146
9147 static void CheckExitSteel(int x, int y)
9148 {
9149   if (game.gems_still_needed > 0 ||
9150       game.sokoban_fields_still_needed > 0 ||
9151       game.sokoban_objects_still_needed > 0 ||
9152       game.lights_still_needed > 0)
9153   {
9154     int element = Feld[x][y];
9155     int graphic = el2img(element);
9156
9157     if (IS_ANIMATED(graphic))
9158       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9159
9160     return;
9161   }
9162
9163   // do not re-open exit door closed after last player
9164   if (game.all_players_gone)
9165     return;
9166
9167   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9168
9169   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9170 }
9171
9172 static void CheckExitSteelEM(int x, int y)
9173 {
9174   if (game.gems_still_needed > 0 ||
9175       game.sokoban_fields_still_needed > 0 ||
9176       game.sokoban_objects_still_needed > 0 ||
9177       game.lights_still_needed > 0)
9178   {
9179     int element = Feld[x][y];
9180     int graphic = el2img(element);
9181
9182     if (IS_ANIMATED(graphic))
9183       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9184
9185     return;
9186   }
9187
9188   // do not re-open exit door closed after last player
9189   if (game.all_players_gone)
9190     return;
9191
9192   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9193
9194   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9195 }
9196
9197 static void CheckExitSP(int x, int y)
9198 {
9199   if (game.gems_still_needed > 0)
9200   {
9201     int element = Feld[x][y];
9202     int graphic = el2img(element);
9203
9204     if (IS_ANIMATED(graphic))
9205       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9206
9207     return;
9208   }
9209
9210   // do not re-open exit door closed after last player
9211   if (game.all_players_gone)
9212     return;
9213
9214   Feld[x][y] = EL_SP_EXIT_OPENING;
9215
9216   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9217 }
9218
9219 static void CloseAllOpenTimegates(void)
9220 {
9221   int x, y;
9222
9223   SCAN_PLAYFIELD(x, y)
9224   {
9225     int element = Feld[x][y];
9226
9227     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9228     {
9229       Feld[x][y] = EL_TIMEGATE_CLOSING;
9230
9231       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9232     }
9233   }
9234 }
9235
9236 static void DrawTwinkleOnField(int x, int y)
9237 {
9238   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9239     return;
9240
9241   if (Feld[x][y] == EL_BD_DIAMOND)
9242     return;
9243
9244   if (MovDelay[x][y] == 0)      // next animation frame
9245     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9246
9247   if (MovDelay[x][y] != 0)      // wait some time before next frame
9248   {
9249     MovDelay[x][y]--;
9250
9251     DrawLevelElementAnimation(x, y, Feld[x][y]);
9252
9253     if (MovDelay[x][y] != 0)
9254     {
9255       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9256                                            10 - MovDelay[x][y]);
9257
9258       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9259     }
9260   }
9261 }
9262
9263 static void MauerWaechst(int x, int y)
9264 {
9265   int delay = 6;
9266
9267   if (!MovDelay[x][y])          // next animation frame
9268     MovDelay[x][y] = 3 * delay;
9269
9270   if (MovDelay[x][y])           // wait some time before next frame
9271   {
9272     MovDelay[x][y]--;
9273
9274     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9275     {
9276       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9277       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9278
9279       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9280     }
9281
9282     if (!MovDelay[x][y])
9283     {
9284       if (MovDir[x][y] == MV_LEFT)
9285       {
9286         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9287           TEST_DrawLevelField(x - 1, y);
9288       }
9289       else if (MovDir[x][y] == MV_RIGHT)
9290       {
9291         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9292           TEST_DrawLevelField(x + 1, y);
9293       }
9294       else if (MovDir[x][y] == MV_UP)
9295       {
9296         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9297           TEST_DrawLevelField(x, y - 1);
9298       }
9299       else
9300       {
9301         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9302           TEST_DrawLevelField(x, y + 1);
9303       }
9304
9305       Feld[x][y] = Store[x][y];
9306       Store[x][y] = 0;
9307       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9308       TEST_DrawLevelField(x, y);
9309     }
9310   }
9311 }
9312
9313 static void MauerAbleger(int ax, int ay)
9314 {
9315   int element = Feld[ax][ay];
9316   int graphic = el2img(element);
9317   boolean oben_frei = FALSE, unten_frei = FALSE;
9318   boolean links_frei = FALSE, rechts_frei = FALSE;
9319   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9320   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9321   boolean new_wall = FALSE;
9322
9323   if (IS_ANIMATED(graphic))
9324     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9325
9326   if (!MovDelay[ax][ay])        // start building new wall
9327     MovDelay[ax][ay] = 6;
9328
9329   if (MovDelay[ax][ay])         // wait some time before building new wall
9330   {
9331     MovDelay[ax][ay]--;
9332     if (MovDelay[ax][ay])
9333       return;
9334   }
9335
9336   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9337     oben_frei = TRUE;
9338   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9339     unten_frei = TRUE;
9340   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9341     links_frei = TRUE;
9342   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9343     rechts_frei = TRUE;
9344
9345   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9346       element == EL_EXPANDABLE_WALL_ANY)
9347   {
9348     if (oben_frei)
9349     {
9350       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9351       Store[ax][ay-1] = element;
9352       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9353       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9354         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9355                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9356       new_wall = TRUE;
9357     }
9358     if (unten_frei)
9359     {
9360       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9361       Store[ax][ay+1] = element;
9362       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9363       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9364         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9365                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9366       new_wall = TRUE;
9367     }
9368   }
9369
9370   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9371       element == EL_EXPANDABLE_WALL_ANY ||
9372       element == EL_EXPANDABLE_WALL ||
9373       element == EL_BD_EXPANDABLE_WALL)
9374   {
9375     if (links_frei)
9376     {
9377       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9378       Store[ax-1][ay] = element;
9379       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9380       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9381         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9382                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9383       new_wall = TRUE;
9384     }
9385
9386     if (rechts_frei)
9387     {
9388       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9389       Store[ax+1][ay] = element;
9390       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9391       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9392         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9393                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9394       new_wall = TRUE;
9395     }
9396   }
9397
9398   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9399     TEST_DrawLevelField(ax, ay);
9400
9401   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9402     oben_massiv = TRUE;
9403   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9404     unten_massiv = TRUE;
9405   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9406     links_massiv = TRUE;
9407   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9408     rechts_massiv = TRUE;
9409
9410   if (((oben_massiv && unten_massiv) ||
9411        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9412        element == EL_EXPANDABLE_WALL) &&
9413       ((links_massiv && rechts_massiv) ||
9414        element == EL_EXPANDABLE_WALL_VERTICAL))
9415     Feld[ax][ay] = EL_WALL;
9416
9417   if (new_wall)
9418     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9419 }
9420
9421 static void MauerAblegerStahl(int ax, int ay)
9422 {
9423   int element = Feld[ax][ay];
9424   int graphic = el2img(element);
9425   boolean oben_frei = FALSE, unten_frei = FALSE;
9426   boolean links_frei = FALSE, rechts_frei = FALSE;
9427   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9428   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9429   boolean new_wall = FALSE;
9430
9431   if (IS_ANIMATED(graphic))
9432     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9433
9434   if (!MovDelay[ax][ay])        // start building new wall
9435     MovDelay[ax][ay] = 6;
9436
9437   if (MovDelay[ax][ay])         // wait some time before building new wall
9438   {
9439     MovDelay[ax][ay]--;
9440     if (MovDelay[ax][ay])
9441       return;
9442   }
9443
9444   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9445     oben_frei = TRUE;
9446   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9447     unten_frei = TRUE;
9448   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9449     links_frei = TRUE;
9450   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9451     rechts_frei = TRUE;
9452
9453   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9454       element == EL_EXPANDABLE_STEELWALL_ANY)
9455   {
9456     if (oben_frei)
9457     {
9458       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9459       Store[ax][ay-1] = element;
9460       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9461       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9462         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9463                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9464       new_wall = TRUE;
9465     }
9466     if (unten_frei)
9467     {
9468       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9469       Store[ax][ay+1] = element;
9470       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9471       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9472         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9473                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9474       new_wall = TRUE;
9475     }
9476   }
9477
9478   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9479       element == EL_EXPANDABLE_STEELWALL_ANY)
9480   {
9481     if (links_frei)
9482     {
9483       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9484       Store[ax-1][ay] = element;
9485       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9486       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9487         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9488                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9489       new_wall = TRUE;
9490     }
9491
9492     if (rechts_frei)
9493     {
9494       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9495       Store[ax+1][ay] = element;
9496       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9497       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9498         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9499                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9500       new_wall = TRUE;
9501     }
9502   }
9503
9504   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9505     oben_massiv = TRUE;
9506   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9507     unten_massiv = TRUE;
9508   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9509     links_massiv = TRUE;
9510   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9511     rechts_massiv = TRUE;
9512
9513   if (((oben_massiv && unten_massiv) ||
9514        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9515       ((links_massiv && rechts_massiv) ||
9516        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9517     Feld[ax][ay] = EL_STEELWALL;
9518
9519   if (new_wall)
9520     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9521 }
9522
9523 static void CheckForDragon(int x, int y)
9524 {
9525   int i, j;
9526   boolean dragon_found = FALSE;
9527   static int xy[4][2] =
9528   {
9529     { 0, -1 },
9530     { -1, 0 },
9531     { +1, 0 },
9532     { 0, +1 }
9533   };
9534
9535   for (i = 0; i < NUM_DIRECTIONS; i++)
9536   {
9537     for (j = 0; j < 4; j++)
9538     {
9539       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9540
9541       if (IN_LEV_FIELD(xx, yy) &&
9542           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9543       {
9544         if (Feld[xx][yy] == EL_DRAGON)
9545           dragon_found = TRUE;
9546       }
9547       else
9548         break;
9549     }
9550   }
9551
9552   if (!dragon_found)
9553   {
9554     for (i = 0; i < NUM_DIRECTIONS; i++)
9555     {
9556       for (j = 0; j < 3; j++)
9557       {
9558         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9559   
9560         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9561         {
9562           Feld[xx][yy] = EL_EMPTY;
9563           TEST_DrawLevelField(xx, yy);
9564         }
9565         else
9566           break;
9567       }
9568     }
9569   }
9570 }
9571
9572 static void InitBuggyBase(int x, int y)
9573 {
9574   int element = Feld[x][y];
9575   int activating_delay = FRAMES_PER_SECOND / 4;
9576
9577   ChangeDelay[x][y] =
9578     (element == EL_SP_BUGGY_BASE ?
9579      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9580      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9581      activating_delay :
9582      element == EL_SP_BUGGY_BASE_ACTIVE ?
9583      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9584 }
9585
9586 static void WarnBuggyBase(int x, int y)
9587 {
9588   int i;
9589   static int xy[4][2] =
9590   {
9591     { 0, -1 },
9592     { -1, 0 },
9593     { +1, 0 },
9594     { 0, +1 }
9595   };
9596
9597   for (i = 0; i < NUM_DIRECTIONS; i++)
9598   {
9599     int xx = x + xy[i][0];
9600     int yy = y + xy[i][1];
9601
9602     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9603     {
9604       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9605
9606       break;
9607     }
9608   }
9609 }
9610
9611 static void InitTrap(int x, int y)
9612 {
9613   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9614 }
9615
9616 static void ActivateTrap(int x, int y)
9617 {
9618   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9619 }
9620
9621 static void ChangeActiveTrap(int x, int y)
9622 {
9623   int graphic = IMG_TRAP_ACTIVE;
9624
9625   // if new animation frame was drawn, correct crumbled sand border
9626   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9627     TEST_DrawLevelFieldCrumbled(x, y);
9628 }
9629
9630 static int getSpecialActionElement(int element, int number, int base_element)
9631 {
9632   return (element != EL_EMPTY ? element :
9633           number != -1 ? base_element + number - 1 :
9634           EL_EMPTY);
9635 }
9636
9637 static int getModifiedActionNumber(int value_old, int operator, int operand,
9638                                    int value_min, int value_max)
9639 {
9640   int value_new = (operator == CA_MODE_SET      ? operand :
9641                    operator == CA_MODE_ADD      ? value_old + operand :
9642                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9643                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9644                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9645                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9646                    value_old);
9647
9648   return (value_new < value_min ? value_min :
9649           value_new > value_max ? value_max :
9650           value_new);
9651 }
9652
9653 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9654 {
9655   struct ElementInfo *ei = &element_info[element];
9656   struct ElementChangeInfo *change = &ei->change_page[page];
9657   int target_element = change->target_element;
9658   int action_type = change->action_type;
9659   int action_mode = change->action_mode;
9660   int action_arg = change->action_arg;
9661   int action_element = change->action_element;
9662   int i;
9663
9664   if (!change->has_action)
9665     return;
9666
9667   // ---------- determine action paramater values -----------------------------
9668
9669   int level_time_value =
9670     (level.time > 0 ? TimeLeft :
9671      TimePlayed);
9672
9673   int action_arg_element_raw =
9674     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9675      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9676      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9677      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9678      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9679      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9680      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9681      EL_EMPTY);
9682   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9683
9684   int action_arg_direction =
9685     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9686      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9687      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9688      change->actual_trigger_side :
9689      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9690      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9691      MV_NONE);
9692
9693   int action_arg_number_min =
9694     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9695      CA_ARG_MIN);
9696
9697   int action_arg_number_max =
9698     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9699      action_type == CA_SET_LEVEL_GEMS ? 999 :
9700      action_type == CA_SET_LEVEL_TIME ? 9999 :
9701      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9702      action_type == CA_SET_CE_VALUE ? 9999 :
9703      action_type == CA_SET_CE_SCORE ? 9999 :
9704      CA_ARG_MAX);
9705
9706   int action_arg_number_reset =
9707     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9708      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9709      action_type == CA_SET_LEVEL_TIME ? level.time :
9710      action_type == CA_SET_LEVEL_SCORE ? 0 :
9711      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9712      action_type == CA_SET_CE_SCORE ? 0 :
9713      0);
9714
9715   int action_arg_number =
9716     (action_arg <= CA_ARG_MAX ? action_arg :
9717      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9718      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9719      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9720      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9721      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9722      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9723      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9724      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9725      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9726      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9727      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9728      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9729      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9730      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9731      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9732      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9733      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9734      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9735      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9736      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9737      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9738      -1);
9739
9740   int action_arg_number_old =
9741     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9742      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9743      action_type == CA_SET_LEVEL_SCORE ? game.score :
9744      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9745      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9746      0);
9747
9748   int action_arg_number_new =
9749     getModifiedActionNumber(action_arg_number_old,
9750                             action_mode, action_arg_number,
9751                             action_arg_number_min, action_arg_number_max);
9752
9753   int trigger_player_bits =
9754     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9755      change->actual_trigger_player_bits : change->trigger_player);
9756
9757   int action_arg_player_bits =
9758     (action_arg >= CA_ARG_PLAYER_1 &&
9759      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9760      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9761      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9762      PLAYER_BITS_ANY);
9763
9764   // ---------- execute action  -----------------------------------------------
9765
9766   switch (action_type)
9767   {
9768     case CA_NO_ACTION:
9769     {
9770       return;
9771     }
9772
9773     // ---------- level actions  ----------------------------------------------
9774
9775     case CA_RESTART_LEVEL:
9776     {
9777       game.restart_level = TRUE;
9778
9779       break;
9780     }
9781
9782     case CA_SHOW_ENVELOPE:
9783     {
9784       int element = getSpecialActionElement(action_arg_element,
9785                                             action_arg_number, EL_ENVELOPE_1);
9786
9787       if (IS_ENVELOPE(element))
9788         local_player->show_envelope = element;
9789
9790       break;
9791     }
9792
9793     case CA_SET_LEVEL_TIME:
9794     {
9795       if (level.time > 0)       // only modify limited time value
9796       {
9797         TimeLeft = action_arg_number_new;
9798
9799         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9800
9801         DisplayGameControlValues();
9802
9803         if (!TimeLeft && setup.time_limit)
9804           for (i = 0; i < MAX_PLAYERS; i++)
9805             KillPlayer(&stored_player[i]);
9806       }
9807
9808       break;
9809     }
9810
9811     case CA_SET_LEVEL_SCORE:
9812     {
9813       game.score = action_arg_number_new;
9814
9815       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9816
9817       DisplayGameControlValues();
9818
9819       break;
9820     }
9821
9822     case CA_SET_LEVEL_GEMS:
9823     {
9824       game.gems_still_needed = action_arg_number_new;
9825
9826       game.snapshot.collected_item = TRUE;
9827
9828       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9829
9830       DisplayGameControlValues();
9831
9832       break;
9833     }
9834
9835     case CA_SET_LEVEL_WIND:
9836     {
9837       game.wind_direction = action_arg_direction;
9838
9839       break;
9840     }
9841
9842     case CA_SET_LEVEL_RANDOM_SEED:
9843     {
9844       // ensure that setting a new random seed while playing is predictable
9845       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9846
9847       break;
9848     }
9849
9850     // ---------- player actions  ---------------------------------------------
9851
9852     case CA_MOVE_PLAYER:
9853     {
9854       // automatically move to the next field in specified direction
9855       for (i = 0; i < MAX_PLAYERS; i++)
9856         if (trigger_player_bits & (1 << i))
9857           stored_player[i].programmed_action = action_arg_direction;
9858
9859       break;
9860     }
9861
9862     case CA_EXIT_PLAYER:
9863     {
9864       for (i = 0; i < MAX_PLAYERS; i++)
9865         if (action_arg_player_bits & (1 << i))
9866           ExitPlayer(&stored_player[i]);
9867
9868       if (game.players_still_needed == 0)
9869         LevelSolved();
9870
9871       break;
9872     }
9873
9874     case CA_KILL_PLAYER:
9875     {
9876       for (i = 0; i < MAX_PLAYERS; i++)
9877         if (action_arg_player_bits & (1 << i))
9878           KillPlayer(&stored_player[i]);
9879
9880       break;
9881     }
9882
9883     case CA_SET_PLAYER_KEYS:
9884     {
9885       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9886       int element = getSpecialActionElement(action_arg_element,
9887                                             action_arg_number, EL_KEY_1);
9888
9889       if (IS_KEY(element))
9890       {
9891         for (i = 0; i < MAX_PLAYERS; i++)
9892         {
9893           if (trigger_player_bits & (1 << i))
9894           {
9895             stored_player[i].key[KEY_NR(element)] = key_state;
9896
9897             DrawGameDoorValues();
9898           }
9899         }
9900       }
9901
9902       break;
9903     }
9904
9905     case CA_SET_PLAYER_SPEED:
9906     {
9907       for (i = 0; i < MAX_PLAYERS; i++)
9908       {
9909         if (trigger_player_bits & (1 << i))
9910         {
9911           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9912
9913           if (action_arg == CA_ARG_SPEED_FASTER &&
9914               stored_player[i].cannot_move)
9915           {
9916             action_arg_number = STEPSIZE_VERY_SLOW;
9917           }
9918           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9919                    action_arg == CA_ARG_SPEED_FASTER)
9920           {
9921             action_arg_number = 2;
9922             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9923                            CA_MODE_MULTIPLY);
9924           }
9925           else if (action_arg == CA_ARG_NUMBER_RESET)
9926           {
9927             action_arg_number = level.initial_player_stepsize[i];
9928           }
9929
9930           move_stepsize =
9931             getModifiedActionNumber(move_stepsize,
9932                                     action_mode,
9933                                     action_arg_number,
9934                                     action_arg_number_min,
9935                                     action_arg_number_max);
9936
9937           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9938         }
9939       }
9940
9941       break;
9942     }
9943
9944     case CA_SET_PLAYER_SHIELD:
9945     {
9946       for (i = 0; i < MAX_PLAYERS; i++)
9947       {
9948         if (trigger_player_bits & (1 << i))
9949         {
9950           if (action_arg == CA_ARG_SHIELD_OFF)
9951           {
9952             stored_player[i].shield_normal_time_left = 0;
9953             stored_player[i].shield_deadly_time_left = 0;
9954           }
9955           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9956           {
9957             stored_player[i].shield_normal_time_left = 999999;
9958           }
9959           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9960           {
9961             stored_player[i].shield_normal_time_left = 999999;
9962             stored_player[i].shield_deadly_time_left = 999999;
9963           }
9964         }
9965       }
9966
9967       break;
9968     }
9969
9970     case CA_SET_PLAYER_GRAVITY:
9971     {
9972       for (i = 0; i < MAX_PLAYERS; i++)
9973       {
9974         if (trigger_player_bits & (1 << i))
9975         {
9976           stored_player[i].gravity =
9977             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9978              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9979              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9980              stored_player[i].gravity);
9981         }
9982       }
9983
9984       break;
9985     }
9986
9987     case CA_SET_PLAYER_ARTWORK:
9988     {
9989       for (i = 0; i < MAX_PLAYERS; i++)
9990       {
9991         if (trigger_player_bits & (1 << i))
9992         {
9993           int artwork_element = action_arg_element;
9994
9995           if (action_arg == CA_ARG_ELEMENT_RESET)
9996             artwork_element =
9997               (level.use_artwork_element[i] ? level.artwork_element[i] :
9998                stored_player[i].element_nr);
9999
10000           if (stored_player[i].artwork_element != artwork_element)
10001             stored_player[i].Frame = 0;
10002
10003           stored_player[i].artwork_element = artwork_element;
10004
10005           SetPlayerWaiting(&stored_player[i], FALSE);
10006
10007           // set number of special actions for bored and sleeping animation
10008           stored_player[i].num_special_action_bored =
10009             get_num_special_action(artwork_element,
10010                                    ACTION_BORING_1, ACTION_BORING_LAST);
10011           stored_player[i].num_special_action_sleeping =
10012             get_num_special_action(artwork_element,
10013                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10014         }
10015       }
10016
10017       break;
10018     }
10019
10020     case CA_SET_PLAYER_INVENTORY:
10021     {
10022       for (i = 0; i < MAX_PLAYERS; i++)
10023       {
10024         struct PlayerInfo *player = &stored_player[i];
10025         int j, k;
10026
10027         if (trigger_player_bits & (1 << i))
10028         {
10029           int inventory_element = action_arg_element;
10030
10031           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10032               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10033               action_arg == CA_ARG_ELEMENT_ACTION)
10034           {
10035             int element = inventory_element;
10036             int collect_count = element_info[element].collect_count_initial;
10037
10038             if (!IS_CUSTOM_ELEMENT(element))
10039               collect_count = 1;
10040
10041             if (collect_count == 0)
10042               player->inventory_infinite_element = element;
10043             else
10044               for (k = 0; k < collect_count; k++)
10045                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10046                   player->inventory_element[player->inventory_size++] =
10047                     element;
10048           }
10049           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10050                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10051                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10052           {
10053             if (player->inventory_infinite_element != EL_UNDEFINED &&
10054                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10055                                      action_arg_element_raw))
10056               player->inventory_infinite_element = EL_UNDEFINED;
10057
10058             for (k = 0, j = 0; j < player->inventory_size; j++)
10059             {
10060               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10061                                         action_arg_element_raw))
10062                 player->inventory_element[k++] = player->inventory_element[j];
10063             }
10064
10065             player->inventory_size = k;
10066           }
10067           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10068           {
10069             if (player->inventory_size > 0)
10070             {
10071               for (j = 0; j < player->inventory_size - 1; j++)
10072                 player->inventory_element[j] = player->inventory_element[j + 1];
10073
10074               player->inventory_size--;
10075             }
10076           }
10077           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10078           {
10079             if (player->inventory_size > 0)
10080               player->inventory_size--;
10081           }
10082           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10083           {
10084             player->inventory_infinite_element = EL_UNDEFINED;
10085             player->inventory_size = 0;
10086           }
10087           else if (action_arg == CA_ARG_INVENTORY_RESET)
10088           {
10089             player->inventory_infinite_element = EL_UNDEFINED;
10090             player->inventory_size = 0;
10091
10092             if (level.use_initial_inventory[i])
10093             {
10094               for (j = 0; j < level.initial_inventory_size[i]; j++)
10095               {
10096                 int element = level.initial_inventory_content[i][j];
10097                 int collect_count = element_info[element].collect_count_initial;
10098
10099                 if (!IS_CUSTOM_ELEMENT(element))
10100                   collect_count = 1;
10101
10102                 if (collect_count == 0)
10103                   player->inventory_infinite_element = element;
10104                 else
10105                   for (k = 0; k < collect_count; k++)
10106                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10107                       player->inventory_element[player->inventory_size++] =
10108                         element;
10109               }
10110             }
10111           }
10112         }
10113       }
10114
10115       break;
10116     }
10117
10118     // ---------- CE actions  -------------------------------------------------
10119
10120     case CA_SET_CE_VALUE:
10121     {
10122       int last_ce_value = CustomValue[x][y];
10123
10124       CustomValue[x][y] = action_arg_number_new;
10125
10126       if (CustomValue[x][y] != last_ce_value)
10127       {
10128         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10129         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10130
10131         if (CustomValue[x][y] == 0)
10132         {
10133           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10134           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10135         }
10136       }
10137
10138       break;
10139     }
10140
10141     case CA_SET_CE_SCORE:
10142     {
10143       int last_ce_score = ei->collect_score;
10144
10145       ei->collect_score = action_arg_number_new;
10146
10147       if (ei->collect_score != last_ce_score)
10148       {
10149         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10150         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10151
10152         if (ei->collect_score == 0)
10153         {
10154           int xx, yy;
10155
10156           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10157           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10158
10159           /*
10160             This is a very special case that seems to be a mixture between
10161             CheckElementChange() and CheckTriggeredElementChange(): while
10162             the first one only affects single elements that are triggered
10163             directly, the second one affects multiple elements in the playfield
10164             that are triggered indirectly by another element. This is a third
10165             case: Changing the CE score always affects multiple identical CEs,
10166             so every affected CE must be checked, not only the single CE for
10167             which the CE score was changed in the first place (as every instance
10168             of that CE shares the same CE score, and therefore also can change)!
10169           */
10170           SCAN_PLAYFIELD(xx, yy)
10171           {
10172             if (Feld[xx][yy] == element)
10173               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10174                                  CE_SCORE_GETS_ZERO);
10175           }
10176         }
10177       }
10178
10179       break;
10180     }
10181
10182     case CA_SET_CE_ARTWORK:
10183     {
10184       int artwork_element = action_arg_element;
10185       boolean reset_frame = FALSE;
10186       int xx, yy;
10187
10188       if (action_arg == CA_ARG_ELEMENT_RESET)
10189         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10190                            element);
10191
10192       if (ei->gfx_element != artwork_element)
10193         reset_frame = TRUE;
10194
10195       ei->gfx_element = artwork_element;
10196
10197       SCAN_PLAYFIELD(xx, yy)
10198       {
10199         if (Feld[xx][yy] == element)
10200         {
10201           if (reset_frame)
10202           {
10203             ResetGfxAnimation(xx, yy);
10204             ResetRandomAnimationValue(xx, yy);
10205           }
10206
10207           TEST_DrawLevelField(xx, yy);
10208         }
10209       }
10210
10211       break;
10212     }
10213
10214     // ---------- engine actions  ---------------------------------------------
10215
10216     case CA_SET_ENGINE_SCAN_MODE:
10217     {
10218       InitPlayfieldScanMode(action_arg);
10219
10220       break;
10221     }
10222
10223     default:
10224       break;
10225   }
10226 }
10227
10228 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10229 {
10230   int old_element = Feld[x][y];
10231   int new_element = GetElementFromGroupElement(element);
10232   int previous_move_direction = MovDir[x][y];
10233   int last_ce_value = CustomValue[x][y];
10234   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10235   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10236   boolean add_player_onto_element = (new_element_is_player &&
10237                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10238                                      IS_WALKABLE(old_element));
10239
10240   if (!add_player_onto_element)
10241   {
10242     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10243       RemoveMovingField(x, y);
10244     else
10245       RemoveField(x, y);
10246
10247     Feld[x][y] = new_element;
10248
10249     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10250       MovDir[x][y] = previous_move_direction;
10251
10252     if (element_info[new_element].use_last_ce_value)
10253       CustomValue[x][y] = last_ce_value;
10254
10255     InitField_WithBug1(x, y, FALSE);
10256
10257     new_element = Feld[x][y];   // element may have changed
10258
10259     ResetGfxAnimation(x, y);
10260     ResetRandomAnimationValue(x, y);
10261
10262     TEST_DrawLevelField(x, y);
10263
10264     if (GFX_CRUMBLED(new_element))
10265       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10266   }
10267
10268   // check if element under the player changes from accessible to unaccessible
10269   // (needed for special case of dropping element which then changes)
10270   // (must be checked after creating new element for walkable group elements)
10271   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10272       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10273   {
10274     Bang(x, y);
10275
10276     return;
10277   }
10278
10279   // "ChangeCount" not set yet to allow "entered by player" change one time
10280   if (new_element_is_player)
10281     RelocatePlayer(x, y, new_element);
10282
10283   if (is_change)
10284     ChangeCount[x][y]++;        // count number of changes in the same frame
10285
10286   TestIfBadThingTouchesPlayer(x, y);
10287   TestIfPlayerTouchesCustomElement(x, y);
10288   TestIfElementTouchesCustomElement(x, y);
10289 }
10290
10291 static void CreateField(int x, int y, int element)
10292 {
10293   CreateFieldExt(x, y, element, FALSE);
10294 }
10295
10296 static void CreateElementFromChange(int x, int y, int element)
10297 {
10298   element = GET_VALID_RUNTIME_ELEMENT(element);
10299
10300   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10301   {
10302     int old_element = Feld[x][y];
10303
10304     // prevent changed element from moving in same engine frame
10305     // unless both old and new element can either fall or move
10306     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10307         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10308       Stop[x][y] = TRUE;
10309   }
10310
10311   CreateFieldExt(x, y, element, TRUE);
10312 }
10313
10314 static boolean ChangeElement(int x, int y, int element, int page)
10315 {
10316   struct ElementInfo *ei = &element_info[element];
10317   struct ElementChangeInfo *change = &ei->change_page[page];
10318   int ce_value = CustomValue[x][y];
10319   int ce_score = ei->collect_score;
10320   int target_element;
10321   int old_element = Feld[x][y];
10322
10323   // always use default change event to prevent running into a loop
10324   if (ChangeEvent[x][y] == -1)
10325     ChangeEvent[x][y] = CE_DELAY;
10326
10327   if (ChangeEvent[x][y] == CE_DELAY)
10328   {
10329     // reset actual trigger element, trigger player and action element
10330     change->actual_trigger_element = EL_EMPTY;
10331     change->actual_trigger_player = EL_EMPTY;
10332     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10333     change->actual_trigger_side = CH_SIDE_NONE;
10334     change->actual_trigger_ce_value = 0;
10335     change->actual_trigger_ce_score = 0;
10336   }
10337
10338   // do not change elements more than a specified maximum number of changes
10339   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10340     return FALSE;
10341
10342   ChangeCount[x][y]++;          // count number of changes in the same frame
10343
10344   if (change->explode)
10345   {
10346     Bang(x, y);
10347
10348     return TRUE;
10349   }
10350
10351   if (change->use_target_content)
10352   {
10353     boolean complete_replace = TRUE;
10354     boolean can_replace[3][3];
10355     int xx, yy;
10356
10357     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10358     {
10359       boolean is_empty;
10360       boolean is_walkable;
10361       boolean is_diggable;
10362       boolean is_collectible;
10363       boolean is_removable;
10364       boolean is_destructible;
10365       int ex = x + xx - 1;
10366       int ey = y + yy - 1;
10367       int content_element = change->target_content.e[xx][yy];
10368       int e;
10369
10370       can_replace[xx][yy] = TRUE;
10371
10372       if (ex == x && ey == y)   // do not check changing element itself
10373         continue;
10374
10375       if (content_element == EL_EMPTY_SPACE)
10376       {
10377         can_replace[xx][yy] = FALSE;    // do not replace border with space
10378
10379         continue;
10380       }
10381
10382       if (!IN_LEV_FIELD(ex, ey))
10383       {
10384         can_replace[xx][yy] = FALSE;
10385         complete_replace = FALSE;
10386
10387         continue;
10388       }
10389
10390       e = Feld[ex][ey];
10391
10392       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10393         e = MovingOrBlocked2Element(ex, ey);
10394
10395       is_empty = (IS_FREE(ex, ey) ||
10396                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10397
10398       is_walkable     = (is_empty || IS_WALKABLE(e));
10399       is_diggable     = (is_empty || IS_DIGGABLE(e));
10400       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10401       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10402       is_removable    = (is_diggable || is_collectible);
10403
10404       can_replace[xx][yy] =
10405         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10406           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10407           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10408           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10409           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10410           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10411          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10412
10413       if (!can_replace[xx][yy])
10414         complete_replace = FALSE;
10415     }
10416
10417     if (!change->only_if_complete || complete_replace)
10418     {
10419       boolean something_has_changed = FALSE;
10420
10421       if (change->only_if_complete && change->use_random_replace &&
10422           RND(100) < change->random_percentage)
10423         return FALSE;
10424
10425       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10426       {
10427         int ex = x + xx - 1;
10428         int ey = y + yy - 1;
10429         int content_element;
10430
10431         if (can_replace[xx][yy] && (!change->use_random_replace ||
10432                                     RND(100) < change->random_percentage))
10433         {
10434           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10435             RemoveMovingField(ex, ey);
10436
10437           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10438
10439           content_element = change->target_content.e[xx][yy];
10440           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10441                                               ce_value, ce_score);
10442
10443           CreateElementFromChange(ex, ey, target_element);
10444
10445           something_has_changed = TRUE;
10446
10447           // for symmetry reasons, freeze newly created border elements
10448           if (ex != x || ey != y)
10449             Stop[ex][ey] = TRUE;        // no more moving in this frame
10450         }
10451       }
10452
10453       if (something_has_changed)
10454       {
10455         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10456         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10457       }
10458     }
10459   }
10460   else
10461   {
10462     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10463                                         ce_value, ce_score);
10464
10465     if (element == EL_DIAGONAL_GROWING ||
10466         element == EL_DIAGONAL_SHRINKING)
10467     {
10468       target_element = Store[x][y];
10469
10470       Store[x][y] = EL_EMPTY;
10471     }
10472
10473     CreateElementFromChange(x, y, target_element);
10474
10475     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10476     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10477   }
10478
10479   // this uses direct change before indirect change
10480   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10481
10482   return TRUE;
10483 }
10484
10485 static void HandleElementChange(int x, int y, int page)
10486 {
10487   int element = MovingOrBlocked2Element(x, y);
10488   struct ElementInfo *ei = &element_info[element];
10489   struct ElementChangeInfo *change = &ei->change_page[page];
10490   boolean handle_action_before_change = FALSE;
10491
10492 #ifdef DEBUG
10493   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10494       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10495   {
10496     printf("\n\n");
10497     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10498            x, y, element, element_info[element].token_name);
10499     printf("HandleElementChange(): This should never happen!\n");
10500     printf("\n\n");
10501   }
10502 #endif
10503
10504   // this can happen with classic bombs on walkable, changing elements
10505   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10506   {
10507     return;
10508   }
10509
10510   if (ChangeDelay[x][y] == 0)           // initialize element change
10511   {
10512     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10513
10514     if (change->can_change)
10515     {
10516       // !!! not clear why graphic animation should be reset at all here !!!
10517       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10518       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10519
10520       /*
10521         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10522
10523         When using an animation frame delay of 1 (this only happens with
10524         "sp_zonk.moving.left/right" in the classic graphics), the default
10525         (non-moving) animation shows wrong animation frames (while the
10526         moving animation, like "sp_zonk.moving.left/right", is correct,
10527         so this graphical bug never shows up with the classic graphics).
10528         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10529         be drawn instead of the correct frames 0,1,2,3. This is caused by
10530         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10531         an element change: First when the change delay ("ChangeDelay[][]")
10532         counter has reached zero after decrementing, then a second time in
10533         the next frame (after "GfxFrame[][]" was already incremented) when
10534         "ChangeDelay[][]" is reset to the initial delay value again.
10535
10536         This causes frame 0 to be drawn twice, while the last frame won't
10537         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10538
10539         As some animations may already be cleverly designed around this bug
10540         (at least the "Snake Bite" snake tail animation does this), it cannot
10541         simply be fixed here without breaking such existing animations.
10542         Unfortunately, it cannot easily be detected if a graphics set was
10543         designed "before" or "after" the bug was fixed. As a workaround,
10544         a new graphics set option "game.graphics_engine_version" was added
10545         to be able to specify the game's major release version for which the
10546         graphics set was designed, which can then be used to decide if the
10547         bugfix should be used (version 4 and above) or not (version 3 or
10548         below, or if no version was specified at all, as with old sets).
10549
10550         (The wrong/fixed animation frames can be tested with the test level set
10551         "test_gfxframe" and level "000", which contains a specially prepared
10552         custom element at level position (x/y) == (11/9) which uses the zonk
10553         animation mentioned above. Using "game.graphics_engine_version: 4"
10554         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10555         This can also be seen from the debug output for this test element.)
10556       */
10557
10558       // when a custom element is about to change (for example by change delay),
10559       // do not reset graphic animation when the custom element is moving
10560       if (game.graphics_engine_version < 4 &&
10561           !IS_MOVING(x, y))
10562       {
10563         ResetGfxAnimation(x, y);
10564         ResetRandomAnimationValue(x, y);
10565       }
10566
10567       if (change->pre_change_function)
10568         change->pre_change_function(x, y);
10569     }
10570   }
10571
10572   ChangeDelay[x][y]--;
10573
10574   if (ChangeDelay[x][y] != 0)           // continue element change
10575   {
10576     if (change->can_change)
10577     {
10578       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10579
10580       if (IS_ANIMATED(graphic))
10581         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10582
10583       if (change->change_function)
10584         change->change_function(x, y);
10585     }
10586   }
10587   else                                  // finish element change
10588   {
10589     if (ChangePage[x][y] != -1)         // remember page from delayed change
10590     {
10591       page = ChangePage[x][y];
10592       ChangePage[x][y] = -1;
10593
10594       change = &ei->change_page[page];
10595     }
10596
10597     if (IS_MOVING(x, y))                // never change a running system ;-)
10598     {
10599       ChangeDelay[x][y] = 1;            // try change after next move step
10600       ChangePage[x][y] = page;          // remember page to use for change
10601
10602       return;
10603     }
10604
10605     // special case: set new level random seed before changing element
10606     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10607       handle_action_before_change = TRUE;
10608
10609     if (change->has_action && handle_action_before_change)
10610       ExecuteCustomElementAction(x, y, element, page);
10611
10612     if (change->can_change)
10613     {
10614       if (ChangeElement(x, y, element, page))
10615       {
10616         if (change->post_change_function)
10617           change->post_change_function(x, y);
10618       }
10619     }
10620
10621     if (change->has_action && !handle_action_before_change)
10622       ExecuteCustomElementAction(x, y, element, page);
10623   }
10624 }
10625
10626 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10627                                               int trigger_element,
10628                                               int trigger_event,
10629                                               int trigger_player,
10630                                               int trigger_side,
10631                                               int trigger_page)
10632 {
10633   boolean change_done_any = FALSE;
10634   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10635   int i;
10636
10637   if (!(trigger_events[trigger_element][trigger_event]))
10638     return FALSE;
10639
10640   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10641
10642   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10643   {
10644     int element = EL_CUSTOM_START + i;
10645     boolean change_done = FALSE;
10646     int p;
10647
10648     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10649         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10650       continue;
10651
10652     for (p = 0; p < element_info[element].num_change_pages; p++)
10653     {
10654       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10655
10656       if (change->can_change_or_has_action &&
10657           change->has_event[trigger_event] &&
10658           change->trigger_side & trigger_side &&
10659           change->trigger_player & trigger_player &&
10660           change->trigger_page & trigger_page_bits &&
10661           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10662       {
10663         change->actual_trigger_element = trigger_element;
10664         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10665         change->actual_trigger_player_bits = trigger_player;
10666         change->actual_trigger_side = trigger_side;
10667         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10668         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10669
10670         if ((change->can_change && !change_done) || change->has_action)
10671         {
10672           int x, y;
10673
10674           SCAN_PLAYFIELD(x, y)
10675           {
10676             if (Feld[x][y] == element)
10677             {
10678               if (change->can_change && !change_done)
10679               {
10680                 // if element already changed in this frame, not only prevent
10681                 // another element change (checked in ChangeElement()), but
10682                 // also prevent additional element actions for this element
10683
10684                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10685                     !level.use_action_after_change_bug)
10686                   continue;
10687
10688                 ChangeDelay[x][y] = 1;
10689                 ChangeEvent[x][y] = trigger_event;
10690
10691                 HandleElementChange(x, y, p);
10692               }
10693               else if (change->has_action)
10694               {
10695                 // if element already changed in this frame, not only prevent
10696                 // another element change (checked in ChangeElement()), but
10697                 // also prevent additional element actions for this element
10698
10699                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10700                     !level.use_action_after_change_bug)
10701                   continue;
10702
10703                 ExecuteCustomElementAction(x, y, element, p);
10704                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10705               }
10706             }
10707           }
10708
10709           if (change->can_change)
10710           {
10711             change_done = TRUE;
10712             change_done_any = TRUE;
10713           }
10714         }
10715       }
10716     }
10717   }
10718
10719   RECURSION_LOOP_DETECTION_END();
10720
10721   return change_done_any;
10722 }
10723
10724 static boolean CheckElementChangeExt(int x, int y,
10725                                      int element,
10726                                      int trigger_element,
10727                                      int trigger_event,
10728                                      int trigger_player,
10729                                      int trigger_side)
10730 {
10731   boolean change_done = FALSE;
10732   int p;
10733
10734   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10735       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10736     return FALSE;
10737
10738   if (Feld[x][y] == EL_BLOCKED)
10739   {
10740     Blocked2Moving(x, y, &x, &y);
10741     element = Feld[x][y];
10742   }
10743
10744   // check if element has already changed or is about to change after moving
10745   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10746        Feld[x][y] != element) ||
10747
10748       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10749        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10750         ChangePage[x][y] != -1)))
10751     return FALSE;
10752
10753   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10754
10755   for (p = 0; p < element_info[element].num_change_pages; p++)
10756   {
10757     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10758
10759     /* check trigger element for all events where the element that is checked
10760        for changing interacts with a directly adjacent element -- this is
10761        different to element changes that affect other elements to change on the
10762        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10763     boolean check_trigger_element =
10764       (trigger_event == CE_TOUCHING_X ||
10765        trigger_event == CE_HITTING_X ||
10766        trigger_event == CE_HIT_BY_X ||
10767        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10768
10769     if (change->can_change_or_has_action &&
10770         change->has_event[trigger_event] &&
10771         change->trigger_side & trigger_side &&
10772         change->trigger_player & trigger_player &&
10773         (!check_trigger_element ||
10774          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10775     {
10776       change->actual_trigger_element = trigger_element;
10777       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10778       change->actual_trigger_player_bits = trigger_player;
10779       change->actual_trigger_side = trigger_side;
10780       change->actual_trigger_ce_value = CustomValue[x][y];
10781       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10782
10783       // special case: trigger element not at (x,y) position for some events
10784       if (check_trigger_element)
10785       {
10786         static struct
10787         {
10788           int dx, dy;
10789         } move_xy[] =
10790           {
10791             {  0,  0 },
10792             { -1,  0 },
10793             { +1,  0 },
10794             {  0,  0 },
10795             {  0, -1 },
10796             {  0,  0 }, { 0, 0 }, { 0, 0 },
10797             {  0, +1 }
10798           };
10799
10800         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10801         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10802
10803         change->actual_trigger_ce_value = CustomValue[xx][yy];
10804         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10805       }
10806
10807       if (change->can_change && !change_done)
10808       {
10809         ChangeDelay[x][y] = 1;
10810         ChangeEvent[x][y] = trigger_event;
10811
10812         HandleElementChange(x, y, p);
10813
10814         change_done = TRUE;
10815       }
10816       else if (change->has_action)
10817       {
10818         ExecuteCustomElementAction(x, y, element, p);
10819         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10820       }
10821     }
10822   }
10823
10824   RECURSION_LOOP_DETECTION_END();
10825
10826   return change_done;
10827 }
10828
10829 static void PlayPlayerSound(struct PlayerInfo *player)
10830 {
10831   int jx = player->jx, jy = player->jy;
10832   int sound_element = player->artwork_element;
10833   int last_action = player->last_action_waiting;
10834   int action = player->action_waiting;
10835
10836   if (player->is_waiting)
10837   {
10838     if (action != last_action)
10839       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10840     else
10841       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10842   }
10843   else
10844   {
10845     if (action != last_action)
10846       StopSound(element_info[sound_element].sound[last_action]);
10847
10848     if (last_action == ACTION_SLEEPING)
10849       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10850   }
10851 }
10852
10853 static void PlayAllPlayersSound(void)
10854 {
10855   int i;
10856
10857   for (i = 0; i < MAX_PLAYERS; i++)
10858     if (stored_player[i].active)
10859       PlayPlayerSound(&stored_player[i]);
10860 }
10861
10862 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10863 {
10864   boolean last_waiting = player->is_waiting;
10865   int move_dir = player->MovDir;
10866
10867   player->dir_waiting = move_dir;
10868   player->last_action_waiting = player->action_waiting;
10869
10870   if (is_waiting)
10871   {
10872     if (!last_waiting)          // not waiting -> waiting
10873     {
10874       player->is_waiting = TRUE;
10875
10876       player->frame_counter_bored =
10877         FrameCounter +
10878         game.player_boring_delay_fixed +
10879         GetSimpleRandom(game.player_boring_delay_random);
10880       player->frame_counter_sleeping =
10881         FrameCounter +
10882         game.player_sleeping_delay_fixed +
10883         GetSimpleRandom(game.player_sleeping_delay_random);
10884
10885       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10886     }
10887
10888     if (game.player_sleeping_delay_fixed +
10889         game.player_sleeping_delay_random > 0 &&
10890         player->anim_delay_counter == 0 &&
10891         player->post_delay_counter == 0 &&
10892         FrameCounter >= player->frame_counter_sleeping)
10893       player->is_sleeping = TRUE;
10894     else if (game.player_boring_delay_fixed +
10895              game.player_boring_delay_random > 0 &&
10896              FrameCounter >= player->frame_counter_bored)
10897       player->is_bored = TRUE;
10898
10899     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10900                               player->is_bored ? ACTION_BORING :
10901                               ACTION_WAITING);
10902
10903     if (player->is_sleeping && player->use_murphy)
10904     {
10905       // special case for sleeping Murphy when leaning against non-free tile
10906
10907       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10908           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10909            !IS_MOVING(player->jx - 1, player->jy)))
10910         move_dir = MV_LEFT;
10911       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10912                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10913                 !IS_MOVING(player->jx + 1, player->jy)))
10914         move_dir = MV_RIGHT;
10915       else
10916         player->is_sleeping = FALSE;
10917
10918       player->dir_waiting = move_dir;
10919     }
10920
10921     if (player->is_sleeping)
10922     {
10923       if (player->num_special_action_sleeping > 0)
10924       {
10925         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10926         {
10927           int last_special_action = player->special_action_sleeping;
10928           int num_special_action = player->num_special_action_sleeping;
10929           int special_action =
10930             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10931              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10932              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10933              last_special_action + 1 : ACTION_SLEEPING);
10934           int special_graphic =
10935             el_act_dir2img(player->artwork_element, special_action, move_dir);
10936
10937           player->anim_delay_counter =
10938             graphic_info[special_graphic].anim_delay_fixed +
10939             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10940           player->post_delay_counter =
10941             graphic_info[special_graphic].post_delay_fixed +
10942             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10943
10944           player->special_action_sleeping = special_action;
10945         }
10946
10947         if (player->anim_delay_counter > 0)
10948         {
10949           player->action_waiting = player->special_action_sleeping;
10950           player->anim_delay_counter--;
10951         }
10952         else if (player->post_delay_counter > 0)
10953         {
10954           player->post_delay_counter--;
10955         }
10956       }
10957     }
10958     else if (player->is_bored)
10959     {
10960       if (player->num_special_action_bored > 0)
10961       {
10962         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10963         {
10964           int special_action =
10965             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10966           int special_graphic =
10967             el_act_dir2img(player->artwork_element, special_action, move_dir);
10968
10969           player->anim_delay_counter =
10970             graphic_info[special_graphic].anim_delay_fixed +
10971             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10972           player->post_delay_counter =
10973             graphic_info[special_graphic].post_delay_fixed +
10974             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10975
10976           player->special_action_bored = special_action;
10977         }
10978
10979         if (player->anim_delay_counter > 0)
10980         {
10981           player->action_waiting = player->special_action_bored;
10982           player->anim_delay_counter--;
10983         }
10984         else if (player->post_delay_counter > 0)
10985         {
10986           player->post_delay_counter--;
10987         }
10988       }
10989     }
10990   }
10991   else if (last_waiting)        // waiting -> not waiting
10992   {
10993     player->is_waiting = FALSE;
10994     player->is_bored = FALSE;
10995     player->is_sleeping = FALSE;
10996
10997     player->frame_counter_bored = -1;
10998     player->frame_counter_sleeping = -1;
10999
11000     player->anim_delay_counter = 0;
11001     player->post_delay_counter = 0;
11002
11003     player->dir_waiting = player->MovDir;
11004     player->action_waiting = ACTION_DEFAULT;
11005
11006     player->special_action_bored = ACTION_DEFAULT;
11007     player->special_action_sleeping = ACTION_DEFAULT;
11008   }
11009 }
11010
11011 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11012 {
11013   if ((!player->is_moving  && player->was_moving) ||
11014       (player->MovPos == 0 && player->was_moving) ||
11015       (player->is_snapping && !player->was_snapping) ||
11016       (player->is_dropping && !player->was_dropping))
11017   {
11018     if (!CheckSaveEngineSnapshotToList())
11019       return;
11020
11021     player->was_moving = FALSE;
11022     player->was_snapping = TRUE;
11023     player->was_dropping = TRUE;
11024   }
11025   else
11026   {
11027     if (player->is_moving)
11028       player->was_moving = TRUE;
11029
11030     if (!player->is_snapping)
11031       player->was_snapping = FALSE;
11032
11033     if (!player->is_dropping)
11034       player->was_dropping = FALSE;
11035   }
11036 }
11037
11038 static void CheckSingleStepMode(struct PlayerInfo *player)
11039 {
11040   if (tape.single_step && tape.recording && !tape.pausing)
11041   {
11042     /* as it is called "single step mode", just return to pause mode when the
11043        player stopped moving after one tile (or never starts moving at all) */
11044     if (!player->is_moving &&
11045         !player->is_pushing &&
11046         !player->is_dropping_pressed)
11047     {
11048       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11049       SnapField(player, 0, 0);                  // stop snapping
11050     }
11051   }
11052
11053   CheckSaveEngineSnapshot(player);
11054 }
11055
11056 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11057 {
11058   int left      = player_action & JOY_LEFT;
11059   int right     = player_action & JOY_RIGHT;
11060   int up        = player_action & JOY_UP;
11061   int down      = player_action & JOY_DOWN;
11062   int button1   = player_action & JOY_BUTTON_1;
11063   int button2   = player_action & JOY_BUTTON_2;
11064   int dx        = (left ? -1 : right ? 1 : 0);
11065   int dy        = (up   ? -1 : down  ? 1 : 0);
11066
11067   if (!player->active || tape.pausing)
11068     return 0;
11069
11070   if (player_action)
11071   {
11072     if (button1)
11073       SnapField(player, dx, dy);
11074     else
11075     {
11076       if (button2)
11077         DropElement(player);
11078
11079       MovePlayer(player, dx, dy);
11080     }
11081
11082     CheckSingleStepMode(player);
11083
11084     SetPlayerWaiting(player, FALSE);
11085
11086     return player_action;
11087   }
11088   else
11089   {
11090     // no actions for this player (no input at player's configured device)
11091
11092     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11093     SnapField(player, 0, 0);
11094     CheckGravityMovementWhenNotMoving(player);
11095
11096     if (player->MovPos == 0)
11097       SetPlayerWaiting(player, TRUE);
11098
11099     if (player->MovPos == 0)    // needed for tape.playing
11100       player->is_moving = FALSE;
11101
11102     player->is_dropping = FALSE;
11103     player->is_dropping_pressed = FALSE;
11104     player->drop_pressed_delay = 0;
11105
11106     CheckSingleStepMode(player);
11107
11108     return 0;
11109   }
11110 }
11111
11112 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11113                                          byte *tape_action)
11114 {
11115   if (!tape.use_mouse)
11116     return;
11117
11118   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11119   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11120   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11121 }
11122
11123 static void SetTapeActionFromMouseAction(byte *tape_action,
11124                                          struct MouseActionInfo *mouse_action)
11125 {
11126   if (!tape.use_mouse)
11127     return;
11128
11129   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11130   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11131   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11132 }
11133
11134 static void CheckLevelSolved(void)
11135 {
11136   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11137   {
11138     if (game_em.level_solved &&
11139         !game_em.game_over)                             // game won
11140     {
11141       LevelSolved();
11142
11143       game_em.game_over = TRUE;
11144
11145       game.all_players_gone = TRUE;
11146     }
11147
11148     if (game_em.game_over)                              // game lost
11149       game.all_players_gone = TRUE;
11150   }
11151   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11152   {
11153     if (game_sp.level_solved &&
11154         !game_sp.game_over)                             // game won
11155     {
11156       LevelSolved();
11157
11158       game_sp.game_over = TRUE;
11159
11160       game.all_players_gone = TRUE;
11161     }
11162
11163     if (game_sp.game_over)                              // game lost
11164       game.all_players_gone = TRUE;
11165   }
11166   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11167   {
11168     if (game_mm.level_solved &&
11169         !game_mm.game_over)                             // game won
11170     {
11171       LevelSolved();
11172
11173       game_mm.game_over = TRUE;
11174
11175       game.all_players_gone = TRUE;
11176     }
11177
11178     if (game_mm.game_over)                              // game lost
11179       game.all_players_gone = TRUE;
11180   }
11181 }
11182
11183 static void CheckLevelTime(void)
11184 {
11185   int i;
11186
11187   if (TimeFrames >= FRAMES_PER_SECOND)
11188   {
11189     TimeFrames = 0;
11190     TapeTime++;
11191
11192     for (i = 0; i < MAX_PLAYERS; i++)
11193     {
11194       struct PlayerInfo *player = &stored_player[i];
11195
11196       if (SHIELD_ON(player))
11197       {
11198         player->shield_normal_time_left--;
11199
11200         if (player->shield_deadly_time_left > 0)
11201           player->shield_deadly_time_left--;
11202       }
11203     }
11204
11205     if (!game.LevelSolved && !level.use_step_counter)
11206     {
11207       TimePlayed++;
11208
11209       if (TimeLeft > 0)
11210       {
11211         TimeLeft--;
11212
11213         if (TimeLeft <= 10 && setup.time_limit)
11214           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11215
11216         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11217            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11218
11219         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11220
11221         if (!TimeLeft && setup.time_limit)
11222         {
11223           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11224             level.native_em_level->lev->killed_out_of_time = TRUE;
11225           else
11226             for (i = 0; i < MAX_PLAYERS; i++)
11227               KillPlayer(&stored_player[i]);
11228         }
11229       }
11230       else if (game.no_time_limit && !game.all_players_gone)
11231       {
11232         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11233       }
11234
11235       level.native_em_level->lev->time =
11236         (game.no_time_limit ? TimePlayed : TimeLeft);
11237     }
11238
11239     if (tape.recording || tape.playing)
11240       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11241   }
11242
11243   if (tape.recording || tape.playing)
11244     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11245
11246   UpdateAndDisplayGameControlValues();
11247 }
11248
11249 void AdvanceFrameAndPlayerCounters(int player_nr)
11250 {
11251   int i;
11252
11253   // advance frame counters (global frame counter and time frame counter)
11254   FrameCounter++;
11255   TimeFrames++;
11256
11257   // advance player counters (counters for move delay, move animation etc.)
11258   for (i = 0; i < MAX_PLAYERS; i++)
11259   {
11260     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11261     int move_delay_value = stored_player[i].move_delay_value;
11262     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11263
11264     if (!advance_player_counters)       // not all players may be affected
11265       continue;
11266
11267     if (move_frames == 0)       // less than one move per game frame
11268     {
11269       int stepsize = TILEX / move_delay_value;
11270       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11271       int count = (stored_player[i].is_moving ?
11272                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11273
11274       if (count % delay == 0)
11275         move_frames = 1;
11276     }
11277
11278     stored_player[i].Frame += move_frames;
11279
11280     if (stored_player[i].MovPos != 0)
11281       stored_player[i].StepFrame += move_frames;
11282
11283     if (stored_player[i].move_delay > 0)
11284       stored_player[i].move_delay--;
11285
11286     // due to bugs in previous versions, counter must count up, not down
11287     if (stored_player[i].push_delay != -1)
11288       stored_player[i].push_delay++;
11289
11290     if (stored_player[i].drop_delay > 0)
11291       stored_player[i].drop_delay--;
11292
11293     if (stored_player[i].is_dropping_pressed)
11294       stored_player[i].drop_pressed_delay++;
11295   }
11296 }
11297
11298 void StartGameActions(boolean init_network_game, boolean record_tape,
11299                       int random_seed)
11300 {
11301   unsigned int new_random_seed = InitRND(random_seed);
11302
11303   if (record_tape)
11304     TapeStartRecording(new_random_seed);
11305
11306   if (init_network_game)
11307   {
11308     SendToServer_LevelFile();
11309     SendToServer_StartPlaying();
11310
11311     return;
11312   }
11313
11314   InitGame();
11315 }
11316
11317 static void GameActionsExt(void)
11318 {
11319 #if 0
11320   static unsigned int game_frame_delay = 0;
11321 #endif
11322   unsigned int game_frame_delay_value;
11323   byte *recorded_player_action;
11324   byte summarized_player_action = 0;
11325   byte tape_action[MAX_PLAYERS];
11326   int i;
11327
11328   // detect endless loops, caused by custom element programming
11329   if (recursion_loop_detected && recursion_loop_depth == 0)
11330   {
11331     char *message = getStringCat3("Internal Error! Element ",
11332                                   EL_NAME(recursion_loop_element),
11333                                   " caused endless loop! Quit the game?");
11334
11335     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11336           EL_NAME(recursion_loop_element));
11337
11338     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11339
11340     recursion_loop_detected = FALSE;    // if game should be continued
11341
11342     free(message);
11343
11344     return;
11345   }
11346
11347   if (game.restart_level)
11348     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11349
11350   CheckLevelSolved();
11351
11352   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11353     GameWon();
11354
11355   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11356     TapeStop();
11357
11358   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11359     return;
11360
11361   game_frame_delay_value =
11362     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11363
11364   if (tape.playing && tape.warp_forward && !tape.pausing)
11365     game_frame_delay_value = 0;
11366
11367   SetVideoFrameDelay(game_frame_delay_value);
11368
11369   // (de)activate virtual buttons depending on current game status
11370   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11371   {
11372     if (game.all_players_gone)  // if no players there to be controlled anymore
11373       SetOverlayActive(FALSE);
11374     else if (!tape.playing)     // if game continues after tape stopped playing
11375       SetOverlayActive(TRUE);
11376   }
11377
11378 #if 0
11379 #if 0
11380   // ---------- main game synchronization point ----------
11381
11382   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11383
11384   printf("::: skip == %d\n", skip);
11385
11386 #else
11387   // ---------- main game synchronization point ----------
11388
11389   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11390 #endif
11391 #endif
11392
11393   if (network_playing && !network_player_action_received)
11394   {
11395     // try to get network player actions in time
11396
11397     // last chance to get network player actions without main loop delay
11398     HandleNetworking();
11399
11400     // game was quit by network peer
11401     if (game_status != GAME_MODE_PLAYING)
11402       return;
11403
11404     // check if network player actions still missing and game still running
11405     if (!network_player_action_received && !checkGameEnded())
11406       return;           // failed to get network player actions in time
11407
11408     // do not yet reset "network_player_action_received" (for tape.pausing)
11409   }
11410
11411   if (tape.pausing)
11412     return;
11413
11414   // at this point we know that we really continue executing the game
11415
11416   network_player_action_received = FALSE;
11417
11418   // when playing tape, read previously recorded player input from tape data
11419   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11420
11421   local_player->effective_mouse_action = local_player->mouse_action;
11422
11423   if (recorded_player_action != NULL)
11424     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11425                                  recorded_player_action);
11426
11427   // TapePlayAction() may return NULL when toggling to "pause before death"
11428   if (tape.pausing)
11429     return;
11430
11431   if (tape.set_centered_player)
11432   {
11433     game.centered_player_nr_next = tape.centered_player_nr_next;
11434     game.set_centered_player = TRUE;
11435   }
11436
11437   for (i = 0; i < MAX_PLAYERS; i++)
11438   {
11439     summarized_player_action |= stored_player[i].action;
11440
11441     if (!network_playing && (game.team_mode || tape.playing))
11442       stored_player[i].effective_action = stored_player[i].action;
11443   }
11444
11445   if (network_playing && !checkGameEnded())
11446     SendToServer_MovePlayer(summarized_player_action);
11447
11448   // summarize all actions at local players mapped input device position
11449   // (this allows using different input devices in single player mode)
11450   if (!network.enabled && !game.team_mode)
11451     stored_player[map_player_action[local_player->index_nr]].effective_action =
11452       summarized_player_action;
11453
11454   if (tape.recording &&
11455       setup.team_mode &&
11456       setup.input_on_focus &&
11457       game.centered_player_nr != -1)
11458   {
11459     for (i = 0; i < MAX_PLAYERS; i++)
11460       stored_player[i].effective_action =
11461         (i == game.centered_player_nr ? summarized_player_action : 0);
11462   }
11463
11464   if (recorded_player_action != NULL)
11465     for (i = 0; i < MAX_PLAYERS; i++)
11466       stored_player[i].effective_action = recorded_player_action[i];
11467
11468   for (i = 0; i < MAX_PLAYERS; i++)
11469   {
11470     tape_action[i] = stored_player[i].effective_action;
11471
11472     /* (this may happen in the RND game engine if a player was not present on
11473        the playfield on level start, but appeared later from a custom element */
11474     if (setup.team_mode &&
11475         tape.recording &&
11476         tape_action[i] &&
11477         !tape.player_participates[i])
11478       tape.player_participates[i] = TRUE;
11479   }
11480
11481   SetTapeActionFromMouseAction(tape_action,
11482                                &local_player->effective_mouse_action);
11483
11484   // only record actions from input devices, but not programmed actions
11485   if (tape.recording)
11486     TapeRecordAction(tape_action);
11487
11488   // remember if game was played (especially after tape stopped playing)
11489   if (!tape.playing && summarized_player_action)
11490     game.GamePlayed = TRUE;
11491
11492 #if USE_NEW_PLAYER_ASSIGNMENTS
11493   // !!! also map player actions in single player mode !!!
11494   // if (game.team_mode)
11495   if (1)
11496   {
11497     byte mapped_action[MAX_PLAYERS];
11498
11499 #if DEBUG_PLAYER_ACTIONS
11500     printf(":::");
11501     for (i = 0; i < MAX_PLAYERS; i++)
11502       printf(" %d, ", stored_player[i].effective_action);
11503 #endif
11504
11505     for (i = 0; i < MAX_PLAYERS; i++)
11506       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11507
11508     for (i = 0; i < MAX_PLAYERS; i++)
11509       stored_player[i].effective_action = mapped_action[i];
11510
11511 #if DEBUG_PLAYER_ACTIONS
11512     printf(" =>");
11513     for (i = 0; i < MAX_PLAYERS; i++)
11514       printf(" %d, ", stored_player[i].effective_action);
11515     printf("\n");
11516 #endif
11517   }
11518 #if DEBUG_PLAYER_ACTIONS
11519   else
11520   {
11521     printf(":::");
11522     for (i = 0; i < MAX_PLAYERS; i++)
11523       printf(" %d, ", stored_player[i].effective_action);
11524     printf("\n");
11525   }
11526 #endif
11527 #endif
11528
11529   for (i = 0; i < MAX_PLAYERS; i++)
11530   {
11531     // allow engine snapshot in case of changed movement attempt
11532     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11533         (stored_player[i].effective_action & KEY_MOTION))
11534       game.snapshot.changed_action = TRUE;
11535
11536     // allow engine snapshot in case of snapping/dropping attempt
11537     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11538         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11539       game.snapshot.changed_action = TRUE;
11540
11541     game.snapshot.last_action[i] = stored_player[i].effective_action;
11542   }
11543
11544   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11545   {
11546     GameActions_EM_Main();
11547   }
11548   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11549   {
11550     GameActions_SP_Main();
11551   }
11552   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11553   {
11554     GameActions_MM_Main();
11555   }
11556   else
11557   {
11558     GameActions_RND_Main();
11559   }
11560
11561   BlitScreenToBitmap(backbuffer);
11562
11563   CheckLevelSolved();
11564   CheckLevelTime();
11565
11566   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11567
11568   if (global.show_frames_per_second)
11569   {
11570     static unsigned int fps_counter = 0;
11571     static int fps_frames = 0;
11572     unsigned int fps_delay_ms = Counter() - fps_counter;
11573
11574     fps_frames++;
11575
11576     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11577     {
11578       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11579
11580       fps_frames = 0;
11581       fps_counter = Counter();
11582
11583       // always draw FPS to screen after FPS value was updated
11584       redraw_mask |= REDRAW_FPS;
11585     }
11586
11587     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11588     if (GetDrawDeactivationMask() == REDRAW_NONE)
11589       redraw_mask |= REDRAW_FPS;
11590   }
11591 }
11592
11593 static void GameActions_CheckSaveEngineSnapshot(void)
11594 {
11595   if (!game.snapshot.save_snapshot)
11596     return;
11597
11598   // clear flag for saving snapshot _before_ saving snapshot
11599   game.snapshot.save_snapshot = FALSE;
11600
11601   SaveEngineSnapshotToList();
11602 }
11603
11604 void GameActions(void)
11605 {
11606   GameActionsExt();
11607
11608   GameActions_CheckSaveEngineSnapshot();
11609 }
11610
11611 void GameActions_EM_Main(void)
11612 {
11613   byte effective_action[MAX_PLAYERS];
11614   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11615   int i;
11616
11617   for (i = 0; i < MAX_PLAYERS; i++)
11618     effective_action[i] = stored_player[i].effective_action;
11619
11620   GameActions_EM(effective_action, warp_mode);
11621 }
11622
11623 void GameActions_SP_Main(void)
11624 {
11625   byte effective_action[MAX_PLAYERS];
11626   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11627   int i;
11628
11629   for (i = 0; i < MAX_PLAYERS; i++)
11630     effective_action[i] = stored_player[i].effective_action;
11631
11632   GameActions_SP(effective_action, warp_mode);
11633
11634   for (i = 0; i < MAX_PLAYERS; i++)
11635   {
11636     if (stored_player[i].force_dropping)
11637       stored_player[i].action |= KEY_BUTTON_DROP;
11638
11639     stored_player[i].force_dropping = FALSE;
11640   }
11641 }
11642
11643 void GameActions_MM_Main(void)
11644 {
11645   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11646
11647   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11648 }
11649
11650 void GameActions_RND_Main(void)
11651 {
11652   GameActions_RND();
11653 }
11654
11655 void GameActions_RND(void)
11656 {
11657   int magic_wall_x = 0, magic_wall_y = 0;
11658   int i, x, y, element, graphic, last_gfx_frame;
11659
11660   InitPlayfieldScanModeVars();
11661
11662   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11663   {
11664     SCAN_PLAYFIELD(x, y)
11665     {
11666       ChangeCount[x][y] = 0;
11667       ChangeEvent[x][y] = -1;
11668     }
11669   }
11670
11671   if (game.set_centered_player)
11672   {
11673     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11674
11675     // switching to "all players" only possible if all players fit to screen
11676     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11677     {
11678       game.centered_player_nr_next = game.centered_player_nr;
11679       game.set_centered_player = FALSE;
11680     }
11681
11682     // do not switch focus to non-existing (or non-active) player
11683     if (game.centered_player_nr_next >= 0 &&
11684         !stored_player[game.centered_player_nr_next].active)
11685     {
11686       game.centered_player_nr_next = game.centered_player_nr;
11687       game.set_centered_player = FALSE;
11688     }
11689   }
11690
11691   if (game.set_centered_player &&
11692       ScreenMovPos == 0)        // screen currently aligned at tile position
11693   {
11694     int sx, sy;
11695
11696     if (game.centered_player_nr_next == -1)
11697     {
11698       setScreenCenteredToAllPlayers(&sx, &sy);
11699     }
11700     else
11701     {
11702       sx = stored_player[game.centered_player_nr_next].jx;
11703       sy = stored_player[game.centered_player_nr_next].jy;
11704     }
11705
11706     game.centered_player_nr = game.centered_player_nr_next;
11707     game.set_centered_player = FALSE;
11708
11709     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11710     DrawGameDoorValues();
11711   }
11712
11713   for (i = 0; i < MAX_PLAYERS; i++)
11714   {
11715     int actual_player_action = stored_player[i].effective_action;
11716
11717 #if 1
11718     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11719        - rnd_equinox_tetrachloride 048
11720        - rnd_equinox_tetrachloride_ii 096
11721        - rnd_emanuel_schmieg 002
11722        - doctor_sloan_ww 001, 020
11723     */
11724     if (stored_player[i].MovPos == 0)
11725       CheckGravityMovement(&stored_player[i]);
11726 #endif
11727
11728     // overwrite programmed action with tape action
11729     if (stored_player[i].programmed_action)
11730       actual_player_action = stored_player[i].programmed_action;
11731
11732     PlayerActions(&stored_player[i], actual_player_action);
11733
11734     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11735   }
11736
11737   ScrollScreen(NULL, SCROLL_GO_ON);
11738
11739   /* for backwards compatibility, the following code emulates a fixed bug that
11740      occured when pushing elements (causing elements that just made their last
11741      pushing step to already (if possible) make their first falling step in the
11742      same game frame, which is bad); this code is also needed to use the famous
11743      "spring push bug" which is used in older levels and might be wanted to be
11744      used also in newer levels, but in this case the buggy pushing code is only
11745      affecting the "spring" element and no other elements */
11746
11747   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11748   {
11749     for (i = 0; i < MAX_PLAYERS; i++)
11750     {
11751       struct PlayerInfo *player = &stored_player[i];
11752       int x = player->jx;
11753       int y = player->jy;
11754
11755       if (player->active && player->is_pushing && player->is_moving &&
11756           IS_MOVING(x, y) &&
11757           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11758            Feld[x][y] == EL_SPRING))
11759       {
11760         ContinueMoving(x, y);
11761
11762         // continue moving after pushing (this is actually a bug)
11763         if (!IS_MOVING(x, y))
11764           Stop[x][y] = FALSE;
11765       }
11766     }
11767   }
11768
11769   SCAN_PLAYFIELD(x, y)
11770   {
11771     Last[x][y] = Feld[x][y];
11772
11773     ChangeCount[x][y] = 0;
11774     ChangeEvent[x][y] = -1;
11775
11776     // this must be handled before main playfield loop
11777     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11778     {
11779       MovDelay[x][y]--;
11780       if (MovDelay[x][y] <= 0)
11781         RemoveField(x, y);
11782     }
11783
11784     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11785     {
11786       MovDelay[x][y]--;
11787       if (MovDelay[x][y] <= 0)
11788       {
11789         RemoveField(x, y);
11790         TEST_DrawLevelField(x, y);
11791
11792         TestIfElementTouchesCustomElement(x, y);        // for empty space
11793       }
11794     }
11795
11796 #if DEBUG
11797     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11798     {
11799       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11800       printf("GameActions(): This should never happen!\n");
11801
11802       ChangePage[x][y] = -1;
11803     }
11804 #endif
11805
11806     Stop[x][y] = FALSE;
11807     if (WasJustMoving[x][y] > 0)
11808       WasJustMoving[x][y]--;
11809     if (WasJustFalling[x][y] > 0)
11810       WasJustFalling[x][y]--;
11811     if (CheckCollision[x][y] > 0)
11812       CheckCollision[x][y]--;
11813     if (CheckImpact[x][y] > 0)
11814       CheckImpact[x][y]--;
11815
11816     GfxFrame[x][y]++;
11817
11818     /* reset finished pushing action (not done in ContinueMoving() to allow
11819        continuous pushing animation for elements with zero push delay) */
11820     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11821     {
11822       ResetGfxAnimation(x, y);
11823       TEST_DrawLevelField(x, y);
11824     }
11825
11826 #if DEBUG
11827     if (IS_BLOCKED(x, y))
11828     {
11829       int oldx, oldy;
11830
11831       Blocked2Moving(x, y, &oldx, &oldy);
11832       if (!IS_MOVING(oldx, oldy))
11833       {
11834         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11835         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11836         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11837         printf("GameActions(): This should never happen!\n");
11838       }
11839     }
11840 #endif
11841   }
11842
11843   SCAN_PLAYFIELD(x, y)
11844   {
11845     element = Feld[x][y];
11846     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11847     last_gfx_frame = GfxFrame[x][y];
11848
11849     ResetGfxFrame(x, y);
11850
11851     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11852       DrawLevelGraphicAnimation(x, y, graphic);
11853
11854     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11855         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11856       ResetRandomAnimationValue(x, y);
11857
11858     SetRandomAnimationValue(x, y);
11859
11860     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11861
11862     if (IS_INACTIVE(element))
11863     {
11864       if (IS_ANIMATED(graphic))
11865         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11866
11867       continue;
11868     }
11869
11870     // this may take place after moving, so 'element' may have changed
11871     if (IS_CHANGING(x, y) &&
11872         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11873     {
11874       int page = element_info[element].event_page_nr[CE_DELAY];
11875
11876       HandleElementChange(x, y, page);
11877
11878       element = Feld[x][y];
11879       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11880     }
11881
11882     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11883     {
11884       StartMoving(x, y);
11885
11886       element = Feld[x][y];
11887       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11888
11889       if (IS_ANIMATED(graphic) &&
11890           !IS_MOVING(x, y) &&
11891           !Stop[x][y])
11892         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11893
11894       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11895         TEST_DrawTwinkleOnField(x, y);
11896     }
11897     else if (element == EL_ACID)
11898     {
11899       if (!Stop[x][y])
11900         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11901     }
11902     else if ((element == EL_EXIT_OPEN ||
11903               element == EL_EM_EXIT_OPEN ||
11904               element == EL_SP_EXIT_OPEN ||
11905               element == EL_STEEL_EXIT_OPEN ||
11906               element == EL_EM_STEEL_EXIT_OPEN ||
11907               element == EL_SP_TERMINAL ||
11908               element == EL_SP_TERMINAL_ACTIVE ||
11909               element == EL_EXTRA_TIME ||
11910               element == EL_SHIELD_NORMAL ||
11911               element == EL_SHIELD_DEADLY) &&
11912              IS_ANIMATED(graphic))
11913       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11914     else if (IS_MOVING(x, y))
11915       ContinueMoving(x, y);
11916     else if (IS_ACTIVE_BOMB(element))
11917       CheckDynamite(x, y);
11918     else if (element == EL_AMOEBA_GROWING)
11919       AmoebeWaechst(x, y);
11920     else if (element == EL_AMOEBA_SHRINKING)
11921       AmoebaDisappearing(x, y);
11922
11923 #if !USE_NEW_AMOEBA_CODE
11924     else if (IS_AMOEBALIVE(element))
11925       AmoebeAbleger(x, y);
11926 #endif
11927
11928     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11929       Life(x, y);
11930     else if (element == EL_EXIT_CLOSED)
11931       CheckExit(x, y);
11932     else if (element == EL_EM_EXIT_CLOSED)
11933       CheckExitEM(x, y);
11934     else if (element == EL_STEEL_EXIT_CLOSED)
11935       CheckExitSteel(x, y);
11936     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11937       CheckExitSteelEM(x, y);
11938     else if (element == EL_SP_EXIT_CLOSED)
11939       CheckExitSP(x, y);
11940     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11941              element == EL_EXPANDABLE_STEELWALL_GROWING)
11942       MauerWaechst(x, y);
11943     else if (element == EL_EXPANDABLE_WALL ||
11944              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11945              element == EL_EXPANDABLE_WALL_VERTICAL ||
11946              element == EL_EXPANDABLE_WALL_ANY ||
11947              element == EL_BD_EXPANDABLE_WALL)
11948       MauerAbleger(x, y);
11949     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11950              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11951              element == EL_EXPANDABLE_STEELWALL_ANY)
11952       MauerAblegerStahl(x, y);
11953     else if (element == EL_FLAMES)
11954       CheckForDragon(x, y);
11955     else if (element == EL_EXPLOSION)
11956       ; // drawing of correct explosion animation is handled separately
11957     else if (element == EL_ELEMENT_SNAPPING ||
11958              element == EL_DIAGONAL_SHRINKING ||
11959              element == EL_DIAGONAL_GROWING)
11960     {
11961       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11962
11963       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11964     }
11965     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11966       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11967
11968     if (IS_BELT_ACTIVE(element))
11969       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11970
11971     if (game.magic_wall_active)
11972     {
11973       int jx = local_player->jx, jy = local_player->jy;
11974
11975       // play the element sound at the position nearest to the player
11976       if ((element == EL_MAGIC_WALL_FULL ||
11977            element == EL_MAGIC_WALL_ACTIVE ||
11978            element == EL_MAGIC_WALL_EMPTYING ||
11979            element == EL_BD_MAGIC_WALL_FULL ||
11980            element == EL_BD_MAGIC_WALL_ACTIVE ||
11981            element == EL_BD_MAGIC_WALL_EMPTYING ||
11982            element == EL_DC_MAGIC_WALL_FULL ||
11983            element == EL_DC_MAGIC_WALL_ACTIVE ||
11984            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11985           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11986       {
11987         magic_wall_x = x;
11988         magic_wall_y = y;
11989       }
11990     }
11991   }
11992
11993 #if USE_NEW_AMOEBA_CODE
11994   // new experimental amoeba growth stuff
11995   if (!(FrameCounter % 8))
11996   {
11997     static unsigned int random = 1684108901;
11998
11999     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12000     {
12001       x = RND(lev_fieldx);
12002       y = RND(lev_fieldy);
12003       element = Feld[x][y];
12004
12005       if (!IS_PLAYER(x,y) &&
12006           (element == EL_EMPTY ||
12007            CAN_GROW_INTO(element) ||
12008            element == EL_QUICKSAND_EMPTY ||
12009            element == EL_QUICKSAND_FAST_EMPTY ||
12010            element == EL_ACID_SPLASH_LEFT ||
12011            element == EL_ACID_SPLASH_RIGHT))
12012       {
12013         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12014             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12015             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12016             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12017           Feld[x][y] = EL_AMOEBA_DROP;
12018       }
12019
12020       random = random * 129 + 1;
12021     }
12022   }
12023 #endif
12024
12025   game.explosions_delayed = FALSE;
12026
12027   SCAN_PLAYFIELD(x, y)
12028   {
12029     element = Feld[x][y];
12030
12031     if (ExplodeField[x][y])
12032       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12033     else if (element == EL_EXPLOSION)
12034       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12035
12036     ExplodeField[x][y] = EX_TYPE_NONE;
12037   }
12038
12039   game.explosions_delayed = TRUE;
12040
12041   if (game.magic_wall_active)
12042   {
12043     if (!(game.magic_wall_time_left % 4))
12044     {
12045       int element = Feld[magic_wall_x][magic_wall_y];
12046
12047       if (element == EL_BD_MAGIC_WALL_FULL ||
12048           element == EL_BD_MAGIC_WALL_ACTIVE ||
12049           element == EL_BD_MAGIC_WALL_EMPTYING)
12050         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12051       else if (element == EL_DC_MAGIC_WALL_FULL ||
12052                element == EL_DC_MAGIC_WALL_ACTIVE ||
12053                element == EL_DC_MAGIC_WALL_EMPTYING)
12054         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12055       else
12056         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12057     }
12058
12059     if (game.magic_wall_time_left > 0)
12060     {
12061       game.magic_wall_time_left--;
12062
12063       if (!game.magic_wall_time_left)
12064       {
12065         SCAN_PLAYFIELD(x, y)
12066         {
12067           element = Feld[x][y];
12068
12069           if (element == EL_MAGIC_WALL_ACTIVE ||
12070               element == EL_MAGIC_WALL_FULL)
12071           {
12072             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12073             TEST_DrawLevelField(x, y);
12074           }
12075           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12076                    element == EL_BD_MAGIC_WALL_FULL)
12077           {
12078             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12079             TEST_DrawLevelField(x, y);
12080           }
12081           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12082                    element == EL_DC_MAGIC_WALL_FULL)
12083           {
12084             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12085             TEST_DrawLevelField(x, y);
12086           }
12087         }
12088
12089         game.magic_wall_active = FALSE;
12090       }
12091     }
12092   }
12093
12094   if (game.light_time_left > 0)
12095   {
12096     game.light_time_left--;
12097
12098     if (game.light_time_left == 0)
12099       RedrawAllLightSwitchesAndInvisibleElements();
12100   }
12101
12102   if (game.timegate_time_left > 0)
12103   {
12104     game.timegate_time_left--;
12105
12106     if (game.timegate_time_left == 0)
12107       CloseAllOpenTimegates();
12108   }
12109
12110   if (game.lenses_time_left > 0)
12111   {
12112     game.lenses_time_left--;
12113
12114     if (game.lenses_time_left == 0)
12115       RedrawAllInvisibleElementsForLenses();
12116   }
12117
12118   if (game.magnify_time_left > 0)
12119   {
12120     game.magnify_time_left--;
12121
12122     if (game.magnify_time_left == 0)
12123       RedrawAllInvisibleElementsForMagnifier();
12124   }
12125
12126   for (i = 0; i < MAX_PLAYERS; i++)
12127   {
12128     struct PlayerInfo *player = &stored_player[i];
12129
12130     if (SHIELD_ON(player))
12131     {
12132       if (player->shield_deadly_time_left)
12133         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12134       else if (player->shield_normal_time_left)
12135         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12136     }
12137   }
12138
12139 #if USE_DELAYED_GFX_REDRAW
12140   SCAN_PLAYFIELD(x, y)
12141   {
12142     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12143     {
12144       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12145          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12146
12147       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12148         DrawLevelField(x, y);
12149
12150       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12151         DrawLevelFieldCrumbled(x, y);
12152
12153       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12154         DrawLevelFieldCrumbledNeighbours(x, y);
12155
12156       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12157         DrawTwinkleOnField(x, y);
12158     }
12159
12160     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12161   }
12162 #endif
12163
12164   DrawAllPlayers();
12165   PlayAllPlayersSound();
12166
12167   if (local_player->show_envelope != 0 && (!local_player->active ||
12168                                            local_player->MovPos == 0))
12169   {
12170     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12171
12172     local_player->show_envelope = 0;
12173   }
12174
12175   // use random number generator in every frame to make it less predictable
12176   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12177     RND(1);
12178 }
12179
12180 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12181 {
12182   int min_x = x, min_y = y, max_x = x, max_y = y;
12183   int i;
12184
12185   for (i = 0; i < MAX_PLAYERS; i++)
12186   {
12187     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12188
12189     if (!stored_player[i].active || &stored_player[i] == player)
12190       continue;
12191
12192     min_x = MIN(min_x, jx);
12193     min_y = MIN(min_y, jy);
12194     max_x = MAX(max_x, jx);
12195     max_y = MAX(max_y, jy);
12196   }
12197
12198   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12199 }
12200
12201 static boolean AllPlayersInVisibleScreen(void)
12202 {
12203   int i;
12204
12205   for (i = 0; i < MAX_PLAYERS; i++)
12206   {
12207     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12208
12209     if (!stored_player[i].active)
12210       continue;
12211
12212     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12213       return FALSE;
12214   }
12215
12216   return TRUE;
12217 }
12218
12219 void ScrollLevel(int dx, int dy)
12220 {
12221   int scroll_offset = 2 * TILEX_VAR;
12222   int x, y;
12223
12224   BlitBitmap(drawto_field, drawto_field,
12225              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12226              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12227              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12228              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12229              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12230              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12231
12232   if (dx != 0)
12233   {
12234     x = (dx == 1 ? BX1 : BX2);
12235     for (y = BY1; y <= BY2; y++)
12236       DrawScreenField(x, y);
12237   }
12238
12239   if (dy != 0)
12240   {
12241     y = (dy == 1 ? BY1 : BY2);
12242     for (x = BX1; x <= BX2; x++)
12243       DrawScreenField(x, y);
12244   }
12245
12246   redraw_mask |= REDRAW_FIELD;
12247 }
12248
12249 static boolean canFallDown(struct PlayerInfo *player)
12250 {
12251   int jx = player->jx, jy = player->jy;
12252
12253   return (IN_LEV_FIELD(jx, jy + 1) &&
12254           (IS_FREE(jx, jy + 1) ||
12255            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12256           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12257           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12258 }
12259
12260 static boolean canPassField(int x, int y, int move_dir)
12261 {
12262   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12263   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12264   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12265   int nextx = x + dx;
12266   int nexty = y + dy;
12267   int element = Feld[x][y];
12268
12269   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12270           !CAN_MOVE(element) &&
12271           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12272           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12273           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12274 }
12275
12276 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12277 {
12278   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12279   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12280   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12281   int newx = x + dx;
12282   int newy = y + dy;
12283
12284   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12285           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12286           (IS_DIGGABLE(Feld[newx][newy]) ||
12287            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12288            canPassField(newx, newy, move_dir)));
12289 }
12290
12291 static void CheckGravityMovement(struct PlayerInfo *player)
12292 {
12293   if (player->gravity && !player->programmed_action)
12294   {
12295     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12296     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12297     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12298     int jx = player->jx, jy = player->jy;
12299     boolean player_is_moving_to_valid_field =
12300       (!player_is_snapping &&
12301        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12302         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12303     boolean player_can_fall_down = canFallDown(player);
12304
12305     if (player_can_fall_down &&
12306         !player_is_moving_to_valid_field)
12307       player->programmed_action = MV_DOWN;
12308   }
12309 }
12310
12311 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12312 {
12313   return CheckGravityMovement(player);
12314
12315   if (player->gravity && !player->programmed_action)
12316   {
12317     int jx = player->jx, jy = player->jy;
12318     boolean field_under_player_is_free =
12319       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12320     boolean player_is_standing_on_valid_field =
12321       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12322        (IS_WALKABLE(Feld[jx][jy]) &&
12323         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12324
12325     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12326       player->programmed_action = MV_DOWN;
12327   }
12328 }
12329
12330 /*
12331   MovePlayerOneStep()
12332   -----------------------------------------------------------------------------
12333   dx, dy:               direction (non-diagonal) to try to move the player to
12334   real_dx, real_dy:     direction as read from input device (can be diagonal)
12335 */
12336
12337 boolean MovePlayerOneStep(struct PlayerInfo *player,
12338                           int dx, int dy, int real_dx, int real_dy)
12339 {
12340   int jx = player->jx, jy = player->jy;
12341   int new_jx = jx + dx, new_jy = jy + dy;
12342   int can_move;
12343   boolean player_can_move = !player->cannot_move;
12344
12345   if (!player->active || (!dx && !dy))
12346     return MP_NO_ACTION;
12347
12348   player->MovDir = (dx < 0 ? MV_LEFT :
12349                     dx > 0 ? MV_RIGHT :
12350                     dy < 0 ? MV_UP :
12351                     dy > 0 ? MV_DOWN :  MV_NONE);
12352
12353   if (!IN_LEV_FIELD(new_jx, new_jy))
12354     return MP_NO_ACTION;
12355
12356   if (!player_can_move)
12357   {
12358     if (player->MovPos == 0)
12359     {
12360       player->is_moving = FALSE;
12361       player->is_digging = FALSE;
12362       player->is_collecting = FALSE;
12363       player->is_snapping = FALSE;
12364       player->is_pushing = FALSE;
12365     }
12366   }
12367
12368   if (!network.enabled && game.centered_player_nr == -1 &&
12369       !AllPlayersInSight(player, new_jx, new_jy))
12370     return MP_NO_ACTION;
12371
12372   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12373   if (can_move != MP_MOVING)
12374     return can_move;
12375
12376   // check if DigField() has caused relocation of the player
12377   if (player->jx != jx || player->jy != jy)
12378     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12379
12380   StorePlayer[jx][jy] = 0;
12381   player->last_jx = jx;
12382   player->last_jy = jy;
12383   player->jx = new_jx;
12384   player->jy = new_jy;
12385   StorePlayer[new_jx][new_jy] = player->element_nr;
12386
12387   if (player->move_delay_value_next != -1)
12388   {
12389     player->move_delay_value = player->move_delay_value_next;
12390     player->move_delay_value_next = -1;
12391   }
12392
12393   player->MovPos =
12394     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12395
12396   player->step_counter++;
12397
12398   PlayerVisit[jx][jy] = FrameCounter;
12399
12400   player->is_moving = TRUE;
12401
12402 #if 1
12403   // should better be called in MovePlayer(), but this breaks some tapes
12404   ScrollPlayer(player, SCROLL_INIT);
12405 #endif
12406
12407   return MP_MOVING;
12408 }
12409
12410 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12411 {
12412   int jx = player->jx, jy = player->jy;
12413   int old_jx = jx, old_jy = jy;
12414   int moved = MP_NO_ACTION;
12415
12416   if (!player->active)
12417     return FALSE;
12418
12419   if (!dx && !dy)
12420   {
12421     if (player->MovPos == 0)
12422     {
12423       player->is_moving = FALSE;
12424       player->is_digging = FALSE;
12425       player->is_collecting = FALSE;
12426       player->is_snapping = FALSE;
12427       player->is_pushing = FALSE;
12428     }
12429
12430     return FALSE;
12431   }
12432
12433   if (player->move_delay > 0)
12434     return FALSE;
12435
12436   player->move_delay = -1;              // set to "uninitialized" value
12437
12438   // store if player is automatically moved to next field
12439   player->is_auto_moving = (player->programmed_action != MV_NONE);
12440
12441   // remove the last programmed player action
12442   player->programmed_action = 0;
12443
12444   if (player->MovPos)
12445   {
12446     // should only happen if pre-1.2 tape recordings are played
12447     // this is only for backward compatibility
12448
12449     int original_move_delay_value = player->move_delay_value;
12450
12451 #if DEBUG
12452     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12453            tape.counter);
12454 #endif
12455
12456     // scroll remaining steps with finest movement resolution
12457     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12458
12459     while (player->MovPos)
12460     {
12461       ScrollPlayer(player, SCROLL_GO_ON);
12462       ScrollScreen(NULL, SCROLL_GO_ON);
12463
12464       AdvanceFrameAndPlayerCounters(player->index_nr);
12465
12466       DrawAllPlayers();
12467       BackToFront_WithFrameDelay(0);
12468     }
12469
12470     player->move_delay_value = original_move_delay_value;
12471   }
12472
12473   player->is_active = FALSE;
12474
12475   if (player->last_move_dir & MV_HORIZONTAL)
12476   {
12477     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12478       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12479   }
12480   else
12481   {
12482     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12483       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12484   }
12485
12486   if (!moved && !player->is_active)
12487   {
12488     player->is_moving = FALSE;
12489     player->is_digging = FALSE;
12490     player->is_collecting = FALSE;
12491     player->is_snapping = FALSE;
12492     player->is_pushing = FALSE;
12493   }
12494
12495   jx = player->jx;
12496   jy = player->jy;
12497
12498   if (moved & MP_MOVING && !ScreenMovPos &&
12499       (player->index_nr == game.centered_player_nr ||
12500        game.centered_player_nr == -1))
12501   {
12502     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12503     int offset = game.scroll_delay_value;
12504
12505     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12506     {
12507       // actual player has left the screen -- scroll in that direction
12508       if (jx != old_jx)         // player has moved horizontally
12509         scroll_x += (jx - old_jx);
12510       else                      // player has moved vertically
12511         scroll_y += (jy - old_jy);
12512     }
12513     else
12514     {
12515       if (jx != old_jx)         // player has moved horizontally
12516       {
12517         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12518             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12519           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12520
12521         // don't scroll over playfield boundaries
12522         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12523           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12524
12525         // don't scroll more than one field at a time
12526         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12527
12528         // don't scroll against the player's moving direction
12529         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12530             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12531           scroll_x = old_scroll_x;
12532       }
12533       else                      // player has moved vertically
12534       {
12535         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12536             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12537           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12538
12539         // don't scroll over playfield boundaries
12540         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12541           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12542
12543         // don't scroll more than one field at a time
12544         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12545
12546         // don't scroll against the player's moving direction
12547         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12548             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12549           scroll_y = old_scroll_y;
12550       }
12551     }
12552
12553     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12554     {
12555       if (!network.enabled && game.centered_player_nr == -1 &&
12556           !AllPlayersInVisibleScreen())
12557       {
12558         scroll_x = old_scroll_x;
12559         scroll_y = old_scroll_y;
12560       }
12561       else
12562       {
12563         ScrollScreen(player, SCROLL_INIT);
12564         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12565       }
12566     }
12567   }
12568
12569   player->StepFrame = 0;
12570
12571   if (moved & MP_MOVING)
12572   {
12573     if (old_jx != jx && old_jy == jy)
12574       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12575     else if (old_jx == jx && old_jy != jy)
12576       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12577
12578     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12579
12580     player->last_move_dir = player->MovDir;
12581     player->is_moving = TRUE;
12582     player->is_snapping = FALSE;
12583     player->is_switching = FALSE;
12584     player->is_dropping = FALSE;
12585     player->is_dropping_pressed = FALSE;
12586     player->drop_pressed_delay = 0;
12587
12588 #if 0
12589     // should better be called here than above, but this breaks some tapes
12590     ScrollPlayer(player, SCROLL_INIT);
12591 #endif
12592   }
12593   else
12594   {
12595     CheckGravityMovementWhenNotMoving(player);
12596
12597     player->is_moving = FALSE;
12598
12599     /* at this point, the player is allowed to move, but cannot move right now
12600        (e.g. because of something blocking the way) -- ensure that the player
12601        is also allowed to move in the next frame (in old versions before 3.1.1,
12602        the player was forced to wait again for eight frames before next try) */
12603
12604     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12605       player->move_delay = 0;   // allow direct movement in the next frame
12606   }
12607
12608   if (player->move_delay == -1)         // not yet initialized by DigField()
12609     player->move_delay = player->move_delay_value;
12610
12611   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12612   {
12613     TestIfPlayerTouchesBadThing(jx, jy);
12614     TestIfPlayerTouchesCustomElement(jx, jy);
12615   }
12616
12617   if (!player->active)
12618     RemovePlayer(player);
12619
12620   return moved;
12621 }
12622
12623 void ScrollPlayer(struct PlayerInfo *player, int mode)
12624 {
12625   int jx = player->jx, jy = player->jy;
12626   int last_jx = player->last_jx, last_jy = player->last_jy;
12627   int move_stepsize = TILEX / player->move_delay_value;
12628
12629   if (!player->active)
12630     return;
12631
12632   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12633     return;
12634
12635   if (mode == SCROLL_INIT)
12636   {
12637     player->actual_frame_counter = FrameCounter;
12638     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12639
12640     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12641         Feld[last_jx][last_jy] == EL_EMPTY)
12642     {
12643       int last_field_block_delay = 0;   // start with no blocking at all
12644       int block_delay_adjustment = player->block_delay_adjustment;
12645
12646       // if player blocks last field, add delay for exactly one move
12647       if (player->block_last_field)
12648       {
12649         last_field_block_delay += player->move_delay_value;
12650
12651         // when blocking enabled, prevent moving up despite gravity
12652         if (player->gravity && player->MovDir == MV_UP)
12653           block_delay_adjustment = -1;
12654       }
12655
12656       // add block delay adjustment (also possible when not blocking)
12657       last_field_block_delay += block_delay_adjustment;
12658
12659       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12660       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12661     }
12662
12663     if (player->MovPos != 0)    // player has not yet reached destination
12664       return;
12665   }
12666   else if (!FrameReached(&player->actual_frame_counter, 1))
12667     return;
12668
12669   if (player->MovPos != 0)
12670   {
12671     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12672     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12673
12674     // before DrawPlayer() to draw correct player graphic for this case
12675     if (player->MovPos == 0)
12676       CheckGravityMovement(player);
12677   }
12678
12679   if (player->MovPos == 0)      // player reached destination field
12680   {
12681     if (player->move_delay_reset_counter > 0)
12682     {
12683       player->move_delay_reset_counter--;
12684
12685       if (player->move_delay_reset_counter == 0)
12686       {
12687         // continue with normal speed after quickly moving through gate
12688         HALVE_PLAYER_SPEED(player);
12689
12690         // be able to make the next move without delay
12691         player->move_delay = 0;
12692       }
12693     }
12694
12695     player->last_jx = jx;
12696     player->last_jy = jy;
12697
12698     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12699         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12700         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12701         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12702         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12703         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12704         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12705         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12706     {
12707       ExitPlayer(player);
12708
12709       if (game.players_still_needed == 0 &&
12710           (game.friends_still_needed == 0 ||
12711            IS_SP_ELEMENT(Feld[jx][jy])))
12712         LevelSolved();
12713     }
12714
12715     // this breaks one level: "machine", level 000
12716     {
12717       int move_direction = player->MovDir;
12718       int enter_side = MV_DIR_OPPOSITE(move_direction);
12719       int leave_side = move_direction;
12720       int old_jx = last_jx;
12721       int old_jy = last_jy;
12722       int old_element = Feld[old_jx][old_jy];
12723       int new_element = Feld[jx][jy];
12724
12725       if (IS_CUSTOM_ELEMENT(old_element))
12726         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12727                                    CE_LEFT_BY_PLAYER,
12728                                    player->index_bit, leave_side);
12729
12730       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12731                                           CE_PLAYER_LEAVES_X,
12732                                           player->index_bit, leave_side);
12733
12734       if (IS_CUSTOM_ELEMENT(new_element))
12735         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12736                                    player->index_bit, enter_side);
12737
12738       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12739                                           CE_PLAYER_ENTERS_X,
12740                                           player->index_bit, enter_side);
12741
12742       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12743                                         CE_MOVE_OF_X, move_direction);
12744     }
12745
12746     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12747     {
12748       TestIfPlayerTouchesBadThing(jx, jy);
12749       TestIfPlayerTouchesCustomElement(jx, jy);
12750
12751       /* needed because pushed element has not yet reached its destination,
12752          so it would trigger a change event at its previous field location */
12753       if (!player->is_pushing)
12754         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12755
12756       if (!player->active)
12757         RemovePlayer(player);
12758     }
12759
12760     if (!game.LevelSolved && level.use_step_counter)
12761     {
12762       int i;
12763
12764       TimePlayed++;
12765
12766       if (TimeLeft > 0)
12767       {
12768         TimeLeft--;
12769
12770         if (TimeLeft <= 10 && setup.time_limit)
12771           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12772
12773         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12774
12775         DisplayGameControlValues();
12776
12777         if (!TimeLeft && setup.time_limit)
12778           for (i = 0; i < MAX_PLAYERS; i++)
12779             KillPlayer(&stored_player[i]);
12780       }
12781       else if (game.no_time_limit && !game.all_players_gone)
12782       {
12783         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12784
12785         DisplayGameControlValues();
12786       }
12787     }
12788
12789     if (tape.single_step && tape.recording && !tape.pausing &&
12790         !player->programmed_action)
12791       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12792
12793     if (!player->programmed_action)
12794       CheckSaveEngineSnapshot(player);
12795   }
12796 }
12797
12798 void ScrollScreen(struct PlayerInfo *player, int mode)
12799 {
12800   static unsigned int screen_frame_counter = 0;
12801
12802   if (mode == SCROLL_INIT)
12803   {
12804     // set scrolling step size according to actual player's moving speed
12805     ScrollStepSize = TILEX / player->move_delay_value;
12806
12807     screen_frame_counter = FrameCounter;
12808     ScreenMovDir = player->MovDir;
12809     ScreenMovPos = player->MovPos;
12810     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12811     return;
12812   }
12813   else if (!FrameReached(&screen_frame_counter, 1))
12814     return;
12815
12816   if (ScreenMovPos)
12817   {
12818     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12819     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12820     redraw_mask |= REDRAW_FIELD;
12821   }
12822   else
12823     ScreenMovDir = MV_NONE;
12824 }
12825
12826 void TestIfPlayerTouchesCustomElement(int x, int y)
12827 {
12828   static int xy[4][2] =
12829   {
12830     { 0, -1 },
12831     { -1, 0 },
12832     { +1, 0 },
12833     { 0, +1 }
12834   };
12835   static int trigger_sides[4][2] =
12836   {
12837     // center side       border side
12838     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12839     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12840     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12841     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12842   };
12843   static int touch_dir[4] =
12844   {
12845     MV_LEFT | MV_RIGHT,
12846     MV_UP   | MV_DOWN,
12847     MV_UP   | MV_DOWN,
12848     MV_LEFT | MV_RIGHT
12849   };
12850   int center_element = Feld[x][y];      // should always be non-moving!
12851   int i;
12852
12853   for (i = 0; i < NUM_DIRECTIONS; i++)
12854   {
12855     int xx = x + xy[i][0];
12856     int yy = y + xy[i][1];
12857     int center_side = trigger_sides[i][0];
12858     int border_side = trigger_sides[i][1];
12859     int border_element;
12860
12861     if (!IN_LEV_FIELD(xx, yy))
12862       continue;
12863
12864     if (IS_PLAYER(x, y))                // player found at center element
12865     {
12866       struct PlayerInfo *player = PLAYERINFO(x, y);
12867
12868       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12869         border_element = Feld[xx][yy];          // may be moving!
12870       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12871         border_element = Feld[xx][yy];
12872       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12873         border_element = MovingOrBlocked2Element(xx, yy);
12874       else
12875         continue;               // center and border element do not touch
12876
12877       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12878                                  player->index_bit, border_side);
12879       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12880                                           CE_PLAYER_TOUCHES_X,
12881                                           player->index_bit, border_side);
12882
12883       {
12884         /* use player element that is initially defined in the level playfield,
12885            not the player element that corresponds to the runtime player number
12886            (example: a level that contains EL_PLAYER_3 as the only player would
12887            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12888         int player_element = PLAYERINFO(x, y)->initial_element;
12889
12890         CheckElementChangeBySide(xx, yy, border_element, player_element,
12891                                  CE_TOUCHING_X, border_side);
12892       }
12893     }
12894     else if (IS_PLAYER(xx, yy))         // player found at border element
12895     {
12896       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12897
12898       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12899       {
12900         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12901           continue;             // center and border element do not touch
12902       }
12903
12904       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12905                                  player->index_bit, center_side);
12906       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12907                                           CE_PLAYER_TOUCHES_X,
12908                                           player->index_bit, center_side);
12909
12910       {
12911         /* use player element that is initially defined in the level playfield,
12912            not the player element that corresponds to the runtime player number
12913            (example: a level that contains EL_PLAYER_3 as the only player would
12914            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12915         int player_element = PLAYERINFO(xx, yy)->initial_element;
12916
12917         CheckElementChangeBySide(x, y, center_element, player_element,
12918                                  CE_TOUCHING_X, center_side);
12919       }
12920
12921       break;
12922     }
12923   }
12924 }
12925
12926 void TestIfElementTouchesCustomElement(int x, int y)
12927 {
12928   static int xy[4][2] =
12929   {
12930     { 0, -1 },
12931     { -1, 0 },
12932     { +1, 0 },
12933     { 0, +1 }
12934   };
12935   static int trigger_sides[4][2] =
12936   {
12937     // center side      border side
12938     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12939     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12940     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12941     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12942   };
12943   static int touch_dir[4] =
12944   {
12945     MV_LEFT | MV_RIGHT,
12946     MV_UP   | MV_DOWN,
12947     MV_UP   | MV_DOWN,
12948     MV_LEFT | MV_RIGHT
12949   };
12950   boolean change_center_element = FALSE;
12951   int center_element = Feld[x][y];      // should always be non-moving!
12952   int border_element_old[NUM_DIRECTIONS];
12953   int i;
12954
12955   for (i = 0; i < NUM_DIRECTIONS; i++)
12956   {
12957     int xx = x + xy[i][0];
12958     int yy = y + xy[i][1];
12959     int border_element;
12960
12961     border_element_old[i] = -1;
12962
12963     if (!IN_LEV_FIELD(xx, yy))
12964       continue;
12965
12966     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12967       border_element = Feld[xx][yy];    // may be moving!
12968     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12969       border_element = Feld[xx][yy];
12970     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12971       border_element = MovingOrBlocked2Element(xx, yy);
12972     else
12973       continue;                 // center and border element do not touch
12974
12975     border_element_old[i] = border_element;
12976   }
12977
12978   for (i = 0; i < NUM_DIRECTIONS; i++)
12979   {
12980     int xx = x + xy[i][0];
12981     int yy = y + xy[i][1];
12982     int center_side = trigger_sides[i][0];
12983     int border_element = border_element_old[i];
12984
12985     if (border_element == -1)
12986       continue;
12987
12988     // check for change of border element
12989     CheckElementChangeBySide(xx, yy, border_element, center_element,
12990                              CE_TOUCHING_X, center_side);
12991
12992     // (center element cannot be player, so we dont have to check this here)
12993   }
12994
12995   for (i = 0; i < NUM_DIRECTIONS; i++)
12996   {
12997     int xx = x + xy[i][0];
12998     int yy = y + xy[i][1];
12999     int border_side = trigger_sides[i][1];
13000     int border_element = border_element_old[i];
13001
13002     if (border_element == -1)
13003       continue;
13004
13005     // check for change of center element (but change it only once)
13006     if (!change_center_element)
13007       change_center_element =
13008         CheckElementChangeBySide(x, y, center_element, border_element,
13009                                  CE_TOUCHING_X, border_side);
13010
13011     if (IS_PLAYER(xx, yy))
13012     {
13013       /* use player element that is initially defined in the level playfield,
13014          not the player element that corresponds to the runtime player number
13015          (example: a level that contains EL_PLAYER_3 as the only player would
13016          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13017       int player_element = PLAYERINFO(xx, yy)->initial_element;
13018
13019       CheckElementChangeBySide(x, y, center_element, player_element,
13020                                CE_TOUCHING_X, border_side);
13021     }
13022   }
13023 }
13024
13025 void TestIfElementHitsCustomElement(int x, int y, int direction)
13026 {
13027   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13028   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13029   int hitx = x + dx, hity = y + dy;
13030   int hitting_element = Feld[x][y];
13031   int touched_element;
13032
13033   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13034     return;
13035
13036   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13037                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13038
13039   if (IN_LEV_FIELD(hitx, hity))
13040   {
13041     int opposite_direction = MV_DIR_OPPOSITE(direction);
13042     int hitting_side = direction;
13043     int touched_side = opposite_direction;
13044     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13045                           MovDir[hitx][hity] != direction ||
13046                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13047
13048     object_hit = TRUE;
13049
13050     if (object_hit)
13051     {
13052       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13053                                CE_HITTING_X, touched_side);
13054
13055       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13056                                CE_HIT_BY_X, hitting_side);
13057
13058       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13059                                CE_HIT_BY_SOMETHING, opposite_direction);
13060
13061       if (IS_PLAYER(hitx, hity))
13062       {
13063         /* use player element that is initially defined in the level playfield,
13064            not the player element that corresponds to the runtime player number
13065            (example: a level that contains EL_PLAYER_3 as the only player would
13066            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13067         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13068
13069         CheckElementChangeBySide(x, y, hitting_element, player_element,
13070                                  CE_HITTING_X, touched_side);
13071       }
13072     }
13073   }
13074
13075   // "hitting something" is also true when hitting the playfield border
13076   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13077                            CE_HITTING_SOMETHING, direction);
13078 }
13079
13080 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13081 {
13082   int i, kill_x = -1, kill_y = -1;
13083
13084   int bad_element = -1;
13085   static int test_xy[4][2] =
13086   {
13087     { 0, -1 },
13088     { -1, 0 },
13089     { +1, 0 },
13090     { 0, +1 }
13091   };
13092   static int test_dir[4] =
13093   {
13094     MV_UP,
13095     MV_LEFT,
13096     MV_RIGHT,
13097     MV_DOWN
13098   };
13099
13100   for (i = 0; i < NUM_DIRECTIONS; i++)
13101   {
13102     int test_x, test_y, test_move_dir, test_element;
13103
13104     test_x = good_x + test_xy[i][0];
13105     test_y = good_y + test_xy[i][1];
13106
13107     if (!IN_LEV_FIELD(test_x, test_y))
13108       continue;
13109
13110     test_move_dir =
13111       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13112
13113     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13114
13115     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13116        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13117     */
13118     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13119         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13120     {
13121       kill_x = test_x;
13122       kill_y = test_y;
13123       bad_element = test_element;
13124
13125       break;
13126     }
13127   }
13128
13129   if (kill_x != -1 || kill_y != -1)
13130   {
13131     if (IS_PLAYER(good_x, good_y))
13132     {
13133       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13134
13135       if (player->shield_deadly_time_left > 0 &&
13136           !IS_INDESTRUCTIBLE(bad_element))
13137         Bang(kill_x, kill_y);
13138       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13139         KillPlayer(player);
13140     }
13141     else
13142       Bang(good_x, good_y);
13143   }
13144 }
13145
13146 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13147 {
13148   int i, kill_x = -1, kill_y = -1;
13149   int bad_element = Feld[bad_x][bad_y];
13150   static int test_xy[4][2] =
13151   {
13152     { 0, -1 },
13153     { -1, 0 },
13154     { +1, 0 },
13155     { 0, +1 }
13156   };
13157   static int touch_dir[4] =
13158   {
13159     MV_LEFT | MV_RIGHT,
13160     MV_UP   | MV_DOWN,
13161     MV_UP   | MV_DOWN,
13162     MV_LEFT | MV_RIGHT
13163   };
13164   static int test_dir[4] =
13165   {
13166     MV_UP,
13167     MV_LEFT,
13168     MV_RIGHT,
13169     MV_DOWN
13170   };
13171
13172   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13173     return;
13174
13175   for (i = 0; i < NUM_DIRECTIONS; i++)
13176   {
13177     int test_x, test_y, test_move_dir, test_element;
13178
13179     test_x = bad_x + test_xy[i][0];
13180     test_y = bad_y + test_xy[i][1];
13181
13182     if (!IN_LEV_FIELD(test_x, test_y))
13183       continue;
13184
13185     test_move_dir =
13186       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13187
13188     test_element = Feld[test_x][test_y];
13189
13190     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13191        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13192     */
13193     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13194         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13195     {
13196       // good thing is player or penguin that does not move away
13197       if (IS_PLAYER(test_x, test_y))
13198       {
13199         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13200
13201         if (bad_element == EL_ROBOT && player->is_moving)
13202           continue;     // robot does not kill player if he is moving
13203
13204         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13205         {
13206           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13207             continue;           // center and border element do not touch
13208         }
13209
13210         kill_x = test_x;
13211         kill_y = test_y;
13212
13213         break;
13214       }
13215       else if (test_element == EL_PENGUIN)
13216       {
13217         kill_x = test_x;
13218         kill_y = test_y;
13219
13220         break;
13221       }
13222     }
13223   }
13224
13225   if (kill_x != -1 || kill_y != -1)
13226   {
13227     if (IS_PLAYER(kill_x, kill_y))
13228     {
13229       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13230
13231       if (player->shield_deadly_time_left > 0 &&
13232           !IS_INDESTRUCTIBLE(bad_element))
13233         Bang(bad_x, bad_y);
13234       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13235         KillPlayer(player);
13236     }
13237     else
13238       Bang(kill_x, kill_y);
13239   }
13240 }
13241
13242 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13243 {
13244   int bad_element = Feld[bad_x][bad_y];
13245   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13246   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13247   int test_x = bad_x + dx, test_y = bad_y + dy;
13248   int test_move_dir, test_element;
13249   int kill_x = -1, kill_y = -1;
13250
13251   if (!IN_LEV_FIELD(test_x, test_y))
13252     return;
13253
13254   test_move_dir =
13255     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13256
13257   test_element = Feld[test_x][test_y];
13258
13259   if (test_move_dir != bad_move_dir)
13260   {
13261     // good thing can be player or penguin that does not move away
13262     if (IS_PLAYER(test_x, test_y))
13263     {
13264       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13265
13266       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13267          player as being hit when he is moving towards the bad thing, because
13268          the "get hit by" condition would be lost after the player stops) */
13269       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13270         return;         // player moves away from bad thing
13271
13272       kill_x = test_x;
13273       kill_y = test_y;
13274     }
13275     else if (test_element == EL_PENGUIN)
13276     {
13277       kill_x = test_x;
13278       kill_y = test_y;
13279     }
13280   }
13281
13282   if (kill_x != -1 || kill_y != -1)
13283   {
13284     if (IS_PLAYER(kill_x, kill_y))
13285     {
13286       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13287
13288       if (player->shield_deadly_time_left > 0 &&
13289           !IS_INDESTRUCTIBLE(bad_element))
13290         Bang(bad_x, bad_y);
13291       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13292         KillPlayer(player);
13293     }
13294     else
13295       Bang(kill_x, kill_y);
13296   }
13297 }
13298
13299 void TestIfPlayerTouchesBadThing(int x, int y)
13300 {
13301   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13302 }
13303
13304 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13305 {
13306   TestIfGoodThingHitsBadThing(x, y, move_dir);
13307 }
13308
13309 void TestIfBadThingTouchesPlayer(int x, int y)
13310 {
13311   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13312 }
13313
13314 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13315 {
13316   TestIfBadThingHitsGoodThing(x, y, move_dir);
13317 }
13318
13319 void TestIfFriendTouchesBadThing(int x, int y)
13320 {
13321   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13322 }
13323
13324 void TestIfBadThingTouchesFriend(int x, int y)
13325 {
13326   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13327 }
13328
13329 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13330 {
13331   int i, kill_x = bad_x, kill_y = bad_y;
13332   static int xy[4][2] =
13333   {
13334     { 0, -1 },
13335     { -1, 0 },
13336     { +1, 0 },
13337     { 0, +1 }
13338   };
13339
13340   for (i = 0; i < NUM_DIRECTIONS; i++)
13341   {
13342     int x, y, element;
13343
13344     x = bad_x + xy[i][0];
13345     y = bad_y + xy[i][1];
13346     if (!IN_LEV_FIELD(x, y))
13347       continue;
13348
13349     element = Feld[x][y];
13350     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13351         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13352     {
13353       kill_x = x;
13354       kill_y = y;
13355       break;
13356     }
13357   }
13358
13359   if (kill_x != bad_x || kill_y != bad_y)
13360     Bang(bad_x, bad_y);
13361 }
13362
13363 void KillPlayer(struct PlayerInfo *player)
13364 {
13365   int jx = player->jx, jy = player->jy;
13366
13367   if (!player->active)
13368     return;
13369
13370 #if 0
13371   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13372          player->killed, player->active, player->reanimated);
13373 #endif
13374
13375   /* the following code was introduced to prevent an infinite loop when calling
13376      -> Bang()
13377      -> CheckTriggeredElementChangeExt()
13378      -> ExecuteCustomElementAction()
13379      -> KillPlayer()
13380      -> (infinitely repeating the above sequence of function calls)
13381      which occurs when killing the player while having a CE with the setting
13382      "kill player X when explosion of <player X>"; the solution using a new
13383      field "player->killed" was chosen for backwards compatibility, although
13384      clever use of the fields "player->active" etc. would probably also work */
13385 #if 1
13386   if (player->killed)
13387     return;
13388 #endif
13389
13390   player->killed = TRUE;
13391
13392   // remove accessible field at the player's position
13393   Feld[jx][jy] = EL_EMPTY;
13394
13395   // deactivate shield (else Bang()/Explode() would not work right)
13396   player->shield_normal_time_left = 0;
13397   player->shield_deadly_time_left = 0;
13398
13399 #if 0
13400   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13401          player->killed, player->active, player->reanimated);
13402 #endif
13403
13404   Bang(jx, jy);
13405
13406 #if 0
13407   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13408          player->killed, player->active, player->reanimated);
13409 #endif
13410
13411   if (player->reanimated)       // killed player may have been reanimated
13412     player->killed = player->reanimated = FALSE;
13413   else
13414     BuryPlayer(player);
13415 }
13416
13417 static void KillPlayerUnlessEnemyProtected(int x, int y)
13418 {
13419   if (!PLAYER_ENEMY_PROTECTED(x, y))
13420     KillPlayer(PLAYERINFO(x, y));
13421 }
13422
13423 static void KillPlayerUnlessExplosionProtected(int x, int y)
13424 {
13425   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13426     KillPlayer(PLAYERINFO(x, y));
13427 }
13428
13429 void BuryPlayer(struct PlayerInfo *player)
13430 {
13431   int jx = player->jx, jy = player->jy;
13432
13433   if (!player->active)
13434     return;
13435
13436   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13437   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13438
13439   RemovePlayer(player);
13440
13441   player->buried = TRUE;
13442
13443   if (game.all_players_gone)
13444     game.GameOver = TRUE;
13445 }
13446
13447 void RemovePlayer(struct PlayerInfo *player)
13448 {
13449   int jx = player->jx, jy = player->jy;
13450   int i, found = FALSE;
13451
13452   player->present = FALSE;
13453   player->active = FALSE;
13454
13455   // required for some CE actions (even if the player is not active anymore)
13456   player->MovPos = 0;
13457
13458   if (!ExplodeField[jx][jy])
13459     StorePlayer[jx][jy] = 0;
13460
13461   if (player->is_moving)
13462     TEST_DrawLevelField(player->last_jx, player->last_jy);
13463
13464   for (i = 0; i < MAX_PLAYERS; i++)
13465     if (stored_player[i].active)
13466       found = TRUE;
13467
13468   if (!found)
13469   {
13470     game.all_players_gone = TRUE;
13471     game.GameOver = TRUE;
13472   }
13473
13474   game.exit_x = game.robot_wheel_x = jx;
13475   game.exit_y = game.robot_wheel_y = jy;
13476 }
13477
13478 void ExitPlayer(struct PlayerInfo *player)
13479 {
13480   DrawPlayer(player);   // needed here only to cleanup last field
13481   RemovePlayer(player);
13482
13483   if (game.players_still_needed > 0)
13484     game.players_still_needed--;
13485 }
13486
13487 static void setFieldForSnapping(int x, int y, int element, int direction)
13488 {
13489   struct ElementInfo *ei = &element_info[element];
13490   int direction_bit = MV_DIR_TO_BIT(direction);
13491   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13492   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13493                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13494
13495   Feld[x][y] = EL_ELEMENT_SNAPPING;
13496   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13497
13498   ResetGfxAnimation(x, y);
13499
13500   GfxElement[x][y] = element;
13501   GfxAction[x][y] = action;
13502   GfxDir[x][y] = direction;
13503   GfxFrame[x][y] = -1;
13504 }
13505
13506 /*
13507   =============================================================================
13508   checkDiagonalPushing()
13509   -----------------------------------------------------------------------------
13510   check if diagonal input device direction results in pushing of object
13511   (by checking if the alternative direction is walkable, diggable, ...)
13512   =============================================================================
13513 */
13514
13515 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13516                                     int x, int y, int real_dx, int real_dy)
13517 {
13518   int jx, jy, dx, dy, xx, yy;
13519
13520   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13521     return TRUE;
13522
13523   // diagonal direction: check alternative direction
13524   jx = player->jx;
13525   jy = player->jy;
13526   dx = x - jx;
13527   dy = y - jy;
13528   xx = jx + (dx == 0 ? real_dx : 0);
13529   yy = jy + (dy == 0 ? real_dy : 0);
13530
13531   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13532 }
13533
13534 /*
13535   =============================================================================
13536   DigField()
13537   -----------------------------------------------------------------------------
13538   x, y:                 field next to player (non-diagonal) to try to dig to
13539   real_dx, real_dy:     direction as read from input device (can be diagonal)
13540   =============================================================================
13541 */
13542
13543 static int DigField(struct PlayerInfo *player,
13544                     int oldx, int oldy, int x, int y,
13545                     int real_dx, int real_dy, int mode)
13546 {
13547   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13548   boolean player_was_pushing = player->is_pushing;
13549   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13550   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13551   int jx = oldx, jy = oldy;
13552   int dx = x - jx, dy = y - jy;
13553   int nextx = x + dx, nexty = y + dy;
13554   int move_direction = (dx == -1 ? MV_LEFT  :
13555                         dx == +1 ? MV_RIGHT :
13556                         dy == -1 ? MV_UP    :
13557                         dy == +1 ? MV_DOWN  : MV_NONE);
13558   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13559   int dig_side = MV_DIR_OPPOSITE(move_direction);
13560   int old_element = Feld[jx][jy];
13561   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13562   int collect_count;
13563
13564   if (is_player)                // function can also be called by EL_PENGUIN
13565   {
13566     if (player->MovPos == 0)
13567     {
13568       player->is_digging = FALSE;
13569       player->is_collecting = FALSE;
13570     }
13571
13572     if (player->MovPos == 0)    // last pushing move finished
13573       player->is_pushing = FALSE;
13574
13575     if (mode == DF_NO_PUSH)     // player just stopped pushing
13576     {
13577       player->is_switching = FALSE;
13578       player->push_delay = -1;
13579
13580       return MP_NO_ACTION;
13581     }
13582   }
13583
13584   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13585     old_element = Back[jx][jy];
13586
13587   // in case of element dropped at player position, check background
13588   else if (Back[jx][jy] != EL_EMPTY &&
13589            game.engine_version >= VERSION_IDENT(2,2,0,0))
13590     old_element = Back[jx][jy];
13591
13592   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13593     return MP_NO_ACTION;        // field has no opening in this direction
13594
13595   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13596     return MP_NO_ACTION;        // field has no opening in this direction
13597
13598   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13599   {
13600     SplashAcid(x, y);
13601
13602     Feld[jx][jy] = player->artwork_element;
13603     InitMovingField(jx, jy, MV_DOWN);
13604     Store[jx][jy] = EL_ACID;
13605     ContinueMoving(jx, jy);
13606     BuryPlayer(player);
13607
13608     return MP_DONT_RUN_INTO;
13609   }
13610
13611   if (player_can_move && DONT_RUN_INTO(element))
13612   {
13613     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13614
13615     return MP_DONT_RUN_INTO;
13616   }
13617
13618   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13619     return MP_NO_ACTION;
13620
13621   collect_count = element_info[element].collect_count_initial;
13622
13623   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13624     return MP_NO_ACTION;
13625
13626   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13627     player_can_move = player_can_move_or_snap;
13628
13629   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13630       game.engine_version >= VERSION_IDENT(2,2,0,0))
13631   {
13632     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13633                                player->index_bit, dig_side);
13634     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13635                                         player->index_bit, dig_side);
13636
13637     if (element == EL_DC_LANDMINE)
13638       Bang(x, y);
13639
13640     if (Feld[x][y] != element)          // field changed by snapping
13641       return MP_ACTION;
13642
13643     return MP_NO_ACTION;
13644   }
13645
13646   if (player->gravity && is_player && !player->is_auto_moving &&
13647       canFallDown(player) && move_direction != MV_DOWN &&
13648       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13649     return MP_NO_ACTION;        // player cannot walk here due to gravity
13650
13651   if (player_can_move &&
13652       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13653   {
13654     int sound_element = SND_ELEMENT(element);
13655     int sound_action = ACTION_WALKING;
13656
13657     if (IS_RND_GATE(element))
13658     {
13659       if (!player->key[RND_GATE_NR(element)])
13660         return MP_NO_ACTION;
13661     }
13662     else if (IS_RND_GATE_GRAY(element))
13663     {
13664       if (!player->key[RND_GATE_GRAY_NR(element)])
13665         return MP_NO_ACTION;
13666     }
13667     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13668     {
13669       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13670         return MP_NO_ACTION;
13671     }
13672     else if (element == EL_EXIT_OPEN ||
13673              element == EL_EM_EXIT_OPEN ||
13674              element == EL_EM_EXIT_OPENING ||
13675              element == EL_STEEL_EXIT_OPEN ||
13676              element == EL_EM_STEEL_EXIT_OPEN ||
13677              element == EL_EM_STEEL_EXIT_OPENING ||
13678              element == EL_SP_EXIT_OPEN ||
13679              element == EL_SP_EXIT_OPENING)
13680     {
13681       sound_action = ACTION_PASSING;    // player is passing exit
13682     }
13683     else if (element == EL_EMPTY)
13684     {
13685       sound_action = ACTION_MOVING;             // nothing to walk on
13686     }
13687
13688     // play sound from background or player, whatever is available
13689     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13690       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13691     else
13692       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13693   }
13694   else if (player_can_move &&
13695            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13696   {
13697     if (!ACCESS_FROM(element, opposite_direction))
13698       return MP_NO_ACTION;      // field not accessible from this direction
13699
13700     if (CAN_MOVE(element))      // only fixed elements can be passed!
13701       return MP_NO_ACTION;
13702
13703     if (IS_EM_GATE(element))
13704     {
13705       if (!player->key[EM_GATE_NR(element)])
13706         return MP_NO_ACTION;
13707     }
13708     else if (IS_EM_GATE_GRAY(element))
13709     {
13710       if (!player->key[EM_GATE_GRAY_NR(element)])
13711         return MP_NO_ACTION;
13712     }
13713     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13714     {
13715       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13716         return MP_NO_ACTION;
13717     }
13718     else if (IS_EMC_GATE(element))
13719     {
13720       if (!player->key[EMC_GATE_NR(element)])
13721         return MP_NO_ACTION;
13722     }
13723     else if (IS_EMC_GATE_GRAY(element))
13724     {
13725       if (!player->key[EMC_GATE_GRAY_NR(element)])
13726         return MP_NO_ACTION;
13727     }
13728     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13729     {
13730       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13731         return MP_NO_ACTION;
13732     }
13733     else if (element == EL_DC_GATE_WHITE ||
13734              element == EL_DC_GATE_WHITE_GRAY ||
13735              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13736     {
13737       if (player->num_white_keys == 0)
13738         return MP_NO_ACTION;
13739
13740       player->num_white_keys--;
13741     }
13742     else if (IS_SP_PORT(element))
13743     {
13744       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13745           element == EL_SP_GRAVITY_PORT_RIGHT ||
13746           element == EL_SP_GRAVITY_PORT_UP ||
13747           element == EL_SP_GRAVITY_PORT_DOWN)
13748         player->gravity = !player->gravity;
13749       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13750                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13751                element == EL_SP_GRAVITY_ON_PORT_UP ||
13752                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13753         player->gravity = TRUE;
13754       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13755                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13756                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13757                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13758         player->gravity = FALSE;
13759     }
13760
13761     // automatically move to the next field with double speed
13762     player->programmed_action = move_direction;
13763
13764     if (player->move_delay_reset_counter == 0)
13765     {
13766       player->move_delay_reset_counter = 2;     // two double speed steps
13767
13768       DOUBLE_PLAYER_SPEED(player);
13769     }
13770
13771     PlayLevelSoundAction(x, y, ACTION_PASSING);
13772   }
13773   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13774   {
13775     RemoveField(x, y);
13776
13777     if (mode != DF_SNAP)
13778     {
13779       GfxElement[x][y] = GFX_ELEMENT(element);
13780       player->is_digging = TRUE;
13781     }
13782
13783     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13784
13785     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13786                                         player->index_bit, dig_side);
13787
13788     if (mode == DF_SNAP)
13789     {
13790       if (level.block_snap_field)
13791         setFieldForSnapping(x, y, element, move_direction);
13792       else
13793         TestIfElementTouchesCustomElement(x, y);        // for empty space
13794
13795       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13796                                           player->index_bit, dig_side);
13797     }
13798   }
13799   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13800   {
13801     RemoveField(x, y);
13802
13803     if (is_player && mode != DF_SNAP)
13804     {
13805       GfxElement[x][y] = element;
13806       player->is_collecting = TRUE;
13807     }
13808
13809     if (element == EL_SPEED_PILL)
13810     {
13811       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13812     }
13813     else if (element == EL_EXTRA_TIME && level.time > 0)
13814     {
13815       TimeLeft += level.extra_time;
13816
13817       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13818
13819       DisplayGameControlValues();
13820     }
13821     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13822     {
13823       player->shield_normal_time_left += level.shield_normal_time;
13824       if (element == EL_SHIELD_DEADLY)
13825         player->shield_deadly_time_left += level.shield_deadly_time;
13826     }
13827     else if (element == EL_DYNAMITE ||
13828              element == EL_EM_DYNAMITE ||
13829              element == EL_SP_DISK_RED)
13830     {
13831       if (player->inventory_size < MAX_INVENTORY_SIZE)
13832         player->inventory_element[player->inventory_size++] = element;
13833
13834       DrawGameDoorValues();
13835     }
13836     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13837     {
13838       player->dynabomb_count++;
13839       player->dynabombs_left++;
13840     }
13841     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13842     {
13843       player->dynabomb_size++;
13844     }
13845     else if (element == EL_DYNABOMB_INCREASE_POWER)
13846     {
13847       player->dynabomb_xl = TRUE;
13848     }
13849     else if (IS_KEY(element))
13850     {
13851       player->key[KEY_NR(element)] = TRUE;
13852
13853       DrawGameDoorValues();
13854     }
13855     else if (element == EL_DC_KEY_WHITE)
13856     {
13857       player->num_white_keys++;
13858
13859       // display white keys?
13860       // DrawGameDoorValues();
13861     }
13862     else if (IS_ENVELOPE(element))
13863     {
13864       player->show_envelope = element;
13865     }
13866     else if (element == EL_EMC_LENSES)
13867     {
13868       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13869
13870       RedrawAllInvisibleElementsForLenses();
13871     }
13872     else if (element == EL_EMC_MAGNIFIER)
13873     {
13874       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13875
13876       RedrawAllInvisibleElementsForMagnifier();
13877     }
13878     else if (IS_DROPPABLE(element) ||
13879              IS_THROWABLE(element))     // can be collected and dropped
13880     {
13881       int i;
13882
13883       if (collect_count == 0)
13884         player->inventory_infinite_element = element;
13885       else
13886         for (i = 0; i < collect_count; i++)
13887           if (player->inventory_size < MAX_INVENTORY_SIZE)
13888             player->inventory_element[player->inventory_size++] = element;
13889
13890       DrawGameDoorValues();
13891     }
13892     else if (collect_count > 0)
13893     {
13894       game.gems_still_needed -= collect_count;
13895       if (game.gems_still_needed < 0)
13896         game.gems_still_needed = 0;
13897
13898       game.snapshot.collected_item = TRUE;
13899
13900       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13901
13902       DisplayGameControlValues();
13903     }
13904
13905     RaiseScoreElement(element);
13906     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13907
13908     if (is_player)
13909       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13910                                           player->index_bit, dig_side);
13911
13912     if (mode == DF_SNAP)
13913     {
13914       if (level.block_snap_field)
13915         setFieldForSnapping(x, y, element, move_direction);
13916       else
13917         TestIfElementTouchesCustomElement(x, y);        // for empty space
13918
13919       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13920                                           player->index_bit, dig_side);
13921     }
13922   }
13923   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13924   {
13925     if (mode == DF_SNAP && element != EL_BD_ROCK)
13926       return MP_NO_ACTION;
13927
13928     if (CAN_FALL(element) && dy)
13929       return MP_NO_ACTION;
13930
13931     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13932         !(element == EL_SPRING && level.use_spring_bug))
13933       return MP_NO_ACTION;
13934
13935     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13936         ((move_direction & MV_VERTICAL &&
13937           ((element_info[element].move_pattern & MV_LEFT &&
13938             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13939            (element_info[element].move_pattern & MV_RIGHT &&
13940             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13941          (move_direction & MV_HORIZONTAL &&
13942           ((element_info[element].move_pattern & MV_UP &&
13943             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13944            (element_info[element].move_pattern & MV_DOWN &&
13945             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13946       return MP_NO_ACTION;
13947
13948     // do not push elements already moving away faster than player
13949     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13950         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13951       return MP_NO_ACTION;
13952
13953     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13954     {
13955       if (player->push_delay_value == -1 || !player_was_pushing)
13956         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13957     }
13958     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13959     {
13960       if (player->push_delay_value == -1)
13961         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13962     }
13963     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13964     {
13965       if (!player->is_pushing)
13966         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13967     }
13968
13969     player->is_pushing = TRUE;
13970     player->is_active = TRUE;
13971
13972     if (!(IN_LEV_FIELD(nextx, nexty) &&
13973           (IS_FREE(nextx, nexty) ||
13974            (IS_SB_ELEMENT(element) &&
13975             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13976            (IS_CUSTOM_ELEMENT(element) &&
13977             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13978       return MP_NO_ACTION;
13979
13980     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13981       return MP_NO_ACTION;
13982
13983     if (player->push_delay == -1)       // new pushing; restart delay
13984       player->push_delay = 0;
13985
13986     if (player->push_delay < player->push_delay_value &&
13987         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13988         element != EL_SPRING && element != EL_BALLOON)
13989     {
13990       // make sure that there is no move delay before next try to push
13991       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13992         player->move_delay = 0;
13993
13994       return MP_NO_ACTION;
13995     }
13996
13997     if (IS_CUSTOM_ELEMENT(element) &&
13998         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13999     {
14000       if (!DigFieldByCE(nextx, nexty, element))
14001         return MP_NO_ACTION;
14002     }
14003
14004     if (IS_SB_ELEMENT(element))
14005     {
14006       boolean sokoban_task_solved = FALSE;
14007
14008       if (element == EL_SOKOBAN_FIELD_FULL)
14009       {
14010         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14011
14012         IncrementSokobanFieldsNeeded();
14013         IncrementSokobanObjectsNeeded();
14014       }
14015
14016       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14017       {
14018         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14019
14020         DecrementSokobanFieldsNeeded();
14021         DecrementSokobanObjectsNeeded();
14022
14023         // sokoban object was pushed from empty field to sokoban field
14024         if (Back[x][y] == EL_EMPTY)
14025           sokoban_task_solved = TRUE;
14026       }
14027
14028       Feld[x][y] = EL_SOKOBAN_OBJECT;
14029
14030       if (Back[x][y] == Back[nextx][nexty])
14031         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14032       else if (Back[x][y] != 0)
14033         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14034                                     ACTION_EMPTYING);
14035       else
14036         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14037                                     ACTION_FILLING);
14038
14039       if (sokoban_task_solved &&
14040           game.sokoban_fields_still_needed == 0 &&
14041           game.sokoban_objects_still_needed == 0 &&
14042           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14043       {
14044         game.players_still_needed = 0;
14045
14046         LevelSolved();
14047
14048         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14049       }
14050     }
14051     else
14052       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14053
14054     InitMovingField(x, y, move_direction);
14055     GfxAction[x][y] = ACTION_PUSHING;
14056
14057     if (mode == DF_SNAP)
14058       ContinueMoving(x, y);
14059     else
14060       MovPos[x][y] = (dx != 0 ? dx : dy);
14061
14062     Pushed[x][y] = TRUE;
14063     Pushed[nextx][nexty] = TRUE;
14064
14065     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14066       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14067     else
14068       player->push_delay_value = -1;    // get new value later
14069
14070     // check for element change _after_ element has been pushed
14071     if (game.use_change_when_pushing_bug)
14072     {
14073       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14074                                  player->index_bit, dig_side);
14075       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14076                                           player->index_bit, dig_side);
14077     }
14078   }
14079   else if (IS_SWITCHABLE(element))
14080   {
14081     if (PLAYER_SWITCHING(player, x, y))
14082     {
14083       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14084                                           player->index_bit, dig_side);
14085
14086       return MP_ACTION;
14087     }
14088
14089     player->is_switching = TRUE;
14090     player->switch_x = x;
14091     player->switch_y = y;
14092
14093     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14094
14095     if (element == EL_ROBOT_WHEEL)
14096     {
14097       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14098
14099       game.robot_wheel_x = x;
14100       game.robot_wheel_y = y;
14101       game.robot_wheel_active = TRUE;
14102
14103       TEST_DrawLevelField(x, y);
14104     }
14105     else if (element == EL_SP_TERMINAL)
14106     {
14107       int xx, yy;
14108
14109       SCAN_PLAYFIELD(xx, yy)
14110       {
14111         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14112         {
14113           Bang(xx, yy);
14114         }
14115         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14116         {
14117           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14118
14119           ResetGfxAnimation(xx, yy);
14120           TEST_DrawLevelField(xx, yy);
14121         }
14122       }
14123     }
14124     else if (IS_BELT_SWITCH(element))
14125     {
14126       ToggleBeltSwitch(x, y);
14127     }
14128     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14129              element == EL_SWITCHGATE_SWITCH_DOWN ||
14130              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14131              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14132     {
14133       ToggleSwitchgateSwitch(x, y);
14134     }
14135     else if (element == EL_LIGHT_SWITCH ||
14136              element == EL_LIGHT_SWITCH_ACTIVE)
14137     {
14138       ToggleLightSwitch(x, y);
14139     }
14140     else if (element == EL_TIMEGATE_SWITCH ||
14141              element == EL_DC_TIMEGATE_SWITCH)
14142     {
14143       ActivateTimegateSwitch(x, y);
14144     }
14145     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14146              element == EL_BALLOON_SWITCH_RIGHT ||
14147              element == EL_BALLOON_SWITCH_UP    ||
14148              element == EL_BALLOON_SWITCH_DOWN  ||
14149              element == EL_BALLOON_SWITCH_NONE  ||
14150              element == EL_BALLOON_SWITCH_ANY)
14151     {
14152       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14153                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14154                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14155                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14156                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14157                              move_direction);
14158     }
14159     else if (element == EL_LAMP)
14160     {
14161       Feld[x][y] = EL_LAMP_ACTIVE;
14162       game.lights_still_needed--;
14163
14164       ResetGfxAnimation(x, y);
14165       TEST_DrawLevelField(x, y);
14166     }
14167     else if (element == EL_TIME_ORB_FULL)
14168     {
14169       Feld[x][y] = EL_TIME_ORB_EMPTY;
14170
14171       if (level.time > 0 || level.use_time_orb_bug)
14172       {
14173         TimeLeft += level.time_orb_time;
14174         game.no_time_limit = FALSE;
14175
14176         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14177
14178         DisplayGameControlValues();
14179       }
14180
14181       ResetGfxAnimation(x, y);
14182       TEST_DrawLevelField(x, y);
14183     }
14184     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14185              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14186     {
14187       int xx, yy;
14188
14189       game.ball_state = !game.ball_state;
14190
14191       SCAN_PLAYFIELD(xx, yy)
14192       {
14193         int e = Feld[xx][yy];
14194
14195         if (game.ball_state)
14196         {
14197           if (e == EL_EMC_MAGIC_BALL)
14198             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14199           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14200             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14201         }
14202         else
14203         {
14204           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14205             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14206           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14207             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14208         }
14209       }
14210     }
14211
14212     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14213                                         player->index_bit, dig_side);
14214
14215     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14216                                         player->index_bit, dig_side);
14217
14218     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14219                                         player->index_bit, dig_side);
14220
14221     return MP_ACTION;
14222   }
14223   else
14224   {
14225     if (!PLAYER_SWITCHING(player, x, y))
14226     {
14227       player->is_switching = TRUE;
14228       player->switch_x = x;
14229       player->switch_y = y;
14230
14231       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14232                                  player->index_bit, dig_side);
14233       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14234                                           player->index_bit, dig_side);
14235
14236       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14237                                  player->index_bit, dig_side);
14238       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14239                                           player->index_bit, dig_side);
14240     }
14241
14242     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14243                                player->index_bit, dig_side);
14244     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14245                                         player->index_bit, dig_side);
14246
14247     return MP_NO_ACTION;
14248   }
14249
14250   player->push_delay = -1;
14251
14252   if (is_player)                // function can also be called by EL_PENGUIN
14253   {
14254     if (Feld[x][y] != element)          // really digged/collected something
14255     {
14256       player->is_collecting = !player->is_digging;
14257       player->is_active = TRUE;
14258     }
14259   }
14260
14261   return MP_MOVING;
14262 }
14263
14264 static boolean DigFieldByCE(int x, int y, int digging_element)
14265 {
14266   int element = Feld[x][y];
14267
14268   if (!IS_FREE(x, y))
14269   {
14270     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14271                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14272                   ACTION_BREAKING);
14273
14274     // no element can dig solid indestructible elements
14275     if (IS_INDESTRUCTIBLE(element) &&
14276         !IS_DIGGABLE(element) &&
14277         !IS_COLLECTIBLE(element))
14278       return FALSE;
14279
14280     if (AmoebaNr[x][y] &&
14281         (element == EL_AMOEBA_FULL ||
14282          element == EL_BD_AMOEBA ||
14283          element == EL_AMOEBA_GROWING))
14284     {
14285       AmoebaCnt[AmoebaNr[x][y]]--;
14286       AmoebaCnt2[AmoebaNr[x][y]]--;
14287     }
14288
14289     if (IS_MOVING(x, y))
14290       RemoveMovingField(x, y);
14291     else
14292     {
14293       RemoveField(x, y);
14294       TEST_DrawLevelField(x, y);
14295     }
14296
14297     // if digged element was about to explode, prevent the explosion
14298     ExplodeField[x][y] = EX_TYPE_NONE;
14299
14300     PlayLevelSoundAction(x, y, action);
14301   }
14302
14303   Store[x][y] = EL_EMPTY;
14304
14305   // this makes it possible to leave the removed element again
14306   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14307     Store[x][y] = element;
14308
14309   return TRUE;
14310 }
14311
14312 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14313 {
14314   int jx = player->jx, jy = player->jy;
14315   int x = jx + dx, y = jy + dy;
14316   int snap_direction = (dx == -1 ? MV_LEFT  :
14317                         dx == +1 ? MV_RIGHT :
14318                         dy == -1 ? MV_UP    :
14319                         dy == +1 ? MV_DOWN  : MV_NONE);
14320   boolean can_continue_snapping = (level.continuous_snapping &&
14321                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14322
14323   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14324     return FALSE;
14325
14326   if (!player->active || !IN_LEV_FIELD(x, y))
14327     return FALSE;
14328
14329   if (dx && dy)
14330     return FALSE;
14331
14332   if (!dx && !dy)
14333   {
14334     if (player->MovPos == 0)
14335       player->is_pushing = FALSE;
14336
14337     player->is_snapping = FALSE;
14338
14339     if (player->MovPos == 0)
14340     {
14341       player->is_moving = FALSE;
14342       player->is_digging = FALSE;
14343       player->is_collecting = FALSE;
14344     }
14345
14346     return FALSE;
14347   }
14348
14349   // prevent snapping with already pressed snap key when not allowed
14350   if (player->is_snapping && !can_continue_snapping)
14351     return FALSE;
14352
14353   player->MovDir = snap_direction;
14354
14355   if (player->MovPos == 0)
14356   {
14357     player->is_moving = FALSE;
14358     player->is_digging = FALSE;
14359     player->is_collecting = FALSE;
14360   }
14361
14362   player->is_dropping = FALSE;
14363   player->is_dropping_pressed = FALSE;
14364   player->drop_pressed_delay = 0;
14365
14366   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14367     return FALSE;
14368
14369   player->is_snapping = TRUE;
14370   player->is_active = TRUE;
14371
14372   if (player->MovPos == 0)
14373   {
14374     player->is_moving = FALSE;
14375     player->is_digging = FALSE;
14376     player->is_collecting = FALSE;
14377   }
14378
14379   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14380     TEST_DrawLevelField(player->last_jx, player->last_jy);
14381
14382   TEST_DrawLevelField(x, y);
14383
14384   return TRUE;
14385 }
14386
14387 static boolean DropElement(struct PlayerInfo *player)
14388 {
14389   int old_element, new_element;
14390   int dropx = player->jx, dropy = player->jy;
14391   int drop_direction = player->MovDir;
14392   int drop_side = drop_direction;
14393   int drop_element = get_next_dropped_element(player);
14394
14395   /* do not drop an element on top of another element; when holding drop key
14396      pressed without moving, dropped element must move away before the next
14397      element can be dropped (this is especially important if the next element
14398      is dynamite, which can be placed on background for historical reasons) */
14399   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14400     return MP_ACTION;
14401
14402   if (IS_THROWABLE(drop_element))
14403   {
14404     dropx += GET_DX_FROM_DIR(drop_direction);
14405     dropy += GET_DY_FROM_DIR(drop_direction);
14406
14407     if (!IN_LEV_FIELD(dropx, dropy))
14408       return FALSE;
14409   }
14410
14411   old_element = Feld[dropx][dropy];     // old element at dropping position
14412   new_element = drop_element;           // default: no change when dropping
14413
14414   // check if player is active, not moving and ready to drop
14415   if (!player->active || player->MovPos || player->drop_delay > 0)
14416     return FALSE;
14417
14418   // check if player has anything that can be dropped
14419   if (new_element == EL_UNDEFINED)
14420     return FALSE;
14421
14422   // only set if player has anything that can be dropped
14423   player->is_dropping_pressed = TRUE;
14424
14425   // check if drop key was pressed long enough for EM style dynamite
14426   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14427     return FALSE;
14428
14429   // check if anything can be dropped at the current position
14430   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14431     return FALSE;
14432
14433   // collected custom elements can only be dropped on empty fields
14434   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14435     return FALSE;
14436
14437   if (old_element != EL_EMPTY)
14438     Back[dropx][dropy] = old_element;   // store old element on this field
14439
14440   ResetGfxAnimation(dropx, dropy);
14441   ResetRandomAnimationValue(dropx, dropy);
14442
14443   if (player->inventory_size > 0 ||
14444       player->inventory_infinite_element != EL_UNDEFINED)
14445   {
14446     if (player->inventory_size > 0)
14447     {
14448       player->inventory_size--;
14449
14450       DrawGameDoorValues();
14451
14452       if (new_element == EL_DYNAMITE)
14453         new_element = EL_DYNAMITE_ACTIVE;
14454       else if (new_element == EL_EM_DYNAMITE)
14455         new_element = EL_EM_DYNAMITE_ACTIVE;
14456       else if (new_element == EL_SP_DISK_RED)
14457         new_element = EL_SP_DISK_RED_ACTIVE;
14458     }
14459
14460     Feld[dropx][dropy] = new_element;
14461
14462     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14463       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14464                           el2img(Feld[dropx][dropy]), 0);
14465
14466     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14467
14468     // needed if previous element just changed to "empty" in the last frame
14469     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14470
14471     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14472                                player->index_bit, drop_side);
14473     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14474                                         CE_PLAYER_DROPS_X,
14475                                         player->index_bit, drop_side);
14476
14477     TestIfElementTouchesCustomElement(dropx, dropy);
14478   }
14479   else          // player is dropping a dyna bomb
14480   {
14481     player->dynabombs_left--;
14482
14483     Feld[dropx][dropy] = new_element;
14484
14485     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14486       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14487                           el2img(Feld[dropx][dropy]), 0);
14488
14489     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14490   }
14491
14492   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14493     InitField_WithBug1(dropx, dropy, FALSE);
14494
14495   new_element = Feld[dropx][dropy];     // element might have changed
14496
14497   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14498       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14499   {
14500     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14501       MovDir[dropx][dropy] = drop_direction;
14502
14503     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14504
14505     // do not cause impact style collision by dropping elements that can fall
14506     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14507   }
14508
14509   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14510   player->is_dropping = TRUE;
14511
14512   player->drop_pressed_delay = 0;
14513   player->is_dropping_pressed = FALSE;
14514
14515   player->drop_x = dropx;
14516   player->drop_y = dropy;
14517
14518   return TRUE;
14519 }
14520
14521 // ----------------------------------------------------------------------------
14522 // game sound playing functions
14523 // ----------------------------------------------------------------------------
14524
14525 static int *loop_sound_frame = NULL;
14526 static int *loop_sound_volume = NULL;
14527
14528 void InitPlayLevelSound(void)
14529 {
14530   int num_sounds = getSoundListSize();
14531
14532   checked_free(loop_sound_frame);
14533   checked_free(loop_sound_volume);
14534
14535   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14536   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14537 }
14538
14539 static void PlayLevelSound(int x, int y, int nr)
14540 {
14541   int sx = SCREENX(x), sy = SCREENY(y);
14542   int volume, stereo_position;
14543   int max_distance = 8;
14544   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14545
14546   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14547       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14548     return;
14549
14550   if (!IN_LEV_FIELD(x, y) ||
14551       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14552       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14553     return;
14554
14555   volume = SOUND_MAX_VOLUME;
14556
14557   if (!IN_SCR_FIELD(sx, sy))
14558   {
14559     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14560     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14561
14562     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14563   }
14564
14565   stereo_position = (SOUND_MAX_LEFT +
14566                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14567                      (SCR_FIELDX + 2 * max_distance));
14568
14569   if (IS_LOOP_SOUND(nr))
14570   {
14571     /* This assures that quieter loop sounds do not overwrite louder ones,
14572        while restarting sound volume comparison with each new game frame. */
14573
14574     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14575       return;
14576
14577     loop_sound_volume[nr] = volume;
14578     loop_sound_frame[nr] = FrameCounter;
14579   }
14580
14581   PlaySoundExt(nr, volume, stereo_position, type);
14582 }
14583
14584 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14585 {
14586   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14587                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14588                  y < LEVELY(BY1) ? LEVELY(BY1) :
14589                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14590                  sound_action);
14591 }
14592
14593 static void PlayLevelSoundAction(int x, int y, int action)
14594 {
14595   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14596 }
14597
14598 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14599 {
14600   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14601
14602   if (sound_effect != SND_UNDEFINED)
14603     PlayLevelSound(x, y, sound_effect);
14604 }
14605
14606 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14607                                               int action)
14608 {
14609   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14610
14611   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14612     PlayLevelSound(x, y, sound_effect);
14613 }
14614
14615 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14616 {
14617   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14618
14619   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14620     PlayLevelSound(x, y, sound_effect);
14621 }
14622
14623 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14624 {
14625   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14626
14627   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14628     StopSound(sound_effect);
14629 }
14630
14631 static int getLevelMusicNr(void)
14632 {
14633   if (levelset.music[level_nr] != MUS_UNDEFINED)
14634     return levelset.music[level_nr];            // from config file
14635   else
14636     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14637 }
14638
14639 static void FadeLevelSounds(void)
14640 {
14641   FadeSounds();
14642 }
14643
14644 static void FadeLevelMusic(void)
14645 {
14646   int music_nr = getLevelMusicNr();
14647   char *curr_music = getCurrentlyPlayingMusicFilename();
14648   char *next_music = getMusicInfoEntryFilename(music_nr);
14649
14650   if (!strEqual(curr_music, next_music))
14651     FadeMusic();
14652 }
14653
14654 void FadeLevelSoundsAndMusic(void)
14655 {
14656   FadeLevelSounds();
14657   FadeLevelMusic();
14658 }
14659
14660 static void PlayLevelMusic(void)
14661 {
14662   int music_nr = getLevelMusicNr();
14663   char *curr_music = getCurrentlyPlayingMusicFilename();
14664   char *next_music = getMusicInfoEntryFilename(music_nr);
14665
14666   if (!strEqual(curr_music, next_music))
14667     PlayMusicLoop(music_nr);
14668 }
14669
14670 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14671 {
14672   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14673   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14674   int x = xx - 1 - offset;
14675   int y = yy - 1 - offset;
14676
14677   switch (sample)
14678   {
14679     case SAMPLE_blank:
14680       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14681       break;
14682
14683     case SAMPLE_roll:
14684       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14685       break;
14686
14687     case SAMPLE_stone:
14688       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14689       break;
14690
14691     case SAMPLE_nut:
14692       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14693       break;
14694
14695     case SAMPLE_crack:
14696       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14697       break;
14698
14699     case SAMPLE_bug:
14700       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14701       break;
14702
14703     case SAMPLE_tank:
14704       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14705       break;
14706
14707     case SAMPLE_android_clone:
14708       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14709       break;
14710
14711     case SAMPLE_android_move:
14712       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14713       break;
14714
14715     case SAMPLE_spring:
14716       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14717       break;
14718
14719     case SAMPLE_slurp:
14720       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14721       break;
14722
14723     case SAMPLE_eater:
14724       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14725       break;
14726
14727     case SAMPLE_eater_eat:
14728       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14729       break;
14730
14731     case SAMPLE_alien:
14732       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14733       break;
14734
14735     case SAMPLE_collect:
14736       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14737       break;
14738
14739     case SAMPLE_diamond:
14740       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14741       break;
14742
14743     case SAMPLE_squash:
14744       // !!! CHECK THIS !!!
14745 #if 1
14746       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14747 #else
14748       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14749 #endif
14750       break;
14751
14752     case SAMPLE_wonderfall:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14754       break;
14755
14756     case SAMPLE_drip:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14758       break;
14759
14760     case SAMPLE_push:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14762       break;
14763
14764     case SAMPLE_dirt:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14766       break;
14767
14768     case SAMPLE_acid:
14769       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14770       break;
14771
14772     case SAMPLE_ball:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14774       break;
14775
14776     case SAMPLE_grow:
14777       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14778       break;
14779
14780     case SAMPLE_wonder:
14781       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14782       break;
14783
14784     case SAMPLE_door:
14785       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14786       break;
14787
14788     case SAMPLE_exit_open:
14789       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14790       break;
14791
14792     case SAMPLE_exit_leave:
14793       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14794       break;
14795
14796     case SAMPLE_dynamite:
14797       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14798       break;
14799
14800     case SAMPLE_tick:
14801       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14802       break;
14803
14804     case SAMPLE_press:
14805       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14806       break;
14807
14808     case SAMPLE_wheel:
14809       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14810       break;
14811
14812     case SAMPLE_boom:
14813       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14814       break;
14815
14816     case SAMPLE_die:
14817       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14818       break;
14819
14820     case SAMPLE_time:
14821       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14822       break;
14823
14824     default:
14825       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14826       break;
14827   }
14828 }
14829
14830 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14831 {
14832   int element = map_element_SP_to_RND(element_sp);
14833   int action = map_action_SP_to_RND(action_sp);
14834   int offset = (setup.sp_show_border_elements ? 0 : 1);
14835   int x = xx - offset;
14836   int y = yy - offset;
14837
14838   PlayLevelSoundElementAction(x, y, element, action);
14839 }
14840
14841 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14842 {
14843   int element = map_element_MM_to_RND(element_mm);
14844   int action = map_action_MM_to_RND(action_mm);
14845   int offset = 0;
14846   int x = xx - offset;
14847   int y = yy - offset;
14848
14849   if (!IS_MM_ELEMENT(element))
14850     element = EL_MM_DEFAULT;
14851
14852   PlayLevelSoundElementAction(x, y, element, action);
14853 }
14854
14855 void PlaySound_MM(int sound_mm)
14856 {
14857   int sound = map_sound_MM_to_RND(sound_mm);
14858
14859   if (sound == SND_UNDEFINED)
14860     return;
14861
14862   PlaySound(sound);
14863 }
14864
14865 void PlaySoundLoop_MM(int sound_mm)
14866 {
14867   int sound = map_sound_MM_to_RND(sound_mm);
14868
14869   if (sound == SND_UNDEFINED)
14870     return;
14871
14872   PlaySoundLoop(sound);
14873 }
14874
14875 void StopSound_MM(int sound_mm)
14876 {
14877   int sound = map_sound_MM_to_RND(sound_mm);
14878
14879   if (sound == SND_UNDEFINED)
14880     return;
14881
14882   StopSound(sound);
14883 }
14884
14885 void RaiseScore(int value)
14886 {
14887   game.score += value;
14888
14889   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14890
14891   DisplayGameControlValues();
14892 }
14893
14894 void RaiseScoreElement(int element)
14895 {
14896   switch (element)
14897   {
14898     case EL_EMERALD:
14899     case EL_BD_DIAMOND:
14900     case EL_EMERALD_YELLOW:
14901     case EL_EMERALD_RED:
14902     case EL_EMERALD_PURPLE:
14903     case EL_SP_INFOTRON:
14904       RaiseScore(level.score[SC_EMERALD]);
14905       break;
14906     case EL_DIAMOND:
14907       RaiseScore(level.score[SC_DIAMOND]);
14908       break;
14909     case EL_CRYSTAL:
14910       RaiseScore(level.score[SC_CRYSTAL]);
14911       break;
14912     case EL_PEARL:
14913       RaiseScore(level.score[SC_PEARL]);
14914       break;
14915     case EL_BUG:
14916     case EL_BD_BUTTERFLY:
14917     case EL_SP_ELECTRON:
14918       RaiseScore(level.score[SC_BUG]);
14919       break;
14920     case EL_SPACESHIP:
14921     case EL_BD_FIREFLY:
14922     case EL_SP_SNIKSNAK:
14923       RaiseScore(level.score[SC_SPACESHIP]);
14924       break;
14925     case EL_YAMYAM:
14926     case EL_DARK_YAMYAM:
14927       RaiseScore(level.score[SC_YAMYAM]);
14928       break;
14929     case EL_ROBOT:
14930       RaiseScore(level.score[SC_ROBOT]);
14931       break;
14932     case EL_PACMAN:
14933       RaiseScore(level.score[SC_PACMAN]);
14934       break;
14935     case EL_NUT:
14936       RaiseScore(level.score[SC_NUT]);
14937       break;
14938     case EL_DYNAMITE:
14939     case EL_EM_DYNAMITE:
14940     case EL_SP_DISK_RED:
14941     case EL_DYNABOMB_INCREASE_NUMBER:
14942     case EL_DYNABOMB_INCREASE_SIZE:
14943     case EL_DYNABOMB_INCREASE_POWER:
14944       RaiseScore(level.score[SC_DYNAMITE]);
14945       break;
14946     case EL_SHIELD_NORMAL:
14947     case EL_SHIELD_DEADLY:
14948       RaiseScore(level.score[SC_SHIELD]);
14949       break;
14950     case EL_EXTRA_TIME:
14951       RaiseScore(level.extra_time_score);
14952       break;
14953     case EL_KEY_1:
14954     case EL_KEY_2:
14955     case EL_KEY_3:
14956     case EL_KEY_4:
14957     case EL_EM_KEY_1:
14958     case EL_EM_KEY_2:
14959     case EL_EM_KEY_3:
14960     case EL_EM_KEY_4:
14961     case EL_EMC_KEY_5:
14962     case EL_EMC_KEY_6:
14963     case EL_EMC_KEY_7:
14964     case EL_EMC_KEY_8:
14965     case EL_DC_KEY_WHITE:
14966       RaiseScore(level.score[SC_KEY]);
14967       break;
14968     default:
14969       RaiseScore(element_info[element].collect_score);
14970       break;
14971   }
14972 }
14973
14974 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14975 {
14976   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14977   {
14978     // closing door required in case of envelope style request dialogs
14979     if (!skip_request)
14980     {
14981       // prevent short reactivation of overlay buttons while closing door
14982       SetOverlayActive(FALSE);
14983
14984       CloseDoor(DOOR_CLOSE_1);
14985     }
14986
14987     if (network.enabled)
14988       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14989     else
14990     {
14991       if (quick_quit)
14992         FadeSkipNextFadeIn();
14993
14994       SetGameStatus(GAME_MODE_MAIN);
14995
14996       DrawMainMenu();
14997     }
14998   }
14999   else          // continue playing the game
15000   {
15001     if (tape.playing && tape.deactivate_display)
15002       TapeDeactivateDisplayOff(TRUE);
15003
15004     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15005
15006     if (tape.playing && tape.deactivate_display)
15007       TapeDeactivateDisplayOn();
15008   }
15009 }
15010
15011 void RequestQuitGame(boolean ask_if_really_quit)
15012 {
15013   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15014   boolean skip_request = game.all_players_gone || quick_quit;
15015
15016   RequestQuitGameExt(skip_request, quick_quit,
15017                      "Do you really want to quit the game?");
15018 }
15019
15020 void RequestRestartGame(char *message)
15021 {
15022   game.restart_game_message = NULL;
15023
15024   boolean has_started_game = hasStartedNetworkGame();
15025   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15026
15027   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15028   {
15029     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15030   }
15031   else
15032   {
15033     SetGameStatus(GAME_MODE_MAIN);
15034
15035     DrawMainMenu();
15036   }
15037 }
15038
15039 void CheckGameOver(void)
15040 {
15041   static boolean last_game_over = FALSE;
15042   static int game_over_delay = 0;
15043   int game_over_delay_value = 50;
15044   boolean game_over = checkGameFailed();
15045
15046   // do not handle game over if request dialog is already active
15047   if (game.request_active)
15048     return;
15049
15050   // do not ask to play again if game was never actually played
15051   if (!game.GamePlayed)
15052     return;
15053
15054   if (!game_over)
15055   {
15056     last_game_over = FALSE;
15057     game_over_delay = game_over_delay_value;
15058
15059     return;
15060   }
15061
15062   if (game_over_delay > 0)
15063   {
15064     game_over_delay--;
15065
15066     return;
15067   }
15068
15069   if (last_game_over != game_over)
15070     game.restart_game_message = (hasStartedNetworkGame() ?
15071                                  "Game over! Play it again?" :
15072                                  "Game over!");
15073
15074   last_game_over = game_over;
15075 }
15076
15077 boolean checkGameSolved(void)
15078 {
15079   // set for all game engines if level was solved
15080   return game.LevelSolved_GameEnd;
15081 }
15082
15083 boolean checkGameFailed(void)
15084 {
15085   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15086     return (game_em.game_over && !game_em.level_solved);
15087   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15088     return (game_sp.game_over && !game_sp.level_solved);
15089   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15090     return (game_mm.game_over && !game_mm.level_solved);
15091   else                          // GAME_ENGINE_TYPE_RND
15092     return (game.GameOver && !game.LevelSolved);
15093 }
15094
15095 boolean checkGameEnded(void)
15096 {
15097   return (checkGameSolved() || checkGameFailed());
15098 }
15099
15100
15101 // ----------------------------------------------------------------------------
15102 // random generator functions
15103 // ----------------------------------------------------------------------------
15104
15105 unsigned int InitEngineRandom_RND(int seed)
15106 {
15107   game.num_random_calls = 0;
15108
15109   return InitEngineRandom(seed);
15110 }
15111
15112 unsigned int RND(int max)
15113 {
15114   if (max > 0)
15115   {
15116     game.num_random_calls++;
15117
15118     return GetEngineRandom(max);
15119   }
15120
15121   return 0;
15122 }
15123
15124
15125 // ----------------------------------------------------------------------------
15126 // game engine snapshot handling functions
15127 // ----------------------------------------------------------------------------
15128
15129 struct EngineSnapshotInfo
15130 {
15131   // runtime values for custom element collect score
15132   int collect_score[NUM_CUSTOM_ELEMENTS];
15133
15134   // runtime values for group element choice position
15135   int choice_pos[NUM_GROUP_ELEMENTS];
15136
15137   // runtime values for belt position animations
15138   int belt_graphic[4][NUM_BELT_PARTS];
15139   int belt_anim_mode[4][NUM_BELT_PARTS];
15140 };
15141
15142 static struct EngineSnapshotInfo engine_snapshot_rnd;
15143 static char *snapshot_level_identifier = NULL;
15144 static int snapshot_level_nr = -1;
15145
15146 static void SaveEngineSnapshotValues_RND(void)
15147 {
15148   static int belt_base_active_element[4] =
15149   {
15150     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15151     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15152     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15153     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15154   };
15155   int i, j;
15156
15157   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15158   {
15159     int element = EL_CUSTOM_START + i;
15160
15161     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15162   }
15163
15164   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15165   {
15166     int element = EL_GROUP_START + i;
15167
15168     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15169   }
15170
15171   for (i = 0; i < 4; i++)
15172   {
15173     for (j = 0; j < NUM_BELT_PARTS; j++)
15174     {
15175       int element = belt_base_active_element[i] + j;
15176       int graphic = el2img(element);
15177       int anim_mode = graphic_info[graphic].anim_mode;
15178
15179       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15180       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15181     }
15182   }
15183 }
15184
15185 static void LoadEngineSnapshotValues_RND(void)
15186 {
15187   unsigned int num_random_calls = game.num_random_calls;
15188   int i, j;
15189
15190   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15191   {
15192     int element = EL_CUSTOM_START + i;
15193
15194     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15195   }
15196
15197   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15198   {
15199     int element = EL_GROUP_START + i;
15200
15201     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15202   }
15203
15204   for (i = 0; i < 4; i++)
15205   {
15206     for (j = 0; j < NUM_BELT_PARTS; j++)
15207     {
15208       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15209       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15210
15211       graphic_info[graphic].anim_mode = anim_mode;
15212     }
15213   }
15214
15215   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15216   {
15217     InitRND(tape.random_seed);
15218     for (i = 0; i < num_random_calls; i++)
15219       RND(1);
15220   }
15221
15222   if (game.num_random_calls != num_random_calls)
15223   {
15224     Error(ERR_INFO, "number of random calls out of sync");
15225     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15226     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15227     Error(ERR_EXIT, "this should not happen -- please debug");
15228   }
15229 }
15230
15231 void FreeEngineSnapshotSingle(void)
15232 {
15233   FreeSnapshotSingle();
15234
15235   setString(&snapshot_level_identifier, NULL);
15236   snapshot_level_nr = -1;
15237 }
15238
15239 void FreeEngineSnapshotList(void)
15240 {
15241   FreeSnapshotList();
15242 }
15243
15244 static ListNode *SaveEngineSnapshotBuffers(void)
15245 {
15246   ListNode *buffers = NULL;
15247
15248   // copy some special values to a structure better suited for the snapshot
15249
15250   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15251     SaveEngineSnapshotValues_RND();
15252   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15253     SaveEngineSnapshotValues_EM();
15254   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15255     SaveEngineSnapshotValues_SP(&buffers);
15256   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15257     SaveEngineSnapshotValues_MM(&buffers);
15258
15259   // save values stored in special snapshot structure
15260
15261   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15262     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15263   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15264     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15265   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15266     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15267   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15268     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15269
15270   // save further RND engine values
15271
15272   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15273   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15274   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15275
15276   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15277   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15278   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15279   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15280   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15281
15282   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15283   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15284   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15285
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15287
15288   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15289   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15290
15291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15293   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15294   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15295   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15296   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15297   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15298   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15299   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15300   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15301   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15302   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15303   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15304   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15305   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15306   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15307   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15308   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15309
15310   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15312
15313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15314   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15316
15317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15318   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15319
15320   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15321   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15322   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15323   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15324   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15325
15326   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15327   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15328
15329 #if 0
15330   ListNode *node = engine_snapshot_list_rnd;
15331   int num_bytes = 0;
15332
15333   while (node != NULL)
15334   {
15335     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15336
15337     node = node->next;
15338   }
15339
15340   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15341 #endif
15342
15343   return buffers;
15344 }
15345
15346 void SaveEngineSnapshotSingle(void)
15347 {
15348   ListNode *buffers = SaveEngineSnapshotBuffers();
15349
15350   // finally save all snapshot buffers to single snapshot
15351   SaveSnapshotSingle(buffers);
15352
15353   // save level identification information
15354   setString(&snapshot_level_identifier, leveldir_current->identifier);
15355   snapshot_level_nr = level_nr;
15356 }
15357
15358 boolean CheckSaveEngineSnapshotToList(void)
15359 {
15360   boolean save_snapshot =
15361     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15362      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15363       game.snapshot.changed_action) ||
15364      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15365       game.snapshot.collected_item));
15366
15367   game.snapshot.changed_action = FALSE;
15368   game.snapshot.collected_item = FALSE;
15369   game.snapshot.save_snapshot = save_snapshot;
15370
15371   return save_snapshot;
15372 }
15373
15374 void SaveEngineSnapshotToList(void)
15375 {
15376   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15377       tape.quick_resume)
15378     return;
15379
15380   ListNode *buffers = SaveEngineSnapshotBuffers();
15381
15382   // finally save all snapshot buffers to snapshot list
15383   SaveSnapshotToList(buffers);
15384 }
15385
15386 void SaveEngineSnapshotToListInitial(void)
15387 {
15388   FreeEngineSnapshotList();
15389
15390   SaveEngineSnapshotToList();
15391 }
15392
15393 static void LoadEngineSnapshotValues(void)
15394 {
15395   // restore special values from snapshot structure
15396
15397   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15398     LoadEngineSnapshotValues_RND();
15399   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15400     LoadEngineSnapshotValues_EM();
15401   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15402     LoadEngineSnapshotValues_SP();
15403   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15404     LoadEngineSnapshotValues_MM();
15405 }
15406
15407 void LoadEngineSnapshotSingle(void)
15408 {
15409   LoadSnapshotSingle();
15410
15411   LoadEngineSnapshotValues();
15412 }
15413
15414 static void LoadEngineSnapshot_Undo(int steps)
15415 {
15416   LoadSnapshotFromList_Older(steps);
15417
15418   LoadEngineSnapshotValues();
15419 }
15420
15421 static void LoadEngineSnapshot_Redo(int steps)
15422 {
15423   LoadSnapshotFromList_Newer(steps);
15424
15425   LoadEngineSnapshotValues();
15426 }
15427
15428 boolean CheckEngineSnapshotSingle(void)
15429 {
15430   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15431           snapshot_level_nr == level_nr);
15432 }
15433
15434 boolean CheckEngineSnapshotList(void)
15435 {
15436   return CheckSnapshotList();
15437 }
15438
15439
15440 // ---------- new game button stuff -------------------------------------------
15441
15442 static struct
15443 {
15444   int graphic;
15445   struct XY *pos;
15446   int gadget_id;
15447   boolean *setup_value;
15448   boolean allowed_on_tape;
15449   char *infotext;
15450 } gamebutton_info[NUM_GAME_BUTTONS] =
15451 {
15452   {
15453     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15454     GAME_CTRL_ID_STOP,                          NULL,
15455     TRUE,                                       "stop game"
15456   },
15457   {
15458     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15459     GAME_CTRL_ID_PAUSE,                         NULL,
15460     TRUE,                                       "pause game"
15461   },
15462   {
15463     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15464     GAME_CTRL_ID_PLAY,                          NULL,
15465     TRUE,                                       "play game"
15466   },
15467   {
15468     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15469     GAME_CTRL_ID_UNDO,                          NULL,
15470     TRUE,                                       "undo step"
15471   },
15472   {
15473     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15474     GAME_CTRL_ID_REDO,                          NULL,
15475     TRUE,                                       "redo step"
15476   },
15477   {
15478     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15479     GAME_CTRL_ID_SAVE,                          NULL,
15480     TRUE,                                       "save game"
15481   },
15482   {
15483     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15484     GAME_CTRL_ID_PAUSE2,                        NULL,
15485     TRUE,                                       "pause game"
15486   },
15487   {
15488     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15489     GAME_CTRL_ID_LOAD,                          NULL,
15490     TRUE,                                       "load game"
15491   },
15492   {
15493     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15494     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15495     FALSE,                                      "stop game"
15496   },
15497   {
15498     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15499     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15500     FALSE,                                      "pause game"
15501   },
15502   {
15503     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15504     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15505     FALSE,                                      "play game"
15506   },
15507   {
15508     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15509     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15510     TRUE,                                       "background music on/off"
15511   },
15512   {
15513     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15514     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15515     TRUE,                                       "sound loops on/off"
15516   },
15517   {
15518     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15519     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15520     TRUE,                                       "normal sounds on/off"
15521   },
15522   {
15523     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15524     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15525     FALSE,                                      "background music on/off"
15526   },
15527   {
15528     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15529     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15530     FALSE,                                      "sound loops on/off"
15531   },
15532   {
15533     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15534     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15535     FALSE,                                      "normal sounds on/off"
15536   }
15537 };
15538
15539 void CreateGameButtons(void)
15540 {
15541   int i;
15542
15543   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15544   {
15545     int graphic = gamebutton_info[i].graphic;
15546     struct GraphicInfo *gfx = &graphic_info[graphic];
15547     struct XY *pos = gamebutton_info[i].pos;
15548     struct GadgetInfo *gi;
15549     int button_type;
15550     boolean checked;
15551     unsigned int event_mask;
15552     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15553     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15554     int base_x = (on_tape ? VX : DX);
15555     int base_y = (on_tape ? VY : DY);
15556     int gd_x   = gfx->src_x;
15557     int gd_y   = gfx->src_y;
15558     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15559     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15560     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15561     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15562     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15563     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15564     int id = i;
15565
15566     if (gfx->bitmap == NULL)
15567     {
15568       game_gadget[id] = NULL;
15569
15570       continue;
15571     }
15572
15573     if (id == GAME_CTRL_ID_STOP ||
15574         id == GAME_CTRL_ID_PANEL_STOP ||
15575         id == GAME_CTRL_ID_PLAY ||
15576         id == GAME_CTRL_ID_PANEL_PLAY ||
15577         id == GAME_CTRL_ID_SAVE ||
15578         id == GAME_CTRL_ID_LOAD)
15579     {
15580       button_type = GD_TYPE_NORMAL_BUTTON;
15581       checked = FALSE;
15582       event_mask = GD_EVENT_RELEASED;
15583     }
15584     else if (id == GAME_CTRL_ID_UNDO ||
15585              id == GAME_CTRL_ID_REDO)
15586     {
15587       button_type = GD_TYPE_NORMAL_BUTTON;
15588       checked = FALSE;
15589       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15590     }
15591     else
15592     {
15593       button_type = GD_TYPE_CHECK_BUTTON;
15594       checked = (gamebutton_info[i].setup_value != NULL ?
15595                  *gamebutton_info[i].setup_value : FALSE);
15596       event_mask = GD_EVENT_PRESSED;
15597     }
15598
15599     gi = CreateGadget(GDI_CUSTOM_ID, id,
15600                       GDI_IMAGE_ID, graphic,
15601                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15602                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15603                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15604                       GDI_WIDTH, gfx->width,
15605                       GDI_HEIGHT, gfx->height,
15606                       GDI_TYPE, button_type,
15607                       GDI_STATE, GD_BUTTON_UNPRESSED,
15608                       GDI_CHECKED, checked,
15609                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15610                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15611                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15612                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15613                       GDI_DIRECT_DRAW, FALSE,
15614                       GDI_EVENT_MASK, event_mask,
15615                       GDI_CALLBACK_ACTION, HandleGameButtons,
15616                       GDI_END);
15617
15618     if (gi == NULL)
15619       Error(ERR_EXIT, "cannot create gadget");
15620
15621     game_gadget[id] = gi;
15622   }
15623 }
15624
15625 void FreeGameButtons(void)
15626 {
15627   int i;
15628
15629   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15630     FreeGadget(game_gadget[i]);
15631 }
15632
15633 static void UnmapGameButtonsAtSamePosition(int id)
15634 {
15635   int i;
15636
15637   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15638     if (i != id &&
15639         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15640         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15641       UnmapGadget(game_gadget[i]);
15642 }
15643
15644 static void UnmapGameButtonsAtSamePosition_All(void)
15645 {
15646   if (setup.show_snapshot_buttons)
15647   {
15648     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15649     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15650     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15651   }
15652   else
15653   {
15654     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15655     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15656     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15657
15658     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15659     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15660     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15661   }
15662 }
15663
15664 static void MapGameButtonsAtSamePosition(int id)
15665 {
15666   int i;
15667
15668   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15669     if (i != id &&
15670         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15671         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15672       MapGadget(game_gadget[i]);
15673
15674   UnmapGameButtonsAtSamePosition_All();
15675 }
15676
15677 void MapUndoRedoButtons(void)
15678 {
15679   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15680   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15681
15682   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15683   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15684
15685   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15686 }
15687
15688 void UnmapUndoRedoButtons(void)
15689 {
15690   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15691   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15692
15693   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15694   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15695
15696   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15697 }
15698
15699 static void MapGameButtonsExt(boolean on_tape)
15700 {
15701   int i;
15702
15703   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15704     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15705         i != GAME_CTRL_ID_UNDO &&
15706         i != GAME_CTRL_ID_REDO)
15707       MapGadget(game_gadget[i]);
15708
15709   UnmapGameButtonsAtSamePosition_All();
15710
15711   RedrawGameButtons();
15712 }
15713
15714 static void UnmapGameButtonsExt(boolean on_tape)
15715 {
15716   int i;
15717
15718   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15719     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15720       UnmapGadget(game_gadget[i]);
15721 }
15722
15723 static void RedrawGameButtonsExt(boolean on_tape)
15724 {
15725   int i;
15726
15727   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15728     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15729       RedrawGadget(game_gadget[i]);
15730
15731   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15732   redraw_mask &= ~REDRAW_ALL;
15733 }
15734
15735 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15736 {
15737   if (gi == NULL)
15738     return;
15739
15740   gi->checked = state;
15741 }
15742
15743 static void RedrawSoundButtonGadget(int id)
15744 {
15745   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15746              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15747              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15748              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15749              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15750              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15751              id);
15752
15753   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15754   RedrawGadget(game_gadget[id2]);
15755 }
15756
15757 void MapGameButtons(void)
15758 {
15759   MapGameButtonsExt(FALSE);
15760 }
15761
15762 void UnmapGameButtons(void)
15763 {
15764   UnmapGameButtonsExt(FALSE);
15765 }
15766
15767 void RedrawGameButtons(void)
15768 {
15769   RedrawGameButtonsExt(FALSE);
15770 }
15771
15772 void MapGameButtonsOnTape(void)
15773 {
15774   MapGameButtonsExt(TRUE);
15775 }
15776
15777 void UnmapGameButtonsOnTape(void)
15778 {
15779   UnmapGameButtonsExt(TRUE);
15780 }
15781
15782 void RedrawGameButtonsOnTape(void)
15783 {
15784   RedrawGameButtonsExt(TRUE);
15785 }
15786
15787 static void GameUndoRedoExt(void)
15788 {
15789   ClearPlayerAction();
15790
15791   tape.pausing = TRUE;
15792
15793   RedrawPlayfield();
15794   UpdateAndDisplayGameControlValues();
15795
15796   DrawCompleteVideoDisplay();
15797   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15798   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15799   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15800
15801   BackToFront();
15802 }
15803
15804 static void GameUndo(int steps)
15805 {
15806   if (!CheckEngineSnapshotList())
15807     return;
15808
15809   LoadEngineSnapshot_Undo(steps);
15810
15811   GameUndoRedoExt();
15812 }
15813
15814 static void GameRedo(int steps)
15815 {
15816   if (!CheckEngineSnapshotList())
15817     return;
15818
15819   LoadEngineSnapshot_Redo(steps);
15820
15821   GameUndoRedoExt();
15822 }
15823
15824 static void HandleGameButtonsExt(int id, int button)
15825 {
15826   static boolean game_undo_executed = FALSE;
15827   int steps = BUTTON_STEPSIZE(button);
15828   boolean handle_game_buttons =
15829     (game_status == GAME_MODE_PLAYING ||
15830      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15831
15832   if (!handle_game_buttons)
15833     return;
15834
15835   switch (id)
15836   {
15837     case GAME_CTRL_ID_STOP:
15838     case GAME_CTRL_ID_PANEL_STOP:
15839       if (game_status == GAME_MODE_MAIN)
15840         break;
15841
15842       if (tape.playing)
15843         TapeStop();
15844       else
15845         RequestQuitGame(TRUE);
15846
15847       break;
15848
15849     case GAME_CTRL_ID_PAUSE:
15850     case GAME_CTRL_ID_PAUSE2:
15851     case GAME_CTRL_ID_PANEL_PAUSE:
15852       if (network.enabled && game_status == GAME_MODE_PLAYING)
15853       {
15854         if (tape.pausing)
15855           SendToServer_ContinuePlaying();
15856         else
15857           SendToServer_PausePlaying();
15858       }
15859       else
15860         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15861
15862       game_undo_executed = FALSE;
15863
15864       break;
15865
15866     case GAME_CTRL_ID_PLAY:
15867     case GAME_CTRL_ID_PANEL_PLAY:
15868       if (game_status == GAME_MODE_MAIN)
15869       {
15870         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15871       }
15872       else if (tape.pausing)
15873       {
15874         if (network.enabled)
15875           SendToServer_ContinuePlaying();
15876         else
15877           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15878       }
15879       break;
15880
15881     case GAME_CTRL_ID_UNDO:
15882       // Important: When using "save snapshot when collecting an item" mode,
15883       // load last (current) snapshot for first "undo" after pressing "pause"
15884       // (else the last-but-one snapshot would be loaded, because the snapshot
15885       // pointer already points to the last snapshot when pressing "pause",
15886       // which is fine for "every step/move" mode, but not for "every collect")
15887       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15888           !game_undo_executed)
15889         steps--;
15890
15891       game_undo_executed = TRUE;
15892
15893       GameUndo(steps);
15894       break;
15895
15896     case GAME_CTRL_ID_REDO:
15897       GameRedo(steps);
15898       break;
15899
15900     case GAME_CTRL_ID_SAVE:
15901       TapeQuickSave();
15902       break;
15903
15904     case GAME_CTRL_ID_LOAD:
15905       TapeQuickLoad();
15906       break;
15907
15908     case SOUND_CTRL_ID_MUSIC:
15909     case SOUND_CTRL_ID_PANEL_MUSIC:
15910       if (setup.sound_music)
15911       { 
15912         setup.sound_music = FALSE;
15913
15914         FadeMusic();
15915       }
15916       else if (audio.music_available)
15917       { 
15918         setup.sound = setup.sound_music = TRUE;
15919
15920         SetAudioMode(setup.sound);
15921
15922         if (game_status == GAME_MODE_PLAYING)
15923           PlayLevelMusic();
15924       }
15925
15926       RedrawSoundButtonGadget(id);
15927
15928       break;
15929
15930     case SOUND_CTRL_ID_LOOPS:
15931     case SOUND_CTRL_ID_PANEL_LOOPS:
15932       if (setup.sound_loops)
15933         setup.sound_loops = FALSE;
15934       else if (audio.loops_available)
15935       {
15936         setup.sound = setup.sound_loops = TRUE;
15937
15938         SetAudioMode(setup.sound);
15939       }
15940
15941       RedrawSoundButtonGadget(id);
15942
15943       break;
15944
15945     case SOUND_CTRL_ID_SIMPLE:
15946     case SOUND_CTRL_ID_PANEL_SIMPLE:
15947       if (setup.sound_simple)
15948         setup.sound_simple = FALSE;
15949       else if (audio.sound_available)
15950       {
15951         setup.sound = setup.sound_simple = TRUE;
15952
15953         SetAudioMode(setup.sound);
15954       }
15955
15956       RedrawSoundButtonGadget(id);
15957
15958       break;
15959
15960     default:
15961       break;
15962   }
15963 }
15964
15965 static void HandleGameButtons(struct GadgetInfo *gi)
15966 {
15967   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15968 }
15969
15970 void HandleSoundButtonKeys(Key key)
15971 {
15972   if (key == setup.shortcut.sound_simple)
15973     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15974   else if (key == setup.shortcut.sound_loops)
15975     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15976   else if (key == setup.shortcut.sound_music)
15977     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15978 }