added sending level file (and level template) for network games
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 void InitGameControlValues()
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 void UpdatePlayfieldElementCount()
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 void UpdateGameControlValues()
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   /* use "level.file_info.nr" instead of "level_nr" (for network games) */
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level.file_info.nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       /* only one player in Supaplex game engine */
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   /* update game panel control frames */
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 void DisplayGameControlValues()
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   /* copy default game door content to main double buffer */
2533
2534   /* !!! CHECK AGAIN !!! */
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   /* redraw game control buttons */
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           /* use dynamic number of digits */
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          /* (used for normal state) */
2728         int font2 = pos->font_alt;      /* (used for active state) */
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           /* don't truncate output if "chars" is zero or less */
2740           size = strlen(s);
2741
2742           /* dynamically correct text alignment */
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues()
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 void UpdateGameDoorValues()
2771 {
2772   UpdateGameControlValues();
2773 }
2774
2775 void DrawGameDoorValues()
2776 {
2777   DisplayGameControlValues();
2778 }
2779
2780
2781 /*
2782   =============================================================================
2783   InitGameEngine()
2784   -----------------------------------------------------------------------------
2785   initialize game engine due to level / tape version number
2786   =============================================================================
2787 */
2788
2789 static void InitGameEngine()
2790 {
2791   int i, j, k, l, x, y;
2792
2793   /* set game engine from tape file when re-playing, else from level file */
2794   game.engine_version = (tape.playing ? tape.engine_version :
2795                          level.game_version);
2796
2797   /* set single or multi-player game mode (needed for re-playing tapes) */
2798   game.team_mode = setup.team_mode;
2799
2800   if (tape.playing)
2801   {
2802     int num_players = 0;
2803
2804     for (i = 0; i < MAX_PLAYERS; i++)
2805       if (tape.player_participates[i])
2806         num_players++;
2807
2808     /* multi-player tapes contain input data for more than one player */
2809     game.team_mode = (num_players > 1);
2810   }
2811
2812   /* ---------------------------------------------------------------------- */
2813   /* set flags for bugs and changes according to active game engine version */
2814   /* ---------------------------------------------------------------------- */
2815
2816   /*
2817     Summary of bugfix/change:
2818     Fixed handling for custom elements that change when pushed by the player.
2819
2820     Fixed/changed in version:
2821     3.1.0
2822
2823     Description:
2824     Before 3.1.0, custom elements that "change when pushing" changed directly
2825     after the player started pushing them (until then handled in "DigField()").
2826     Since 3.1.0, these custom elements are not changed until the "pushing"
2827     move of the element is finished (now handled in "ContinueMoving()").
2828
2829     Affected levels/tapes:
2830     The first condition is generally needed for all levels/tapes before version
2831     3.1.0, which might use the old behaviour before it was changed; known tapes
2832     that are affected are some tapes from the level set "Walpurgis Gardens" by
2833     Jamie Cullen.
2834     The second condition is an exception from the above case and is needed for
2835     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2836     above (including some development versions of 3.1.0), but before it was
2837     known that this change would break tapes like the above and was fixed in
2838     3.1.1, so that the changed behaviour was active although the engine version
2839     while recording maybe was before 3.1.0. There is at least one tape that is
2840     affected by this exception, which is the tape for the one-level set "Bug
2841     Machine" by Juergen Bonhagen.
2842   */
2843
2844   game.use_change_when_pushing_bug =
2845     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2846      !(tape.playing &&
2847        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2848        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2849
2850   /*
2851     Summary of bugfix/change:
2852     Fixed handling for blocking the field the player leaves when moving.
2853
2854     Fixed/changed in version:
2855     3.1.1
2856
2857     Description:
2858     Before 3.1.1, when "block last field when moving" was enabled, the field
2859     the player is leaving when moving was blocked for the time of the move,
2860     and was directly unblocked afterwards. This resulted in the last field
2861     being blocked for exactly one less than the number of frames of one player
2862     move. Additionally, even when blocking was disabled, the last field was
2863     blocked for exactly one frame.
2864     Since 3.1.1, due to changes in player movement handling, the last field
2865     is not blocked at all when blocking is disabled. When blocking is enabled,
2866     the last field is blocked for exactly the number of frames of one player
2867     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2868     last field is blocked for exactly one more than the number of frames of
2869     one player move.
2870
2871     Affected levels/tapes:
2872     (!!! yet to be determined -- probably many !!!)
2873   */
2874
2875   game.use_block_last_field_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,1,0));
2877
2878   game_em.use_single_button =
2879     (game.engine_version > VERSION_IDENT(4,0,0,2));
2880
2881   game_em.use_snap_key_bug =
2882     (game.engine_version < VERSION_IDENT(4,0,1,0));
2883
2884   /* ---------------------------------------------------------------------- */
2885
2886   /* set maximal allowed number of custom element changes per game frame */
2887   game.max_num_changes_per_frame = 1;
2888
2889   /* default scan direction: scan playfield from top/left to bottom/right */
2890   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2891
2892   /* dynamically adjust element properties according to game engine version */
2893   InitElementPropertiesEngine(game.engine_version);
2894
2895 #if 0
2896   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2897   printf("          tape version == %06d [%s] [file: %06d]\n",
2898          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2899          tape.file_version);
2900   printf("       => game.engine_version == %06d\n", game.engine_version);
2901 #endif
2902
2903   /* ---------- initialize player's initial move delay --------------------- */
2904
2905   /* dynamically adjust player properties according to level information */
2906   for (i = 0; i < MAX_PLAYERS; i++)
2907     game.initial_move_delay_value[i] =
2908       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2909
2910   /* dynamically adjust player properties according to game engine version */
2911   for (i = 0; i < MAX_PLAYERS; i++)
2912     game.initial_move_delay[i] =
2913       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2914        game.initial_move_delay_value[i] : 0);
2915
2916   /* ---------- initialize player's initial push delay --------------------- */
2917
2918   /* dynamically adjust player properties according to game engine version */
2919   game.initial_push_delay_value =
2920     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2921
2922   /* ---------- initialize changing elements ------------------------------- */
2923
2924   /* initialize changing elements information */
2925   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2926   {
2927     struct ElementInfo *ei = &element_info[i];
2928
2929     /* this pointer might have been changed in the level editor */
2930     ei->change = &ei->change_page[0];
2931
2932     if (!IS_CUSTOM_ELEMENT(i))
2933     {
2934       ei->change->target_element = EL_EMPTY_SPACE;
2935       ei->change->delay_fixed = 0;
2936       ei->change->delay_random = 0;
2937       ei->change->delay_frames = 1;
2938     }
2939
2940     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2941     {
2942       ei->has_change_event[j] = FALSE;
2943
2944       ei->event_page_nr[j] = 0;
2945       ei->event_page[j] = &ei->change_page[0];
2946     }
2947   }
2948
2949   /* add changing elements from pre-defined list */
2950   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2951   {
2952     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2953     struct ElementInfo *ei = &element_info[ch_delay->element];
2954
2955     ei->change->target_element       = ch_delay->target_element;
2956     ei->change->delay_fixed          = ch_delay->change_delay;
2957
2958     ei->change->pre_change_function  = ch_delay->pre_change_function;
2959     ei->change->change_function      = ch_delay->change_function;
2960     ei->change->post_change_function = ch_delay->post_change_function;
2961
2962     ei->change->can_change = TRUE;
2963     ei->change->can_change_or_has_action = TRUE;
2964
2965     ei->has_change_event[CE_DELAY] = TRUE;
2966
2967     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2968     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969   }
2970
2971   /* ---------- initialize internal run-time variables --------------------- */
2972
2973   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974   {
2975     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976
2977     for (j = 0; j < ei->num_change_pages; j++)
2978     {
2979       ei->change_page[j].can_change_or_has_action =
2980         (ei->change_page[j].can_change |
2981          ei->change_page[j].has_action);
2982     }
2983   }
2984
2985   /* add change events from custom element configuration */
2986   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2987   {
2988     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2989
2990     for (j = 0; j < ei->num_change_pages; j++)
2991     {
2992       if (!ei->change_page[j].can_change_or_has_action)
2993         continue;
2994
2995       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2996       {
2997         /* only add event page for the first page found with this event */
2998         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2999         {
3000           ei->has_change_event[k] = TRUE;
3001
3002           ei->event_page_nr[k] = j;
3003           ei->event_page[k] = &ei->change_page[j];
3004         }
3005       }
3006     }
3007   }
3008
3009   /* ---------- initialize reference elements in change conditions --------- */
3010
3011   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3012   {
3013     int element = EL_CUSTOM_START + i;
3014     struct ElementInfo *ei = &element_info[element];
3015
3016     for (j = 0; j < ei->num_change_pages; j++)
3017     {
3018       int trigger_element = ei->change_page[j].initial_trigger_element;
3019
3020       if (trigger_element >= EL_PREV_CE_8 &&
3021           trigger_element <= EL_NEXT_CE_8)
3022         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3023
3024       ei->change_page[j].trigger_element = trigger_element;
3025     }
3026   }
3027
3028   /* ---------- initialize run-time trigger player and element ------------- */
3029
3030   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3031   {
3032     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3033
3034     for (j = 0; j < ei->num_change_pages; j++)
3035     {
3036       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3037       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3038       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3039       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3040       ei->change_page[j].actual_trigger_ce_value = 0;
3041       ei->change_page[j].actual_trigger_ce_score = 0;
3042     }
3043   }
3044
3045   /* ---------- initialize trigger events ---------------------------------- */
3046
3047   /* initialize trigger events information */
3048   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3049     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3050       trigger_events[i][j] = FALSE;
3051
3052   /* add trigger events from element change event properties */
3053   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054   {
3055     struct ElementInfo *ei = &element_info[i];
3056
3057     for (j = 0; j < ei->num_change_pages; j++)
3058     {
3059       if (!ei->change_page[j].can_change_or_has_action)
3060         continue;
3061
3062       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3063       {
3064         int trigger_element = ei->change_page[j].trigger_element;
3065
3066         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3067         {
3068           if (ei->change_page[j].has_event[k])
3069           {
3070             if (IS_GROUP_ELEMENT(trigger_element))
3071             {
3072               struct ElementGroupInfo *group =
3073                 element_info[trigger_element].group;
3074
3075               for (l = 0; l < group->num_elements_resolved; l++)
3076                 trigger_events[group->element_resolved[l]][k] = TRUE;
3077             }
3078             else if (trigger_element == EL_ANY_ELEMENT)
3079               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3080                 trigger_events[l][k] = TRUE;
3081             else
3082               trigger_events[trigger_element][k] = TRUE;
3083           }
3084         }
3085       }
3086     }
3087   }
3088
3089   /* ---------- initialize push delay -------------------------------------- */
3090
3091   /* initialize push delay values to default */
3092   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3093   {
3094     if (!IS_CUSTOM_ELEMENT(i))
3095     {
3096       /* set default push delay values (corrected since version 3.0.7-1) */
3097       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3098       {
3099         element_info[i].push_delay_fixed = 2;
3100         element_info[i].push_delay_random = 8;
3101       }
3102       else
3103       {
3104         element_info[i].push_delay_fixed = 8;
3105         element_info[i].push_delay_random = 8;
3106       }
3107     }
3108   }
3109
3110   /* set push delay value for certain elements from pre-defined list */
3111   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3112   {
3113     int e = push_delay_list[i].element;
3114
3115     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3116     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117   }
3118
3119   /* set push delay value for Supaplex elements for newer engine versions */
3120   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3121   {
3122     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123     {
3124       if (IS_SP_ELEMENT(i))
3125       {
3126         /* set SP push delay to just enough to push under a falling zonk */
3127         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3128
3129         element_info[i].push_delay_fixed  = delay;
3130         element_info[i].push_delay_random = 0;
3131       }
3132     }
3133   }
3134
3135   /* ---------- initialize move stepsize ----------------------------------- */
3136
3137   /* initialize move stepsize values to default */
3138   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3139     if (!IS_CUSTOM_ELEMENT(i))
3140       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3141
3142   /* set move stepsize value for certain elements from pre-defined list */
3143   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3144   {
3145     int e = move_stepsize_list[i].element;
3146
3147     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148   }
3149
3150   /* ---------- initialize collect score ----------------------------------- */
3151
3152   /* initialize collect score values for custom elements from initial value */
3153   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3154     if (IS_CUSTOM_ELEMENT(i))
3155       element_info[i].collect_score = element_info[i].collect_score_initial;
3156
3157   /* ---------- initialize collect count ----------------------------------- */
3158
3159   /* initialize collect count values for non-custom elements */
3160   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3161     if (!IS_CUSTOM_ELEMENT(i))
3162       element_info[i].collect_count_initial = 0;
3163
3164   /* add collect count values for all elements from pre-defined list */
3165   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3166     element_info[collect_count_list[i].element].collect_count_initial =
3167       collect_count_list[i].count;
3168
3169   /* ---------- initialize access direction -------------------------------- */
3170
3171   /* initialize access direction values to default (access from every side) */
3172   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3173     if (!IS_CUSTOM_ELEMENT(i))
3174       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3175
3176   /* set access direction value for certain elements from pre-defined list */
3177   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3178     element_info[access_direction_list[i].element].access_direction =
3179       access_direction_list[i].direction;
3180
3181   /* ---------- initialize explosion content ------------------------------- */
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183   {
3184     if (IS_CUSTOM_ELEMENT(i))
3185       continue;
3186
3187     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3188     {
3189       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3190
3191       element_info[i].content.e[x][y] =
3192         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3193          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3194          i == EL_PLAYER_3 ? EL_EMERALD :
3195          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3196          i == EL_MOLE ? EL_EMERALD_RED :
3197          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3198          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3199          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3200          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3201          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3202          i == EL_WALL_EMERALD ? EL_EMERALD :
3203          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3204          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3205          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3206          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3207          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3208          i == EL_WALL_PEARL ? EL_PEARL :
3209          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3210          EL_EMPTY);
3211     }
3212   }
3213
3214   /* ---------- initialize recursion detection ------------------------------ */
3215   recursion_loop_depth = 0;
3216   recursion_loop_detected = FALSE;
3217   recursion_loop_element = EL_UNDEFINED;
3218
3219   /* ---------- initialize graphics engine ---------------------------------- */
3220   game.scroll_delay_value =
3221     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3222      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3223   game.scroll_delay_value =
3224     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3225
3226   /* ---------- initialize game engine snapshots ---------------------------- */
3227   for (i = 0; i < MAX_PLAYERS; i++)
3228     game.snapshot.last_action[i] = 0;
3229   game.snapshot.changed_action = FALSE;
3230   game.snapshot.collected_item = FALSE;
3231   game.snapshot.mode =
3232     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3233      SNAPSHOT_MODE_EVERY_STEP :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3235      SNAPSHOT_MODE_EVERY_MOVE :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3237      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3238   game.snapshot.save_snapshot = FALSE;
3239
3240   /* ---------- initialize level time for Supaplex engine ------------------- */
3241   /* Supaplex levels with time limit currently unsupported -- should be added */
3242   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3243     level.time = 0;
3244 }
3245
3246 int get_num_special_action(int element, int action_first, int action_last)
3247 {
3248   int num_special_action = 0;
3249   int i, j;
3250
3251   for (i = action_first; i <= action_last; i++)
3252   {
3253     boolean found = FALSE;
3254
3255     for (j = 0; j < NUM_DIRECTIONS; j++)
3256       if (el_act_dir2img(element, i, j) !=
3257           el_act_dir2img(element, ACTION_DEFAULT, j))
3258         found = TRUE;
3259
3260     if (found)
3261       num_special_action++;
3262     else
3263       break;
3264   }
3265
3266   return num_special_action;
3267 }
3268
3269
3270 /*
3271   =============================================================================
3272   InitGame()
3273   -----------------------------------------------------------------------------
3274   initialize and start new game
3275   =============================================================================
3276 */
3277
3278 #if DEBUG_INIT_PLAYER
3279 static void DebugPrintPlayerStatus(char *message)
3280 {
3281   int i;
3282
3283   if (!options.debug)
3284     return;
3285
3286   printf("%s:\n", message);
3287
3288   for (i = 0; i < MAX_PLAYERS; i++)
3289   {
3290     struct PlayerInfo *player = &stored_player[i];
3291
3292     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3293            i + 1,
3294            player->present,
3295            player->connected,
3296            player->connected_locally,
3297            player->connected_network,
3298            player->active);
3299
3300     if (local_player == player)
3301       printf(" (local player)");
3302
3303     printf("\n");
3304   }
3305 }
3306 #endif
3307
3308 void InitGame()
3309 {
3310   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3312   int fade_mask = REDRAW_FIELD;
3313
3314   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3315   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3316   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3317   int initial_move_dir = MV_DOWN;
3318   int i, j, x, y;
3319
3320   // required here to update video display before fading (FIX THIS)
3321   DrawMaskedBorder(REDRAW_DOOR_2);
3322
3323   if (!game.restart_level)
3324     CloseDoor(DOOR_CLOSE_1);
3325
3326   SetGameStatus(GAME_MODE_PLAYING);
3327
3328   if (level_editor_test_game)
3329     FadeSkipNextFadeIn();
3330   else
3331     FadeSetEnterScreen();
3332
3333   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3334     fade_mask = REDRAW_ALL;
3335
3336   FadeLevelSoundsAndMusic();
3337
3338   ExpireSoundLoops(TRUE);
3339
3340   if (!level_editor_test_game)
3341     FadeOut(fade_mask);
3342
3343   /* needed if different viewport properties defined for playing */
3344   ChangeViewportPropertiesIfNeeded();
3345
3346   ClearField();
3347
3348   DrawCompleteVideoDisplay();
3349
3350   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3351
3352   InitGameEngine();
3353   InitGameControlValues();
3354
3355   /* don't play tapes over network */
3356   network_playing = (network.enabled && !tape.playing);
3357
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359   {
3360     struct PlayerInfo *player = &stored_player[i];
3361
3362     player->index_nr = i;
3363     player->index_bit = (1 << i);
3364     player->element_nr = EL_PLAYER_1 + i;
3365
3366     player->present = FALSE;
3367     player->active = FALSE;
3368     player->mapped = FALSE;
3369
3370     player->killed = FALSE;
3371     player->reanimated = FALSE;
3372
3373     player->action = 0;
3374     player->effective_action = 0;
3375     player->programmed_action = 0;
3376
3377     player->mouse_action.lx = 0;
3378     player->mouse_action.ly = 0;
3379     player->mouse_action.button = 0;
3380     player->mouse_action.button_hint = 0;
3381
3382     player->effective_mouse_action.lx = 0;
3383     player->effective_mouse_action.ly = 0;
3384     player->effective_mouse_action.button = 0;
3385     player->effective_mouse_action.button_hint = 0;
3386
3387     player->score = 0;
3388     player->score_final = 0;
3389
3390     player->health = MAX_HEALTH;
3391     player->health_final = MAX_HEALTH;
3392
3393     player->gems_still_needed = level.gems_needed;
3394     player->sokobanfields_still_needed = 0;
3395     player->lights_still_needed = 0;
3396     player->players_still_needed = 0;
3397     player->friends_still_needed = 0;
3398
3399     for (j = 0; j < MAX_NUM_KEYS; j++)
3400       player->key[j] = FALSE;
3401
3402     player->num_white_keys = 0;
3403
3404     player->dynabomb_count = 0;
3405     player->dynabomb_size = 1;
3406     player->dynabombs_left = 0;
3407     player->dynabomb_xl = FALSE;
3408
3409     player->MovDir = initial_move_dir;
3410     player->MovPos = 0;
3411     player->GfxPos = 0;
3412     player->GfxDir = initial_move_dir;
3413     player->GfxAction = ACTION_DEFAULT;
3414     player->Frame = 0;
3415     player->StepFrame = 0;
3416
3417     player->initial_element = player->element_nr;
3418     player->artwork_element =
3419       (level.use_artwork_element[i] ? level.artwork_element[i] :
3420        player->element_nr);
3421     player->use_murphy = FALSE;
3422
3423     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3424     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3425
3426     player->gravity = level.initial_player_gravity[i];
3427
3428     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3429
3430     player->actual_frame_counter = 0;
3431
3432     player->step_counter = 0;
3433
3434     player->last_move_dir = initial_move_dir;
3435
3436     player->is_active = FALSE;
3437
3438     player->is_waiting = FALSE;
3439     player->is_moving = FALSE;
3440     player->is_auto_moving = FALSE;
3441     player->is_digging = FALSE;
3442     player->is_snapping = FALSE;
3443     player->is_collecting = FALSE;
3444     player->is_pushing = FALSE;
3445     player->is_switching = FALSE;
3446     player->is_dropping = FALSE;
3447     player->is_dropping_pressed = FALSE;
3448
3449     player->is_bored = FALSE;
3450     player->is_sleeping = FALSE;
3451
3452     player->was_waiting = TRUE;
3453     player->was_moving = FALSE;
3454     player->was_snapping = FALSE;
3455     player->was_dropping = FALSE;
3456
3457     player->force_dropping = FALSE;
3458
3459     player->frame_counter_bored = -1;
3460     player->frame_counter_sleeping = -1;
3461
3462     player->anim_delay_counter = 0;
3463     player->post_delay_counter = 0;
3464
3465     player->dir_waiting = initial_move_dir;
3466     player->action_waiting = ACTION_DEFAULT;
3467     player->last_action_waiting = ACTION_DEFAULT;
3468     player->special_action_bored = ACTION_DEFAULT;
3469     player->special_action_sleeping = ACTION_DEFAULT;
3470
3471     player->switch_x = -1;
3472     player->switch_y = -1;
3473
3474     player->drop_x = -1;
3475     player->drop_y = -1;
3476
3477     player->show_envelope = 0;
3478
3479     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3480
3481     player->push_delay       = -1;      /* initialized when pushing starts */
3482     player->push_delay_value = game.initial_push_delay_value;
3483
3484     player->drop_delay = 0;
3485     player->drop_pressed_delay = 0;
3486
3487     player->last_jx = -1;
3488     player->last_jy = -1;
3489     player->jx = -1;
3490     player->jy = -1;
3491
3492     player->shield_normal_time_left = 0;
3493     player->shield_deadly_time_left = 0;
3494
3495     player->inventory_infinite_element = EL_UNDEFINED;
3496     player->inventory_size = 0;
3497
3498     if (level.use_initial_inventory[i])
3499     {
3500       for (j = 0; j < level.initial_inventory_size[i]; j++)
3501       {
3502         int element = level.initial_inventory_content[i][j];
3503         int collect_count = element_info[element].collect_count_initial;
3504         int k;
3505
3506         if (!IS_CUSTOM_ELEMENT(element))
3507           collect_count = 1;
3508
3509         if (collect_count == 0)
3510           player->inventory_infinite_element = element;
3511         else
3512           for (k = 0; k < collect_count; k++)
3513             if (player->inventory_size < MAX_INVENTORY_SIZE)
3514               player->inventory_element[player->inventory_size++] = element;
3515       }
3516     }
3517
3518     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3519     SnapField(player, 0, 0);
3520
3521     player->LevelSolved = FALSE;
3522     player->GameOver = FALSE;
3523
3524     player->LevelSolved_GameWon = FALSE;
3525     player->LevelSolved_GameEnd = FALSE;
3526     player->LevelSolved_PanelOff = FALSE;
3527     player->LevelSolved_SaveTape = FALSE;
3528     player->LevelSolved_SaveScore = FALSE;
3529
3530     player->LevelSolved_CountingTime = 0;
3531     player->LevelSolved_CountingScore = 0;
3532     player->LevelSolved_CountingHealth = 0;
3533
3534     map_player_action[i] = i;
3535   }
3536
3537   network_player_action_received = FALSE;
3538
3539   /* initial null action */
3540   if (network_playing)
3541     SendToServer_MovePlayer(MV_NONE);
3542
3543   ZX = ZY = -1;
3544   ExitX = ExitY = -1;
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   AllPlayersGone = FALSE;
3559
3560   game.no_time_limit = (level.time == 0);
3561
3562   game.yamyam_content_nr = 0;
3563   game.robot_wheel_active = FALSE;
3564   game.magic_wall_active = FALSE;
3565   game.magic_wall_time_left = 0;
3566   game.light_time_left = 0;
3567   game.timegate_time_left = 0;
3568   game.switchgate_pos = 0;
3569   game.wind_direction = level.wind_direction_initial;
3570
3571   game.lenses_time_left = 0;
3572   game.magnify_time_left = 0;
3573
3574   game.ball_state = level.ball_state_initial;
3575   game.ball_content_nr = 0;
3576
3577   game.envelope_active = FALSE;
3578
3579   for (i = 0; i < NUM_BELTS; i++)
3580   {
3581     game.belt_dir[i] = MV_NONE;
3582     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3583   }
3584
3585   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3586     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3587
3588 #if DEBUG_INIT_PLAYER
3589   DebugPrintPlayerStatus("Player status at level initialization");
3590 #endif
3591
3592   SCAN_PLAYFIELD(x, y)
3593   {
3594     Feld[x][y] = level.field[x][y];
3595     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3596     ChangeDelay[x][y] = 0;
3597     ChangePage[x][y] = -1;
3598     CustomValue[x][y] = 0;              /* initialized in InitField() */
3599     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3600     AmoebaNr[x][y] = 0;
3601     WasJustMoving[x][y] = 0;
3602     WasJustFalling[x][y] = 0;
3603     CheckCollision[x][y] = 0;
3604     CheckImpact[x][y] = 0;
3605     Stop[x][y] = FALSE;
3606     Pushed[x][y] = FALSE;
3607
3608     ChangeCount[x][y] = 0;
3609     ChangeEvent[x][y] = -1;
3610
3611     ExplodePhase[x][y] = 0;
3612     ExplodeDelay[x][y] = 0;
3613     ExplodeField[x][y] = EX_TYPE_NONE;
3614
3615     RunnerVisit[x][y] = 0;
3616     PlayerVisit[x][y] = 0;
3617
3618     GfxFrame[x][y] = 0;
3619     GfxRandom[x][y] = INIT_GFX_RANDOM();
3620     GfxElement[x][y] = EL_UNDEFINED;
3621     GfxAction[x][y] = ACTION_DEFAULT;
3622     GfxDir[x][y] = MV_NONE;
3623     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3624   }
3625
3626   SCAN_PLAYFIELD(x, y)
3627   {
3628     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3629       emulate_bd = FALSE;
3630     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3631       emulate_sb = FALSE;
3632     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3633       emulate_sp = FALSE;
3634
3635     InitField(x, y, TRUE);
3636
3637     ResetGfxAnimation(x, y);
3638   }
3639
3640   InitBeltMovement();
3641
3642   for (i = 0; i < MAX_PLAYERS; i++)
3643   {
3644     struct PlayerInfo *player = &stored_player[i];
3645
3646     /* set number of special actions for bored and sleeping animation */
3647     player->num_special_action_bored =
3648       get_num_special_action(player->artwork_element,
3649                              ACTION_BORING_1, ACTION_BORING_LAST);
3650     player->num_special_action_sleeping =
3651       get_num_special_action(player->artwork_element,
3652                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3653   }
3654
3655   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3656                     emulate_sb ? EMU_SOKOBAN :
3657                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3658
3659   /* initialize type of slippery elements */
3660   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3661   {
3662     if (!IS_CUSTOM_ELEMENT(i))
3663     {
3664       /* default: elements slip down either to the left or right randomly */
3665       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3666
3667       /* SP style elements prefer to slip down on the left side */
3668       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3669         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3670
3671       /* BD style elements prefer to slip down on the left side */
3672       if (game.emulation == EMU_BOULDERDASH)
3673         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3674     }
3675   }
3676
3677   /* initialize explosion and ignition delay */
3678   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3679   {
3680     if (!IS_CUSTOM_ELEMENT(i))
3681     {
3682       int num_phase = 8;
3683       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3684                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3685                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3686       int last_phase = (num_phase + 1) * delay;
3687       int half_phase = (num_phase / 2) * delay;
3688
3689       element_info[i].explosion_delay = last_phase - 1;
3690       element_info[i].ignition_delay = half_phase;
3691
3692       if (i == EL_BLACK_ORB)
3693         element_info[i].ignition_delay = 1;
3694     }
3695   }
3696
3697   /* correct non-moving belts to start moving left */
3698   for (i = 0; i < NUM_BELTS; i++)
3699     if (game.belt_dir[i] == MV_NONE)
3700       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3701
3702 #if USE_NEW_PLAYER_ASSIGNMENTS
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     stored_player[i].connected = FALSE;
3706
3707     /* in network game mode, the local player might not be the first player */
3708     if (stored_player[i].connected_locally)
3709       local_player = &stored_player[i];
3710   }
3711
3712   if (!network.enabled)
3713     local_player->connected = TRUE;
3714
3715   if (tape.playing)
3716   {
3717     for (i = 0; i < MAX_PLAYERS; i++)
3718       stored_player[i].connected = tape.player_participates[i];
3719   }
3720   else if (network.enabled)
3721   {
3722     /* add team mode players connected over the network (needed for correct
3723        assignment of player figures from level to locally playing players) */
3724
3725     for (i = 0; i < MAX_PLAYERS; i++)
3726       if (stored_player[i].connected_network)
3727         stored_player[i].connected = TRUE;
3728   }
3729   else if (game.team_mode)
3730   {
3731     /* try to guess locally connected team mode players (needed for correct
3732        assignment of player figures from level to locally playing players) */
3733
3734     for (i = 0; i < MAX_PLAYERS; i++)
3735       if (setup.input[i].use_joystick ||
3736           setup.input[i].key.left != KSYM_UNDEFINED)
3737         stored_player[i].connected = TRUE;
3738   }
3739
3740 #if DEBUG_INIT_PLAYER
3741   DebugPrintPlayerStatus("Player status after level initialization");
3742 #endif
3743
3744 #if DEBUG_INIT_PLAYER
3745   if (options.debug)
3746     printf("Reassigning players ...\n");
3747 #endif
3748
3749   /* check if any connected player was not found in playfield */
3750   for (i = 0; i < MAX_PLAYERS; i++)
3751   {
3752     struct PlayerInfo *player = &stored_player[i];
3753
3754     if (player->connected && !player->present)
3755     {
3756       struct PlayerInfo *field_player = NULL;
3757
3758 #if DEBUG_INIT_PLAYER
3759       if (options.debug)
3760         printf("- looking for field player for player %d ...\n", i + 1);
3761 #endif
3762
3763       /* assign first free player found that is present in the playfield */
3764
3765       /* first try: look for unmapped playfield player that is not connected */
3766       for (j = 0; j < MAX_PLAYERS; j++)
3767         if (field_player == NULL &&
3768             stored_player[j].present &&
3769             !stored_player[j].mapped &&
3770             !stored_player[j].connected)
3771           field_player = &stored_player[j];
3772
3773       /* second try: look for *any* unmapped playfield player */
3774       for (j = 0; j < MAX_PLAYERS; j++)
3775         if (field_player == NULL &&
3776             stored_player[j].present &&
3777             !stored_player[j].mapped)
3778           field_player = &stored_player[j];
3779
3780       if (field_player != NULL)
3781       {
3782         int jx = field_player->jx, jy = field_player->jy;
3783
3784 #if DEBUG_INIT_PLAYER
3785         if (options.debug)
3786           printf("- found player %d\n", field_player->index_nr + 1);
3787 #endif
3788
3789         player->present = FALSE;
3790         player->active = FALSE;
3791
3792         field_player->present = TRUE;
3793         field_player->active = TRUE;
3794
3795         /*
3796         player->initial_element = field_player->initial_element;
3797         player->artwork_element = field_player->artwork_element;
3798
3799         player->block_last_field       = field_player->block_last_field;
3800         player->block_delay_adjustment = field_player->block_delay_adjustment;
3801         */
3802
3803         StorePlayer[jx][jy] = field_player->element_nr;
3804
3805         field_player->jx = field_player->last_jx = jx;
3806         field_player->jy = field_player->last_jy = jy;
3807
3808         if (local_player == player)
3809           local_player = field_player;
3810
3811         map_player_action[field_player->index_nr] = i;
3812
3813         field_player->mapped = TRUE;
3814
3815 #if DEBUG_INIT_PLAYER
3816         if (options.debug)
3817           printf("- map_player_action[%d] == %d\n",
3818                  field_player->index_nr + 1, i + 1);
3819 #endif
3820       }
3821     }
3822
3823     if (player->connected && player->present)
3824       player->mapped = TRUE;
3825   }
3826
3827 #if DEBUG_INIT_PLAYER
3828   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3829 #endif
3830
3831 #else
3832
3833   /* check if any connected player was not found in playfield */
3834   for (i = 0; i < MAX_PLAYERS; i++)
3835   {
3836     struct PlayerInfo *player = &stored_player[i];
3837
3838     if (player->connected && !player->present)
3839     {
3840       for (j = 0; j < MAX_PLAYERS; j++)
3841       {
3842         struct PlayerInfo *field_player = &stored_player[j];
3843         int jx = field_player->jx, jy = field_player->jy;
3844
3845         /* assign first free player found that is present in the playfield */
3846         if (field_player->present && !field_player->connected)
3847         {
3848           player->present = TRUE;
3849           player->active = TRUE;
3850
3851           field_player->present = FALSE;
3852           field_player->active = FALSE;
3853
3854           player->initial_element = field_player->initial_element;
3855           player->artwork_element = field_player->artwork_element;
3856
3857           player->block_last_field       = field_player->block_last_field;
3858           player->block_delay_adjustment = field_player->block_delay_adjustment;
3859
3860           StorePlayer[jx][jy] = player->element_nr;
3861
3862           player->jx = player->last_jx = jx;
3863           player->jy = player->last_jy = jy;
3864
3865           break;
3866         }
3867       }
3868     }
3869   }
3870 #endif
3871
3872 #if 0
3873   printf("::: local_player->present == %d\n", local_player->present);
3874 #endif
3875
3876   /* set focus to local player for network games, else to all players */
3877   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3878   game.centered_player_nr_next = game.centered_player_nr;
3879   game.set_centered_player = FALSE;
3880
3881   if (network_playing && tape.recording)
3882   {
3883     /* store client dependent player focus when recording network games */
3884     tape.centered_player_nr_next = game.centered_player_nr_next;
3885     tape.set_centered_player = TRUE;
3886   }
3887
3888   if (tape.playing)
3889   {
3890     /* when playing a tape, eliminate all players who do not participate */
3891
3892 #if USE_NEW_PLAYER_ASSIGNMENTS
3893
3894     if (!game.team_mode)
3895     {
3896       for (i = 0; i < MAX_PLAYERS; i++)
3897       {
3898         if (stored_player[i].active &&
3899             !tape.player_participates[map_player_action[i]])
3900         {
3901           struct PlayerInfo *player = &stored_player[i];
3902           int jx = player->jx, jy = player->jy;
3903
3904 #if DEBUG_INIT_PLAYER
3905           if (options.debug)
3906             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3907 #endif
3908
3909           player->active = FALSE;
3910           StorePlayer[jx][jy] = 0;
3911           Feld[jx][jy] = EL_EMPTY;
3912         }
3913       }
3914     }
3915
3916 #else
3917
3918     for (i = 0; i < MAX_PLAYERS; i++)
3919     {
3920       if (stored_player[i].active &&
3921           !tape.player_participates[i])
3922       {
3923         struct PlayerInfo *player = &stored_player[i];
3924         int jx = player->jx, jy = player->jy;
3925
3926         player->active = FALSE;
3927         StorePlayer[jx][jy] = 0;
3928         Feld[jx][jy] = EL_EMPTY;
3929       }
3930     }
3931 #endif
3932   }
3933   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3934   {
3935     /* when in single player mode, eliminate all but the local player */
3936
3937     for (i = 0; i < MAX_PLAYERS; i++)
3938     {
3939       struct PlayerInfo *player = &stored_player[i];
3940
3941       if (player->active && player != local_player)
3942       {
3943         int jx = player->jx, jy = player->jy;
3944
3945         player->active = FALSE;
3946         player->present = FALSE;
3947
3948         StorePlayer[jx][jy] = 0;
3949         Feld[jx][jy] = EL_EMPTY;
3950       }
3951     }
3952   }
3953
3954   for (i = 0; i < MAX_PLAYERS; i++)
3955     if (stored_player[i].active)
3956       local_player->players_still_needed++;
3957
3958   /* when recording the game, store which players take part in the game */
3959   if (tape.recording)
3960   {
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962     for (i = 0; i < MAX_PLAYERS; i++)
3963       if (stored_player[i].connected)
3964         tape.player_participates[i] = TRUE;
3965 #else
3966     for (i = 0; i < MAX_PLAYERS; i++)
3967       if (stored_player[i].active)
3968         tape.player_participates[i] = TRUE;
3969 #endif
3970   }
3971
3972 #if DEBUG_INIT_PLAYER
3973   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3974 #endif
3975
3976   if (BorderElement == EL_EMPTY)
3977   {
3978     SBX_Left = 0;
3979     SBX_Right = lev_fieldx - SCR_FIELDX;
3980     SBY_Upper = 0;
3981     SBY_Lower = lev_fieldy - SCR_FIELDY;
3982   }
3983   else
3984   {
3985     SBX_Left = -1;
3986     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3987     SBY_Upper = -1;
3988     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3989   }
3990
3991   if (full_lev_fieldx <= SCR_FIELDX)
3992     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3993   if (full_lev_fieldy <= SCR_FIELDY)
3994     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3995
3996   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3997     SBX_Left--;
3998   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3999     SBY_Upper--;
4000
4001   /* if local player not found, look for custom element that might create
4002      the player (make some assumptions about the right custom element) */
4003   if (!local_player->present)
4004   {
4005     int start_x = 0, start_y = 0;
4006     int found_rating = 0;
4007     int found_element = EL_UNDEFINED;
4008     int player_nr = local_player->index_nr;
4009
4010     SCAN_PLAYFIELD(x, y)
4011     {
4012       int element = Feld[x][y];
4013       int content;
4014       int xx, yy;
4015       boolean is_player;
4016
4017       if (level.use_start_element[player_nr] &&
4018           level.start_element[player_nr] == element &&
4019           found_rating < 4)
4020       {
4021         start_x = x;
4022         start_y = y;
4023
4024         found_rating = 4;
4025         found_element = element;
4026       }
4027
4028       if (!IS_CUSTOM_ELEMENT(element))
4029         continue;
4030
4031       if (CAN_CHANGE(element))
4032       {
4033         for (i = 0; i < element_info[element].num_change_pages; i++)
4034         {
4035           /* check for player created from custom element as single target */
4036           content = element_info[element].change_page[i].target_element;
4037           is_player = ELEM_IS_PLAYER(content);
4038
4039           if (is_player && (found_rating < 3 ||
4040                             (found_rating == 3 && element < found_element)))
4041           {
4042             start_x = x;
4043             start_y = y;
4044
4045             found_rating = 3;
4046             found_element = element;
4047           }
4048         }
4049       }
4050
4051       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4052       {
4053         /* check for player created from custom element as explosion content */
4054         content = element_info[element].content.e[xx][yy];
4055         is_player = ELEM_IS_PLAYER(content);
4056
4057         if (is_player && (found_rating < 2 ||
4058                           (found_rating == 2 && element < found_element)))
4059         {
4060           start_x = x + xx - 1;
4061           start_y = y + yy - 1;
4062
4063           found_rating = 2;
4064           found_element = element;
4065         }
4066
4067         if (!CAN_CHANGE(element))
4068           continue;
4069
4070         for (i = 0; i < element_info[element].num_change_pages; i++)
4071         {
4072           /* check for player created from custom element as extended target */
4073           content =
4074             element_info[element].change_page[i].target_content.e[xx][yy];
4075
4076           is_player = ELEM_IS_PLAYER(content);
4077
4078           if (is_player && (found_rating < 1 ||
4079                             (found_rating == 1 && element < found_element)))
4080           {
4081             start_x = x + xx - 1;
4082             start_y = y + yy - 1;
4083
4084             found_rating = 1;
4085             found_element = element;
4086           }
4087         }
4088       }
4089     }
4090
4091     scroll_x = SCROLL_POSITION_X(start_x);
4092     scroll_y = SCROLL_POSITION_Y(start_y);
4093   }
4094   else
4095   {
4096     scroll_x = SCROLL_POSITION_X(local_player->jx);
4097     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4098   }
4099
4100   /* !!! FIX THIS (START) !!! */
4101   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4102   {
4103     InitGameEngine_EM();
4104   }
4105   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4106   {
4107     InitGameEngine_SP();
4108   }
4109   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4110   {
4111     InitGameEngine_MM();
4112   }
4113   else
4114   {
4115     DrawLevel(REDRAW_FIELD);
4116     DrawAllPlayers();
4117
4118     /* after drawing the level, correct some elements */
4119     if (game.timegate_time_left == 0)
4120       CloseAllOpenTimegates();
4121   }
4122
4123   /* blit playfield from scroll buffer to normal back buffer for fading in */
4124   BlitScreenToBitmap(backbuffer);
4125   /* !!! FIX THIS (END) !!! */
4126
4127   DrawMaskedBorder(fade_mask);
4128
4129   FadeIn(fade_mask);
4130
4131 #if 1
4132   // full screen redraw is required at this point in the following cases:
4133   // - special editor door undrawn when game was started from level editor
4134   // - drawing area (playfield) was changed and has to be removed completely
4135   redraw_mask = REDRAW_ALL;
4136   BackToFront();
4137 #endif
4138
4139   if (!game.restart_level)
4140   {
4141     /* copy default game door content to main double buffer */
4142
4143     /* !!! CHECK AGAIN !!! */
4144     SetPanelBackground();
4145     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4146     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4147   }
4148
4149   SetPanelBackground();
4150   SetDrawBackgroundMask(REDRAW_DOOR_1);
4151
4152   UpdateAndDisplayGameControlValues();
4153
4154   if (!game.restart_level)
4155   {
4156     UnmapGameButtons();
4157     UnmapTapeButtons();
4158
4159     FreeGameButtons();
4160     CreateGameButtons();
4161
4162     MapGameButtons();
4163     MapTapeButtons();
4164
4165     /* copy actual game door content to door double buffer for OpenDoor() */
4166     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4167
4168     OpenDoor(DOOR_OPEN_ALL);
4169
4170     KeyboardAutoRepeatOffUnlessAutoplay();
4171
4172 #if DEBUG_INIT_PLAYER
4173     DebugPrintPlayerStatus("Player status (final)");
4174 #endif
4175   }
4176
4177   UnmapAllGadgets();
4178
4179   MapGameButtons();
4180   MapTapeButtons();
4181
4182   if (!game.restart_level && !tape.playing)
4183   {
4184     LevelStats_incPlayed(level_nr);
4185
4186     SaveLevelSetup_SeriesInfo();
4187   }
4188
4189   game.restart_level = FALSE;
4190   game.restart_game_message = NULL;
4191
4192   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4193     InitGameActions_MM();
4194
4195   SaveEngineSnapshotToListInitial();
4196
4197   if (!game.restart_level)
4198   {
4199     PlaySound(SND_GAME_STARTING);
4200
4201     if (setup.sound_music)
4202       PlayLevelMusic();
4203   }
4204 }
4205
4206 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4207                         int actual_player_x, int actual_player_y)
4208 {
4209   /* this is used for non-R'n'D game engines to update certain engine values */
4210
4211   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4212   {
4213     actual_player_x = correctLevelPosX_EM(actual_player_x);
4214     actual_player_y = correctLevelPosY_EM(actual_player_y);
4215   }
4216
4217   /* needed to determine if sounds are played within the visible screen area */
4218   scroll_x = actual_scroll_x;
4219   scroll_y = actual_scroll_y;
4220
4221   /* needed to get player position for "follow finger" playing input method */
4222   local_player->jx = actual_player_x;
4223   local_player->jy = actual_player_y;
4224 }
4225
4226 void InitMovDir(int x, int y)
4227 {
4228   int i, element = Feld[x][y];
4229   static int xy[4][2] =
4230   {
4231     {  0, +1 },
4232     { +1,  0 },
4233     {  0, -1 },
4234     { -1,  0 }
4235   };
4236   static int direction[3][4] =
4237   {
4238     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4239     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4240     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4241   };
4242
4243   switch (element)
4244   {
4245     case EL_BUG_RIGHT:
4246     case EL_BUG_UP:
4247     case EL_BUG_LEFT:
4248     case EL_BUG_DOWN:
4249       Feld[x][y] = EL_BUG;
4250       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4251       break;
4252
4253     case EL_SPACESHIP_RIGHT:
4254     case EL_SPACESHIP_UP:
4255     case EL_SPACESHIP_LEFT:
4256     case EL_SPACESHIP_DOWN:
4257       Feld[x][y] = EL_SPACESHIP;
4258       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4259       break;
4260
4261     case EL_BD_BUTTERFLY_RIGHT:
4262     case EL_BD_BUTTERFLY_UP:
4263     case EL_BD_BUTTERFLY_LEFT:
4264     case EL_BD_BUTTERFLY_DOWN:
4265       Feld[x][y] = EL_BD_BUTTERFLY;
4266       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4267       break;
4268
4269     case EL_BD_FIREFLY_RIGHT:
4270     case EL_BD_FIREFLY_UP:
4271     case EL_BD_FIREFLY_LEFT:
4272     case EL_BD_FIREFLY_DOWN:
4273       Feld[x][y] = EL_BD_FIREFLY;
4274       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4275       break;
4276
4277     case EL_PACMAN_RIGHT:
4278     case EL_PACMAN_UP:
4279     case EL_PACMAN_LEFT:
4280     case EL_PACMAN_DOWN:
4281       Feld[x][y] = EL_PACMAN;
4282       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4283       break;
4284
4285     case EL_YAMYAM_LEFT:
4286     case EL_YAMYAM_RIGHT:
4287     case EL_YAMYAM_UP:
4288     case EL_YAMYAM_DOWN:
4289       Feld[x][y] = EL_YAMYAM;
4290       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4291       break;
4292
4293     case EL_SP_SNIKSNAK:
4294       MovDir[x][y] = MV_UP;
4295       break;
4296
4297     case EL_SP_ELECTRON:
4298       MovDir[x][y] = MV_LEFT;
4299       break;
4300
4301     case EL_MOLE_LEFT:
4302     case EL_MOLE_RIGHT:
4303     case EL_MOLE_UP:
4304     case EL_MOLE_DOWN:
4305       Feld[x][y] = EL_MOLE;
4306       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4307       break;
4308
4309     default:
4310       if (IS_CUSTOM_ELEMENT(element))
4311       {
4312         struct ElementInfo *ei = &element_info[element];
4313         int move_direction_initial = ei->move_direction_initial;
4314         int move_pattern = ei->move_pattern;
4315
4316         if (move_direction_initial == MV_START_PREVIOUS)
4317         {
4318           if (MovDir[x][y] != MV_NONE)
4319             return;
4320
4321           move_direction_initial = MV_START_AUTOMATIC;
4322         }
4323
4324         if (move_direction_initial == MV_START_RANDOM)
4325           MovDir[x][y] = 1 << RND(4);
4326         else if (move_direction_initial & MV_ANY_DIRECTION)
4327           MovDir[x][y] = move_direction_initial;
4328         else if (move_pattern == MV_ALL_DIRECTIONS ||
4329                  move_pattern == MV_TURNING_LEFT ||
4330                  move_pattern == MV_TURNING_RIGHT ||
4331                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4332                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4333                  move_pattern == MV_TURNING_RANDOM)
4334           MovDir[x][y] = 1 << RND(4);
4335         else if (move_pattern == MV_HORIZONTAL)
4336           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4337         else if (move_pattern == MV_VERTICAL)
4338           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4339         else if (move_pattern & MV_ANY_DIRECTION)
4340           MovDir[x][y] = element_info[element].move_pattern;
4341         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4342                  move_pattern == MV_ALONG_RIGHT_SIDE)
4343         {
4344           /* use random direction as default start direction */
4345           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4346             MovDir[x][y] = 1 << RND(4);
4347
4348           for (i = 0; i < NUM_DIRECTIONS; i++)
4349           {
4350             int x1 = x + xy[i][0];
4351             int y1 = y + xy[i][1];
4352
4353             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4354             {
4355               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4356                 MovDir[x][y] = direction[0][i];
4357               else
4358                 MovDir[x][y] = direction[1][i];
4359
4360               break;
4361             }
4362           }
4363         }                
4364       }
4365       else
4366       {
4367         MovDir[x][y] = 1 << RND(4);
4368
4369         if (element != EL_BUG &&
4370             element != EL_SPACESHIP &&
4371             element != EL_BD_BUTTERFLY &&
4372             element != EL_BD_FIREFLY)
4373           break;
4374
4375         for (i = 0; i < NUM_DIRECTIONS; i++)
4376         {
4377           int x1 = x + xy[i][0];
4378           int y1 = y + xy[i][1];
4379
4380           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4381           {
4382             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4383             {
4384               MovDir[x][y] = direction[0][i];
4385               break;
4386             }
4387             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4388                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4389             {
4390               MovDir[x][y] = direction[1][i];
4391               break;
4392             }
4393           }
4394         }
4395       }
4396       break;
4397   }
4398
4399   GfxDir[x][y] = MovDir[x][y];
4400 }
4401
4402 void InitAmoebaNr(int x, int y)
4403 {
4404   int i;
4405   int group_nr = AmoebeNachbarNr(x, y);
4406
4407   if (group_nr == 0)
4408   {
4409     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4410     {
4411       if (AmoebaCnt[i] == 0)
4412       {
4413         group_nr = i;
4414         break;
4415       }
4416     }
4417   }
4418
4419   AmoebaNr[x][y] = group_nr;
4420   AmoebaCnt[group_nr]++;
4421   AmoebaCnt2[group_nr]++;
4422 }
4423
4424 static void PlayerWins(struct PlayerInfo *player)
4425 {
4426   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4427       local_player->players_still_needed > 0)
4428     return;
4429
4430   player->LevelSolved = TRUE;
4431   player->GameOver = TRUE;
4432
4433   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4434                          level.native_em_level->lev->score :
4435                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4436                          game_mm.score :
4437                          player->score);
4438   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4439                           MM_HEALTH(game_mm.laser_overload_value) :
4440                           player->health);
4441
4442   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4443                                       TimeLeft);
4444   player->LevelSolved_CountingScore = player->score_final;
4445   player->LevelSolved_CountingHealth = player->health_final;
4446 }
4447
4448 void GameWon()
4449 {
4450   static int time_count_steps;
4451   static int time, time_final;
4452   static int score, score_final;
4453   static int health, health_final;
4454   static int game_over_delay_1 = 0;
4455   static int game_over_delay_2 = 0;
4456   static int game_over_delay_3 = 0;
4457   int game_over_delay_value_1 = 50;
4458   int game_over_delay_value_2 = 25;
4459   int game_over_delay_value_3 = 50;
4460
4461   if (!local_player->LevelSolved_GameWon)
4462   {
4463     int i;
4464
4465     /* do not start end game actions before the player stops moving (to exit) */
4466     if (local_player->MovPos)
4467       return;
4468
4469     local_player->LevelSolved_GameWon = TRUE;
4470     local_player->LevelSolved_SaveTape = tape.recording;
4471     local_player->LevelSolved_SaveScore = !tape.playing;
4472
4473     if (!tape.playing)
4474     {
4475       LevelStats_incSolved(level_nr);
4476
4477       SaveLevelSetup_SeriesInfo();
4478     }
4479
4480     if (tape.auto_play)         /* tape might already be stopped here */
4481       tape.auto_play_level_solved = TRUE;
4482
4483     TapeStop();
4484
4485     game_over_delay_1 = 0;
4486     game_over_delay_2 = 0;
4487     game_over_delay_3 = game_over_delay_value_3;
4488
4489     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4490     score = score_final = local_player->score_final;
4491     health = health_final = local_player->health_final;
4492
4493     if (level.score[SC_TIME_BONUS] > 0)
4494     {
4495       if (TimeLeft > 0)
4496       {
4497         time_final = 0;
4498         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4499       }
4500       else if (game.no_time_limit && TimePlayed < 999)
4501       {
4502         time_final = 999;
4503         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4504       }
4505
4506       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4507
4508       game_over_delay_1 = game_over_delay_value_1;
4509
4510       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4511       {
4512         health_final = 0;
4513         score_final += health * level.score[SC_TIME_BONUS];
4514
4515         game_over_delay_2 = game_over_delay_value_2;
4516       }
4517
4518       local_player->score_final = score_final;
4519       local_player->health_final = health_final;
4520     }
4521
4522     if (level_editor_test_game)
4523     {
4524       time = time_final;
4525       score = score_final;
4526
4527       local_player->LevelSolved_CountingTime = time;
4528       local_player->LevelSolved_CountingScore = score;
4529
4530       game_panel_controls[GAME_PANEL_TIME].value = time;
4531       game_panel_controls[GAME_PANEL_SCORE].value = score;
4532
4533       DisplayGameControlValues();
4534     }
4535
4536     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4537     {
4538       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4539       {
4540         /* close exit door after last player */
4541         if ((AllPlayersGone &&
4542              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4543               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4544               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4545             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4546             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4547         {
4548           int element = Feld[ExitX][ExitY];
4549
4550           Feld[ExitX][ExitY] =
4551             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4552              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4553              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4554              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4555              EL_EM_STEEL_EXIT_CLOSING);
4556
4557           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4558         }
4559
4560         /* player disappears */
4561         DrawLevelField(ExitX, ExitY);
4562       }
4563
4564       for (i = 0; i < MAX_PLAYERS; i++)
4565       {
4566         struct PlayerInfo *player = &stored_player[i];
4567
4568         if (player->present)
4569         {
4570           RemovePlayer(player);
4571
4572           /* player disappears */
4573           DrawLevelField(player->jx, player->jy);
4574         }
4575       }
4576     }
4577
4578     PlaySound(SND_GAME_WINNING);
4579   }
4580
4581   if (game_over_delay_1 > 0)
4582   {
4583     game_over_delay_1--;
4584
4585     return;
4586   }
4587
4588   if (time != time_final)
4589   {
4590     int time_to_go = ABS(time_final - time);
4591     int time_count_dir = (time < time_final ? +1 : -1);
4592
4593     if (time_to_go < time_count_steps)
4594       time_count_steps = 1;
4595
4596     time  += time_count_steps * time_count_dir;
4597     score += time_count_steps * level.score[SC_TIME_BONUS];
4598
4599     local_player->LevelSolved_CountingTime = time;
4600     local_player->LevelSolved_CountingScore = score;
4601
4602     game_panel_controls[GAME_PANEL_TIME].value = time;
4603     game_panel_controls[GAME_PANEL_SCORE].value = score;
4604
4605     DisplayGameControlValues();
4606
4607     if (time == time_final)
4608       StopSound(SND_GAME_LEVELTIME_BONUS);
4609     else if (setup.sound_loops)
4610       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4611     else
4612       PlaySound(SND_GAME_LEVELTIME_BONUS);
4613
4614     return;
4615   }
4616
4617   if (game_over_delay_2 > 0)
4618   {
4619     game_over_delay_2--;
4620
4621     return;
4622   }
4623
4624   if (health != health_final)
4625   {
4626     int health_count_dir = (health < health_final ? +1 : -1);
4627
4628     health += health_count_dir;
4629     score  += level.score[SC_TIME_BONUS];
4630
4631     local_player->LevelSolved_CountingHealth = health;
4632     local_player->LevelSolved_CountingScore = score;
4633
4634     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4635     game_panel_controls[GAME_PANEL_SCORE].value = score;
4636
4637     DisplayGameControlValues();
4638
4639     if (health == health_final)
4640       StopSound(SND_GAME_LEVELTIME_BONUS);
4641     else if (setup.sound_loops)
4642       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4643     else
4644       PlaySound(SND_GAME_LEVELTIME_BONUS);
4645
4646     return;
4647   }
4648
4649   local_player->LevelSolved_PanelOff = TRUE;
4650
4651   if (game_over_delay_3 > 0)
4652   {
4653     game_over_delay_3--;
4654
4655     return;
4656   }
4657
4658   GameEnd();
4659 }
4660
4661 void GameEnd()
4662 {
4663   int hi_pos;
4664   int last_level_nr = level_nr;
4665
4666   local_player->LevelSolved_GameEnd = TRUE;
4667
4668   if (local_player->LevelSolved_SaveTape)
4669   {
4670     /* make sure that request dialog to save tape does not open door again */
4671     if (!global.use_envelope_request)
4672       CloseDoor(DOOR_CLOSE_1);
4673
4674     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4675   }
4676
4677   /* if no tape is to be saved, close both doors simultaneously */
4678   CloseDoor(DOOR_CLOSE_ALL);
4679
4680   if (level_editor_test_game)
4681   {
4682     SetGameStatus(GAME_MODE_MAIN);
4683
4684     DrawMainMenu();
4685
4686     return;
4687   }
4688
4689   if (!local_player->LevelSolved_SaveScore)
4690   {
4691     SetGameStatus(GAME_MODE_MAIN);
4692
4693     DrawMainMenu();
4694
4695     return;
4696   }
4697
4698   if (level_nr == leveldir_current->handicap_level)
4699   {
4700     leveldir_current->handicap_level++;
4701
4702     SaveLevelSetup_SeriesInfo();
4703   }
4704
4705   if (setup.increment_levels &&
4706       level_nr < leveldir_current->last_level)
4707   {
4708     level_nr++;         /* advance to next level */
4709     TapeErase();        /* start with empty tape */
4710
4711     if (setup.auto_play_next_level)
4712     {
4713       LoadLevel(level_nr);
4714
4715       SaveLevelSetup_SeriesInfo();
4716     }
4717   }
4718
4719   hi_pos = NewHiScore(last_level_nr);
4720
4721   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4722   {
4723     SetGameStatus(GAME_MODE_SCORES);
4724
4725     DrawHallOfFame(last_level_nr, hi_pos);
4726   }
4727   else if (!setup.auto_play_next_level || !setup.increment_levels)
4728   {
4729     SetGameStatus(GAME_MODE_MAIN);
4730
4731     DrawMainMenu();
4732   }
4733   else
4734   {
4735     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4736   }
4737 }
4738
4739 int NewHiScore(int level_nr)
4740 {
4741   int k, l;
4742   int position = -1;
4743   boolean one_score_entry_per_name = !program.many_scores_per_name;
4744
4745   LoadScore(level_nr);
4746
4747   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4748       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4749     return -1;
4750
4751   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4752   {
4753     if (local_player->score_final > highscore[k].Score)
4754     {
4755       /* player has made it to the hall of fame */
4756
4757       if (k < MAX_SCORE_ENTRIES - 1)
4758       {
4759         int m = MAX_SCORE_ENTRIES - 1;
4760
4761         if (one_score_entry_per_name)
4762         {
4763           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4764             if (strEqual(setup.player_name, highscore[l].Name))
4765               m = l;
4766
4767           if (m == k)   /* player's new highscore overwrites his old one */
4768             goto put_into_list;
4769         }
4770
4771         for (l = m; l > k; l--)
4772         {
4773           strcpy(highscore[l].Name, highscore[l - 1].Name);
4774           highscore[l].Score = highscore[l - 1].Score;
4775         }
4776       }
4777
4778       put_into_list:
4779
4780       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4781       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4782       highscore[k].Score = local_player->score_final; 
4783       position = k;
4784
4785       break;
4786     }
4787     else if (one_score_entry_per_name &&
4788              !strncmp(setup.player_name, highscore[k].Name,
4789                       MAX_PLAYER_NAME_LEN))
4790       break;    /* player already there with a higher score */
4791   }
4792
4793   if (position >= 0) 
4794     SaveScore(level_nr);
4795
4796   return position;
4797 }
4798
4799 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4800 {
4801   int element = Feld[x][y];
4802   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4803   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4804   int horiz_move = (dx != 0);
4805   int sign = (horiz_move ? dx : dy);
4806   int step = sign * element_info[element].move_stepsize;
4807
4808   /* special values for move stepsize for spring and things on conveyor belt */
4809   if (horiz_move)
4810   {
4811     if (CAN_FALL(element) &&
4812         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4813       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4814     else if (element == EL_SPRING)
4815       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4816   }
4817
4818   return step;
4819 }
4820
4821 inline static int getElementMoveStepsize(int x, int y)
4822 {
4823   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4824 }
4825
4826 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4827 {
4828   if (player->GfxAction != action || player->GfxDir != dir)
4829   {
4830     player->GfxAction = action;
4831     player->GfxDir = dir;
4832     player->Frame = 0;
4833     player->StepFrame = 0;
4834   }
4835 }
4836
4837 static void ResetGfxFrame(int x, int y)
4838 {
4839   // profiling showed that "autotest" spends 10~20% of its time in this function
4840   if (DrawingDeactivatedField())
4841     return;
4842
4843   int element = Feld[x][y];
4844   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4845
4846   if (graphic_info[graphic].anim_global_sync)
4847     GfxFrame[x][y] = FrameCounter;
4848   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4849     GfxFrame[x][y] = CustomValue[x][y];
4850   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4851     GfxFrame[x][y] = element_info[element].collect_score;
4852   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4853     GfxFrame[x][y] = ChangeDelay[x][y];
4854 }
4855
4856 static void ResetGfxAnimation(int x, int y)
4857 {
4858   GfxAction[x][y] = ACTION_DEFAULT;
4859   GfxDir[x][y] = MovDir[x][y];
4860   GfxFrame[x][y] = 0;
4861
4862   ResetGfxFrame(x, y);
4863 }
4864
4865 static void ResetRandomAnimationValue(int x, int y)
4866 {
4867   GfxRandom[x][y] = INIT_GFX_RANDOM();
4868 }
4869
4870 void InitMovingField(int x, int y, int direction)
4871 {
4872   int element = Feld[x][y];
4873   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4874   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4875   int newx = x + dx;
4876   int newy = y + dy;
4877   boolean is_moving_before, is_moving_after;
4878
4879   /* check if element was/is moving or being moved before/after mode change */
4880   is_moving_before = (WasJustMoving[x][y] != 0);
4881   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4882
4883   /* reset animation only for moving elements which change direction of moving
4884      or which just started or stopped moving
4885      (else CEs with property "can move" / "not moving" are reset each frame) */
4886   if (is_moving_before != is_moving_after ||
4887       direction != MovDir[x][y])
4888     ResetGfxAnimation(x, y);
4889
4890   MovDir[x][y] = direction;
4891   GfxDir[x][y] = direction;
4892
4893   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4894                      direction == MV_DOWN && CAN_FALL(element) ?
4895                      ACTION_FALLING : ACTION_MOVING);
4896
4897   /* this is needed for CEs with property "can move" / "not moving" */
4898
4899   if (is_moving_after)
4900   {
4901     if (Feld[newx][newy] == EL_EMPTY)
4902       Feld[newx][newy] = EL_BLOCKED;
4903
4904     MovDir[newx][newy] = MovDir[x][y];
4905
4906     CustomValue[newx][newy] = CustomValue[x][y];
4907
4908     GfxFrame[newx][newy] = GfxFrame[x][y];
4909     GfxRandom[newx][newy] = GfxRandom[x][y];
4910     GfxAction[newx][newy] = GfxAction[x][y];
4911     GfxDir[newx][newy] = GfxDir[x][y];
4912   }
4913 }
4914
4915 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4916 {
4917   int direction = MovDir[x][y];
4918   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4919   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4920
4921   *goes_to_x = newx;
4922   *goes_to_y = newy;
4923 }
4924
4925 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4926 {
4927   int oldx = x, oldy = y;
4928   int direction = MovDir[x][y];
4929
4930   if (direction == MV_LEFT)
4931     oldx++;
4932   else if (direction == MV_RIGHT)
4933     oldx--;
4934   else if (direction == MV_UP)
4935     oldy++;
4936   else if (direction == MV_DOWN)
4937     oldy--;
4938
4939   *comes_from_x = oldx;
4940   *comes_from_y = oldy;
4941 }
4942
4943 int MovingOrBlocked2Element(int x, int y)
4944 {
4945   int element = Feld[x][y];
4946
4947   if (element == EL_BLOCKED)
4948   {
4949     int oldx, oldy;
4950
4951     Blocked2Moving(x, y, &oldx, &oldy);
4952     return Feld[oldx][oldy];
4953   }
4954   else
4955     return element;
4956 }
4957
4958 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4959 {
4960   /* like MovingOrBlocked2Element(), but if element is moving
4961      and (x,y) is the field the moving element is just leaving,
4962      return EL_BLOCKED instead of the element value */
4963   int element = Feld[x][y];
4964
4965   if (IS_MOVING(x, y))
4966   {
4967     if (element == EL_BLOCKED)
4968     {
4969       int oldx, oldy;
4970
4971       Blocked2Moving(x, y, &oldx, &oldy);
4972       return Feld[oldx][oldy];
4973     }
4974     else
4975       return EL_BLOCKED;
4976   }
4977   else
4978     return element;
4979 }
4980
4981 static void RemoveField(int x, int y)
4982 {
4983   Feld[x][y] = EL_EMPTY;
4984
4985   MovPos[x][y] = 0;
4986   MovDir[x][y] = 0;
4987   MovDelay[x][y] = 0;
4988
4989   CustomValue[x][y] = 0;
4990
4991   AmoebaNr[x][y] = 0;
4992   ChangeDelay[x][y] = 0;
4993   ChangePage[x][y] = -1;
4994   Pushed[x][y] = FALSE;
4995
4996   GfxElement[x][y] = EL_UNDEFINED;
4997   GfxAction[x][y] = ACTION_DEFAULT;
4998   GfxDir[x][y] = MV_NONE;
4999 }
5000
5001 void RemoveMovingField(int x, int y)
5002 {
5003   int oldx = x, oldy = y, newx = x, newy = y;
5004   int element = Feld[x][y];
5005   int next_element = EL_UNDEFINED;
5006
5007   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5008     return;
5009
5010   if (IS_MOVING(x, y))
5011   {
5012     Moving2Blocked(x, y, &newx, &newy);
5013
5014     if (Feld[newx][newy] != EL_BLOCKED)
5015     {
5016       /* element is moving, but target field is not free (blocked), but
5017          already occupied by something different (example: acid pool);
5018          in this case, only remove the moving field, but not the target */
5019
5020       RemoveField(oldx, oldy);
5021
5022       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5023
5024       TEST_DrawLevelField(oldx, oldy);
5025
5026       return;
5027     }
5028   }
5029   else if (element == EL_BLOCKED)
5030   {
5031     Blocked2Moving(x, y, &oldx, &oldy);
5032     if (!IS_MOVING(oldx, oldy))
5033       return;
5034   }
5035
5036   if (element == EL_BLOCKED &&
5037       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5038        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5039        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5040        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5041        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5042        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5043     next_element = get_next_element(Feld[oldx][oldy]);
5044
5045   RemoveField(oldx, oldy);
5046   RemoveField(newx, newy);
5047
5048   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5049
5050   if (next_element != EL_UNDEFINED)
5051     Feld[oldx][oldy] = next_element;
5052
5053   TEST_DrawLevelField(oldx, oldy);
5054   TEST_DrawLevelField(newx, newy);
5055 }
5056
5057 void DrawDynamite(int x, int y)
5058 {
5059   int sx = SCREENX(x), sy = SCREENY(y);
5060   int graphic = el2img(Feld[x][y]);
5061   int frame;
5062
5063   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5064     return;
5065
5066   if (IS_WALKABLE_INSIDE(Back[x][y]))
5067     return;
5068
5069   if (Back[x][y])
5070     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5071   else if (Store[x][y])
5072     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5073
5074   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5075
5076   if (Back[x][y] || Store[x][y])
5077     DrawGraphicThruMask(sx, sy, graphic, frame);
5078   else
5079     DrawGraphic(sx, sy, graphic, frame);
5080 }
5081
5082 void CheckDynamite(int x, int y)
5083 {
5084   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5085   {
5086     MovDelay[x][y]--;
5087
5088     if (MovDelay[x][y] != 0)
5089     {
5090       DrawDynamite(x, y);
5091       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5092
5093       return;
5094     }
5095   }
5096
5097   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5098
5099   Bang(x, y);
5100 }
5101
5102 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5103 {
5104   boolean num_checked_players = 0;
5105   int i;
5106
5107   for (i = 0; i < MAX_PLAYERS; i++)
5108   {
5109     if (stored_player[i].active)
5110     {
5111       int sx = stored_player[i].jx;
5112       int sy = stored_player[i].jy;
5113
5114       if (num_checked_players == 0)
5115       {
5116         *sx1 = *sx2 = sx;
5117         *sy1 = *sy2 = sy;
5118       }
5119       else
5120       {
5121         *sx1 = MIN(*sx1, sx);
5122         *sy1 = MIN(*sy1, sy);
5123         *sx2 = MAX(*sx2, sx);
5124         *sy2 = MAX(*sy2, sy);
5125       }
5126
5127       num_checked_players++;
5128     }
5129   }
5130 }
5131
5132 static boolean checkIfAllPlayersFitToScreen_RND()
5133 {
5134   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5135
5136   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5137
5138   return (sx2 - sx1 < SCR_FIELDX &&
5139           sy2 - sy1 < SCR_FIELDY);
5140 }
5141
5142 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5143 {
5144   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5145
5146   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5147
5148   *sx = (sx1 + sx2) / 2;
5149   *sy = (sy1 + sy2) / 2;
5150 }
5151
5152 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5153                         boolean center_screen, boolean quick_relocation)
5154 {
5155   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5156   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5157   boolean no_delay = (tape.warp_forward);
5158   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5159   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5160   int new_scroll_x, new_scroll_y;
5161
5162   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5163   {
5164     /* case 1: quick relocation inside visible screen (without scrolling) */
5165
5166     RedrawPlayfield();
5167
5168     return;
5169   }
5170
5171   if (!level.shifted_relocation || center_screen)
5172   {
5173     /* relocation _with_ centering of screen */
5174
5175     new_scroll_x = SCROLL_POSITION_X(x);
5176     new_scroll_y = SCROLL_POSITION_Y(y);
5177   }
5178   else
5179   {
5180     /* relocation _without_ centering of screen */
5181
5182     int center_scroll_x = SCROLL_POSITION_X(old_x);
5183     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5184     int offset_x = x + (scroll_x - center_scroll_x);
5185     int offset_y = y + (scroll_y - center_scroll_y);
5186
5187     /* for new screen position, apply previous offset to center position */
5188     new_scroll_x = SCROLL_POSITION_X(offset_x);
5189     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5190   }
5191
5192   if (quick_relocation)
5193   {
5194     /* case 2: quick relocation (redraw without visible scrolling) */
5195
5196     scroll_x = new_scroll_x;
5197     scroll_y = new_scroll_y;
5198
5199     RedrawPlayfield();
5200
5201     return;
5202   }
5203
5204   /* case 3: visible relocation (with scrolling to new position) */
5205
5206   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5207
5208   SetVideoFrameDelay(wait_delay_value);
5209
5210   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5211   {
5212     int dx = 0, dy = 0;
5213     int fx = FX, fy = FY;
5214
5215     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5216     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5217
5218     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5219       break;
5220
5221     scroll_x -= dx;
5222     scroll_y -= dy;
5223
5224     fx += dx * TILEX / 2;
5225     fy += dy * TILEY / 2;
5226
5227     ScrollLevel(dx, dy);
5228     DrawAllPlayers();
5229
5230     /* scroll in two steps of half tile size to make things smoother */
5231     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5232
5233     /* scroll second step to align at full tile size */
5234     BlitScreenToBitmap(window);
5235   }
5236
5237   DrawAllPlayers();
5238   BackToFront();
5239
5240   SetVideoFrameDelay(frame_delay_value_old);
5241 }
5242
5243 void RelocatePlayer(int jx, int jy, int el_player_raw)
5244 {
5245   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5246   int player_nr = GET_PLAYER_NR(el_player);
5247   struct PlayerInfo *player = &stored_player[player_nr];
5248   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5249   boolean no_delay = (tape.warp_forward);
5250   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5251   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5252   int old_jx = player->jx;
5253   int old_jy = player->jy;
5254   int old_element = Feld[old_jx][old_jy];
5255   int element = Feld[jx][jy];
5256   boolean player_relocated = (old_jx != jx || old_jy != jy);
5257
5258   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5259   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5260   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5261   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5262   int leave_side_horiz = move_dir_horiz;
5263   int leave_side_vert  = move_dir_vert;
5264   int enter_side = enter_side_horiz | enter_side_vert;
5265   int leave_side = leave_side_horiz | leave_side_vert;
5266
5267   if (player->GameOver)         /* do not reanimate dead player */
5268     return;
5269
5270   if (!player_relocated)        /* no need to relocate the player */
5271     return;
5272
5273   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5274   {
5275     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5276     DrawLevelField(jx, jy);
5277   }
5278
5279   if (player->present)
5280   {
5281     while (player->MovPos)
5282     {
5283       ScrollPlayer(player, SCROLL_GO_ON);
5284       ScrollScreen(NULL, SCROLL_GO_ON);
5285
5286       AdvanceFrameAndPlayerCounters(player->index_nr);
5287
5288       DrawPlayer(player);
5289
5290       BackToFront_WithFrameDelay(wait_delay_value);
5291     }
5292
5293     DrawPlayer(player);         /* needed here only to cleanup last field */
5294     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5295
5296     player->is_moving = FALSE;
5297   }
5298
5299   if (IS_CUSTOM_ELEMENT(old_element))
5300     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5301                                CE_LEFT_BY_PLAYER,
5302                                player->index_bit, leave_side);
5303
5304   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5305                                       CE_PLAYER_LEAVES_X,
5306                                       player->index_bit, leave_side);
5307
5308   Feld[jx][jy] = el_player;
5309   InitPlayerField(jx, jy, el_player, TRUE);
5310
5311   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5312      possible that the relocation target field did not contain a player element,
5313      but a walkable element, to which the new player was relocated -- in this
5314      case, restore that (already initialized!) element on the player field */
5315   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5316   {
5317     Feld[jx][jy] = element;     /* restore previously existing element */
5318   }
5319
5320   /* only visually relocate centered player */
5321   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5322                      FALSE, level.instant_relocation);
5323
5324   TestIfPlayerTouchesBadThing(jx, jy);
5325   TestIfPlayerTouchesCustomElement(jx, jy);
5326
5327   if (IS_CUSTOM_ELEMENT(element))
5328     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5329                                player->index_bit, enter_side);
5330
5331   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5332                                       player->index_bit, enter_side);
5333
5334   if (player->is_switching)
5335   {
5336     /* ensure that relocation while still switching an element does not cause
5337        a new element to be treated as also switched directly after relocation
5338        (this is important for teleporter switches that teleport the player to
5339        a place where another teleporter switch is in the same direction, which
5340        would then incorrectly be treated as immediately switched before the
5341        direction key that caused the switch was released) */
5342
5343     player->switch_x += jx - old_jx;
5344     player->switch_y += jy - old_jy;
5345   }
5346 }
5347
5348 void Explode(int ex, int ey, int phase, int mode)
5349 {
5350   int x, y;
5351   int last_phase;
5352   int border_element;
5353
5354   /* !!! eliminate this variable !!! */
5355   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5356
5357   if (game.explosions_delayed)
5358   {
5359     ExplodeField[ex][ey] = mode;
5360     return;
5361   }
5362
5363   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5364   {
5365     int center_element = Feld[ex][ey];
5366     int artwork_element, explosion_element;     /* set these values later */
5367
5368     /* remove things displayed in background while burning dynamite */
5369     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5370       Back[ex][ey] = 0;
5371
5372     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5373     {
5374       /* put moving element to center field (and let it explode there) */
5375       center_element = MovingOrBlocked2Element(ex, ey);
5376       RemoveMovingField(ex, ey);
5377       Feld[ex][ey] = center_element;
5378     }
5379
5380     /* now "center_element" is finally determined -- set related values now */
5381     artwork_element = center_element;           /* for custom player artwork */
5382     explosion_element = center_element;         /* for custom player artwork */
5383
5384     if (IS_PLAYER(ex, ey))
5385     {
5386       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5387
5388       artwork_element = stored_player[player_nr].artwork_element;
5389
5390       if (level.use_explosion_element[player_nr])
5391       {
5392         explosion_element = level.explosion_element[player_nr];
5393         artwork_element = explosion_element;
5394       }
5395     }
5396
5397     if (mode == EX_TYPE_NORMAL ||
5398         mode == EX_TYPE_CENTER ||
5399         mode == EX_TYPE_CROSS)
5400       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5401
5402     last_phase = element_info[explosion_element].explosion_delay + 1;
5403
5404     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5405     {
5406       int xx = x - ex + 1;
5407       int yy = y - ey + 1;
5408       int element;
5409
5410       if (!IN_LEV_FIELD(x, y) ||
5411           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5412           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5413         continue;
5414
5415       element = Feld[x][y];
5416
5417       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5418       {
5419         element = MovingOrBlocked2Element(x, y);
5420
5421         if (!IS_EXPLOSION_PROOF(element))
5422           RemoveMovingField(x, y);
5423       }
5424
5425       /* indestructible elements can only explode in center (but not flames) */
5426       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5427                                            mode == EX_TYPE_BORDER)) ||
5428           element == EL_FLAMES)
5429         continue;
5430
5431       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5432          behaviour, for example when touching a yamyam that explodes to rocks
5433          with active deadly shield, a rock is created under the player !!! */
5434       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5435 #if 0
5436       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5437           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5438            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5439 #else
5440       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5441 #endif
5442       {
5443         if (IS_ACTIVE_BOMB(element))
5444         {
5445           /* re-activate things under the bomb like gate or penguin */
5446           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5447           Back[x][y] = 0;
5448         }
5449
5450         continue;
5451       }
5452
5453       /* save walkable background elements while explosion on same tile */
5454       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5455           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5456         Back[x][y] = element;
5457
5458       /* ignite explodable elements reached by other explosion */
5459       if (element == EL_EXPLOSION)
5460         element = Store2[x][y];
5461
5462       if (AmoebaNr[x][y] &&
5463           (element == EL_AMOEBA_FULL ||
5464            element == EL_BD_AMOEBA ||
5465            element == EL_AMOEBA_GROWING))
5466       {
5467         AmoebaCnt[AmoebaNr[x][y]]--;
5468         AmoebaCnt2[AmoebaNr[x][y]]--;
5469       }
5470
5471       RemoveField(x, y);
5472
5473       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5474       {
5475         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5476
5477         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5478
5479         if (PLAYERINFO(ex, ey)->use_murphy)
5480           Store[x][y] = EL_EMPTY;
5481       }
5482
5483       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5484          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5485       else if (ELEM_IS_PLAYER(center_element))
5486         Store[x][y] = EL_EMPTY;
5487       else if (center_element == EL_YAMYAM)
5488         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5489       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5490         Store[x][y] = element_info[center_element].content.e[xx][yy];
5491 #if 1
5492       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5493          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5494          otherwise) -- FIX THIS !!! */
5495       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5496         Store[x][y] = element_info[element].content.e[1][1];
5497 #else
5498       else if (!CAN_EXPLODE(element))
5499         Store[x][y] = element_info[element].content.e[1][1];
5500 #endif
5501       else
5502         Store[x][y] = EL_EMPTY;
5503
5504       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5505           center_element == EL_AMOEBA_TO_DIAMOND)
5506         Store2[x][y] = element;
5507
5508       Feld[x][y] = EL_EXPLOSION;
5509       GfxElement[x][y] = artwork_element;
5510
5511       ExplodePhase[x][y] = 1;
5512       ExplodeDelay[x][y] = last_phase;
5513
5514       Stop[x][y] = TRUE;
5515     }
5516
5517     if (center_element == EL_YAMYAM)
5518       game.yamyam_content_nr =
5519         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5520
5521     return;
5522   }
5523
5524   if (Stop[ex][ey])
5525     return;
5526
5527   x = ex;
5528   y = ey;
5529
5530   if (phase == 1)
5531     GfxFrame[x][y] = 0;         /* restart explosion animation */
5532
5533   last_phase = ExplodeDelay[x][y];
5534
5535   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5536
5537   /* this can happen if the player leaves an explosion just in time */
5538   if (GfxElement[x][y] == EL_UNDEFINED)
5539     GfxElement[x][y] = EL_EMPTY;
5540
5541   border_element = Store2[x][y];
5542   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5543     border_element = StorePlayer[x][y];
5544
5545   if (phase == element_info[border_element].ignition_delay ||
5546       phase == last_phase)
5547   {
5548     boolean border_explosion = FALSE;
5549
5550     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5551         !PLAYER_EXPLOSION_PROTECTED(x, y))
5552     {
5553       KillPlayerUnlessExplosionProtected(x, y);
5554       border_explosion = TRUE;
5555     }
5556     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5557     {
5558       Feld[x][y] = Store2[x][y];
5559       Store2[x][y] = 0;
5560       Bang(x, y);
5561       border_explosion = TRUE;
5562     }
5563     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5564     {
5565       AmoebeUmwandeln(x, y);
5566       Store2[x][y] = 0;
5567       border_explosion = TRUE;
5568     }
5569
5570     /* if an element just explodes due to another explosion (chain-reaction),
5571        do not immediately end the new explosion when it was the last frame of
5572        the explosion (as it would be done in the following "if"-statement!) */
5573     if (border_explosion && phase == last_phase)
5574       return;
5575   }
5576
5577   if (phase == last_phase)
5578   {
5579     int element;
5580
5581     element = Feld[x][y] = Store[x][y];
5582     Store[x][y] = Store2[x][y] = 0;
5583     GfxElement[x][y] = EL_UNDEFINED;
5584
5585     /* player can escape from explosions and might therefore be still alive */
5586     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5587         element <= EL_PLAYER_IS_EXPLODING_4)
5588     {
5589       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5590       int explosion_element = EL_PLAYER_1 + player_nr;
5591       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5592       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5593
5594       if (level.use_explosion_element[player_nr])
5595         explosion_element = level.explosion_element[player_nr];
5596
5597       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5598                     element_info[explosion_element].content.e[xx][yy]);
5599     }
5600
5601     /* restore probably existing indestructible background element */
5602     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5603       element = Feld[x][y] = Back[x][y];
5604     Back[x][y] = 0;
5605
5606     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5607     GfxDir[x][y] = MV_NONE;
5608     ChangeDelay[x][y] = 0;
5609     ChangePage[x][y] = -1;
5610
5611     CustomValue[x][y] = 0;
5612
5613     InitField_WithBug2(x, y, FALSE);
5614
5615     TEST_DrawLevelField(x, y);
5616
5617     TestIfElementTouchesCustomElement(x, y);
5618
5619     if (GFX_CRUMBLED(element))
5620       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5621
5622     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5623       StorePlayer[x][y] = 0;
5624
5625     if (ELEM_IS_PLAYER(element))
5626       RelocatePlayer(x, y, element);
5627   }
5628   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5629   {
5630     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5631     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5632
5633     if (phase == delay)
5634       TEST_DrawLevelFieldCrumbled(x, y);
5635
5636     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5637     {
5638       DrawLevelElement(x, y, Back[x][y]);
5639       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5640     }
5641     else if (IS_WALKABLE_UNDER(Back[x][y]))
5642     {
5643       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5644       DrawLevelElementThruMask(x, y, Back[x][y]);
5645     }
5646     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5647       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5648   }
5649 }
5650
5651 void DynaExplode(int ex, int ey)
5652 {
5653   int i, j;
5654   int dynabomb_element = Feld[ex][ey];
5655   int dynabomb_size = 1;
5656   boolean dynabomb_xl = FALSE;
5657   struct PlayerInfo *player;
5658   static int xy[4][2] =
5659   {
5660     { 0, -1 },
5661     { -1, 0 },
5662     { +1, 0 },
5663     { 0, +1 }
5664   };
5665
5666   if (IS_ACTIVE_BOMB(dynabomb_element))
5667   {
5668     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5669     dynabomb_size = player->dynabomb_size;
5670     dynabomb_xl = player->dynabomb_xl;
5671     player->dynabombs_left++;
5672   }
5673
5674   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5675
5676   for (i = 0; i < NUM_DIRECTIONS; i++)
5677   {
5678     for (j = 1; j <= dynabomb_size; j++)
5679     {
5680       int x = ex + j * xy[i][0];
5681       int y = ey + j * xy[i][1];
5682       int element;
5683
5684       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5685         break;
5686
5687       element = Feld[x][y];
5688
5689       /* do not restart explosions of fields with active bombs */
5690       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5691         continue;
5692
5693       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5694
5695       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5696           !IS_DIGGABLE(element) && !dynabomb_xl)
5697         break;
5698     }
5699   }
5700 }
5701
5702 void Bang(int x, int y)
5703 {
5704   int element = MovingOrBlocked2Element(x, y);
5705   int explosion_type = EX_TYPE_NORMAL;
5706
5707   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5708   {
5709     struct PlayerInfo *player = PLAYERINFO(x, y);
5710
5711     element = Feld[x][y] = player->initial_element;
5712
5713     if (level.use_explosion_element[player->index_nr])
5714     {
5715       int explosion_element = level.explosion_element[player->index_nr];
5716
5717       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5718         explosion_type = EX_TYPE_CROSS;
5719       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5720         explosion_type = EX_TYPE_CENTER;
5721     }
5722   }
5723
5724   switch (element)
5725   {
5726     case EL_BUG:
5727     case EL_SPACESHIP:
5728     case EL_BD_BUTTERFLY:
5729     case EL_BD_FIREFLY:
5730     case EL_YAMYAM:
5731     case EL_DARK_YAMYAM:
5732     case EL_ROBOT:
5733     case EL_PACMAN:
5734     case EL_MOLE:
5735       RaiseScoreElement(element);
5736       break;
5737
5738     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5739     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5740     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5741     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5742     case EL_DYNABOMB_INCREASE_NUMBER:
5743     case EL_DYNABOMB_INCREASE_SIZE:
5744     case EL_DYNABOMB_INCREASE_POWER:
5745       explosion_type = EX_TYPE_DYNA;
5746       break;
5747
5748     case EL_DC_LANDMINE:
5749       explosion_type = EX_TYPE_CENTER;
5750       break;
5751
5752     case EL_PENGUIN:
5753     case EL_LAMP:
5754     case EL_LAMP_ACTIVE:
5755     case EL_AMOEBA_TO_DIAMOND:
5756       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5757         explosion_type = EX_TYPE_CENTER;
5758       break;
5759
5760     default:
5761       if (element_info[element].explosion_type == EXPLODES_CROSS)
5762         explosion_type = EX_TYPE_CROSS;
5763       else if (element_info[element].explosion_type == EXPLODES_1X1)
5764         explosion_type = EX_TYPE_CENTER;
5765       break;
5766   }
5767
5768   if (explosion_type == EX_TYPE_DYNA)
5769     DynaExplode(x, y);
5770   else
5771     Explode(x, y, EX_PHASE_START, explosion_type);
5772
5773   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5774 }
5775
5776 void SplashAcid(int x, int y)
5777 {
5778   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5779       (!IN_LEV_FIELD(x - 1, y - 2) ||
5780        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5781     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5782
5783   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5784       (!IN_LEV_FIELD(x + 1, y - 2) ||
5785        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5786     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5787
5788   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5789 }
5790
5791 static void InitBeltMovement()
5792 {
5793   static int belt_base_element[4] =
5794   {
5795     EL_CONVEYOR_BELT_1_LEFT,
5796     EL_CONVEYOR_BELT_2_LEFT,
5797     EL_CONVEYOR_BELT_3_LEFT,
5798     EL_CONVEYOR_BELT_4_LEFT
5799   };
5800   static int belt_base_active_element[4] =
5801   {
5802     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5803     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5804     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5805     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5806   };
5807
5808   int x, y, i, j;
5809
5810   /* set frame order for belt animation graphic according to belt direction */
5811   for (i = 0; i < NUM_BELTS; i++)
5812   {
5813     int belt_nr = i;
5814
5815     for (j = 0; j < NUM_BELT_PARTS; j++)
5816     {
5817       int element = belt_base_active_element[belt_nr] + j;
5818       int graphic_1 = el2img(element);
5819       int graphic_2 = el2panelimg(element);
5820
5821       if (game.belt_dir[i] == MV_LEFT)
5822       {
5823         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5824         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5825       }
5826       else
5827       {
5828         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5829         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5830       }
5831     }
5832   }
5833
5834   SCAN_PLAYFIELD(x, y)
5835   {
5836     int element = Feld[x][y];
5837
5838     for (i = 0; i < NUM_BELTS; i++)
5839     {
5840       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5841       {
5842         int e_belt_nr = getBeltNrFromBeltElement(element);
5843         int belt_nr = i;
5844
5845         if (e_belt_nr == belt_nr)
5846         {
5847           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5848
5849           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5850         }
5851       }
5852     }
5853   }
5854 }
5855
5856 static void ToggleBeltSwitch(int x, int y)
5857 {
5858   static int belt_base_element[4] =
5859   {
5860     EL_CONVEYOR_BELT_1_LEFT,
5861     EL_CONVEYOR_BELT_2_LEFT,
5862     EL_CONVEYOR_BELT_3_LEFT,
5863     EL_CONVEYOR_BELT_4_LEFT
5864   };
5865   static int belt_base_active_element[4] =
5866   {
5867     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5868     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5869     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5870     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5871   };
5872   static int belt_base_switch_element[4] =
5873   {
5874     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5875     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5876     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5877     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5878   };
5879   static int belt_move_dir[4] =
5880   {
5881     MV_LEFT,
5882     MV_NONE,
5883     MV_RIGHT,
5884     MV_NONE,
5885   };
5886
5887   int element = Feld[x][y];
5888   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5889   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5890   int belt_dir = belt_move_dir[belt_dir_nr];
5891   int xx, yy, i;
5892
5893   if (!IS_BELT_SWITCH(element))
5894     return;
5895
5896   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5897   game.belt_dir[belt_nr] = belt_dir;
5898
5899   if (belt_dir_nr == 3)
5900     belt_dir_nr = 1;
5901
5902   /* set frame order for belt animation graphic according to belt direction */
5903   for (i = 0; i < NUM_BELT_PARTS; i++)
5904   {
5905     int element = belt_base_active_element[belt_nr] + i;
5906     int graphic_1 = el2img(element);
5907     int graphic_2 = el2panelimg(element);
5908
5909     if (belt_dir == MV_LEFT)
5910     {
5911       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5912       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5913     }
5914     else
5915     {
5916       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5917       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5918     }
5919   }
5920
5921   SCAN_PLAYFIELD(xx, yy)
5922   {
5923     int element = Feld[xx][yy];
5924
5925     if (IS_BELT_SWITCH(element))
5926     {
5927       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5928
5929       if (e_belt_nr == belt_nr)
5930       {
5931         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5932         TEST_DrawLevelField(xx, yy);
5933       }
5934     }
5935     else if (IS_BELT(element) && belt_dir != MV_NONE)
5936     {
5937       int e_belt_nr = getBeltNrFromBeltElement(element);
5938
5939       if (e_belt_nr == belt_nr)
5940       {
5941         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5942
5943         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5944         TEST_DrawLevelField(xx, yy);
5945       }
5946     }
5947     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5948     {
5949       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5950
5951       if (e_belt_nr == belt_nr)
5952       {
5953         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5954
5955         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5956         TEST_DrawLevelField(xx, yy);
5957       }
5958     }
5959   }
5960 }
5961
5962 static void ToggleSwitchgateSwitch(int x, int y)
5963 {
5964   int xx, yy;
5965
5966   game.switchgate_pos = !game.switchgate_pos;
5967
5968   SCAN_PLAYFIELD(xx, yy)
5969   {
5970     int element = Feld[xx][yy];
5971
5972     if (element == EL_SWITCHGATE_SWITCH_UP)
5973     {
5974       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5975       TEST_DrawLevelField(xx, yy);
5976     }
5977     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5978     {
5979       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5980       TEST_DrawLevelField(xx, yy);
5981     }
5982     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5983     {
5984       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5985       TEST_DrawLevelField(xx, yy);
5986     }
5987     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5988     {
5989       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5990       TEST_DrawLevelField(xx, yy);
5991     }
5992     else if (element == EL_SWITCHGATE_OPEN ||
5993              element == EL_SWITCHGATE_OPENING)
5994     {
5995       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5996
5997       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5998     }
5999     else if (element == EL_SWITCHGATE_CLOSED ||
6000              element == EL_SWITCHGATE_CLOSING)
6001     {
6002       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6003
6004       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6005     }
6006   }
6007 }
6008
6009 static int getInvisibleActiveFromInvisibleElement(int element)
6010 {
6011   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6012           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6013           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6014           element);
6015 }
6016
6017 static int getInvisibleFromInvisibleActiveElement(int element)
6018 {
6019   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6020           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6021           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6022           element);
6023 }
6024
6025 static void RedrawAllLightSwitchesAndInvisibleElements()
6026 {
6027   int x, y;
6028
6029   SCAN_PLAYFIELD(x, y)
6030   {
6031     int element = Feld[x][y];
6032
6033     if (element == EL_LIGHT_SWITCH &&
6034         game.light_time_left > 0)
6035     {
6036       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6037       TEST_DrawLevelField(x, y);
6038     }
6039     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6040              game.light_time_left == 0)
6041     {
6042       Feld[x][y] = EL_LIGHT_SWITCH;
6043       TEST_DrawLevelField(x, y);
6044     }
6045     else if (element == EL_EMC_DRIPPER &&
6046              game.light_time_left > 0)
6047     {
6048       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6049       TEST_DrawLevelField(x, y);
6050     }
6051     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6052              game.light_time_left == 0)
6053     {
6054       Feld[x][y] = EL_EMC_DRIPPER;
6055       TEST_DrawLevelField(x, y);
6056     }
6057     else if (element == EL_INVISIBLE_STEELWALL ||
6058              element == EL_INVISIBLE_WALL ||
6059              element == EL_INVISIBLE_SAND)
6060     {
6061       if (game.light_time_left > 0)
6062         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6063
6064       TEST_DrawLevelField(x, y);
6065
6066       /* uncrumble neighbour fields, if needed */
6067       if (element == EL_INVISIBLE_SAND)
6068         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6069     }
6070     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6071              element == EL_INVISIBLE_WALL_ACTIVE ||
6072              element == EL_INVISIBLE_SAND_ACTIVE)
6073     {
6074       if (game.light_time_left == 0)
6075         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6076
6077       TEST_DrawLevelField(x, y);
6078
6079       /* re-crumble neighbour fields, if needed */
6080       if (element == EL_INVISIBLE_SAND)
6081         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6082     }
6083   }
6084 }
6085
6086 static void RedrawAllInvisibleElementsForLenses()
6087 {
6088   int x, y;
6089
6090   SCAN_PLAYFIELD(x, y)
6091   {
6092     int element = Feld[x][y];
6093
6094     if (element == EL_EMC_DRIPPER &&
6095         game.lenses_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.lenses_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.lenses_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.lenses_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 RedrawAllInvisibleElementsForMagnifier()
6136 {
6137   int x, y;
6138
6139   SCAN_PLAYFIELD(x, y)
6140   {
6141     int element = Feld[x][y];
6142
6143     if (element == EL_EMC_FAKE_GRASS &&
6144         game.magnify_time_left > 0)
6145     {
6146       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6147       TEST_DrawLevelField(x, y);
6148     }
6149     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6150              game.magnify_time_left == 0)
6151     {
6152       Feld[x][y] = EL_EMC_FAKE_GRASS;
6153       TEST_DrawLevelField(x, y);
6154     }
6155     else if (IS_GATE_GRAY(element) &&
6156              game.magnify_time_left > 0)
6157     {
6158       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6159                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6160                     IS_EM_GATE_GRAY(element) ?
6161                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6162                     IS_EMC_GATE_GRAY(element) ?
6163                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6164                     IS_DC_GATE_GRAY(element) ?
6165                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6166                     element);
6167       TEST_DrawLevelField(x, y);
6168     }
6169     else if (IS_GATE_GRAY_ACTIVE(element) &&
6170              game.magnify_time_left == 0)
6171     {
6172       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6173                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6174                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6175                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6176                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6177                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6178                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6179                     EL_DC_GATE_WHITE_GRAY :
6180                     element);
6181       TEST_DrawLevelField(x, y);
6182     }
6183   }
6184 }
6185
6186 static void ToggleLightSwitch(int x, int y)
6187 {
6188   int element = Feld[x][y];
6189
6190   game.light_time_left =
6191     (element == EL_LIGHT_SWITCH ?
6192      level.time_light * FRAMES_PER_SECOND : 0);
6193
6194   RedrawAllLightSwitchesAndInvisibleElements();
6195 }
6196
6197 static void ActivateTimegateSwitch(int x, int y)
6198 {
6199   int xx, yy;
6200
6201   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6202
6203   SCAN_PLAYFIELD(xx, yy)
6204   {
6205     int element = Feld[xx][yy];
6206
6207     if (element == EL_TIMEGATE_CLOSED ||
6208         element == EL_TIMEGATE_CLOSING)
6209     {
6210       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6211       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6212     }
6213
6214     /*
6215     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6216     {
6217       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6218       TEST_DrawLevelField(xx, yy);
6219     }
6220     */
6221
6222   }
6223
6224   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6225                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6226 }
6227
6228 void Impact(int x, int y)
6229 {
6230   boolean last_line = (y == lev_fieldy - 1);
6231   boolean object_hit = FALSE;
6232   boolean impact = (last_line || object_hit);
6233   int element = Feld[x][y];
6234   int smashed = EL_STEELWALL;
6235
6236   if (!last_line)       /* check if element below was hit */
6237   {
6238     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6239       return;
6240
6241     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6242                                          MovDir[x][y + 1] != MV_DOWN ||
6243                                          MovPos[x][y + 1] <= TILEY / 2));
6244
6245     /* do not smash moving elements that left the smashed field in time */
6246     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6247         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6248       object_hit = FALSE;
6249
6250 #if USE_QUICKSAND_IMPACT_BUGFIX
6251     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6252     {
6253       RemoveMovingField(x, y + 1);
6254       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6255       Feld[x][y + 2] = EL_ROCK;
6256       TEST_DrawLevelField(x, y + 2);
6257
6258       object_hit = TRUE;
6259     }
6260
6261     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6262     {
6263       RemoveMovingField(x, y + 1);
6264       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6265       Feld[x][y + 2] = EL_ROCK;
6266       TEST_DrawLevelField(x, y + 2);
6267
6268       object_hit = TRUE;
6269     }
6270 #endif
6271
6272     if (object_hit)
6273       smashed = MovingOrBlocked2Element(x, y + 1);
6274
6275     impact = (last_line || object_hit);
6276   }
6277
6278   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6279   {
6280     SplashAcid(x, y + 1);
6281     return;
6282   }
6283
6284   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6285   /* only reset graphic animation if graphic really changes after impact */
6286   if (impact &&
6287       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6288   {
6289     ResetGfxAnimation(x, y);
6290     TEST_DrawLevelField(x, y);
6291   }
6292
6293   if (impact && CAN_EXPLODE_IMPACT(element))
6294   {
6295     Bang(x, y);
6296     return;
6297   }
6298   else if (impact && element == EL_PEARL &&
6299            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6300   {
6301     ResetGfxAnimation(x, y);
6302
6303     Feld[x][y] = EL_PEARL_BREAKING;
6304     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6305     return;
6306   }
6307   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6308   {
6309     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6310
6311     return;
6312   }
6313
6314   if (impact && element == EL_AMOEBA_DROP)
6315   {
6316     if (object_hit && IS_PLAYER(x, y + 1))
6317       KillPlayerUnlessEnemyProtected(x, y + 1);
6318     else if (object_hit && smashed == EL_PENGUIN)
6319       Bang(x, y + 1);
6320     else
6321     {
6322       Feld[x][y] = EL_AMOEBA_GROWING;
6323       Store[x][y] = EL_AMOEBA_WET;
6324
6325       ResetRandomAnimationValue(x, y);
6326     }
6327     return;
6328   }
6329
6330   if (object_hit)               /* check which object was hit */
6331   {
6332     if ((CAN_PASS_MAGIC_WALL(element) && 
6333          (smashed == EL_MAGIC_WALL ||
6334           smashed == EL_BD_MAGIC_WALL)) ||
6335         (CAN_PASS_DC_MAGIC_WALL(element) &&
6336          smashed == EL_DC_MAGIC_WALL))
6337     {
6338       int xx, yy;
6339       int activated_magic_wall =
6340         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6341          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6342          EL_DC_MAGIC_WALL_ACTIVE);
6343
6344       /* activate magic wall / mill */
6345       SCAN_PLAYFIELD(xx, yy)
6346       {
6347         if (Feld[xx][yy] == smashed)
6348           Feld[xx][yy] = activated_magic_wall;
6349       }
6350
6351       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6352       game.magic_wall_active = TRUE;
6353
6354       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6355                             SND_MAGIC_WALL_ACTIVATING :
6356                             smashed == EL_BD_MAGIC_WALL ?
6357                             SND_BD_MAGIC_WALL_ACTIVATING :
6358                             SND_DC_MAGIC_WALL_ACTIVATING));
6359     }
6360
6361     if (IS_PLAYER(x, y + 1))
6362     {
6363       if (CAN_SMASH_PLAYER(element))
6364       {
6365         KillPlayerUnlessEnemyProtected(x, y + 1);
6366         return;
6367       }
6368     }
6369     else if (smashed == EL_PENGUIN)
6370     {
6371       if (CAN_SMASH_PLAYER(element))
6372       {
6373         Bang(x, y + 1);
6374         return;
6375       }
6376     }
6377     else if (element == EL_BD_DIAMOND)
6378     {
6379       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6380       {
6381         Bang(x, y + 1);
6382         return;
6383       }
6384     }
6385     else if (((element == EL_SP_INFOTRON ||
6386                element == EL_SP_ZONK) &&
6387               (smashed == EL_SP_SNIKSNAK ||
6388                smashed == EL_SP_ELECTRON ||
6389                smashed == EL_SP_DISK_ORANGE)) ||
6390              (element == EL_SP_INFOTRON &&
6391               smashed == EL_SP_DISK_YELLOW))
6392     {
6393       Bang(x, y + 1);
6394       return;
6395     }
6396     else if (CAN_SMASH_EVERYTHING(element))
6397     {
6398       if (IS_CLASSIC_ENEMY(smashed) ||
6399           CAN_EXPLODE_SMASHED(smashed))
6400       {
6401         Bang(x, y + 1);
6402         return;
6403       }
6404       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6405       {
6406         if (smashed == EL_LAMP ||
6407             smashed == EL_LAMP_ACTIVE)
6408         {
6409           Bang(x, y + 1);
6410           return;
6411         }
6412         else if (smashed == EL_NUT)
6413         {
6414           Feld[x][y + 1] = EL_NUT_BREAKING;
6415           PlayLevelSound(x, y, SND_NUT_BREAKING);
6416           RaiseScoreElement(EL_NUT);
6417           return;
6418         }
6419         else if (smashed == EL_PEARL)
6420         {
6421           ResetGfxAnimation(x, y);
6422
6423           Feld[x][y + 1] = EL_PEARL_BREAKING;
6424           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6425           return;
6426         }
6427         else if (smashed == EL_DIAMOND)
6428         {
6429           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6430           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6431           return;
6432         }
6433         else if (IS_BELT_SWITCH(smashed))
6434         {
6435           ToggleBeltSwitch(x, y + 1);
6436         }
6437         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6438                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6439                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6440                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6441         {
6442           ToggleSwitchgateSwitch(x, y + 1);
6443         }
6444         else if (smashed == EL_LIGHT_SWITCH ||
6445                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6446         {
6447           ToggleLightSwitch(x, y + 1);
6448         }
6449         else
6450         {
6451           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6452
6453           CheckElementChangeBySide(x, y + 1, smashed, element,
6454                                    CE_SWITCHED, CH_SIDE_TOP);
6455           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6456                                             CH_SIDE_TOP);
6457         }
6458       }
6459       else
6460       {
6461         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6462       }
6463     }
6464   }
6465
6466   /* play sound of magic wall / mill */
6467   if (!last_line &&
6468       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6469        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6470        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6471   {
6472     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6473       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6474     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6475       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6476     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6477       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6478
6479     return;
6480   }
6481
6482   /* play sound of object that hits the ground */
6483   if (last_line || object_hit)
6484     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6485 }
6486
6487 inline static void TurnRoundExt(int x, int y)
6488 {
6489   static struct
6490   {
6491     int dx, dy;
6492   } move_xy[] =
6493   {
6494     {  0,  0 },
6495     { -1,  0 },
6496     { +1,  0 },
6497     {  0,  0 },
6498     {  0, -1 },
6499     {  0,  0 }, { 0, 0 }, { 0, 0 },
6500     {  0, +1 }
6501   };
6502   static struct
6503   {
6504     int left, right, back;
6505   } turn[] =
6506   {
6507     { 0,        0,              0        },
6508     { MV_DOWN,  MV_UP,          MV_RIGHT },
6509     { MV_UP,    MV_DOWN,        MV_LEFT  },
6510     { 0,        0,              0        },
6511     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6512     { 0,        0,              0        },
6513     { 0,        0,              0        },
6514     { 0,        0,              0        },
6515     { MV_RIGHT, MV_LEFT,        MV_UP    }
6516   };
6517
6518   int element = Feld[x][y];
6519   int move_pattern = element_info[element].move_pattern;
6520
6521   int old_move_dir = MovDir[x][y];
6522   int left_dir  = turn[old_move_dir].left;
6523   int right_dir = turn[old_move_dir].right;
6524   int back_dir  = turn[old_move_dir].back;
6525
6526   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6527   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6528   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6529   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6530
6531   int left_x  = x + left_dx,  left_y  = y + left_dy;
6532   int right_x = x + right_dx, right_y = y + right_dy;
6533   int move_x  = x + move_dx,  move_y  = y + move_dy;
6534
6535   int xx, yy;
6536
6537   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6538   {
6539     TestIfBadThingTouchesOtherBadThing(x, y);
6540
6541     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6542       MovDir[x][y] = right_dir;
6543     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6544       MovDir[x][y] = left_dir;
6545
6546     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6547       MovDelay[x][y] = 9;
6548     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6549       MovDelay[x][y] = 1;
6550   }
6551   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6552   {
6553     TestIfBadThingTouchesOtherBadThing(x, y);
6554
6555     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6556       MovDir[x][y] = left_dir;
6557     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6558       MovDir[x][y] = right_dir;
6559
6560     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6561       MovDelay[x][y] = 9;
6562     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6563       MovDelay[x][y] = 1;
6564   }
6565   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6566   {
6567     TestIfBadThingTouchesOtherBadThing(x, y);
6568
6569     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6570       MovDir[x][y] = left_dir;
6571     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6572       MovDir[x][y] = right_dir;
6573
6574     if (MovDir[x][y] != old_move_dir)
6575       MovDelay[x][y] = 9;
6576   }
6577   else if (element == EL_YAMYAM)
6578   {
6579     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6580     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6581
6582     if (can_turn_left && can_turn_right)
6583       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6584     else if (can_turn_left)
6585       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6586     else if (can_turn_right)
6587       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6588     else
6589       MovDir[x][y] = back_dir;
6590
6591     MovDelay[x][y] = 16 + 16 * RND(3);
6592   }
6593   else if (element == EL_DARK_YAMYAM)
6594   {
6595     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6596                                                          left_x, left_y);
6597     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6598                                                          right_x, right_y);
6599
6600     if (can_turn_left && can_turn_right)
6601       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6602     else if (can_turn_left)
6603       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6604     else if (can_turn_right)
6605       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6606     else
6607       MovDir[x][y] = back_dir;
6608
6609     MovDelay[x][y] = 16 + 16 * RND(3);
6610   }
6611   else if (element == EL_PACMAN)
6612   {
6613     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6614     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6615
6616     if (can_turn_left && can_turn_right)
6617       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6618     else if (can_turn_left)
6619       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6620     else if (can_turn_right)
6621       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6622     else
6623       MovDir[x][y] = back_dir;
6624
6625     MovDelay[x][y] = 6 + RND(40);
6626   }
6627   else if (element == EL_PIG)
6628   {
6629     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6630     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6631     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6632     boolean should_turn_left, should_turn_right, should_move_on;
6633     int rnd_value = 24;
6634     int rnd = RND(rnd_value);
6635
6636     should_turn_left = (can_turn_left &&
6637                         (!can_move_on ||
6638                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6639                                                    y + back_dy + left_dy)));
6640     should_turn_right = (can_turn_right &&
6641                          (!can_move_on ||
6642                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6643                                                     y + back_dy + right_dy)));
6644     should_move_on = (can_move_on &&
6645                       (!can_turn_left ||
6646                        !can_turn_right ||
6647                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6648                                                  y + move_dy + left_dy) ||
6649                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6650                                                  y + move_dy + right_dy)));
6651
6652     if (should_turn_left || should_turn_right || should_move_on)
6653     {
6654       if (should_turn_left && should_turn_right && should_move_on)
6655         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6656                         rnd < 2 * rnd_value / 3 ? right_dir :
6657                         old_move_dir);
6658       else if (should_turn_left && should_turn_right)
6659         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6660       else if (should_turn_left && should_move_on)
6661         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6662       else if (should_turn_right && should_move_on)
6663         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6664       else if (should_turn_left)
6665         MovDir[x][y] = left_dir;
6666       else if (should_turn_right)
6667         MovDir[x][y] = right_dir;
6668       else if (should_move_on)
6669         MovDir[x][y] = old_move_dir;
6670     }
6671     else if (can_move_on && rnd > rnd_value / 8)
6672       MovDir[x][y] = old_move_dir;
6673     else if (can_turn_left && can_turn_right)
6674       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6675     else if (can_turn_left && rnd > rnd_value / 8)
6676       MovDir[x][y] = left_dir;
6677     else if (can_turn_right && rnd > rnd_value/8)
6678       MovDir[x][y] = right_dir;
6679     else
6680       MovDir[x][y] = back_dir;
6681
6682     xx = x + move_xy[MovDir[x][y]].dx;
6683     yy = y + move_xy[MovDir[x][y]].dy;
6684
6685     if (!IN_LEV_FIELD(xx, yy) ||
6686         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6687       MovDir[x][y] = old_move_dir;
6688
6689     MovDelay[x][y] = 0;
6690   }
6691   else if (element == EL_DRAGON)
6692   {
6693     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6694     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6695     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6696     int rnd_value = 24;
6697     int rnd = RND(rnd_value);
6698
6699     if (can_move_on && rnd > rnd_value / 8)
6700       MovDir[x][y] = old_move_dir;
6701     else if (can_turn_left && can_turn_right)
6702       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6703     else if (can_turn_left && rnd > rnd_value / 8)
6704       MovDir[x][y] = left_dir;
6705     else if (can_turn_right && rnd > rnd_value / 8)
6706       MovDir[x][y] = right_dir;
6707     else
6708       MovDir[x][y] = back_dir;
6709
6710     xx = x + move_xy[MovDir[x][y]].dx;
6711     yy = y + move_xy[MovDir[x][y]].dy;
6712
6713     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6714       MovDir[x][y] = old_move_dir;
6715
6716     MovDelay[x][y] = 0;
6717   }
6718   else if (element == EL_MOLE)
6719   {
6720     boolean can_move_on =
6721       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6722                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6723                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6724     if (!can_move_on)
6725     {
6726       boolean can_turn_left =
6727         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6728                               IS_AMOEBOID(Feld[left_x][left_y])));
6729
6730       boolean can_turn_right =
6731         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6732                               IS_AMOEBOID(Feld[right_x][right_y])));
6733
6734       if (can_turn_left && can_turn_right)
6735         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6736       else if (can_turn_left)
6737         MovDir[x][y] = left_dir;
6738       else
6739         MovDir[x][y] = right_dir;
6740     }
6741
6742     if (MovDir[x][y] != old_move_dir)
6743       MovDelay[x][y] = 9;
6744   }
6745   else if (element == EL_BALLOON)
6746   {
6747     MovDir[x][y] = game.wind_direction;
6748     MovDelay[x][y] = 0;
6749   }
6750   else if (element == EL_SPRING)
6751   {
6752     if (MovDir[x][y] & MV_HORIZONTAL)
6753     {
6754       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6755           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6756       {
6757         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6758         ResetGfxAnimation(move_x, move_y);
6759         TEST_DrawLevelField(move_x, move_y);
6760
6761         MovDir[x][y] = back_dir;
6762       }
6763       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6764                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6765         MovDir[x][y] = MV_NONE;
6766     }
6767
6768     MovDelay[x][y] = 0;
6769   }
6770   else if (element == EL_ROBOT ||
6771            element == EL_SATELLITE ||
6772            element == EL_PENGUIN ||
6773            element == EL_EMC_ANDROID)
6774   {
6775     int attr_x = -1, attr_y = -1;
6776
6777     if (AllPlayersGone)
6778     {
6779       attr_x = ExitX;
6780       attr_y = ExitY;
6781     }
6782     else
6783     {
6784       int i;
6785
6786       for (i = 0; i < MAX_PLAYERS; i++)
6787       {
6788         struct PlayerInfo *player = &stored_player[i];
6789         int jx = player->jx, jy = player->jy;
6790
6791         if (!player->active)
6792           continue;
6793
6794         if (attr_x == -1 ||
6795             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6796         {
6797           attr_x = jx;
6798           attr_y = jy;
6799         }
6800       }
6801     }
6802
6803     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6804         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6805          game.engine_version < VERSION_IDENT(3,1,0,0)))
6806     {
6807       attr_x = ZX;
6808       attr_y = ZY;
6809     }
6810
6811     if (element == EL_PENGUIN)
6812     {
6813       int i;
6814       static int xy[4][2] =
6815       {
6816         { 0, -1 },
6817         { -1, 0 },
6818         { +1, 0 },
6819         { 0, +1 }
6820       };
6821
6822       for (i = 0; i < NUM_DIRECTIONS; i++)
6823       {
6824         int ex = x + xy[i][0];
6825         int ey = y + xy[i][1];
6826
6827         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6828                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6829                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6830                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6831         {
6832           attr_x = ex;
6833           attr_y = ey;
6834           break;
6835         }
6836       }
6837     }
6838
6839     MovDir[x][y] = MV_NONE;
6840     if (attr_x < x)
6841       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6842     else if (attr_x > x)
6843       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6844     if (attr_y < y)
6845       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6846     else if (attr_y > y)
6847       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6848
6849     if (element == EL_ROBOT)
6850     {
6851       int newx, newy;
6852
6853       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6854         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6855       Moving2Blocked(x, y, &newx, &newy);
6856
6857       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6858         MovDelay[x][y] = 8 + 8 * !RND(3);
6859       else
6860         MovDelay[x][y] = 16;
6861     }
6862     else if (element == EL_PENGUIN)
6863     {
6864       int newx, newy;
6865
6866       MovDelay[x][y] = 1;
6867
6868       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6869       {
6870         boolean first_horiz = RND(2);
6871         int new_move_dir = MovDir[x][y];
6872
6873         MovDir[x][y] =
6874           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6875         Moving2Blocked(x, y, &newx, &newy);
6876
6877         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6878           return;
6879
6880         MovDir[x][y] =
6881           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6882         Moving2Blocked(x, y, &newx, &newy);
6883
6884         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6885           return;
6886
6887         MovDir[x][y] = old_move_dir;
6888         return;
6889       }
6890     }
6891     else if (element == EL_SATELLITE)
6892     {
6893       int newx, newy;
6894
6895       MovDelay[x][y] = 1;
6896
6897       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6898       {
6899         boolean first_horiz = RND(2);
6900         int new_move_dir = MovDir[x][y];
6901
6902         MovDir[x][y] =
6903           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6904         Moving2Blocked(x, y, &newx, &newy);
6905
6906         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6907           return;
6908
6909         MovDir[x][y] =
6910           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6911         Moving2Blocked(x, y, &newx, &newy);
6912
6913         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6914           return;
6915
6916         MovDir[x][y] = old_move_dir;
6917         return;
6918       }
6919     }
6920     else if (element == EL_EMC_ANDROID)
6921     {
6922       static int check_pos[16] =
6923       {
6924         -1,             /*  0 => (invalid)          */
6925         7,              /*  1 => MV_LEFT            */
6926         3,              /*  2 => MV_RIGHT           */
6927         -1,             /*  3 => (invalid)          */
6928         1,              /*  4 =>            MV_UP   */
6929         0,              /*  5 => MV_LEFT  | MV_UP   */
6930         2,              /*  6 => MV_RIGHT | MV_UP   */
6931         -1,             /*  7 => (invalid)          */
6932         5,              /*  8 =>            MV_DOWN */
6933         6,              /*  9 => MV_LEFT  | MV_DOWN */
6934         4,              /* 10 => MV_RIGHT | MV_DOWN */
6935         -1,             /* 11 => (invalid)          */
6936         -1,             /* 12 => (invalid)          */
6937         -1,             /* 13 => (invalid)          */
6938         -1,             /* 14 => (invalid)          */
6939         -1,             /* 15 => (invalid)          */
6940       };
6941       static struct
6942       {
6943         int dx, dy;
6944         int dir;
6945       } check_xy[8] =
6946       {
6947         { -1, -1,       MV_LEFT  | MV_UP   },
6948         {  0, -1,                  MV_UP   },
6949         { +1, -1,       MV_RIGHT | MV_UP   },
6950         { +1,  0,       MV_RIGHT           },
6951         { +1, +1,       MV_RIGHT | MV_DOWN },
6952         {  0, +1,                  MV_DOWN },
6953         { -1, +1,       MV_LEFT  | MV_DOWN },
6954         { -1,  0,       MV_LEFT            },
6955       };
6956       int start_pos, check_order;
6957       boolean can_clone = FALSE;
6958       int i;
6959
6960       /* check if there is any free field around current position */
6961       for (i = 0; i < 8; i++)
6962       {
6963         int newx = x + check_xy[i].dx;
6964         int newy = y + check_xy[i].dy;
6965
6966         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6967         {
6968           can_clone = TRUE;
6969
6970           break;
6971         }
6972       }
6973
6974       if (can_clone)            /* randomly find an element to clone */
6975       {
6976         can_clone = FALSE;
6977
6978         start_pos = check_pos[RND(8)];
6979         check_order = (RND(2) ? -1 : +1);
6980
6981         for (i = 0; i < 8; i++)
6982         {
6983           int pos_raw = start_pos + i * check_order;
6984           int pos = (pos_raw + 8) % 8;
6985           int newx = x + check_xy[pos].dx;
6986           int newy = y + check_xy[pos].dy;
6987
6988           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6989           {
6990             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6991             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6992
6993             Store[x][y] = Feld[newx][newy];
6994
6995             can_clone = TRUE;
6996
6997             break;
6998           }
6999         }
7000       }
7001
7002       if (can_clone)            /* randomly find a direction to move */
7003       {
7004         can_clone = FALSE;
7005
7006         start_pos = check_pos[RND(8)];
7007         check_order = (RND(2) ? -1 : +1);
7008
7009         for (i = 0; i < 8; i++)
7010         {
7011           int pos_raw = start_pos + i * check_order;
7012           int pos = (pos_raw + 8) % 8;
7013           int newx = x + check_xy[pos].dx;
7014           int newy = y + check_xy[pos].dy;
7015           int new_move_dir = check_xy[pos].dir;
7016
7017           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7018           {
7019             MovDir[x][y] = new_move_dir;
7020             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7021
7022             can_clone = TRUE;
7023
7024             break;
7025           }
7026         }
7027       }
7028
7029       if (can_clone)            /* cloning and moving successful */
7030         return;
7031
7032       /* cannot clone -- try to move towards player */
7033
7034       start_pos = check_pos[MovDir[x][y] & 0x0f];
7035       check_order = (RND(2) ? -1 : +1);
7036
7037       for (i = 0; i < 3; i++)
7038       {
7039         /* first check start_pos, then previous/next or (next/previous) pos */
7040         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7041         int pos = (pos_raw + 8) % 8;
7042         int newx = x + check_xy[pos].dx;
7043         int newy = y + check_xy[pos].dy;
7044         int new_move_dir = check_xy[pos].dir;
7045
7046         if (IS_PLAYER(newx, newy))
7047           break;
7048
7049         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7050         {
7051           MovDir[x][y] = new_move_dir;
7052           MovDelay[x][y] = level.android_move_time * 8 + 1;
7053
7054           break;
7055         }
7056       }
7057     }
7058   }
7059   else if (move_pattern == MV_TURNING_LEFT ||
7060            move_pattern == MV_TURNING_RIGHT ||
7061            move_pattern == MV_TURNING_LEFT_RIGHT ||
7062            move_pattern == MV_TURNING_RIGHT_LEFT ||
7063            move_pattern == MV_TURNING_RANDOM ||
7064            move_pattern == MV_ALL_DIRECTIONS)
7065   {
7066     boolean can_turn_left =
7067       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7068     boolean can_turn_right =
7069       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7070
7071     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7072       return;
7073
7074     if (move_pattern == MV_TURNING_LEFT)
7075       MovDir[x][y] = left_dir;
7076     else if (move_pattern == MV_TURNING_RIGHT)
7077       MovDir[x][y] = right_dir;
7078     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7079       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7080     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7081       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7082     else if (move_pattern == MV_TURNING_RANDOM)
7083       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7084                       can_turn_right && !can_turn_left ? right_dir :
7085                       RND(2) ? left_dir : right_dir);
7086     else if (can_turn_left && can_turn_right)
7087       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7088     else if (can_turn_left)
7089       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7090     else if (can_turn_right)
7091       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7092     else
7093       MovDir[x][y] = back_dir;
7094
7095     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7096   }
7097   else if (move_pattern == MV_HORIZONTAL ||
7098            move_pattern == MV_VERTICAL)
7099   {
7100     if (move_pattern & old_move_dir)
7101       MovDir[x][y] = back_dir;
7102     else if (move_pattern == MV_HORIZONTAL)
7103       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7104     else if (move_pattern == MV_VERTICAL)
7105       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7106
7107     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7108   }
7109   else if (move_pattern & MV_ANY_DIRECTION)
7110   {
7111     MovDir[x][y] = move_pattern;
7112     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7113   }
7114   else if (move_pattern & MV_WIND_DIRECTION)
7115   {
7116     MovDir[x][y] = game.wind_direction;
7117     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7118   }
7119   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7120   {
7121     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7122       MovDir[x][y] = left_dir;
7123     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7124       MovDir[x][y] = right_dir;
7125
7126     if (MovDir[x][y] != old_move_dir)
7127       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128   }
7129   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7130   {
7131     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7132       MovDir[x][y] = right_dir;
7133     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7134       MovDir[x][y] = left_dir;
7135
7136     if (MovDir[x][y] != old_move_dir)
7137       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138   }
7139   else if (move_pattern == MV_TOWARDS_PLAYER ||
7140            move_pattern == MV_AWAY_FROM_PLAYER)
7141   {
7142     int attr_x = -1, attr_y = -1;
7143     int newx, newy;
7144     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7145
7146     if (AllPlayersGone)
7147     {
7148       attr_x = ExitX;
7149       attr_y = ExitY;
7150     }
7151     else
7152     {
7153       int i;
7154
7155       for (i = 0; i < MAX_PLAYERS; i++)
7156       {
7157         struct PlayerInfo *player = &stored_player[i];
7158         int jx = player->jx, jy = player->jy;
7159
7160         if (!player->active)
7161           continue;
7162
7163         if (attr_x == -1 ||
7164             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7165         {
7166           attr_x = jx;
7167           attr_y = jy;
7168         }
7169       }
7170     }
7171
7172     MovDir[x][y] = MV_NONE;
7173     if (attr_x < x)
7174       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7175     else if (attr_x > x)
7176       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7177     if (attr_y < y)
7178       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7179     else if (attr_y > y)
7180       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7181
7182     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7183
7184     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7185     {
7186       boolean first_horiz = RND(2);
7187       int new_move_dir = MovDir[x][y];
7188
7189       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7190       {
7191         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7192         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7193
7194         return;
7195       }
7196
7197       MovDir[x][y] =
7198         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7199       Moving2Blocked(x, y, &newx, &newy);
7200
7201       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7202         return;
7203
7204       MovDir[x][y] =
7205         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7206       Moving2Blocked(x, y, &newx, &newy);
7207
7208       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7209         return;
7210
7211       MovDir[x][y] = old_move_dir;
7212     }
7213   }
7214   else if (move_pattern == MV_WHEN_PUSHED ||
7215            move_pattern == MV_WHEN_DROPPED)
7216   {
7217     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7218       MovDir[x][y] = MV_NONE;
7219
7220     MovDelay[x][y] = 0;
7221   }
7222   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7223   {
7224     static int test_xy[7][2] =
7225     {
7226       { 0, -1 },
7227       { -1, 0 },
7228       { +1, 0 },
7229       { 0, +1 },
7230       { 0, -1 },
7231       { -1, 0 },
7232       { +1, 0 },
7233     };
7234     static int test_dir[7] =
7235     {
7236       MV_UP,
7237       MV_LEFT,
7238       MV_RIGHT,
7239       MV_DOWN,
7240       MV_UP,
7241       MV_LEFT,
7242       MV_RIGHT,
7243     };
7244     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7245     int move_preference = -1000000;     /* start with very low preference */
7246     int new_move_dir = MV_NONE;
7247     int start_test = RND(4);
7248     int i;
7249
7250     for (i = 0; i < NUM_DIRECTIONS; i++)
7251     {
7252       int move_dir = test_dir[start_test + i];
7253       int move_dir_preference;
7254
7255       xx = x + test_xy[start_test + i][0];
7256       yy = y + test_xy[start_test + i][1];
7257
7258       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7259           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7260       {
7261         new_move_dir = move_dir;
7262
7263         break;
7264       }
7265
7266       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7267         continue;
7268
7269       move_dir_preference = -1 * RunnerVisit[xx][yy];
7270       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7271         move_dir_preference = PlayerVisit[xx][yy];
7272
7273       if (move_dir_preference > move_preference)
7274       {
7275         /* prefer field that has not been visited for the longest time */
7276         move_preference = move_dir_preference;
7277         new_move_dir = move_dir;
7278       }
7279       else if (move_dir_preference == move_preference &&
7280                move_dir == old_move_dir)
7281       {
7282         /* prefer last direction when all directions are preferred equally */
7283         move_preference = move_dir_preference;
7284         new_move_dir = move_dir;
7285       }
7286     }
7287
7288     MovDir[x][y] = new_move_dir;
7289     if (old_move_dir != new_move_dir)
7290       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7291   }
7292 }
7293
7294 static void TurnRound(int x, int y)
7295 {
7296   int direction = MovDir[x][y];
7297
7298   TurnRoundExt(x, y);
7299
7300   GfxDir[x][y] = MovDir[x][y];
7301
7302   if (direction != MovDir[x][y])
7303     GfxFrame[x][y] = 0;
7304
7305   if (MovDelay[x][y])
7306     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7307
7308   ResetGfxFrame(x, y);
7309 }
7310
7311 static boolean JustBeingPushed(int x, int y)
7312 {
7313   int i;
7314
7315   for (i = 0; i < MAX_PLAYERS; i++)
7316   {
7317     struct PlayerInfo *player = &stored_player[i];
7318
7319     if (player->active && player->is_pushing && player->MovPos)
7320     {
7321       int next_jx = player->jx + (player->jx - player->last_jx);
7322       int next_jy = player->jy + (player->jy - player->last_jy);
7323
7324       if (x == next_jx && y == next_jy)
7325         return TRUE;
7326     }
7327   }
7328
7329   return FALSE;
7330 }
7331
7332 void StartMoving(int x, int y)
7333 {
7334   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7335   int element = Feld[x][y];
7336
7337   if (Stop[x][y])
7338     return;
7339
7340   if (MovDelay[x][y] == 0)
7341     GfxAction[x][y] = ACTION_DEFAULT;
7342
7343   if (CAN_FALL(element) && y < lev_fieldy - 1)
7344   {
7345     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7346         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7347       if (JustBeingPushed(x, y))
7348         return;
7349
7350     if (element == EL_QUICKSAND_FULL)
7351     {
7352       if (IS_FREE(x, y + 1))
7353       {
7354         InitMovingField(x, y, MV_DOWN);
7355         started_moving = TRUE;
7356
7357         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7358 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7359         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7360           Store[x][y] = EL_ROCK;
7361 #else
7362         Store[x][y] = EL_ROCK;
7363 #endif
7364
7365         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7366       }
7367       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7368       {
7369         if (!MovDelay[x][y])
7370         {
7371           MovDelay[x][y] = TILEY + 1;
7372
7373           ResetGfxAnimation(x, y);
7374           ResetGfxAnimation(x, y + 1);
7375         }
7376
7377         if (MovDelay[x][y])
7378         {
7379           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7380           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7381
7382           MovDelay[x][y]--;
7383           if (MovDelay[x][y])
7384             return;
7385         }
7386
7387         Feld[x][y] = EL_QUICKSAND_EMPTY;
7388         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7389         Store[x][y + 1] = Store[x][y];
7390         Store[x][y] = 0;
7391
7392         PlayLevelSoundAction(x, y, ACTION_FILLING);
7393       }
7394       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7395       {
7396         if (!MovDelay[x][y])
7397         {
7398           MovDelay[x][y] = TILEY + 1;
7399
7400           ResetGfxAnimation(x, y);
7401           ResetGfxAnimation(x, y + 1);
7402         }
7403
7404         if (MovDelay[x][y])
7405         {
7406           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7407           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7408
7409           MovDelay[x][y]--;
7410           if (MovDelay[x][y])
7411             return;
7412         }
7413
7414         Feld[x][y] = EL_QUICKSAND_EMPTY;
7415         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7416         Store[x][y + 1] = Store[x][y];
7417         Store[x][y] = 0;
7418
7419         PlayLevelSoundAction(x, y, ACTION_FILLING);
7420       }
7421     }
7422     else if (element == EL_QUICKSAND_FAST_FULL)
7423     {
7424       if (IS_FREE(x, y + 1))
7425       {
7426         InitMovingField(x, y, MV_DOWN);
7427         started_moving = TRUE;
7428
7429         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7430 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7431         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7432           Store[x][y] = EL_ROCK;
7433 #else
7434         Store[x][y] = EL_ROCK;
7435 #endif
7436
7437         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7438       }
7439       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7440       {
7441         if (!MovDelay[x][y])
7442         {
7443           MovDelay[x][y] = TILEY + 1;
7444
7445           ResetGfxAnimation(x, y);
7446           ResetGfxAnimation(x, y + 1);
7447         }
7448
7449         if (MovDelay[x][y])
7450         {
7451           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7452           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7453
7454           MovDelay[x][y]--;
7455           if (MovDelay[x][y])
7456             return;
7457         }
7458
7459         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7460         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7461         Store[x][y + 1] = Store[x][y];
7462         Store[x][y] = 0;
7463
7464         PlayLevelSoundAction(x, y, ACTION_FILLING);
7465       }
7466       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7467       {
7468         if (!MovDelay[x][y])
7469         {
7470           MovDelay[x][y] = TILEY + 1;
7471
7472           ResetGfxAnimation(x, y);
7473           ResetGfxAnimation(x, y + 1);
7474         }
7475
7476         if (MovDelay[x][y])
7477         {
7478           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7479           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7480
7481           MovDelay[x][y]--;
7482           if (MovDelay[x][y])
7483             return;
7484         }
7485
7486         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7487         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7488         Store[x][y + 1] = Store[x][y];
7489         Store[x][y] = 0;
7490
7491         PlayLevelSoundAction(x, y, ACTION_FILLING);
7492       }
7493     }
7494     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7495              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7496     {
7497       InitMovingField(x, y, MV_DOWN);
7498       started_moving = TRUE;
7499
7500       Feld[x][y] = EL_QUICKSAND_FILLING;
7501       Store[x][y] = element;
7502
7503       PlayLevelSoundAction(x, y, ACTION_FILLING);
7504     }
7505     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7506              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7507     {
7508       InitMovingField(x, y, MV_DOWN);
7509       started_moving = TRUE;
7510
7511       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7512       Store[x][y] = element;
7513
7514       PlayLevelSoundAction(x, y, ACTION_FILLING);
7515     }
7516     else if (element == EL_MAGIC_WALL_FULL)
7517     {
7518       if (IS_FREE(x, y + 1))
7519       {
7520         InitMovingField(x, y, MV_DOWN);
7521         started_moving = TRUE;
7522
7523         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7524         Store[x][y] = EL_CHANGED(Store[x][y]);
7525       }
7526       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7527       {
7528         if (!MovDelay[x][y])
7529           MovDelay[x][y] = TILEY / 4 + 1;
7530
7531         if (MovDelay[x][y])
7532         {
7533           MovDelay[x][y]--;
7534           if (MovDelay[x][y])
7535             return;
7536         }
7537
7538         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7539         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7540         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7541         Store[x][y] = 0;
7542       }
7543     }
7544     else if (element == EL_BD_MAGIC_WALL_FULL)
7545     {
7546       if (IS_FREE(x, y + 1))
7547       {
7548         InitMovingField(x, y, MV_DOWN);
7549         started_moving = TRUE;
7550
7551         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7552         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7553       }
7554       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7555       {
7556         if (!MovDelay[x][y])
7557           MovDelay[x][y] = TILEY / 4 + 1;
7558
7559         if (MovDelay[x][y])
7560         {
7561           MovDelay[x][y]--;
7562           if (MovDelay[x][y])
7563             return;
7564         }
7565
7566         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7567         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7568         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7569         Store[x][y] = 0;
7570       }
7571     }
7572     else if (element == EL_DC_MAGIC_WALL_FULL)
7573     {
7574       if (IS_FREE(x, y + 1))
7575       {
7576         InitMovingField(x, y, MV_DOWN);
7577         started_moving = TRUE;
7578
7579         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7580         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7581       }
7582       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7583       {
7584         if (!MovDelay[x][y])
7585           MovDelay[x][y] = TILEY / 4 + 1;
7586
7587         if (MovDelay[x][y])
7588         {
7589           MovDelay[x][y]--;
7590           if (MovDelay[x][y])
7591             return;
7592         }
7593
7594         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7595         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7596         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7597         Store[x][y] = 0;
7598       }
7599     }
7600     else if ((CAN_PASS_MAGIC_WALL(element) &&
7601               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7602                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7603              (CAN_PASS_DC_MAGIC_WALL(element) &&
7604               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7605
7606     {
7607       InitMovingField(x, y, MV_DOWN);
7608       started_moving = TRUE;
7609
7610       Feld[x][y] =
7611         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7612          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7613          EL_DC_MAGIC_WALL_FILLING);
7614       Store[x][y] = element;
7615     }
7616     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7617     {
7618       SplashAcid(x, y + 1);
7619
7620       InitMovingField(x, y, MV_DOWN);
7621       started_moving = TRUE;
7622
7623       Store[x][y] = EL_ACID;
7624     }
7625     else if (
7626              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7627               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7628              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7629               CAN_FALL(element) && WasJustFalling[x][y] &&
7630               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7631
7632              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7633               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7634               (Feld[x][y + 1] == EL_BLOCKED)))
7635     {
7636       /* this is needed for a special case not covered by calling "Impact()"
7637          from "ContinueMoving()": if an element moves to a tile directly below
7638          another element which was just falling on that tile (which was empty
7639          in the previous frame), the falling element above would just stop
7640          instead of smashing the element below (in previous version, the above
7641          element was just checked for "moving" instead of "falling", resulting
7642          in incorrect smashes caused by horizontal movement of the above
7643          element; also, the case of the player being the element to smash was
7644          simply not covered here... :-/ ) */
7645
7646       CheckCollision[x][y] = 0;
7647       CheckImpact[x][y] = 0;
7648
7649       Impact(x, y);
7650     }
7651     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7652     {
7653       if (MovDir[x][y] == MV_NONE)
7654       {
7655         InitMovingField(x, y, MV_DOWN);
7656         started_moving = TRUE;
7657       }
7658     }
7659     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7660     {
7661       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7662         MovDir[x][y] = MV_DOWN;
7663
7664       InitMovingField(x, y, MV_DOWN);
7665       started_moving = TRUE;
7666     }
7667     else if (element == EL_AMOEBA_DROP)
7668     {
7669       Feld[x][y] = EL_AMOEBA_GROWING;
7670       Store[x][y] = EL_AMOEBA_WET;
7671     }
7672     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7673               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7674              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7675              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7676     {
7677       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7678                                 (IS_FREE(x - 1, y + 1) ||
7679                                  Feld[x - 1][y + 1] == EL_ACID));
7680       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7681                                 (IS_FREE(x + 1, y + 1) ||
7682                                  Feld[x + 1][y + 1] == EL_ACID));
7683       boolean can_fall_any  = (can_fall_left || can_fall_right);
7684       boolean can_fall_both = (can_fall_left && can_fall_right);
7685       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7686
7687       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7688       {
7689         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7690           can_fall_right = FALSE;
7691         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7692           can_fall_left = FALSE;
7693         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7694           can_fall_right = FALSE;
7695         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7696           can_fall_left = FALSE;
7697
7698         can_fall_any  = (can_fall_left || can_fall_right);
7699         can_fall_both = FALSE;
7700       }
7701
7702       if (can_fall_both)
7703       {
7704         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7705           can_fall_right = FALSE;       /* slip down on left side */
7706         else
7707           can_fall_left = !(can_fall_right = RND(2));
7708
7709         can_fall_both = FALSE;
7710       }
7711
7712       if (can_fall_any)
7713       {
7714         /* if not determined otherwise, prefer left side for slipping down */
7715         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7716         started_moving = TRUE;
7717       }
7718     }
7719     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7720     {
7721       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7722       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7723       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7724       int belt_dir = game.belt_dir[belt_nr];
7725
7726       if ((belt_dir == MV_LEFT  && left_is_free) ||
7727           (belt_dir == MV_RIGHT && right_is_free))
7728       {
7729         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7730
7731         InitMovingField(x, y, belt_dir);
7732         started_moving = TRUE;
7733
7734         Pushed[x][y] = TRUE;
7735         Pushed[nextx][y] = TRUE;
7736
7737         GfxAction[x][y] = ACTION_DEFAULT;
7738       }
7739       else
7740       {
7741         MovDir[x][y] = 0;       /* if element was moving, stop it */
7742       }
7743     }
7744   }
7745
7746   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7747   if (CAN_MOVE(element) && !started_moving)
7748   {
7749     int move_pattern = element_info[element].move_pattern;
7750     int newx, newy;
7751
7752     Moving2Blocked(x, y, &newx, &newy);
7753
7754     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7755       return;
7756
7757     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7758         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7759     {
7760       WasJustMoving[x][y] = 0;
7761       CheckCollision[x][y] = 0;
7762
7763       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7764
7765       if (Feld[x][y] != element)        /* element has changed */
7766         return;
7767     }
7768
7769     if (!MovDelay[x][y])        /* start new movement phase */
7770     {
7771       /* all objects that can change their move direction after each step
7772          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7773
7774       if (element != EL_YAMYAM &&
7775           element != EL_DARK_YAMYAM &&
7776           element != EL_PACMAN &&
7777           !(move_pattern & MV_ANY_DIRECTION) &&
7778           move_pattern != MV_TURNING_LEFT &&
7779           move_pattern != MV_TURNING_RIGHT &&
7780           move_pattern != MV_TURNING_LEFT_RIGHT &&
7781           move_pattern != MV_TURNING_RIGHT_LEFT &&
7782           move_pattern != MV_TURNING_RANDOM)
7783       {
7784         TurnRound(x, y);
7785
7786         if (MovDelay[x][y] && (element == EL_BUG ||
7787                                element == EL_SPACESHIP ||
7788                                element == EL_SP_SNIKSNAK ||
7789                                element == EL_SP_ELECTRON ||
7790                                element == EL_MOLE))
7791           TEST_DrawLevelField(x, y);
7792       }
7793     }
7794
7795     if (MovDelay[x][y])         /* wait some time before next movement */
7796     {
7797       MovDelay[x][y]--;
7798
7799       if (element == EL_ROBOT ||
7800           element == EL_YAMYAM ||
7801           element == EL_DARK_YAMYAM)
7802       {
7803         DrawLevelElementAnimationIfNeeded(x, y, element);
7804         PlayLevelSoundAction(x, y, ACTION_WAITING);
7805       }
7806       else if (element == EL_SP_ELECTRON)
7807         DrawLevelElementAnimationIfNeeded(x, y, element);
7808       else if (element == EL_DRAGON)
7809       {
7810         int i;
7811         int dir = MovDir[x][y];
7812         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7813         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7814         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7815                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7816                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7817                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7818         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7819
7820         GfxAction[x][y] = ACTION_ATTACKING;
7821
7822         if (IS_PLAYER(x, y))
7823           DrawPlayerField(x, y);
7824         else
7825           TEST_DrawLevelField(x, y);
7826
7827         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7828
7829         for (i = 1; i <= 3; i++)
7830         {
7831           int xx = x + i * dx;
7832           int yy = y + i * dy;
7833           int sx = SCREENX(xx);
7834           int sy = SCREENY(yy);
7835           int flame_graphic = graphic + (i - 1);
7836
7837           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7838             break;
7839
7840           if (MovDelay[x][y])
7841           {
7842             int flamed = MovingOrBlocked2Element(xx, yy);
7843
7844             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7845               Bang(xx, yy);
7846             else
7847               RemoveMovingField(xx, yy);
7848
7849             ChangeDelay[xx][yy] = 0;
7850
7851             Feld[xx][yy] = EL_FLAMES;
7852
7853             if (IN_SCR_FIELD(sx, sy))
7854             {
7855               TEST_DrawLevelFieldCrumbled(xx, yy);
7856               DrawGraphic(sx, sy, flame_graphic, frame);
7857             }
7858           }
7859           else
7860           {
7861             if (Feld[xx][yy] == EL_FLAMES)
7862               Feld[xx][yy] = EL_EMPTY;
7863             TEST_DrawLevelField(xx, yy);
7864           }
7865         }
7866       }
7867
7868       if (MovDelay[x][y])       /* element still has to wait some time */
7869       {
7870         PlayLevelSoundAction(x, y, ACTION_WAITING);
7871
7872         return;
7873       }
7874     }
7875
7876     /* now make next step */
7877
7878     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7879
7880     if (DONT_COLLIDE_WITH(element) &&
7881         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7882         !PLAYER_ENEMY_PROTECTED(newx, newy))
7883     {
7884       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7885
7886       return;
7887     }
7888
7889     else if (CAN_MOVE_INTO_ACID(element) &&
7890              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7891              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7892              (MovDir[x][y] == MV_DOWN ||
7893               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7894     {
7895       SplashAcid(newx, newy);
7896       Store[x][y] = EL_ACID;
7897     }
7898     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7899     {
7900       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7901           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7902           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7903           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7904       {
7905         RemoveField(x, y);
7906         TEST_DrawLevelField(x, y);
7907
7908         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7909         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7910           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7911
7912         local_player->friends_still_needed--;
7913         if (!local_player->friends_still_needed &&
7914             !local_player->GameOver && AllPlayersGone)
7915           PlayerWins(local_player);
7916
7917         return;
7918       }
7919       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7920       {
7921         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7922           TEST_DrawLevelField(newx, newy);
7923         else
7924           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7925       }
7926       else if (!IS_FREE(newx, newy))
7927       {
7928         GfxAction[x][y] = ACTION_WAITING;
7929
7930         if (IS_PLAYER(x, y))
7931           DrawPlayerField(x, y);
7932         else
7933           TEST_DrawLevelField(x, y);
7934
7935         return;
7936       }
7937     }
7938     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7939     {
7940       if (IS_FOOD_PIG(Feld[newx][newy]))
7941       {
7942         if (IS_MOVING(newx, newy))
7943           RemoveMovingField(newx, newy);
7944         else
7945         {
7946           Feld[newx][newy] = EL_EMPTY;
7947           TEST_DrawLevelField(newx, newy);
7948         }
7949
7950         PlayLevelSound(x, y, SND_PIG_DIGGING);
7951       }
7952       else if (!IS_FREE(newx, newy))
7953       {
7954         if (IS_PLAYER(x, y))
7955           DrawPlayerField(x, y);
7956         else
7957           TEST_DrawLevelField(x, y);
7958
7959         return;
7960       }
7961     }
7962     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7963     {
7964       if (Store[x][y] != EL_EMPTY)
7965       {
7966         boolean can_clone = FALSE;
7967         int xx, yy;
7968
7969         /* check if element to clone is still there */
7970         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7971         {
7972           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7973           {
7974             can_clone = TRUE;
7975
7976             break;
7977           }
7978         }
7979
7980         /* cannot clone or target field not free anymore -- do not clone */
7981         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7982           Store[x][y] = EL_EMPTY;
7983       }
7984
7985       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7986       {
7987         if (IS_MV_DIAGONAL(MovDir[x][y]))
7988         {
7989           int diagonal_move_dir = MovDir[x][y];
7990           int stored = Store[x][y];
7991           int change_delay = 8;
7992           int graphic;
7993
7994           /* android is moving diagonally */
7995
7996           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7997
7998           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7999           GfxElement[x][y] = EL_EMC_ANDROID;
8000           GfxAction[x][y] = ACTION_SHRINKING;
8001           GfxDir[x][y] = diagonal_move_dir;
8002           ChangeDelay[x][y] = change_delay;
8003
8004           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8005                                    GfxDir[x][y]);
8006
8007           DrawLevelGraphicAnimation(x, y, graphic);
8008           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8009
8010           if (Feld[newx][newy] == EL_ACID)
8011           {
8012             SplashAcid(newx, newy);
8013
8014             return;
8015           }
8016
8017           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8018
8019           Store[newx][newy] = EL_EMC_ANDROID;
8020           GfxElement[newx][newy] = EL_EMC_ANDROID;
8021           GfxAction[newx][newy] = ACTION_GROWING;
8022           GfxDir[newx][newy] = diagonal_move_dir;
8023           ChangeDelay[newx][newy] = change_delay;
8024
8025           graphic = el_act_dir2img(GfxElement[newx][newy],
8026                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8027
8028           DrawLevelGraphicAnimation(newx, newy, graphic);
8029           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8030
8031           return;
8032         }
8033         else
8034         {
8035           Feld[newx][newy] = EL_EMPTY;
8036           TEST_DrawLevelField(newx, newy);
8037
8038           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8039         }
8040       }
8041       else if (!IS_FREE(newx, newy))
8042       {
8043         return;
8044       }
8045     }
8046     else if (IS_CUSTOM_ELEMENT(element) &&
8047              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8048     {
8049       if (!DigFieldByCE(newx, newy, element))
8050         return;
8051
8052       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8053       {
8054         RunnerVisit[x][y] = FrameCounter;
8055         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8056       }
8057     }
8058     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8059     {
8060       if (!IS_FREE(newx, newy))
8061       {
8062         if (IS_PLAYER(x, y))
8063           DrawPlayerField(x, y);
8064         else
8065           TEST_DrawLevelField(x, y);
8066
8067         return;
8068       }
8069       else
8070       {
8071         boolean wanna_flame = !RND(10);
8072         int dx = newx - x, dy = newy - y;
8073         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8074         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8075         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8076                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8077         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8078                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8079
8080         if ((wanna_flame ||
8081              IS_CLASSIC_ENEMY(element1) ||
8082              IS_CLASSIC_ENEMY(element2)) &&
8083             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8084             element1 != EL_FLAMES && element2 != EL_FLAMES)
8085         {
8086           ResetGfxAnimation(x, y);
8087           GfxAction[x][y] = ACTION_ATTACKING;
8088
8089           if (IS_PLAYER(x, y))
8090             DrawPlayerField(x, y);
8091           else
8092             TEST_DrawLevelField(x, y);
8093
8094           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8095
8096           MovDelay[x][y] = 50;
8097
8098           Feld[newx][newy] = EL_FLAMES;
8099           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8100             Feld[newx1][newy1] = EL_FLAMES;
8101           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8102             Feld[newx2][newy2] = EL_FLAMES;
8103
8104           return;
8105         }
8106       }
8107     }
8108     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8109              Feld[newx][newy] == EL_DIAMOND)
8110     {
8111       if (IS_MOVING(newx, newy))
8112         RemoveMovingField(newx, newy);
8113       else
8114       {
8115         Feld[newx][newy] = EL_EMPTY;
8116         TEST_DrawLevelField(newx, newy);
8117       }
8118
8119       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8120     }
8121     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8122              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8123     {
8124       if (AmoebaNr[newx][newy])
8125       {
8126         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8127         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8128             Feld[newx][newy] == EL_BD_AMOEBA)
8129           AmoebaCnt[AmoebaNr[newx][newy]]--;
8130       }
8131
8132       if (IS_MOVING(newx, newy))
8133       {
8134         RemoveMovingField(newx, newy);
8135       }
8136       else
8137       {
8138         Feld[newx][newy] = EL_EMPTY;
8139         TEST_DrawLevelField(newx, newy);
8140       }
8141
8142       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8143     }
8144     else if ((element == EL_PACMAN || element == EL_MOLE)
8145              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8146     {
8147       if (AmoebaNr[newx][newy])
8148       {
8149         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8150         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8151             Feld[newx][newy] == EL_BD_AMOEBA)
8152           AmoebaCnt[AmoebaNr[newx][newy]]--;
8153       }
8154
8155       if (element == EL_MOLE)
8156       {
8157         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8158         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8159
8160         ResetGfxAnimation(x, y);
8161         GfxAction[x][y] = ACTION_DIGGING;
8162         TEST_DrawLevelField(x, y);
8163
8164         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8165
8166         return;                         /* wait for shrinking amoeba */
8167       }
8168       else      /* element == EL_PACMAN */
8169       {
8170         Feld[newx][newy] = EL_EMPTY;
8171         TEST_DrawLevelField(newx, newy);
8172         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8173       }
8174     }
8175     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8176              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8177               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8178     {
8179       /* wait for shrinking amoeba to completely disappear */
8180       return;
8181     }
8182     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8183     {
8184       /* object was running against a wall */
8185
8186       TurnRound(x, y);
8187
8188       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8189         DrawLevelElementAnimation(x, y, element);
8190
8191       if (DONT_TOUCH(element))
8192         TestIfBadThingTouchesPlayer(x, y);
8193
8194       return;
8195     }
8196
8197     InitMovingField(x, y, MovDir[x][y]);
8198
8199     PlayLevelSoundAction(x, y, ACTION_MOVING);
8200   }
8201
8202   if (MovDir[x][y])
8203     ContinueMoving(x, y);
8204 }
8205
8206 void ContinueMoving(int x, int y)
8207 {
8208   int element = Feld[x][y];
8209   struct ElementInfo *ei = &element_info[element];
8210   int direction = MovDir[x][y];
8211   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8212   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8213   int newx = x + dx, newy = y + dy;
8214   int stored = Store[x][y];
8215   int stored_new = Store[newx][newy];
8216   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8217   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8218   boolean last_line = (newy == lev_fieldy - 1);
8219
8220   MovPos[x][y] += getElementMoveStepsize(x, y);
8221
8222   if (pushed_by_player) /* special case: moving object pushed by player */
8223     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8224
8225   if (ABS(MovPos[x][y]) < TILEX)
8226   {
8227     TEST_DrawLevelField(x, y);
8228
8229     return;     /* element is still moving */
8230   }
8231
8232   /* element reached destination field */
8233
8234   Feld[x][y] = EL_EMPTY;
8235   Feld[newx][newy] = element;
8236   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8237
8238   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8239   {
8240     element = Feld[newx][newy] = EL_ACID;
8241   }
8242   else if (element == EL_MOLE)
8243   {
8244     Feld[x][y] = EL_SAND;
8245
8246     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8247   }
8248   else if (element == EL_QUICKSAND_FILLING)
8249   {
8250     element = Feld[newx][newy] = get_next_element(element);
8251     Store[newx][newy] = Store[x][y];
8252   }
8253   else if (element == EL_QUICKSAND_EMPTYING)
8254   {
8255     Feld[x][y] = get_next_element(element);
8256     element = Feld[newx][newy] = Store[x][y];
8257   }
8258   else if (element == EL_QUICKSAND_FAST_FILLING)
8259   {
8260     element = Feld[newx][newy] = get_next_element(element);
8261     Store[newx][newy] = Store[x][y];
8262   }
8263   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8264   {
8265     Feld[x][y] = get_next_element(element);
8266     element = Feld[newx][newy] = Store[x][y];
8267   }
8268   else if (element == EL_MAGIC_WALL_FILLING)
8269   {
8270     element = Feld[newx][newy] = get_next_element(element);
8271     if (!game.magic_wall_active)
8272       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8273     Store[newx][newy] = Store[x][y];
8274   }
8275   else if (element == EL_MAGIC_WALL_EMPTYING)
8276   {
8277     Feld[x][y] = get_next_element(element);
8278     if (!game.magic_wall_active)
8279       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8280     element = Feld[newx][newy] = Store[x][y];
8281
8282     InitField(newx, newy, FALSE);
8283   }
8284   else if (element == EL_BD_MAGIC_WALL_FILLING)
8285   {
8286     element = Feld[newx][newy] = get_next_element(element);
8287     if (!game.magic_wall_active)
8288       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8289     Store[newx][newy] = Store[x][y];
8290   }
8291   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8292   {
8293     Feld[x][y] = get_next_element(element);
8294     if (!game.magic_wall_active)
8295       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8296     element = Feld[newx][newy] = Store[x][y];
8297
8298     InitField(newx, newy, FALSE);
8299   }
8300   else if (element == EL_DC_MAGIC_WALL_FILLING)
8301   {
8302     element = Feld[newx][newy] = get_next_element(element);
8303     if (!game.magic_wall_active)
8304       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8305     Store[newx][newy] = Store[x][y];
8306   }
8307   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8308   {
8309     Feld[x][y] = get_next_element(element);
8310     if (!game.magic_wall_active)
8311       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8312     element = Feld[newx][newy] = Store[x][y];
8313
8314     InitField(newx, newy, FALSE);
8315   }
8316   else if (element == EL_AMOEBA_DROPPING)
8317   {
8318     Feld[x][y] = get_next_element(element);
8319     element = Feld[newx][newy] = Store[x][y];
8320   }
8321   else if (element == EL_SOKOBAN_OBJECT)
8322   {
8323     if (Back[x][y])
8324       Feld[x][y] = Back[x][y];
8325
8326     if (Back[newx][newy])
8327       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8328
8329     Back[x][y] = Back[newx][newy] = 0;
8330   }
8331
8332   Store[x][y] = EL_EMPTY;
8333   MovPos[x][y] = 0;
8334   MovDir[x][y] = 0;
8335   MovDelay[x][y] = 0;
8336
8337   MovDelay[newx][newy] = 0;
8338
8339   if (CAN_CHANGE_OR_HAS_ACTION(element))
8340   {
8341     /* copy element change control values to new field */
8342     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8343     ChangePage[newx][newy]  = ChangePage[x][y];
8344     ChangeCount[newx][newy] = ChangeCount[x][y];
8345     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8346   }
8347
8348   CustomValue[newx][newy] = CustomValue[x][y];
8349
8350   ChangeDelay[x][y] = 0;
8351   ChangePage[x][y] = -1;
8352   ChangeCount[x][y] = 0;
8353   ChangeEvent[x][y] = -1;
8354
8355   CustomValue[x][y] = 0;
8356
8357   /* copy animation control values to new field */
8358   GfxFrame[newx][newy]  = GfxFrame[x][y];
8359   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8360   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8361   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8362
8363   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8364
8365   /* some elements can leave other elements behind after moving */
8366   if (ei->move_leave_element != EL_EMPTY &&
8367       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8368       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8369   {
8370     int move_leave_element = ei->move_leave_element;
8371
8372     /* this makes it possible to leave the removed element again */
8373     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8374       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8375
8376     Feld[x][y] = move_leave_element;
8377
8378     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8379       MovDir[x][y] = direction;
8380
8381     InitField(x, y, FALSE);
8382
8383     if (GFX_CRUMBLED(Feld[x][y]))
8384       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8385
8386     if (ELEM_IS_PLAYER(move_leave_element))
8387       RelocatePlayer(x, y, move_leave_element);
8388   }
8389
8390   /* do this after checking for left-behind element */
8391   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8392
8393   if (!CAN_MOVE(element) ||
8394       (CAN_FALL(element) && direction == MV_DOWN &&
8395        (element == EL_SPRING ||
8396         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8397         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8398     GfxDir[x][y] = MovDir[newx][newy] = 0;
8399
8400   TEST_DrawLevelField(x, y);
8401   TEST_DrawLevelField(newx, newy);
8402
8403   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8404
8405   /* prevent pushed element from moving on in pushed direction */
8406   if (pushed_by_player && CAN_MOVE(element) &&
8407       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8408       !(element_info[element].move_pattern & direction))
8409     TurnRound(newx, newy);
8410
8411   /* prevent elements on conveyor belt from moving on in last direction */
8412   if (pushed_by_conveyor && CAN_FALL(element) &&
8413       direction & MV_HORIZONTAL)
8414     MovDir[newx][newy] = 0;
8415
8416   if (!pushed_by_player)
8417   {
8418     int nextx = newx + dx, nexty = newy + dy;
8419     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8420
8421     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8422
8423     if (CAN_FALL(element) && direction == MV_DOWN)
8424       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8425
8426     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8427       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8428
8429     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8430       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8431   }
8432
8433   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8434   {
8435     TestIfBadThingTouchesPlayer(newx, newy);
8436     TestIfBadThingTouchesFriend(newx, newy);
8437
8438     if (!IS_CUSTOM_ELEMENT(element))
8439       TestIfBadThingTouchesOtherBadThing(newx, newy);
8440   }
8441   else if (element == EL_PENGUIN)
8442     TestIfFriendTouchesBadThing(newx, newy);
8443
8444   if (DONT_GET_HIT_BY(element))
8445   {
8446     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8447   }
8448
8449   /* give the player one last chance (one more frame) to move away */
8450   if (CAN_FALL(element) && direction == MV_DOWN &&
8451       (last_line || (!IS_FREE(x, newy + 1) &&
8452                      (!IS_PLAYER(x, newy + 1) ||
8453                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8454     Impact(x, newy);
8455
8456   if (pushed_by_player && !game.use_change_when_pushing_bug)
8457   {
8458     int push_side = MV_DIR_OPPOSITE(direction);
8459     struct PlayerInfo *player = PLAYERINFO(x, y);
8460
8461     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8462                                player->index_bit, push_side);
8463     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8464                                         player->index_bit, push_side);
8465   }
8466
8467   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8468     MovDelay[newx][newy] = 1;
8469
8470   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8471
8472   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8473   TestIfElementHitsCustomElement(newx, newy, direction);
8474   TestIfPlayerTouchesCustomElement(newx, newy);
8475   TestIfElementTouchesCustomElement(newx, newy);
8476
8477   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8478       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8479     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8480                              MV_DIR_OPPOSITE(direction));
8481 }
8482
8483 int AmoebeNachbarNr(int ax, int ay)
8484 {
8485   int i;
8486   int element = Feld[ax][ay];
8487   int group_nr = 0;
8488   static int xy[4][2] =
8489   {
8490     { 0, -1 },
8491     { -1, 0 },
8492     { +1, 0 },
8493     { 0, +1 }
8494   };
8495
8496   for (i = 0; i < NUM_DIRECTIONS; i++)
8497   {
8498     int x = ax + xy[i][0];
8499     int y = ay + xy[i][1];
8500
8501     if (!IN_LEV_FIELD(x, y))
8502       continue;
8503
8504     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8505       group_nr = AmoebaNr[x][y];
8506   }
8507
8508   return group_nr;
8509 }
8510
8511 void AmoebenVereinigen(int ax, int ay)
8512 {
8513   int i, x, y, xx, yy;
8514   int new_group_nr = AmoebaNr[ax][ay];
8515   static int xy[4][2] =
8516   {
8517     { 0, -1 },
8518     { -1, 0 },
8519     { +1, 0 },
8520     { 0, +1 }
8521   };
8522
8523   if (new_group_nr == 0)
8524     return;
8525
8526   for (i = 0; i < NUM_DIRECTIONS; i++)
8527   {
8528     x = ax + xy[i][0];
8529     y = ay + xy[i][1];
8530
8531     if (!IN_LEV_FIELD(x, y))
8532       continue;
8533
8534     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8535          Feld[x][y] == EL_BD_AMOEBA ||
8536          Feld[x][y] == EL_AMOEBA_DEAD) &&
8537         AmoebaNr[x][y] != new_group_nr)
8538     {
8539       int old_group_nr = AmoebaNr[x][y];
8540
8541       if (old_group_nr == 0)
8542         return;
8543
8544       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8545       AmoebaCnt[old_group_nr] = 0;
8546       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8547       AmoebaCnt2[old_group_nr] = 0;
8548
8549       SCAN_PLAYFIELD(xx, yy)
8550       {
8551         if (AmoebaNr[xx][yy] == old_group_nr)
8552           AmoebaNr[xx][yy] = new_group_nr;
8553       }
8554     }
8555   }
8556 }
8557
8558 void AmoebeUmwandeln(int ax, int ay)
8559 {
8560   int i, x, y;
8561
8562   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8563   {
8564     int group_nr = AmoebaNr[ax][ay];
8565
8566 #ifdef DEBUG
8567     if (group_nr == 0)
8568     {
8569       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8570       printf("AmoebeUmwandeln(): This should never happen!\n");
8571       return;
8572     }
8573 #endif
8574
8575     SCAN_PLAYFIELD(x, y)
8576     {
8577       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8578       {
8579         AmoebaNr[x][y] = 0;
8580         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8581       }
8582     }
8583
8584     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8585                             SND_AMOEBA_TURNING_TO_GEM :
8586                             SND_AMOEBA_TURNING_TO_ROCK));
8587     Bang(ax, ay);
8588   }
8589   else
8590   {
8591     static int xy[4][2] =
8592     {
8593       { 0, -1 },
8594       { -1, 0 },
8595       { +1, 0 },
8596       { 0, +1 }
8597     };
8598
8599     for (i = 0; i < NUM_DIRECTIONS; i++)
8600     {
8601       x = ax + xy[i][0];
8602       y = ay + xy[i][1];
8603
8604       if (!IN_LEV_FIELD(x, y))
8605         continue;
8606
8607       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8608       {
8609         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8610                               SND_AMOEBA_TURNING_TO_GEM :
8611                               SND_AMOEBA_TURNING_TO_ROCK));
8612         Bang(x, y);
8613       }
8614     }
8615   }
8616 }
8617
8618 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8619 {
8620   int x, y;
8621   int group_nr = AmoebaNr[ax][ay];
8622   boolean done = FALSE;
8623
8624 #ifdef DEBUG
8625   if (group_nr == 0)
8626   {
8627     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8628     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8629     return;
8630   }
8631 #endif
8632
8633   SCAN_PLAYFIELD(x, y)
8634   {
8635     if (AmoebaNr[x][y] == group_nr &&
8636         (Feld[x][y] == EL_AMOEBA_DEAD ||
8637          Feld[x][y] == EL_BD_AMOEBA ||
8638          Feld[x][y] == EL_AMOEBA_GROWING))
8639     {
8640       AmoebaNr[x][y] = 0;
8641       Feld[x][y] = new_element;
8642       InitField(x, y, FALSE);
8643       TEST_DrawLevelField(x, y);
8644       done = TRUE;
8645     }
8646   }
8647
8648   if (done)
8649     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8650                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8651                             SND_BD_AMOEBA_TURNING_TO_GEM));
8652 }
8653
8654 void AmoebeWaechst(int x, int y)
8655 {
8656   static unsigned int sound_delay = 0;
8657   static unsigned int sound_delay_value = 0;
8658
8659   if (!MovDelay[x][y])          /* start new growing cycle */
8660   {
8661     MovDelay[x][y] = 7;
8662
8663     if (DelayReached(&sound_delay, sound_delay_value))
8664     {
8665       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8666       sound_delay_value = 30;
8667     }
8668   }
8669
8670   if (MovDelay[x][y])           /* wait some time before growing bigger */
8671   {
8672     MovDelay[x][y]--;
8673     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8674     {
8675       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8676                                            6 - MovDelay[x][y]);
8677
8678       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8679     }
8680
8681     if (!MovDelay[x][y])
8682     {
8683       Feld[x][y] = Store[x][y];
8684       Store[x][y] = 0;
8685       TEST_DrawLevelField(x, y);
8686     }
8687   }
8688 }
8689
8690 void AmoebaDisappearing(int x, int y)
8691 {
8692   static unsigned int sound_delay = 0;
8693   static unsigned int sound_delay_value = 0;
8694
8695   if (!MovDelay[x][y])          /* start new shrinking cycle */
8696   {
8697     MovDelay[x][y] = 7;
8698
8699     if (DelayReached(&sound_delay, sound_delay_value))
8700       sound_delay_value = 30;
8701   }
8702
8703   if (MovDelay[x][y])           /* wait some time before shrinking */
8704   {
8705     MovDelay[x][y]--;
8706     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8707     {
8708       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8709                                            6 - MovDelay[x][y]);
8710
8711       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8712     }
8713
8714     if (!MovDelay[x][y])
8715     {
8716       Feld[x][y] = EL_EMPTY;
8717       TEST_DrawLevelField(x, y);
8718
8719       /* don't let mole enter this field in this cycle;
8720          (give priority to objects falling to this field from above) */
8721       Stop[x][y] = TRUE;
8722     }
8723   }
8724 }
8725
8726 void AmoebeAbleger(int ax, int ay)
8727 {
8728   int i;
8729   int element = Feld[ax][ay];
8730   int graphic = el2img(element);
8731   int newax = ax, neway = ay;
8732   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8733   static int xy[4][2] =
8734   {
8735     { 0, -1 },
8736     { -1, 0 },
8737     { +1, 0 },
8738     { 0, +1 }
8739   };
8740
8741   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8742   {
8743     Feld[ax][ay] = EL_AMOEBA_DEAD;
8744     TEST_DrawLevelField(ax, ay);
8745     return;
8746   }
8747
8748   if (IS_ANIMATED(graphic))
8749     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8750
8751   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8752     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8753
8754   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8755   {
8756     MovDelay[ax][ay]--;
8757     if (MovDelay[ax][ay])
8758       return;
8759   }
8760
8761   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8762   {
8763     int start = RND(4);
8764     int x = ax + xy[start][0];
8765     int y = ay + xy[start][1];
8766
8767     if (!IN_LEV_FIELD(x, y))
8768       return;
8769
8770     if (IS_FREE(x, y) ||
8771         CAN_GROW_INTO(Feld[x][y]) ||
8772         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8773         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8774     {
8775       newax = x;
8776       neway = y;
8777     }
8778
8779     if (newax == ax && neway == ay)
8780       return;
8781   }
8782   else                          /* normal or "filled" (BD style) amoeba */
8783   {
8784     int start = RND(4);
8785     boolean waiting_for_player = FALSE;
8786
8787     for (i = 0; i < NUM_DIRECTIONS; i++)
8788     {
8789       int j = (start + i) % 4;
8790       int x = ax + xy[j][0];
8791       int y = ay + xy[j][1];
8792
8793       if (!IN_LEV_FIELD(x, y))
8794         continue;
8795
8796       if (IS_FREE(x, y) ||
8797           CAN_GROW_INTO(Feld[x][y]) ||
8798           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8799           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8800       {
8801         newax = x;
8802         neway = y;
8803         break;
8804       }
8805       else if (IS_PLAYER(x, y))
8806         waiting_for_player = TRUE;
8807     }
8808
8809     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8810     {
8811       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8812       {
8813         Feld[ax][ay] = EL_AMOEBA_DEAD;
8814         TEST_DrawLevelField(ax, ay);
8815         AmoebaCnt[AmoebaNr[ax][ay]]--;
8816
8817         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8818         {
8819           if (element == EL_AMOEBA_FULL)
8820             AmoebeUmwandeln(ax, ay);
8821           else if (element == EL_BD_AMOEBA)
8822             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8823         }
8824       }
8825       return;
8826     }
8827     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8828     {
8829       /* amoeba gets larger by growing in some direction */
8830
8831       int new_group_nr = AmoebaNr[ax][ay];
8832
8833 #ifdef DEBUG
8834   if (new_group_nr == 0)
8835   {
8836     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8837     printf("AmoebeAbleger(): This should never happen!\n");
8838     return;
8839   }
8840 #endif
8841
8842       AmoebaNr[newax][neway] = new_group_nr;
8843       AmoebaCnt[new_group_nr]++;
8844       AmoebaCnt2[new_group_nr]++;
8845
8846       /* if amoeba touches other amoeba(s) after growing, unify them */
8847       AmoebenVereinigen(newax, neway);
8848
8849       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8850       {
8851         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8852         return;
8853       }
8854     }
8855   }
8856
8857   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8858       (neway == lev_fieldy - 1 && newax != ax))
8859   {
8860     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8861     Store[newax][neway] = element;
8862   }
8863   else if (neway == ay || element == EL_EMC_DRIPPER)
8864   {
8865     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8866
8867     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8868   }
8869   else
8870   {
8871     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8872     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8873     Store[ax][ay] = EL_AMOEBA_DROP;
8874     ContinueMoving(ax, ay);
8875     return;
8876   }
8877
8878   TEST_DrawLevelField(newax, neway);
8879 }
8880
8881 void Life(int ax, int ay)
8882 {
8883   int x1, y1, x2, y2;
8884   int life_time = 40;
8885   int element = Feld[ax][ay];
8886   int graphic = el2img(element);
8887   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8888                          level.biomaze);
8889   boolean changed = FALSE;
8890
8891   if (IS_ANIMATED(graphic))
8892     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8893
8894   if (Stop[ax][ay])
8895     return;
8896
8897   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8898     MovDelay[ax][ay] = life_time;
8899
8900   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8901   {
8902     MovDelay[ax][ay]--;
8903     if (MovDelay[ax][ay])
8904       return;
8905   }
8906
8907   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8908   {
8909     int xx = ax+x1, yy = ay+y1;
8910     int nachbarn = 0;
8911
8912     if (!IN_LEV_FIELD(xx, yy))
8913       continue;
8914
8915     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8916     {
8917       int x = xx+x2, y = yy+y2;
8918
8919       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8920         continue;
8921
8922       if (((Feld[x][y] == element ||
8923             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8924            !Stop[x][y]) ||
8925           (IS_FREE(x, y) && Stop[x][y]))
8926         nachbarn++;
8927     }
8928
8929     if (xx == ax && yy == ay)           /* field in the middle */
8930     {
8931       if (nachbarn < life_parameter[0] ||
8932           nachbarn > life_parameter[1])
8933       {
8934         Feld[xx][yy] = EL_EMPTY;
8935         if (!Stop[xx][yy])
8936           TEST_DrawLevelField(xx, yy);
8937         Stop[xx][yy] = TRUE;
8938         changed = TRUE;
8939       }
8940     }
8941     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8942     {                                   /* free border field */
8943       if (nachbarn >= life_parameter[2] &&
8944           nachbarn <= life_parameter[3])
8945       {
8946         Feld[xx][yy] = element;
8947         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8948         if (!Stop[xx][yy])
8949           TEST_DrawLevelField(xx, yy);
8950         Stop[xx][yy] = TRUE;
8951         changed = TRUE;
8952       }
8953     }
8954   }
8955
8956   if (changed)
8957     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8958                    SND_GAME_OF_LIFE_GROWING);
8959 }
8960
8961 static void InitRobotWheel(int x, int y)
8962 {
8963   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8964 }
8965
8966 static void RunRobotWheel(int x, int y)
8967 {
8968   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8969 }
8970
8971 static void StopRobotWheel(int x, int y)
8972 {
8973   if (ZX == x && ZY == y)
8974   {
8975     ZX = ZY = -1;
8976
8977     game.robot_wheel_active = FALSE;
8978   }
8979 }
8980
8981 static void InitTimegateWheel(int x, int y)
8982 {
8983   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8984 }
8985
8986 static void RunTimegateWheel(int x, int y)
8987 {
8988   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8989 }
8990
8991 static void InitMagicBallDelay(int x, int y)
8992 {
8993   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8994 }
8995
8996 static void ActivateMagicBall(int bx, int by)
8997 {
8998   int x, y;
8999
9000   if (level.ball_random)
9001   {
9002     int pos_border = RND(8);    /* select one of the eight border elements */
9003     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9004     int xx = pos_content % 3;
9005     int yy = pos_content / 3;
9006
9007     x = bx - 1 + xx;
9008     y = by - 1 + yy;
9009
9010     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9011       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9012   }
9013   else
9014   {
9015     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9016     {
9017       int xx = x - bx + 1;
9018       int yy = y - by + 1;
9019
9020       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9021         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9022     }
9023   }
9024
9025   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9026 }
9027
9028 void CheckExit(int x, int y)
9029 {
9030   if (local_player->gems_still_needed > 0 ||
9031       local_player->sokobanfields_still_needed > 0 ||
9032       local_player->lights_still_needed > 0)
9033   {
9034     int element = Feld[x][y];
9035     int graphic = el2img(element);
9036
9037     if (IS_ANIMATED(graphic))
9038       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9039
9040     return;
9041   }
9042
9043   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9044     return;
9045
9046   Feld[x][y] = EL_EXIT_OPENING;
9047
9048   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9049 }
9050
9051 void CheckExitEM(int x, int y)
9052 {
9053   if (local_player->gems_still_needed > 0 ||
9054       local_player->sokobanfields_still_needed > 0 ||
9055       local_player->lights_still_needed > 0)
9056   {
9057     int element = Feld[x][y];
9058     int graphic = el2img(element);
9059
9060     if (IS_ANIMATED(graphic))
9061       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9062
9063     return;
9064   }
9065
9066   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9067     return;
9068
9069   Feld[x][y] = EL_EM_EXIT_OPENING;
9070
9071   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9072 }
9073
9074 void CheckExitSteel(int x, int y)
9075 {
9076   if (local_player->gems_still_needed > 0 ||
9077       local_player->sokobanfields_still_needed > 0 ||
9078       local_player->lights_still_needed > 0)
9079   {
9080     int element = Feld[x][y];
9081     int graphic = el2img(element);
9082
9083     if (IS_ANIMATED(graphic))
9084       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9085
9086     return;
9087   }
9088
9089   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9090     return;
9091
9092   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9093
9094   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9095 }
9096
9097 void CheckExitSteelEM(int x, int y)
9098 {
9099   if (local_player->gems_still_needed > 0 ||
9100       local_player->sokobanfields_still_needed > 0 ||
9101       local_player->lights_still_needed > 0)
9102   {
9103     int element = Feld[x][y];
9104     int graphic = el2img(element);
9105
9106     if (IS_ANIMATED(graphic))
9107       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9108
9109     return;
9110   }
9111
9112   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9113     return;
9114
9115   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9116
9117   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9118 }
9119
9120 void CheckExitSP(int x, int y)
9121 {
9122   if (local_player->gems_still_needed > 0)
9123   {
9124     int element = Feld[x][y];
9125     int graphic = el2img(element);
9126
9127     if (IS_ANIMATED(graphic))
9128       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9129
9130     return;
9131   }
9132
9133   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9134     return;
9135
9136   Feld[x][y] = EL_SP_EXIT_OPENING;
9137
9138   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9139 }
9140
9141 static void CloseAllOpenTimegates()
9142 {
9143   int x, y;
9144
9145   SCAN_PLAYFIELD(x, y)
9146   {
9147     int element = Feld[x][y];
9148
9149     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9150     {
9151       Feld[x][y] = EL_TIMEGATE_CLOSING;
9152
9153       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9154     }
9155   }
9156 }
9157
9158 void DrawTwinkleOnField(int x, int y)
9159 {
9160   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9161     return;
9162
9163   if (Feld[x][y] == EL_BD_DIAMOND)
9164     return;
9165
9166   if (MovDelay[x][y] == 0)      /* next animation frame */
9167     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9168
9169   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9170   {
9171     MovDelay[x][y]--;
9172
9173     DrawLevelElementAnimation(x, y, Feld[x][y]);
9174
9175     if (MovDelay[x][y] != 0)
9176     {
9177       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9178                                            10 - MovDelay[x][y]);
9179
9180       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9181     }
9182   }
9183 }
9184
9185 void MauerWaechst(int x, int y)
9186 {
9187   int delay = 6;
9188
9189   if (!MovDelay[x][y])          /* next animation frame */
9190     MovDelay[x][y] = 3 * delay;
9191
9192   if (MovDelay[x][y])           /* wait some time before next frame */
9193   {
9194     MovDelay[x][y]--;
9195
9196     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9197     {
9198       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9199       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9200
9201       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9202     }
9203
9204     if (!MovDelay[x][y])
9205     {
9206       if (MovDir[x][y] == MV_LEFT)
9207       {
9208         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9209           TEST_DrawLevelField(x - 1, y);
9210       }
9211       else if (MovDir[x][y] == MV_RIGHT)
9212       {
9213         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9214           TEST_DrawLevelField(x + 1, y);
9215       }
9216       else if (MovDir[x][y] == MV_UP)
9217       {
9218         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9219           TEST_DrawLevelField(x, y - 1);
9220       }
9221       else
9222       {
9223         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9224           TEST_DrawLevelField(x, y + 1);
9225       }
9226
9227       Feld[x][y] = Store[x][y];
9228       Store[x][y] = 0;
9229       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9230       TEST_DrawLevelField(x, y);
9231     }
9232   }
9233 }
9234
9235 void MauerAbleger(int ax, int ay)
9236 {
9237   int element = Feld[ax][ay];
9238   int graphic = el2img(element);
9239   boolean oben_frei = FALSE, unten_frei = FALSE;
9240   boolean links_frei = FALSE, rechts_frei = FALSE;
9241   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9242   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9243   boolean new_wall = FALSE;
9244
9245   if (IS_ANIMATED(graphic))
9246     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9247
9248   if (!MovDelay[ax][ay])        /* start building new wall */
9249     MovDelay[ax][ay] = 6;
9250
9251   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9252   {
9253     MovDelay[ax][ay]--;
9254     if (MovDelay[ax][ay])
9255       return;
9256   }
9257
9258   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9259     oben_frei = TRUE;
9260   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9261     unten_frei = TRUE;
9262   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9263     links_frei = TRUE;
9264   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9265     rechts_frei = TRUE;
9266
9267   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9268       element == EL_EXPANDABLE_WALL_ANY)
9269   {
9270     if (oben_frei)
9271     {
9272       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9273       Store[ax][ay-1] = element;
9274       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9275       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9276         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9277                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9278       new_wall = TRUE;
9279     }
9280     if (unten_frei)
9281     {
9282       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9283       Store[ax][ay+1] = element;
9284       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9285       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9286         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9287                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9288       new_wall = TRUE;
9289     }
9290   }
9291
9292   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9293       element == EL_EXPANDABLE_WALL_ANY ||
9294       element == EL_EXPANDABLE_WALL ||
9295       element == EL_BD_EXPANDABLE_WALL)
9296   {
9297     if (links_frei)
9298     {
9299       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9300       Store[ax-1][ay] = element;
9301       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9302       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9303         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9304                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9305       new_wall = TRUE;
9306     }
9307
9308     if (rechts_frei)
9309     {
9310       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9311       Store[ax+1][ay] = element;
9312       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9313       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9314         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9315                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9316       new_wall = TRUE;
9317     }
9318   }
9319
9320   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9321     TEST_DrawLevelField(ax, ay);
9322
9323   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9324     oben_massiv = TRUE;
9325   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9326     unten_massiv = TRUE;
9327   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9328     links_massiv = TRUE;
9329   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9330     rechts_massiv = TRUE;
9331
9332   if (((oben_massiv && unten_massiv) ||
9333        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9334        element == EL_EXPANDABLE_WALL) &&
9335       ((links_massiv && rechts_massiv) ||
9336        element == EL_EXPANDABLE_WALL_VERTICAL))
9337     Feld[ax][ay] = EL_WALL;
9338
9339   if (new_wall)
9340     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9341 }
9342
9343 void MauerAblegerStahl(int ax, int ay)
9344 {
9345   int element = Feld[ax][ay];
9346   int graphic = el2img(element);
9347   boolean oben_frei = FALSE, unten_frei = FALSE;
9348   boolean links_frei = FALSE, rechts_frei = FALSE;
9349   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9350   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9351   boolean new_wall = FALSE;
9352
9353   if (IS_ANIMATED(graphic))
9354     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9355
9356   if (!MovDelay[ax][ay])        /* start building new wall */
9357     MovDelay[ax][ay] = 6;
9358
9359   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9360   {
9361     MovDelay[ax][ay]--;
9362     if (MovDelay[ax][ay])
9363       return;
9364   }
9365
9366   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9367     oben_frei = TRUE;
9368   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9369     unten_frei = TRUE;
9370   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9371     links_frei = TRUE;
9372   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9373     rechts_frei = TRUE;
9374
9375   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9376       element == EL_EXPANDABLE_STEELWALL_ANY)
9377   {
9378     if (oben_frei)
9379     {
9380       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9381       Store[ax][ay-1] = element;
9382       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9383       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9384         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9385                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9386       new_wall = TRUE;
9387     }
9388     if (unten_frei)
9389     {
9390       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9391       Store[ax][ay+1] = element;
9392       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9393       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9394         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9395                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9396       new_wall = TRUE;
9397     }
9398   }
9399
9400   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9401       element == EL_EXPANDABLE_STEELWALL_ANY)
9402   {
9403     if (links_frei)
9404     {
9405       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9406       Store[ax-1][ay] = element;
9407       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9408       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9409         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9410                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9411       new_wall = TRUE;
9412     }
9413
9414     if (rechts_frei)
9415     {
9416       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9417       Store[ax+1][ay] = element;
9418       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9419       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9420         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9421                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9422       new_wall = TRUE;
9423     }
9424   }
9425
9426   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9427     oben_massiv = TRUE;
9428   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9429     unten_massiv = TRUE;
9430   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9431     links_massiv = TRUE;
9432   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9433     rechts_massiv = TRUE;
9434
9435   if (((oben_massiv && unten_massiv) ||
9436        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9437       ((links_massiv && rechts_massiv) ||
9438        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9439     Feld[ax][ay] = EL_STEELWALL;
9440
9441   if (new_wall)
9442     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9443 }
9444
9445 void CheckForDragon(int x, int y)
9446 {
9447   int i, j;
9448   boolean dragon_found = FALSE;
9449   static int xy[4][2] =
9450   {
9451     { 0, -1 },
9452     { -1, 0 },
9453     { +1, 0 },
9454     { 0, +1 }
9455   };
9456
9457   for (i = 0; i < NUM_DIRECTIONS; i++)
9458   {
9459     for (j = 0; j < 4; j++)
9460     {
9461       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9462
9463       if (IN_LEV_FIELD(xx, yy) &&
9464           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9465       {
9466         if (Feld[xx][yy] == EL_DRAGON)
9467           dragon_found = TRUE;
9468       }
9469       else
9470         break;
9471     }
9472   }
9473
9474   if (!dragon_found)
9475   {
9476     for (i = 0; i < NUM_DIRECTIONS; i++)
9477     {
9478       for (j = 0; j < 3; j++)
9479       {
9480         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9481   
9482         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9483         {
9484           Feld[xx][yy] = EL_EMPTY;
9485           TEST_DrawLevelField(xx, yy);
9486         }
9487         else
9488           break;
9489       }
9490     }
9491   }
9492 }
9493
9494 static void InitBuggyBase(int x, int y)
9495 {
9496   int element = Feld[x][y];
9497   int activating_delay = FRAMES_PER_SECOND / 4;
9498
9499   ChangeDelay[x][y] =
9500     (element == EL_SP_BUGGY_BASE ?
9501      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9502      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9503      activating_delay :
9504      element == EL_SP_BUGGY_BASE_ACTIVE ?
9505      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9506 }
9507
9508 static void WarnBuggyBase(int x, int y)
9509 {
9510   int i;
9511   static int xy[4][2] =
9512   {
9513     { 0, -1 },
9514     { -1, 0 },
9515     { +1, 0 },
9516     { 0, +1 }
9517   };
9518
9519   for (i = 0; i < NUM_DIRECTIONS; i++)
9520   {
9521     int xx = x + xy[i][0];
9522     int yy = y + xy[i][1];
9523
9524     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9525     {
9526       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9527
9528       break;
9529     }
9530   }
9531 }
9532
9533 static void InitTrap(int x, int y)
9534 {
9535   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9536 }
9537
9538 static void ActivateTrap(int x, int y)
9539 {
9540   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9541 }
9542
9543 static void ChangeActiveTrap(int x, int y)
9544 {
9545   int graphic = IMG_TRAP_ACTIVE;
9546
9547   /* if new animation frame was drawn, correct crumbled sand border */
9548   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9549     TEST_DrawLevelFieldCrumbled(x, y);
9550 }
9551
9552 static int getSpecialActionElement(int element, int number, int base_element)
9553 {
9554   return (element != EL_EMPTY ? element :
9555           number != -1 ? base_element + number - 1 :
9556           EL_EMPTY);
9557 }
9558
9559 static int getModifiedActionNumber(int value_old, int operator, int operand,
9560                                    int value_min, int value_max)
9561 {
9562   int value_new = (operator == CA_MODE_SET      ? operand :
9563                    operator == CA_MODE_ADD      ? value_old + operand :
9564                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9565                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9566                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9567                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9568                    value_old);
9569
9570   return (value_new < value_min ? value_min :
9571           value_new > value_max ? value_max :
9572           value_new);
9573 }
9574
9575 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9576 {
9577   struct ElementInfo *ei = &element_info[element];
9578   struct ElementChangeInfo *change = &ei->change_page[page];
9579   int target_element = change->target_element;
9580   int action_type = change->action_type;
9581   int action_mode = change->action_mode;
9582   int action_arg = change->action_arg;
9583   int action_element = change->action_element;
9584   int i;
9585
9586   if (!change->has_action)
9587     return;
9588
9589   /* ---------- determine action paramater values -------------------------- */
9590
9591   int level_time_value =
9592     (level.time > 0 ? TimeLeft :
9593      TimePlayed);
9594
9595   int action_arg_element_raw =
9596     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9597      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9598      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9599      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9600      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9601      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9602      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9603      EL_EMPTY);
9604   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9605
9606   int action_arg_direction =
9607     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9608      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9609      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9610      change->actual_trigger_side :
9611      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9612      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9613      MV_NONE);
9614
9615   int action_arg_number_min =
9616     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9617      CA_ARG_MIN);
9618
9619   int action_arg_number_max =
9620     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9621      action_type == CA_SET_LEVEL_GEMS ? 999 :
9622      action_type == CA_SET_LEVEL_TIME ? 9999 :
9623      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9624      action_type == CA_SET_CE_VALUE ? 9999 :
9625      action_type == CA_SET_CE_SCORE ? 9999 :
9626      CA_ARG_MAX);
9627
9628   int action_arg_number_reset =
9629     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9630      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9631      action_type == CA_SET_LEVEL_TIME ? level.time :
9632      action_type == CA_SET_LEVEL_SCORE ? 0 :
9633      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9634      action_type == CA_SET_CE_SCORE ? 0 :
9635      0);
9636
9637   int action_arg_number =
9638     (action_arg <= CA_ARG_MAX ? action_arg :
9639      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9640      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9641      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9642      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9643      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9644      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9645      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9646      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9647      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9648      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9649      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9650      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9651      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9652      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9653      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9654      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9655      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9656      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9657      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9658      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9659      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9660      -1);
9661
9662   int action_arg_number_old =
9663     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9664      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9665      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9666      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9667      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9668      0);
9669
9670   int action_arg_number_new =
9671     getModifiedActionNumber(action_arg_number_old,
9672                             action_mode, action_arg_number,
9673                             action_arg_number_min, action_arg_number_max);
9674
9675   int trigger_player_bits =
9676     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9677      change->actual_trigger_player_bits : change->trigger_player);
9678
9679   int action_arg_player_bits =
9680     (action_arg >= CA_ARG_PLAYER_1 &&
9681      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9682      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9683      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9684      PLAYER_BITS_ANY);
9685
9686   /* ---------- execute action  -------------------------------------------- */
9687
9688   switch (action_type)
9689   {
9690     case CA_NO_ACTION:
9691     {
9692       return;
9693     }
9694
9695     /* ---------- level actions  ------------------------------------------- */
9696
9697     case CA_RESTART_LEVEL:
9698     {
9699       game.restart_level = TRUE;
9700
9701       break;
9702     }
9703
9704     case CA_SHOW_ENVELOPE:
9705     {
9706       int element = getSpecialActionElement(action_arg_element,
9707                                             action_arg_number, EL_ENVELOPE_1);
9708
9709       if (IS_ENVELOPE(element))
9710         local_player->show_envelope = element;
9711
9712       break;
9713     }
9714
9715     case CA_SET_LEVEL_TIME:
9716     {
9717       if (level.time > 0)       /* only modify limited time value */
9718       {
9719         TimeLeft = action_arg_number_new;
9720
9721         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9722
9723         DisplayGameControlValues();
9724
9725         if (!TimeLeft && setup.time_limit)
9726           for (i = 0; i < MAX_PLAYERS; i++)
9727             KillPlayer(&stored_player[i]);
9728       }
9729
9730       break;
9731     }
9732
9733     case CA_SET_LEVEL_SCORE:
9734     {
9735       local_player->score = action_arg_number_new;
9736
9737       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9738
9739       DisplayGameControlValues();
9740
9741       break;
9742     }
9743
9744     case CA_SET_LEVEL_GEMS:
9745     {
9746       local_player->gems_still_needed = action_arg_number_new;
9747
9748       game.snapshot.collected_item = TRUE;
9749
9750       game_panel_controls[GAME_PANEL_GEMS].value =
9751         local_player->gems_still_needed;
9752
9753       DisplayGameControlValues();
9754
9755       break;
9756     }
9757
9758     case CA_SET_LEVEL_WIND:
9759     {
9760       game.wind_direction = action_arg_direction;
9761
9762       break;
9763     }
9764
9765     case CA_SET_LEVEL_RANDOM_SEED:
9766     {
9767       /* ensure that setting a new random seed while playing is predictable */
9768       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9769
9770       break;
9771     }
9772
9773     /* ---------- player actions  ------------------------------------------ */
9774
9775     case CA_MOVE_PLAYER:
9776     {
9777       /* automatically move to the next field in specified direction */
9778       for (i = 0; i < MAX_PLAYERS; i++)
9779         if (trigger_player_bits & (1 << i))
9780           stored_player[i].programmed_action = action_arg_direction;
9781
9782       break;
9783     }
9784
9785     case CA_EXIT_PLAYER:
9786     {
9787       for (i = 0; i < MAX_PLAYERS; i++)
9788         if (action_arg_player_bits & (1 << i))
9789           ExitPlayer(&stored_player[i]);
9790
9791       if (AllPlayersGone)
9792         PlayerWins(local_player);
9793
9794       break;
9795     }
9796
9797     case CA_KILL_PLAYER:
9798     {
9799       for (i = 0; i < MAX_PLAYERS; i++)
9800         if (action_arg_player_bits & (1 << i))
9801           KillPlayer(&stored_player[i]);
9802
9803       break;
9804     }
9805
9806     case CA_SET_PLAYER_KEYS:
9807     {
9808       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9809       int element = getSpecialActionElement(action_arg_element,
9810                                             action_arg_number, EL_KEY_1);
9811
9812       if (IS_KEY(element))
9813       {
9814         for (i = 0; i < MAX_PLAYERS; i++)
9815         {
9816           if (trigger_player_bits & (1 << i))
9817           {
9818             stored_player[i].key[KEY_NR(element)] = key_state;
9819
9820             DrawGameDoorValues();
9821           }
9822         }
9823       }
9824
9825       break;
9826     }
9827
9828     case CA_SET_PLAYER_SPEED:
9829     {
9830       for (i = 0; i < MAX_PLAYERS; i++)
9831       {
9832         if (trigger_player_bits & (1 << i))
9833         {
9834           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9835
9836           if (action_arg == CA_ARG_SPEED_FASTER &&
9837               stored_player[i].cannot_move)
9838           {
9839             action_arg_number = STEPSIZE_VERY_SLOW;
9840           }
9841           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9842                    action_arg == CA_ARG_SPEED_FASTER)
9843           {
9844             action_arg_number = 2;
9845             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9846                            CA_MODE_MULTIPLY);
9847           }
9848           else if (action_arg == CA_ARG_NUMBER_RESET)
9849           {
9850             action_arg_number = level.initial_player_stepsize[i];
9851           }
9852
9853           move_stepsize =
9854             getModifiedActionNumber(move_stepsize,
9855                                     action_mode,
9856                                     action_arg_number,
9857                                     action_arg_number_min,
9858                                     action_arg_number_max);
9859
9860           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9861         }
9862       }
9863
9864       break;
9865     }
9866
9867     case CA_SET_PLAYER_SHIELD:
9868     {
9869       for (i = 0; i < MAX_PLAYERS; i++)
9870       {
9871         if (trigger_player_bits & (1 << i))
9872         {
9873           if (action_arg == CA_ARG_SHIELD_OFF)
9874           {
9875             stored_player[i].shield_normal_time_left = 0;
9876             stored_player[i].shield_deadly_time_left = 0;
9877           }
9878           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9879           {
9880             stored_player[i].shield_normal_time_left = 999999;
9881           }
9882           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9883           {
9884             stored_player[i].shield_normal_time_left = 999999;
9885             stored_player[i].shield_deadly_time_left = 999999;
9886           }
9887         }
9888       }
9889
9890       break;
9891     }
9892
9893     case CA_SET_PLAYER_GRAVITY:
9894     {
9895       for (i = 0; i < MAX_PLAYERS; i++)
9896       {
9897         if (trigger_player_bits & (1 << i))
9898         {
9899           stored_player[i].gravity =
9900             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9901              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9902              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9903              stored_player[i].gravity);
9904         }
9905       }
9906
9907       break;
9908     }
9909
9910     case CA_SET_PLAYER_ARTWORK:
9911     {
9912       for (i = 0; i < MAX_PLAYERS; i++)
9913       {
9914         if (trigger_player_bits & (1 << i))
9915         {
9916           int artwork_element = action_arg_element;
9917
9918           if (action_arg == CA_ARG_ELEMENT_RESET)
9919             artwork_element =
9920               (level.use_artwork_element[i] ? level.artwork_element[i] :
9921                stored_player[i].element_nr);
9922
9923           if (stored_player[i].artwork_element != artwork_element)
9924             stored_player[i].Frame = 0;
9925
9926           stored_player[i].artwork_element = artwork_element;
9927
9928           SetPlayerWaiting(&stored_player[i], FALSE);
9929
9930           /* set number of special actions for bored and sleeping animation */
9931           stored_player[i].num_special_action_bored =
9932             get_num_special_action(artwork_element,
9933                                    ACTION_BORING_1, ACTION_BORING_LAST);
9934           stored_player[i].num_special_action_sleeping =
9935             get_num_special_action(artwork_element,
9936                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9937         }
9938       }
9939
9940       break;
9941     }
9942
9943     case CA_SET_PLAYER_INVENTORY:
9944     {
9945       for (i = 0; i < MAX_PLAYERS; i++)
9946       {
9947         struct PlayerInfo *player = &stored_player[i];
9948         int j, k;
9949
9950         if (trigger_player_bits & (1 << i))
9951         {
9952           int inventory_element = action_arg_element;
9953
9954           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9955               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9956               action_arg == CA_ARG_ELEMENT_ACTION)
9957           {
9958             int element = inventory_element;
9959             int collect_count = element_info[element].collect_count_initial;
9960
9961             if (!IS_CUSTOM_ELEMENT(element))
9962               collect_count = 1;
9963
9964             if (collect_count == 0)
9965               player->inventory_infinite_element = element;
9966             else
9967               for (k = 0; k < collect_count; k++)
9968                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9969                   player->inventory_element[player->inventory_size++] =
9970                     element;
9971           }
9972           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9973                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9974                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9975           {
9976             if (player->inventory_infinite_element != EL_UNDEFINED &&
9977                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9978                                      action_arg_element_raw))
9979               player->inventory_infinite_element = EL_UNDEFINED;
9980
9981             for (k = 0, j = 0; j < player->inventory_size; j++)
9982             {
9983               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9984                                         action_arg_element_raw))
9985                 player->inventory_element[k++] = player->inventory_element[j];
9986             }
9987
9988             player->inventory_size = k;
9989           }
9990           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9991           {
9992             if (player->inventory_size > 0)
9993             {
9994               for (j = 0; j < player->inventory_size - 1; j++)
9995                 player->inventory_element[j] = player->inventory_element[j + 1];
9996
9997               player->inventory_size--;
9998             }
9999           }
10000           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10001           {
10002             if (player->inventory_size > 0)
10003               player->inventory_size--;
10004           }
10005           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10006           {
10007             player->inventory_infinite_element = EL_UNDEFINED;
10008             player->inventory_size = 0;
10009           }
10010           else if (action_arg == CA_ARG_INVENTORY_RESET)
10011           {
10012             player->inventory_infinite_element = EL_UNDEFINED;
10013             player->inventory_size = 0;
10014
10015             if (level.use_initial_inventory[i])
10016             {
10017               for (j = 0; j < level.initial_inventory_size[i]; j++)
10018               {
10019                 int element = level.initial_inventory_content[i][j];
10020                 int collect_count = element_info[element].collect_count_initial;
10021
10022                 if (!IS_CUSTOM_ELEMENT(element))
10023                   collect_count = 1;
10024
10025                 if (collect_count == 0)
10026                   player->inventory_infinite_element = element;
10027                 else
10028                   for (k = 0; k < collect_count; k++)
10029                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10030                       player->inventory_element[player->inventory_size++] =
10031                         element;
10032               }
10033             }
10034           }
10035         }
10036       }
10037
10038       break;
10039     }
10040
10041     /* ---------- CE actions  ---------------------------------------------- */
10042
10043     case CA_SET_CE_VALUE:
10044     {
10045       int last_ce_value = CustomValue[x][y];
10046
10047       CustomValue[x][y] = action_arg_number_new;
10048
10049       if (CustomValue[x][y] != last_ce_value)
10050       {
10051         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10052         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10053
10054         if (CustomValue[x][y] == 0)
10055         {
10056           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10057           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10058         }
10059       }
10060
10061       break;
10062     }
10063
10064     case CA_SET_CE_SCORE:
10065     {
10066       int last_ce_score = ei->collect_score;
10067
10068       ei->collect_score = action_arg_number_new;
10069
10070       if (ei->collect_score != last_ce_score)
10071       {
10072         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10073         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10074
10075         if (ei->collect_score == 0)
10076         {
10077           int xx, yy;
10078
10079           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10080           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10081
10082           /*
10083             This is a very special case that seems to be a mixture between
10084             CheckElementChange() and CheckTriggeredElementChange(): while
10085             the first one only affects single elements that are triggered
10086             directly, the second one affects multiple elements in the playfield
10087             that are triggered indirectly by another element. This is a third
10088             case: Changing the CE score always affects multiple identical CEs,
10089             so every affected CE must be checked, not only the single CE for
10090             which the CE score was changed in the first place (as every instance
10091             of that CE shares the same CE score, and therefore also can change)!
10092           */
10093           SCAN_PLAYFIELD(xx, yy)
10094           {
10095             if (Feld[xx][yy] == element)
10096               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10097                                  CE_SCORE_GETS_ZERO);
10098           }
10099         }
10100       }
10101
10102       break;
10103     }
10104
10105     case CA_SET_CE_ARTWORK:
10106     {
10107       int artwork_element = action_arg_element;
10108       boolean reset_frame = FALSE;
10109       int xx, yy;
10110
10111       if (action_arg == CA_ARG_ELEMENT_RESET)
10112         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10113                            element);
10114
10115       if (ei->gfx_element != artwork_element)
10116         reset_frame = TRUE;
10117
10118       ei->gfx_element = artwork_element;
10119
10120       SCAN_PLAYFIELD(xx, yy)
10121       {
10122         if (Feld[xx][yy] == element)
10123         {
10124           if (reset_frame)
10125           {
10126             ResetGfxAnimation(xx, yy);
10127             ResetRandomAnimationValue(xx, yy);
10128           }
10129
10130           TEST_DrawLevelField(xx, yy);
10131         }
10132       }
10133
10134       break;
10135     }
10136
10137     /* ---------- engine actions  ------------------------------------------ */
10138
10139     case CA_SET_ENGINE_SCAN_MODE:
10140     {
10141       InitPlayfieldScanMode(action_arg);
10142
10143       break;
10144     }
10145
10146     default:
10147       break;
10148   }
10149 }
10150
10151 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10152 {
10153   int old_element = Feld[x][y];
10154   int new_element = GetElementFromGroupElement(element);
10155   int previous_move_direction = MovDir[x][y];
10156   int last_ce_value = CustomValue[x][y];
10157   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10158   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10159   boolean add_player_onto_element = (new_element_is_player &&
10160                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10161                                      IS_WALKABLE(old_element));
10162
10163   if (!add_player_onto_element)
10164   {
10165     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10166       RemoveMovingField(x, y);
10167     else
10168       RemoveField(x, y);
10169
10170     Feld[x][y] = new_element;
10171
10172     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10173       MovDir[x][y] = previous_move_direction;
10174
10175     if (element_info[new_element].use_last_ce_value)
10176       CustomValue[x][y] = last_ce_value;
10177
10178     InitField_WithBug1(x, y, FALSE);
10179
10180     new_element = Feld[x][y];   /* element may have changed */
10181
10182     ResetGfxAnimation(x, y);
10183     ResetRandomAnimationValue(x, y);
10184
10185     TEST_DrawLevelField(x, y);
10186
10187     if (GFX_CRUMBLED(new_element))
10188       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10189   }
10190
10191   /* check if element under the player changes from accessible to unaccessible
10192      (needed for special case of dropping element which then changes) */
10193   /* (must be checked after creating new element for walkable group elements) */
10194   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10195       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10196   {
10197     Bang(x, y);
10198
10199     return;
10200   }
10201
10202   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10203   if (new_element_is_player)
10204     RelocatePlayer(x, y, new_element);
10205
10206   if (is_change)
10207     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10208
10209   TestIfBadThingTouchesPlayer(x, y);
10210   TestIfPlayerTouchesCustomElement(x, y);
10211   TestIfElementTouchesCustomElement(x, y);
10212 }
10213
10214 static void CreateField(int x, int y, int element)
10215 {
10216   CreateFieldExt(x, y, element, FALSE);
10217 }
10218
10219 static void CreateElementFromChange(int x, int y, int element)
10220 {
10221   element = GET_VALID_RUNTIME_ELEMENT(element);
10222
10223   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10224   {
10225     int old_element = Feld[x][y];
10226
10227     /* prevent changed element from moving in same engine frame
10228        unless both old and new element can either fall or move */
10229     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10230         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10231       Stop[x][y] = TRUE;
10232   }
10233
10234   CreateFieldExt(x, y, element, TRUE);
10235 }
10236
10237 static boolean ChangeElement(int x, int y, int element, int page)
10238 {
10239   struct ElementInfo *ei = &element_info[element];
10240   struct ElementChangeInfo *change = &ei->change_page[page];
10241   int ce_value = CustomValue[x][y];
10242   int ce_score = ei->collect_score;
10243   int target_element;
10244   int old_element = Feld[x][y];
10245
10246   /* always use default change event to prevent running into a loop */
10247   if (ChangeEvent[x][y] == -1)
10248     ChangeEvent[x][y] = CE_DELAY;
10249
10250   if (ChangeEvent[x][y] == CE_DELAY)
10251   {
10252     /* reset actual trigger element, trigger player and action element */
10253     change->actual_trigger_element = EL_EMPTY;
10254     change->actual_trigger_player = EL_EMPTY;
10255     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10256     change->actual_trigger_side = CH_SIDE_NONE;
10257     change->actual_trigger_ce_value = 0;
10258     change->actual_trigger_ce_score = 0;
10259   }
10260
10261   /* do not change elements more than a specified maximum number of changes */
10262   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10263     return FALSE;
10264
10265   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10266
10267   if (change->explode)
10268   {
10269     Bang(x, y);
10270
10271     return TRUE;
10272   }
10273
10274   if (change->use_target_content)
10275   {
10276     boolean complete_replace = TRUE;
10277     boolean can_replace[3][3];
10278     int xx, yy;
10279
10280     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10281     {
10282       boolean is_empty;
10283       boolean is_walkable;
10284       boolean is_diggable;
10285       boolean is_collectible;
10286       boolean is_removable;
10287       boolean is_destructible;
10288       int ex = x + xx - 1;
10289       int ey = y + yy - 1;
10290       int content_element = change->target_content.e[xx][yy];
10291       int e;
10292
10293       can_replace[xx][yy] = TRUE;
10294
10295       if (ex == x && ey == y)   /* do not check changing element itself */
10296         continue;
10297
10298       if (content_element == EL_EMPTY_SPACE)
10299       {
10300         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10301
10302         continue;
10303       }
10304
10305       if (!IN_LEV_FIELD(ex, ey))
10306       {
10307         can_replace[xx][yy] = FALSE;
10308         complete_replace = FALSE;
10309
10310         continue;
10311       }
10312
10313       e = Feld[ex][ey];
10314
10315       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10316         e = MovingOrBlocked2Element(ex, ey);
10317
10318       is_empty = (IS_FREE(ex, ey) ||
10319                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10320
10321       is_walkable     = (is_empty || IS_WALKABLE(e));
10322       is_diggable     = (is_empty || IS_DIGGABLE(e));
10323       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10324       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10325       is_removable    = (is_diggable || is_collectible);
10326
10327       can_replace[xx][yy] =
10328         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10329           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10330           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10331           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10332           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10333           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10334          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10335
10336       if (!can_replace[xx][yy])
10337         complete_replace = FALSE;
10338     }
10339
10340     if (!change->only_if_complete || complete_replace)
10341     {
10342       boolean something_has_changed = FALSE;
10343
10344       if (change->only_if_complete && change->use_random_replace &&
10345           RND(100) < change->random_percentage)
10346         return FALSE;
10347
10348       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10349       {
10350         int ex = x + xx - 1;
10351         int ey = y + yy - 1;
10352         int content_element;
10353
10354         if (can_replace[xx][yy] && (!change->use_random_replace ||
10355                                     RND(100) < change->random_percentage))
10356         {
10357           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10358             RemoveMovingField(ex, ey);
10359
10360           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10361
10362           content_element = change->target_content.e[xx][yy];
10363           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10364                                               ce_value, ce_score);
10365
10366           CreateElementFromChange(ex, ey, target_element);
10367
10368           something_has_changed = TRUE;
10369
10370           /* for symmetry reasons, freeze newly created border elements */
10371           if (ex != x || ey != y)
10372             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10373         }
10374       }
10375
10376       if (something_has_changed)
10377       {
10378         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10379         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10380       }
10381     }
10382   }
10383   else
10384   {
10385     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10386                                         ce_value, ce_score);
10387
10388     if (element == EL_DIAGONAL_GROWING ||
10389         element == EL_DIAGONAL_SHRINKING)
10390     {
10391       target_element = Store[x][y];
10392
10393       Store[x][y] = EL_EMPTY;
10394     }
10395
10396     CreateElementFromChange(x, y, target_element);
10397
10398     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10399     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10400   }
10401
10402   /* this uses direct change before indirect change */
10403   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10404
10405   return TRUE;
10406 }
10407
10408 static void HandleElementChange(int x, int y, int page)
10409 {
10410   int element = MovingOrBlocked2Element(x, y);
10411   struct ElementInfo *ei = &element_info[element];
10412   struct ElementChangeInfo *change = &ei->change_page[page];
10413   boolean handle_action_before_change = FALSE;
10414
10415 #ifdef DEBUG
10416   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10417       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10418   {
10419     printf("\n\n");
10420     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10421            x, y, element, element_info[element].token_name);
10422     printf("HandleElementChange(): This should never happen!\n");
10423     printf("\n\n");
10424   }
10425 #endif
10426
10427   /* this can happen with classic bombs on walkable, changing elements */
10428   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10429   {
10430     return;
10431   }
10432
10433   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10434   {
10435     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10436
10437     if (change->can_change)
10438     {
10439       /* !!! not clear why graphic animation should be reset at all here !!! */
10440       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10441       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10442
10443       /*
10444         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10445
10446         When using an animation frame delay of 1 (this only happens with
10447         "sp_zonk.moving.left/right" in the classic graphics), the default
10448         (non-moving) animation shows wrong animation frames (while the
10449         moving animation, like "sp_zonk.moving.left/right", is correct,
10450         so this graphical bug never shows up with the classic graphics).
10451         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10452         be drawn instead of the correct frames 0,1,2,3. This is caused by
10453         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10454         an element change: First when the change delay ("ChangeDelay[][]")
10455         counter has reached zero after decrementing, then a second time in
10456         the next frame (after "GfxFrame[][]" was already incremented) when
10457         "ChangeDelay[][]" is reset to the initial delay value again.
10458
10459         This causes frame 0 to be drawn twice, while the last frame won't
10460         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10461
10462         As some animations may already be cleverly designed around this bug
10463         (at least the "Snake Bite" snake tail animation does this), it cannot
10464         simply be fixed here without breaking such existing animations.
10465         Unfortunately, it cannot easily be detected if a graphics set was
10466         designed "before" or "after" the bug was fixed. As a workaround,
10467         a new graphics set option "game.graphics_engine_version" was added
10468         to be able to specify the game's major release version for which the
10469         graphics set was designed, which can then be used to decide if the
10470         bugfix should be used (version 4 and above) or not (version 3 or
10471         below, or if no version was specified at all, as with old sets).
10472
10473         (The wrong/fixed animation frames can be tested with the test level set
10474         "test_gfxframe" and level "000", which contains a specially prepared
10475         custom element at level position (x/y) == (11/9) which uses the zonk
10476         animation mentioned above. Using "game.graphics_engine_version: 4"
10477         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10478         This can also be seen from the debug output for this test element.)
10479       */
10480
10481       /* when a custom element is about to change (for example by change delay),
10482          do not reset graphic animation when the custom element is moving */
10483       if (game.graphics_engine_version < 4 &&
10484           !IS_MOVING(x, y))
10485       {
10486         ResetGfxAnimation(x, y);
10487         ResetRandomAnimationValue(x, y);
10488       }
10489
10490       if (change->pre_change_function)
10491         change->pre_change_function(x, y);
10492     }
10493   }
10494
10495   ChangeDelay[x][y]--;
10496
10497   if (ChangeDelay[x][y] != 0)           /* continue element change */
10498   {
10499     if (change->can_change)
10500     {
10501       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10502
10503       if (IS_ANIMATED(graphic))
10504         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10505
10506       if (change->change_function)
10507         change->change_function(x, y);
10508     }
10509   }
10510   else                                  /* finish element change */
10511   {
10512     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10513     {
10514       page = ChangePage[x][y];
10515       ChangePage[x][y] = -1;
10516
10517       change = &ei->change_page[page];
10518     }
10519
10520     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10521     {
10522       ChangeDelay[x][y] = 1;            /* try change after next move step */
10523       ChangePage[x][y] = page;          /* remember page to use for change */
10524
10525       return;
10526     }
10527
10528     /* special case: set new level random seed before changing element */
10529     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10530       handle_action_before_change = TRUE;
10531
10532     if (change->has_action && handle_action_before_change)
10533       ExecuteCustomElementAction(x, y, element, page);
10534
10535     if (change->can_change)
10536     {
10537       if (ChangeElement(x, y, element, page))
10538       {
10539         if (change->post_change_function)
10540           change->post_change_function(x, y);
10541       }
10542     }
10543
10544     if (change->has_action && !handle_action_before_change)
10545       ExecuteCustomElementAction(x, y, element, page);
10546   }
10547 }
10548
10549 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10550                                               int trigger_element,
10551                                               int trigger_event,
10552                                               int trigger_player,
10553                                               int trigger_side,
10554                                               int trigger_page)
10555 {
10556   boolean change_done_any = FALSE;
10557   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10558   int i;
10559
10560   if (!(trigger_events[trigger_element][trigger_event]))
10561     return FALSE;
10562
10563   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10564
10565   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10566   {
10567     int element = EL_CUSTOM_START + i;
10568     boolean change_done = FALSE;
10569     int p;
10570
10571     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10572         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10573       continue;
10574
10575     for (p = 0; p < element_info[element].num_change_pages; p++)
10576     {
10577       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10578
10579       if (change->can_change_or_has_action &&
10580           change->has_event[trigger_event] &&
10581           change->trigger_side & trigger_side &&
10582           change->trigger_player & trigger_player &&
10583           change->trigger_page & trigger_page_bits &&
10584           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10585       {
10586         change->actual_trigger_element = trigger_element;
10587         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10588         change->actual_trigger_player_bits = trigger_player;
10589         change->actual_trigger_side = trigger_side;
10590         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10591         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10592
10593         if ((change->can_change && !change_done) || change->has_action)
10594         {
10595           int x, y;
10596
10597           SCAN_PLAYFIELD(x, y)
10598           {
10599             if (Feld[x][y] == element)
10600             {
10601               if (change->can_change && !change_done)
10602               {
10603                 /* if element already changed in this frame, not only prevent
10604                    another element change (checked in ChangeElement()), but
10605                    also prevent additional element actions for this element */
10606
10607                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10608                     !level.use_action_after_change_bug)
10609                   continue;
10610
10611                 ChangeDelay[x][y] = 1;
10612                 ChangeEvent[x][y] = trigger_event;
10613
10614                 HandleElementChange(x, y, p);
10615               }
10616               else if (change->has_action)
10617               {
10618                 /* if element already changed in this frame, not only prevent
10619                    another element change (checked in ChangeElement()), but
10620                    also prevent additional element actions for this element */
10621
10622                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10623                     !level.use_action_after_change_bug)
10624                   continue;
10625
10626                 ExecuteCustomElementAction(x, y, element, p);
10627                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10628               }
10629             }
10630           }
10631
10632           if (change->can_change)
10633           {
10634             change_done = TRUE;
10635             change_done_any = TRUE;
10636           }
10637         }
10638       }
10639     }
10640   }
10641
10642   RECURSION_LOOP_DETECTION_END();
10643
10644   return change_done_any;
10645 }
10646
10647 static boolean CheckElementChangeExt(int x, int y,
10648                                      int element,
10649                                      int trigger_element,
10650                                      int trigger_event,
10651                                      int trigger_player,
10652                                      int trigger_side)
10653 {
10654   boolean change_done = FALSE;
10655   int p;
10656
10657   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10658       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10659     return FALSE;
10660
10661   if (Feld[x][y] == EL_BLOCKED)
10662   {
10663     Blocked2Moving(x, y, &x, &y);
10664     element = Feld[x][y];
10665   }
10666
10667   /* check if element has already changed or is about to change after moving */
10668   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10669        Feld[x][y] != element) ||
10670
10671       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10672        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10673         ChangePage[x][y] != -1)))
10674     return FALSE;
10675
10676   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10677
10678   for (p = 0; p < element_info[element].num_change_pages; p++)
10679   {
10680     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10681
10682     /* check trigger element for all events where the element that is checked
10683        for changing interacts with a directly adjacent element -- this is
10684        different to element changes that affect other elements to change on the
10685        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10686     boolean check_trigger_element =
10687       (trigger_event == CE_TOUCHING_X ||
10688        trigger_event == CE_HITTING_X ||
10689        trigger_event == CE_HIT_BY_X ||
10690        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10691
10692     if (change->can_change_or_has_action &&
10693         change->has_event[trigger_event] &&
10694         change->trigger_side & trigger_side &&
10695         change->trigger_player & trigger_player &&
10696         (!check_trigger_element ||
10697          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10698     {
10699       change->actual_trigger_element = trigger_element;
10700       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10701       change->actual_trigger_player_bits = trigger_player;
10702       change->actual_trigger_side = trigger_side;
10703       change->actual_trigger_ce_value = CustomValue[x][y];
10704       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10705
10706       /* special case: trigger element not at (x,y) position for some events */
10707       if (check_trigger_element)
10708       {
10709         static struct
10710         {
10711           int dx, dy;
10712         } move_xy[] =
10713           {
10714             {  0,  0 },
10715             { -1,  0 },
10716             { +1,  0 },
10717             {  0,  0 },
10718             {  0, -1 },
10719             {  0,  0 }, { 0, 0 }, { 0, 0 },
10720             {  0, +1 }
10721           };
10722
10723         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10724         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10725
10726         change->actual_trigger_ce_value = CustomValue[xx][yy];
10727         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10728       }
10729
10730       if (change->can_change && !change_done)
10731       {
10732         ChangeDelay[x][y] = 1;
10733         ChangeEvent[x][y] = trigger_event;
10734
10735         HandleElementChange(x, y, p);
10736
10737         change_done = TRUE;
10738       }
10739       else if (change->has_action)
10740       {
10741         ExecuteCustomElementAction(x, y, element, p);
10742         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10743       }
10744     }
10745   }
10746
10747   RECURSION_LOOP_DETECTION_END();
10748
10749   return change_done;
10750 }
10751
10752 static void PlayPlayerSound(struct PlayerInfo *player)
10753 {
10754   int jx = player->jx, jy = player->jy;
10755   int sound_element = player->artwork_element;
10756   int last_action = player->last_action_waiting;
10757   int action = player->action_waiting;
10758
10759   if (player->is_waiting)
10760   {
10761     if (action != last_action)
10762       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10763     else
10764       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10765   }
10766   else
10767   {
10768     if (action != last_action)
10769       StopSound(element_info[sound_element].sound[last_action]);
10770
10771     if (last_action == ACTION_SLEEPING)
10772       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10773   }
10774 }
10775
10776 static void PlayAllPlayersSound()
10777 {
10778   int i;
10779
10780   for (i = 0; i < MAX_PLAYERS; i++)
10781     if (stored_player[i].active)
10782       PlayPlayerSound(&stored_player[i]);
10783 }
10784
10785 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10786 {
10787   boolean last_waiting = player->is_waiting;
10788   int move_dir = player->MovDir;
10789
10790   player->dir_waiting = move_dir;
10791   player->last_action_waiting = player->action_waiting;
10792
10793   if (is_waiting)
10794   {
10795     if (!last_waiting)          /* not waiting -> waiting */
10796     {
10797       player->is_waiting = TRUE;
10798
10799       player->frame_counter_bored =
10800         FrameCounter +
10801         game.player_boring_delay_fixed +
10802         GetSimpleRandom(game.player_boring_delay_random);
10803       player->frame_counter_sleeping =
10804         FrameCounter +
10805         game.player_sleeping_delay_fixed +
10806         GetSimpleRandom(game.player_sleeping_delay_random);
10807
10808       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10809     }
10810
10811     if (game.player_sleeping_delay_fixed +
10812         game.player_sleeping_delay_random > 0 &&
10813         player->anim_delay_counter == 0 &&
10814         player->post_delay_counter == 0 &&
10815         FrameCounter >= player->frame_counter_sleeping)
10816       player->is_sleeping = TRUE;
10817     else if (game.player_boring_delay_fixed +
10818              game.player_boring_delay_random > 0 &&
10819              FrameCounter >= player->frame_counter_bored)
10820       player->is_bored = TRUE;
10821
10822     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10823                               player->is_bored ? ACTION_BORING :
10824                               ACTION_WAITING);
10825
10826     if (player->is_sleeping && player->use_murphy)
10827     {
10828       /* special case for sleeping Murphy when leaning against non-free tile */
10829
10830       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10831           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10832            !IS_MOVING(player->jx - 1, player->jy)))
10833         move_dir = MV_LEFT;
10834       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10835                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10836                 !IS_MOVING(player->jx + 1, player->jy)))
10837         move_dir = MV_RIGHT;
10838       else
10839         player->is_sleeping = FALSE;
10840
10841       player->dir_waiting = move_dir;
10842     }
10843
10844     if (player->is_sleeping)
10845     {
10846       if (player->num_special_action_sleeping > 0)
10847       {
10848         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10849         {
10850           int last_special_action = player->special_action_sleeping;
10851           int num_special_action = player->num_special_action_sleeping;
10852           int special_action =
10853             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10854              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10855              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10856              last_special_action + 1 : ACTION_SLEEPING);
10857           int special_graphic =
10858             el_act_dir2img(player->artwork_element, special_action, move_dir);
10859
10860           player->anim_delay_counter =
10861             graphic_info[special_graphic].anim_delay_fixed +
10862             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10863           player->post_delay_counter =
10864             graphic_info[special_graphic].post_delay_fixed +
10865             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10866
10867           player->special_action_sleeping = special_action;
10868         }
10869
10870         if (player->anim_delay_counter > 0)
10871         {
10872           player->action_waiting = player->special_action_sleeping;
10873           player->anim_delay_counter--;
10874         }
10875         else if (player->post_delay_counter > 0)
10876         {
10877           player->post_delay_counter--;
10878         }
10879       }
10880     }
10881     else if (player->is_bored)
10882     {
10883       if (player->num_special_action_bored > 0)
10884       {
10885         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10886         {
10887           int special_action =
10888             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10889           int special_graphic =
10890             el_act_dir2img(player->artwork_element, special_action, move_dir);
10891
10892           player->anim_delay_counter =
10893             graphic_info[special_graphic].anim_delay_fixed +
10894             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10895           player->post_delay_counter =
10896             graphic_info[special_graphic].post_delay_fixed +
10897             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10898
10899           player->special_action_bored = special_action;
10900         }
10901
10902         if (player->anim_delay_counter > 0)
10903         {
10904           player->action_waiting = player->special_action_bored;
10905           player->anim_delay_counter--;
10906         }
10907         else if (player->post_delay_counter > 0)
10908         {
10909           player->post_delay_counter--;
10910         }
10911       }
10912     }
10913   }
10914   else if (last_waiting)        /* waiting -> not waiting */
10915   {
10916     player->is_waiting = FALSE;
10917     player->is_bored = FALSE;
10918     player->is_sleeping = FALSE;
10919
10920     player->frame_counter_bored = -1;
10921     player->frame_counter_sleeping = -1;
10922
10923     player->anim_delay_counter = 0;
10924     player->post_delay_counter = 0;
10925
10926     player->dir_waiting = player->MovDir;
10927     player->action_waiting = ACTION_DEFAULT;
10928
10929     player->special_action_bored = ACTION_DEFAULT;
10930     player->special_action_sleeping = ACTION_DEFAULT;
10931   }
10932 }
10933
10934 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10935 {
10936   if ((!player->is_moving  && player->was_moving) ||
10937       (player->MovPos == 0 && player->was_moving) ||
10938       (player->is_snapping && !player->was_snapping) ||
10939       (player->is_dropping && !player->was_dropping))
10940   {
10941     if (!CheckSaveEngineSnapshotToList())
10942       return;
10943
10944     player->was_moving = FALSE;
10945     player->was_snapping = TRUE;
10946     player->was_dropping = TRUE;
10947   }
10948   else
10949   {
10950     if (player->is_moving)
10951       player->was_moving = TRUE;
10952
10953     if (!player->is_snapping)
10954       player->was_snapping = FALSE;
10955
10956     if (!player->is_dropping)
10957       player->was_dropping = FALSE;
10958   }
10959 }
10960
10961 static void CheckSingleStepMode(struct PlayerInfo *player)
10962 {
10963   if (tape.single_step && tape.recording && !tape.pausing)
10964   {
10965     /* as it is called "single step mode", just return to pause mode when the
10966        player stopped moving after one tile (or never starts moving at all) */
10967     if (!player->is_moving &&
10968         !player->is_pushing &&
10969         !player->is_dropping_pressed)
10970     {
10971       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10972       SnapField(player, 0, 0);                  /* stop snapping */
10973     }
10974   }
10975
10976   CheckSaveEngineSnapshot(player);
10977 }
10978
10979 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10980 {
10981   int left      = player_action & JOY_LEFT;
10982   int right     = player_action & JOY_RIGHT;
10983   int up        = player_action & JOY_UP;
10984   int down      = player_action & JOY_DOWN;
10985   int button1   = player_action & JOY_BUTTON_1;
10986   int button2   = player_action & JOY_BUTTON_2;
10987   int dx        = (left ? -1 : right ? 1 : 0);
10988   int dy        = (up   ? -1 : down  ? 1 : 0);
10989
10990   if (!player->active || tape.pausing)
10991     return 0;
10992
10993   if (player_action)
10994   {
10995     if (button1)
10996       SnapField(player, dx, dy);
10997     else
10998     {
10999       if (button2)
11000         DropElement(player);
11001
11002       MovePlayer(player, dx, dy);
11003     }
11004
11005     CheckSingleStepMode(player);
11006
11007     SetPlayerWaiting(player, FALSE);
11008
11009     return player_action;
11010   }
11011   else
11012   {
11013     /* no actions for this player (no input at player's configured device) */
11014
11015     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11016     SnapField(player, 0, 0);
11017     CheckGravityMovementWhenNotMoving(player);
11018
11019     if (player->MovPos == 0)
11020       SetPlayerWaiting(player, TRUE);
11021
11022     if (player->MovPos == 0)    /* needed for tape.playing */
11023       player->is_moving = FALSE;
11024
11025     player->is_dropping = FALSE;
11026     player->is_dropping_pressed = FALSE;
11027     player->drop_pressed_delay = 0;
11028
11029     CheckSingleStepMode(player);
11030
11031     return 0;
11032   }
11033 }
11034
11035 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11036                                          byte *tape_action)
11037 {
11038   if (!tape.use_mouse)
11039     return;
11040
11041   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11042   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11043   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11044 }
11045
11046 static void SetTapeActionFromMouseAction(byte *tape_action,
11047                                          struct MouseActionInfo *mouse_action)
11048 {
11049   if (!tape.use_mouse)
11050     return;
11051
11052   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11053   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11054   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11055 }
11056
11057 static void CheckLevelTime()
11058 {
11059   int i;
11060
11061   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11062   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11063   {
11064     if (level.native_em_level->lev->home == 0)  /* all players at home */
11065     {
11066       PlayerWins(local_player);
11067
11068       AllPlayersGone = TRUE;
11069
11070       level.native_em_level->lev->home = -1;
11071     }
11072
11073     if (level.native_em_level->ply[0]->alive == 0 &&
11074         level.native_em_level->ply[1]->alive == 0 &&
11075         level.native_em_level->ply[2]->alive == 0 &&
11076         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11077       AllPlayersGone = TRUE;
11078   }
11079   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11080   {
11081     if (game_sp.LevelSolved &&
11082         !game_sp.GameOver)                              /* game won */
11083     {
11084       PlayerWins(local_player);
11085
11086       game_sp.GameOver = TRUE;
11087
11088       AllPlayersGone = TRUE;
11089     }
11090
11091     if (game_sp.GameOver)                               /* game lost */
11092       AllPlayersGone = TRUE;
11093   }
11094   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11095   {
11096     if (game_mm.level_solved &&
11097         !game_mm.game_over)                             /* game won */
11098     {
11099       PlayerWins(local_player);
11100
11101       game_mm.game_over = TRUE;
11102
11103       AllPlayersGone = TRUE;
11104     }
11105
11106     if (game_mm.game_over)                              /* game lost */
11107       AllPlayersGone = TRUE;
11108   }
11109
11110   if (TimeFrames >= FRAMES_PER_SECOND)
11111   {
11112     TimeFrames = 0;
11113     TapeTime++;
11114
11115     for (i = 0; i < MAX_PLAYERS; i++)
11116     {
11117       struct PlayerInfo *player = &stored_player[i];
11118
11119       if (SHIELD_ON(player))
11120       {
11121         player->shield_normal_time_left--;
11122
11123         if (player->shield_deadly_time_left > 0)
11124           player->shield_deadly_time_left--;
11125       }
11126     }
11127
11128     if (!local_player->LevelSolved && !level.use_step_counter)
11129     {
11130       TimePlayed++;
11131
11132       if (TimeLeft > 0)
11133       {
11134         TimeLeft--;
11135
11136         if (TimeLeft <= 10 && setup.time_limit)
11137           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11138
11139         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11140            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11141
11142         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11143
11144         if (!TimeLeft && setup.time_limit)
11145         {
11146           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11147             level.native_em_level->lev->killed_out_of_time = TRUE;
11148           else
11149             for (i = 0; i < MAX_PLAYERS; i++)
11150               KillPlayer(&stored_player[i]);
11151         }
11152       }
11153       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11154       {
11155         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11156       }
11157
11158       level.native_em_level->lev->time =
11159         (game.no_time_limit ? TimePlayed : TimeLeft);
11160     }
11161
11162     if (tape.recording || tape.playing)
11163       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11164   }
11165
11166   if (tape.recording || tape.playing)
11167     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11168
11169   UpdateAndDisplayGameControlValues();
11170 }
11171
11172 void AdvanceFrameAndPlayerCounters(int player_nr)
11173 {
11174   int i;
11175
11176   /* advance frame counters (global frame counter and time frame counter) */
11177   FrameCounter++;
11178   TimeFrames++;
11179
11180   /* advance player counters (counters for move delay, move animation etc.) */
11181   for (i = 0; i < MAX_PLAYERS; i++)
11182   {
11183     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11184     int move_delay_value = stored_player[i].move_delay_value;
11185     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11186
11187     if (!advance_player_counters)       /* not all players may be affected */
11188       continue;
11189
11190     if (move_frames == 0)       /* less than one move per game frame */
11191     {
11192       int stepsize = TILEX / move_delay_value;
11193       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11194       int count = (stored_player[i].is_moving ?
11195                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11196
11197       if (count % delay == 0)
11198         move_frames = 1;
11199     }
11200
11201     stored_player[i].Frame += move_frames;
11202
11203     if (stored_player[i].MovPos != 0)
11204       stored_player[i].StepFrame += move_frames;
11205
11206     if (stored_player[i].move_delay > 0)
11207       stored_player[i].move_delay--;
11208
11209     /* due to bugs in previous versions, counter must count up, not down */
11210     if (stored_player[i].push_delay != -1)
11211       stored_player[i].push_delay++;
11212
11213     if (stored_player[i].drop_delay > 0)
11214       stored_player[i].drop_delay--;
11215
11216     if (stored_player[i].is_dropping_pressed)
11217       stored_player[i].drop_pressed_delay++;
11218   }
11219 }
11220
11221 void StartGameActions(boolean init_network_game, boolean record_tape,
11222                       int random_seed)
11223 {
11224   unsigned int new_random_seed = InitRND(random_seed);
11225
11226   if (record_tape)
11227     TapeStartRecording(new_random_seed);
11228
11229   if (init_network_game)
11230   {
11231     SendToServer_LevelFile();
11232     SendToServer_StartPlaying();
11233
11234     return;
11235   }
11236
11237   InitGame();
11238 }
11239
11240 void GameActionsExt()
11241 {
11242 #if 0
11243   static unsigned int game_frame_delay = 0;
11244 #endif
11245   unsigned int game_frame_delay_value;
11246   byte *recorded_player_action;
11247   byte summarized_player_action = 0;
11248   byte tape_action[MAX_PLAYERS];
11249   int i;
11250
11251   /* detect endless loops, caused by custom element programming */
11252   if (recursion_loop_detected && recursion_loop_depth == 0)
11253   {
11254     char *message = getStringCat3("Internal Error! Element ",
11255                                   EL_NAME(recursion_loop_element),
11256                                   " caused endless loop! Quit the game?");
11257
11258     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11259           EL_NAME(recursion_loop_element));
11260
11261     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11262
11263     recursion_loop_detected = FALSE;    /* if game should be continued */
11264
11265     free(message);
11266
11267     return;
11268   }
11269
11270   if (game.restart_level)
11271     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11272
11273   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11274   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11275   {
11276     if (level.native_em_level->lev->home == 0)  /* all players at home */
11277     {
11278       PlayerWins(local_player);
11279
11280       AllPlayersGone = TRUE;
11281
11282       level.native_em_level->lev->home = -1;
11283     }
11284
11285     if (level.native_em_level->ply[0]->alive == 0 &&
11286         level.native_em_level->ply[1]->alive == 0 &&
11287         level.native_em_level->ply[2]->alive == 0 &&
11288         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11289       AllPlayersGone = TRUE;
11290   }
11291   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11292   {
11293     if (game_sp.LevelSolved &&
11294         !game_sp.GameOver)                              /* game won */
11295     {
11296       PlayerWins(local_player);
11297
11298       game_sp.GameOver = TRUE;
11299
11300       AllPlayersGone = TRUE;
11301     }
11302
11303     if (game_sp.GameOver)                               /* game lost */
11304       AllPlayersGone = TRUE;
11305   }
11306   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11307   {
11308     if (game_mm.level_solved &&
11309         !game_mm.game_over)                             /* game won */
11310     {
11311       PlayerWins(local_player);
11312
11313       game_mm.game_over = TRUE;
11314
11315       AllPlayersGone = TRUE;
11316     }
11317
11318     if (game_mm.game_over)                              /* game lost */
11319       AllPlayersGone = TRUE;
11320   }
11321
11322   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11323     GameWon();
11324
11325   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11326     TapeStop();
11327
11328   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11329     return;
11330
11331   game_frame_delay_value =
11332     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11333
11334   if (tape.playing && tape.warp_forward && !tape.pausing)
11335     game_frame_delay_value = 0;
11336
11337   SetVideoFrameDelay(game_frame_delay_value);
11338
11339 #if 0
11340 #if 0
11341   /* ---------- main game synchronization point ---------- */
11342
11343   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11344
11345   printf("::: skip == %d\n", skip);
11346
11347 #else
11348   /* ---------- main game synchronization point ---------- */
11349
11350   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11351 #endif
11352 #endif
11353
11354   if (network_playing && !network_player_action_received)
11355   {
11356     /* try to get network player actions in time */
11357
11358     /* last chance to get network player actions without main loop delay */
11359     HandleNetworking();
11360
11361     /* game was quit by network peer */
11362     if (game_status != GAME_MODE_PLAYING)
11363       return;
11364
11365     if (!network_player_action_received)
11366       return;           /* failed to get network player actions in time */
11367
11368     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11369   }
11370
11371   if (tape.pausing)
11372     return;
11373
11374   /* at this point we know that we really continue executing the game */
11375
11376   network_player_action_received = FALSE;
11377
11378   /* when playing tape, read previously recorded player input from tape data */
11379   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11380
11381   local_player->effective_mouse_action = local_player->mouse_action;
11382
11383   if (recorded_player_action != NULL)
11384     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11385                                  recorded_player_action);
11386
11387   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11388   if (tape.pausing)
11389     return;
11390
11391   if (tape.set_centered_player)
11392   {
11393     game.centered_player_nr_next = tape.centered_player_nr_next;
11394     game.set_centered_player = TRUE;
11395   }
11396
11397   for (i = 0; i < MAX_PLAYERS; i++)
11398   {
11399     summarized_player_action |= stored_player[i].action;
11400
11401     if (!network_playing && (game.team_mode || tape.playing))
11402       stored_player[i].effective_action = stored_player[i].action;
11403   }
11404
11405   if (network_playing)
11406     SendToServer_MovePlayer(summarized_player_action);
11407
11408   // summarize all actions at local players mapped input device position
11409   // (this allows using different input devices in single player mode)
11410   if (!network.enabled && !game.team_mode)
11411     stored_player[map_player_action[local_player->index_nr]].effective_action =
11412       summarized_player_action;
11413
11414   if (tape.recording &&
11415       setup.team_mode &&
11416       setup.input_on_focus &&
11417       game.centered_player_nr != -1)
11418   {
11419     for (i = 0; i < MAX_PLAYERS; i++)
11420       stored_player[i].effective_action =
11421         (i == game.centered_player_nr ? summarized_player_action : 0);
11422   }
11423
11424   if (recorded_player_action != NULL)
11425     for (i = 0; i < MAX_PLAYERS; i++)
11426       stored_player[i].effective_action = recorded_player_action[i];
11427
11428   for (i = 0; i < MAX_PLAYERS; i++)
11429   {
11430     tape_action[i] = stored_player[i].effective_action;
11431
11432     /* (this may happen in the RND game engine if a player was not present on
11433        the playfield on level start, but appeared later from a custom element */
11434     if (setup.team_mode &&
11435         tape.recording &&
11436         tape_action[i] &&
11437         !tape.player_participates[i])
11438       tape.player_participates[i] = TRUE;
11439   }
11440
11441   SetTapeActionFromMouseAction(tape_action,
11442                                &local_player->effective_mouse_action);
11443
11444   /* only record actions from input devices, but not programmed actions */
11445   if (tape.recording)
11446     TapeRecordAction(tape_action);
11447
11448 #if USE_NEW_PLAYER_ASSIGNMENTS
11449   // !!! also map player actions in single player mode !!!
11450   // if (game.team_mode)
11451   if (1)
11452   {
11453     byte mapped_action[MAX_PLAYERS];
11454
11455 #if DEBUG_PLAYER_ACTIONS
11456     printf(":::");
11457     for (i = 0; i < MAX_PLAYERS; i++)
11458       printf(" %d, ", stored_player[i].effective_action);
11459 #endif
11460
11461     for (i = 0; i < MAX_PLAYERS; i++)
11462       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11463
11464     for (i = 0; i < MAX_PLAYERS; i++)
11465       stored_player[i].effective_action = mapped_action[i];
11466
11467 #if DEBUG_PLAYER_ACTIONS
11468     printf(" =>");
11469     for (i = 0; i < MAX_PLAYERS; i++)
11470       printf(" %d, ", stored_player[i].effective_action);
11471     printf("\n");
11472 #endif
11473   }
11474 #if DEBUG_PLAYER_ACTIONS
11475   else
11476   {
11477     printf(":::");
11478     for (i = 0; i < MAX_PLAYERS; i++)
11479       printf(" %d, ", stored_player[i].effective_action);
11480     printf("\n");
11481   }
11482 #endif
11483 #endif
11484
11485   for (i = 0; i < MAX_PLAYERS; i++)
11486   {
11487     // allow engine snapshot in case of changed movement attempt
11488     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11489         (stored_player[i].effective_action & KEY_MOTION))
11490       game.snapshot.changed_action = TRUE;
11491
11492     // allow engine snapshot in case of snapping/dropping attempt
11493     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11494         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11495       game.snapshot.changed_action = TRUE;
11496
11497     game.snapshot.last_action[i] = stored_player[i].effective_action;
11498   }
11499
11500   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11501   {
11502     GameActions_EM_Main();
11503   }
11504   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11505   {
11506     GameActions_SP_Main();
11507   }
11508   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11509   {
11510     GameActions_MM_Main();
11511   }
11512   else
11513   {
11514     GameActions_RND_Main();
11515   }
11516
11517   BlitScreenToBitmap(backbuffer);
11518
11519   CheckLevelTime();
11520
11521   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11522
11523   if (global.show_frames_per_second)
11524   {
11525     static unsigned int fps_counter = 0;
11526     static int fps_frames = 0;
11527     unsigned int fps_delay_ms = Counter() - fps_counter;
11528
11529     fps_frames++;
11530
11531     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11532     {
11533       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11534
11535       fps_frames = 0;
11536       fps_counter = Counter();
11537
11538       /* always draw FPS to screen after FPS value was updated */
11539       redraw_mask |= REDRAW_FPS;
11540     }
11541
11542     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11543     if (GetDrawDeactivationMask() == REDRAW_NONE)
11544       redraw_mask |= REDRAW_FPS;
11545   }
11546 }
11547
11548 static void GameActions_CheckSaveEngineSnapshot()
11549 {
11550   if (!game.snapshot.save_snapshot)
11551     return;
11552
11553   // clear flag for saving snapshot _before_ saving snapshot
11554   game.snapshot.save_snapshot = FALSE;
11555
11556   SaveEngineSnapshotToList();
11557 }
11558
11559 void GameActions()
11560 {
11561   GameActionsExt();
11562
11563   GameActions_CheckSaveEngineSnapshot();
11564 }
11565
11566 void GameActions_EM_Main()
11567 {
11568   byte effective_action[MAX_PLAYERS];
11569   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11570   int i;
11571
11572   for (i = 0; i < MAX_PLAYERS; i++)
11573     effective_action[i] = stored_player[i].effective_action;
11574
11575   GameActions_EM(effective_action, warp_mode);
11576 }
11577
11578 void GameActions_SP_Main()
11579 {
11580   byte effective_action[MAX_PLAYERS];
11581   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11582   int i;
11583
11584   for (i = 0; i < MAX_PLAYERS; i++)
11585     effective_action[i] = stored_player[i].effective_action;
11586
11587   GameActions_SP(effective_action, warp_mode);
11588
11589   for (i = 0; i < MAX_PLAYERS; i++)
11590   {
11591     if (stored_player[i].force_dropping)
11592       stored_player[i].action |= KEY_BUTTON_DROP;
11593
11594     stored_player[i].force_dropping = FALSE;
11595   }
11596 }
11597
11598 void GameActions_MM_Main()
11599 {
11600   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11601
11602   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11603 }
11604
11605 void GameActions_RND_Main()
11606 {
11607   GameActions_RND();
11608 }
11609
11610 void GameActions_RND()
11611 {
11612   int magic_wall_x = 0, magic_wall_y = 0;
11613   int i, x, y, element, graphic, last_gfx_frame;
11614
11615   InitPlayfieldScanModeVars();
11616
11617   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11618   {
11619     SCAN_PLAYFIELD(x, y)
11620     {
11621       ChangeCount[x][y] = 0;
11622       ChangeEvent[x][y] = -1;
11623     }
11624   }
11625
11626   if (game.set_centered_player)
11627   {
11628     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11629
11630     /* switching to "all players" only possible if all players fit to screen */
11631     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11632     {
11633       game.centered_player_nr_next = game.centered_player_nr;
11634       game.set_centered_player = FALSE;
11635     }
11636
11637     /* do not switch focus to non-existing (or non-active) player */
11638     if (game.centered_player_nr_next >= 0 &&
11639         !stored_player[game.centered_player_nr_next].active)
11640     {
11641       game.centered_player_nr_next = game.centered_player_nr;
11642       game.set_centered_player = FALSE;
11643     }
11644   }
11645
11646   if (game.set_centered_player &&
11647       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11648   {
11649     int sx, sy;
11650
11651     if (game.centered_player_nr_next == -1)
11652     {
11653       setScreenCenteredToAllPlayers(&sx, &sy);
11654     }
11655     else
11656     {
11657       sx = stored_player[game.centered_player_nr_next].jx;
11658       sy = stored_player[game.centered_player_nr_next].jy;
11659     }
11660
11661     game.centered_player_nr = game.centered_player_nr_next;
11662     game.set_centered_player = FALSE;
11663
11664     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11665     DrawGameDoorValues();
11666   }
11667
11668   for (i = 0; i < MAX_PLAYERS; i++)
11669   {
11670     int actual_player_action = stored_player[i].effective_action;
11671
11672 #if 1
11673     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11674        - rnd_equinox_tetrachloride 048
11675        - rnd_equinox_tetrachloride_ii 096
11676        - rnd_emanuel_schmieg 002
11677        - doctor_sloan_ww 001, 020
11678     */
11679     if (stored_player[i].MovPos == 0)
11680       CheckGravityMovement(&stored_player[i]);
11681 #endif
11682
11683     /* overwrite programmed action with tape action */
11684     if (stored_player[i].programmed_action)
11685       actual_player_action = stored_player[i].programmed_action;
11686
11687     PlayerActions(&stored_player[i], actual_player_action);
11688
11689     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11690   }
11691
11692   ScrollScreen(NULL, SCROLL_GO_ON);
11693
11694   /* for backwards compatibility, the following code emulates a fixed bug that
11695      occured when pushing elements (causing elements that just made their last
11696      pushing step to already (if possible) make their first falling step in the
11697      same game frame, which is bad); this code is also needed to use the famous
11698      "spring push bug" which is used in older levels and might be wanted to be
11699      used also in newer levels, but in this case the buggy pushing code is only
11700      affecting the "spring" element and no other elements */
11701
11702   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11703   {
11704     for (i = 0; i < MAX_PLAYERS; i++)
11705     {
11706       struct PlayerInfo *player = &stored_player[i];
11707       int x = player->jx;
11708       int y = player->jy;
11709
11710       if (player->active && player->is_pushing && player->is_moving &&
11711           IS_MOVING(x, y) &&
11712           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11713            Feld[x][y] == EL_SPRING))
11714       {
11715         ContinueMoving(x, y);
11716
11717         /* continue moving after pushing (this is actually a bug) */
11718         if (!IS_MOVING(x, y))
11719           Stop[x][y] = FALSE;
11720       }
11721     }
11722   }
11723
11724   SCAN_PLAYFIELD(x, y)
11725   {
11726     ChangeCount[x][y] = 0;
11727     ChangeEvent[x][y] = -1;
11728
11729     /* this must be handled before main playfield loop */
11730     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11731     {
11732       MovDelay[x][y]--;
11733       if (MovDelay[x][y] <= 0)
11734         RemoveField(x, y);
11735     }
11736
11737     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11738     {
11739       MovDelay[x][y]--;
11740       if (MovDelay[x][y] <= 0)
11741       {
11742         RemoveField(x, y);
11743         TEST_DrawLevelField(x, y);
11744
11745         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11746       }
11747     }
11748
11749 #if DEBUG
11750     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11751     {
11752       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11753       printf("GameActions(): This should never happen!\n");
11754
11755       ChangePage[x][y] = -1;
11756     }
11757 #endif
11758
11759     Stop[x][y] = FALSE;
11760     if (WasJustMoving[x][y] > 0)
11761       WasJustMoving[x][y]--;
11762     if (WasJustFalling[x][y] > 0)
11763       WasJustFalling[x][y]--;
11764     if (CheckCollision[x][y] > 0)
11765       CheckCollision[x][y]--;
11766     if (CheckImpact[x][y] > 0)
11767       CheckImpact[x][y]--;
11768
11769     GfxFrame[x][y]++;
11770
11771     /* reset finished pushing action (not done in ContinueMoving() to allow
11772        continuous pushing animation for elements with zero push delay) */
11773     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11774     {
11775       ResetGfxAnimation(x, y);
11776       TEST_DrawLevelField(x, y);
11777     }
11778
11779 #if DEBUG
11780     if (IS_BLOCKED(x, y))
11781     {
11782       int oldx, oldy;
11783
11784       Blocked2Moving(x, y, &oldx, &oldy);
11785       if (!IS_MOVING(oldx, oldy))
11786       {
11787         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11788         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11789         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11790         printf("GameActions(): This should never happen!\n");
11791       }
11792     }
11793 #endif
11794   }
11795
11796   SCAN_PLAYFIELD(x, y)
11797   {
11798     element = Feld[x][y];
11799     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11800     last_gfx_frame = GfxFrame[x][y];
11801
11802     ResetGfxFrame(x, y);
11803
11804     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11805       DrawLevelGraphicAnimation(x, y, graphic);
11806
11807     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11808         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11809       ResetRandomAnimationValue(x, y);
11810
11811     SetRandomAnimationValue(x, y);
11812
11813     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11814
11815     if (IS_INACTIVE(element))
11816     {
11817       if (IS_ANIMATED(graphic))
11818         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11819
11820       continue;
11821     }
11822
11823     /* this may take place after moving, so 'element' may have changed */
11824     if (IS_CHANGING(x, y) &&
11825         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11826     {
11827       int page = element_info[element].event_page_nr[CE_DELAY];
11828
11829       HandleElementChange(x, y, page);
11830
11831       element = Feld[x][y];
11832       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11833     }
11834
11835     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11836     {
11837       StartMoving(x, y);
11838
11839       element = Feld[x][y];
11840       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11841
11842       if (IS_ANIMATED(graphic) &&
11843           !IS_MOVING(x, y) &&
11844           !Stop[x][y])
11845         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11846
11847       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11848         TEST_DrawTwinkleOnField(x, y);
11849     }
11850     else if (element == EL_ACID)
11851     {
11852       if (!Stop[x][y])
11853         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11854     }
11855     else if ((element == EL_EXIT_OPEN ||
11856               element == EL_EM_EXIT_OPEN ||
11857               element == EL_SP_EXIT_OPEN ||
11858               element == EL_STEEL_EXIT_OPEN ||
11859               element == EL_EM_STEEL_EXIT_OPEN ||
11860               element == EL_SP_TERMINAL ||
11861               element == EL_SP_TERMINAL_ACTIVE ||
11862               element == EL_EXTRA_TIME ||
11863               element == EL_SHIELD_NORMAL ||
11864               element == EL_SHIELD_DEADLY) &&
11865              IS_ANIMATED(graphic))
11866       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11867     else if (IS_MOVING(x, y))
11868       ContinueMoving(x, y);
11869     else if (IS_ACTIVE_BOMB(element))
11870       CheckDynamite(x, y);
11871     else if (element == EL_AMOEBA_GROWING)
11872       AmoebeWaechst(x, y);
11873     else if (element == EL_AMOEBA_SHRINKING)
11874       AmoebaDisappearing(x, y);
11875
11876 #if !USE_NEW_AMOEBA_CODE
11877     else if (IS_AMOEBALIVE(element))
11878       AmoebeAbleger(x, y);
11879 #endif
11880
11881     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11882       Life(x, y);
11883     else if (element == EL_EXIT_CLOSED)
11884       CheckExit(x, y);
11885     else if (element == EL_EM_EXIT_CLOSED)
11886       CheckExitEM(x, y);
11887     else if (element == EL_STEEL_EXIT_CLOSED)
11888       CheckExitSteel(x, y);
11889     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11890       CheckExitSteelEM(x, y);
11891     else if (element == EL_SP_EXIT_CLOSED)
11892       CheckExitSP(x, y);
11893     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11894              element == EL_EXPANDABLE_STEELWALL_GROWING)
11895       MauerWaechst(x, y);
11896     else if (element == EL_EXPANDABLE_WALL ||
11897              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11898              element == EL_EXPANDABLE_WALL_VERTICAL ||
11899              element == EL_EXPANDABLE_WALL_ANY ||
11900              element == EL_BD_EXPANDABLE_WALL)
11901       MauerAbleger(x, y);
11902     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11903              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11904              element == EL_EXPANDABLE_STEELWALL_ANY)
11905       MauerAblegerStahl(x, y);
11906     else if (element == EL_FLAMES)
11907       CheckForDragon(x, y);
11908     else if (element == EL_EXPLOSION)
11909       ; /* drawing of correct explosion animation is handled separately */
11910     else if (element == EL_ELEMENT_SNAPPING ||
11911              element == EL_DIAGONAL_SHRINKING ||
11912              element == EL_DIAGONAL_GROWING)
11913     {
11914       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11915
11916       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11917     }
11918     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11919       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11920
11921     if (IS_BELT_ACTIVE(element))
11922       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11923
11924     if (game.magic_wall_active)
11925     {
11926       int jx = local_player->jx, jy = local_player->jy;
11927
11928       /* play the element sound at the position nearest to the player */
11929       if ((element == EL_MAGIC_WALL_FULL ||
11930            element == EL_MAGIC_WALL_ACTIVE ||
11931            element == EL_MAGIC_WALL_EMPTYING ||
11932            element == EL_BD_MAGIC_WALL_FULL ||
11933            element == EL_BD_MAGIC_WALL_ACTIVE ||
11934            element == EL_BD_MAGIC_WALL_EMPTYING ||
11935            element == EL_DC_MAGIC_WALL_FULL ||
11936            element == EL_DC_MAGIC_WALL_ACTIVE ||
11937            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11938           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11939       {
11940         magic_wall_x = x;
11941         magic_wall_y = y;
11942       }
11943     }
11944   }
11945
11946 #if USE_NEW_AMOEBA_CODE
11947   /* new experimental amoeba growth stuff */
11948   if (!(FrameCounter % 8))
11949   {
11950     static unsigned int random = 1684108901;
11951
11952     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11953     {
11954       x = RND(lev_fieldx);
11955       y = RND(lev_fieldy);
11956       element = Feld[x][y];
11957
11958       if (!IS_PLAYER(x,y) &&
11959           (element == EL_EMPTY ||
11960            CAN_GROW_INTO(element) ||
11961            element == EL_QUICKSAND_EMPTY ||
11962            element == EL_QUICKSAND_FAST_EMPTY ||
11963            element == EL_ACID_SPLASH_LEFT ||
11964            element == EL_ACID_SPLASH_RIGHT))
11965       {
11966         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11967             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11968             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11969             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11970           Feld[x][y] = EL_AMOEBA_DROP;
11971       }
11972
11973       random = random * 129 + 1;
11974     }
11975   }
11976 #endif
11977
11978   game.explosions_delayed = FALSE;
11979
11980   SCAN_PLAYFIELD(x, y)
11981   {
11982     element = Feld[x][y];
11983
11984     if (ExplodeField[x][y])
11985       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11986     else if (element == EL_EXPLOSION)
11987       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11988
11989     ExplodeField[x][y] = EX_TYPE_NONE;
11990   }
11991
11992   game.explosions_delayed = TRUE;
11993
11994   if (game.magic_wall_active)
11995   {
11996     if (!(game.magic_wall_time_left % 4))
11997     {
11998       int element = Feld[magic_wall_x][magic_wall_y];
11999
12000       if (element == EL_BD_MAGIC_WALL_FULL ||
12001           element == EL_BD_MAGIC_WALL_ACTIVE ||
12002           element == EL_BD_MAGIC_WALL_EMPTYING)
12003         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12004       else if (element == EL_DC_MAGIC_WALL_FULL ||
12005                element == EL_DC_MAGIC_WALL_ACTIVE ||
12006                element == EL_DC_MAGIC_WALL_EMPTYING)
12007         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12008       else
12009         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12010     }
12011
12012     if (game.magic_wall_time_left > 0)
12013     {
12014       game.magic_wall_time_left--;
12015
12016       if (!game.magic_wall_time_left)
12017       {
12018         SCAN_PLAYFIELD(x, y)
12019         {
12020           element = Feld[x][y];
12021
12022           if (element == EL_MAGIC_WALL_ACTIVE ||
12023               element == EL_MAGIC_WALL_FULL)
12024           {
12025             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12026             TEST_DrawLevelField(x, y);
12027           }
12028           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12029                    element == EL_BD_MAGIC_WALL_FULL)
12030           {
12031             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12032             TEST_DrawLevelField(x, y);
12033           }
12034           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12035                    element == EL_DC_MAGIC_WALL_FULL)
12036           {
12037             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12038             TEST_DrawLevelField(x, y);
12039           }
12040         }
12041
12042         game.magic_wall_active = FALSE;
12043       }
12044     }
12045   }
12046
12047   if (game.light_time_left > 0)
12048   {
12049     game.light_time_left--;
12050
12051     if (game.light_time_left == 0)
12052       RedrawAllLightSwitchesAndInvisibleElements();
12053   }
12054
12055   if (game.timegate_time_left > 0)
12056   {
12057     game.timegate_time_left--;
12058
12059     if (game.timegate_time_left == 0)
12060       CloseAllOpenTimegates();
12061   }
12062
12063   if (game.lenses_time_left > 0)
12064   {
12065     game.lenses_time_left--;
12066
12067     if (game.lenses_time_left == 0)
12068       RedrawAllInvisibleElementsForLenses();
12069   }
12070
12071   if (game.magnify_time_left > 0)
12072   {
12073     game.magnify_time_left--;
12074
12075     if (game.magnify_time_left == 0)
12076       RedrawAllInvisibleElementsForMagnifier();
12077   }
12078
12079   for (i = 0; i < MAX_PLAYERS; i++)
12080   {
12081     struct PlayerInfo *player = &stored_player[i];
12082
12083     if (SHIELD_ON(player))
12084     {
12085       if (player->shield_deadly_time_left)
12086         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12087       else if (player->shield_normal_time_left)
12088         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12089     }
12090   }
12091
12092 #if USE_DELAYED_GFX_REDRAW
12093   SCAN_PLAYFIELD(x, y)
12094   {
12095     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12096     {
12097       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12098          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12099
12100       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12101         DrawLevelField(x, y);
12102
12103       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12104         DrawLevelFieldCrumbled(x, y);
12105
12106       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12107         DrawLevelFieldCrumbledNeighbours(x, y);
12108
12109       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12110         DrawTwinkleOnField(x, y);
12111     }
12112
12113     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12114   }
12115 #endif
12116
12117   DrawAllPlayers();
12118   PlayAllPlayersSound();
12119
12120   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12121   {
12122     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12123
12124     local_player->show_envelope = 0;
12125   }
12126
12127   /* use random number generator in every frame to make it less predictable */
12128   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12129     RND(1);
12130 }
12131
12132 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12133 {
12134   int min_x = x, min_y = y, max_x = x, max_y = y;
12135   int i;
12136
12137   for (i = 0; i < MAX_PLAYERS; i++)
12138   {
12139     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12140
12141     if (!stored_player[i].active || &stored_player[i] == player)
12142       continue;
12143
12144     min_x = MIN(min_x, jx);
12145     min_y = MIN(min_y, jy);
12146     max_x = MAX(max_x, jx);
12147     max_y = MAX(max_y, jy);
12148   }
12149
12150   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12151 }
12152
12153 static boolean AllPlayersInVisibleScreen()
12154 {
12155   int i;
12156
12157   for (i = 0; i < MAX_PLAYERS; i++)
12158   {
12159     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12160
12161     if (!stored_player[i].active)
12162       continue;
12163
12164     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12165       return FALSE;
12166   }
12167
12168   return TRUE;
12169 }
12170
12171 void ScrollLevel(int dx, int dy)
12172 {
12173   int scroll_offset = 2 * TILEX_VAR;
12174   int x, y;
12175
12176   BlitBitmap(drawto_field, drawto_field,
12177              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12178              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12179              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12180              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12181              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12182              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12183
12184   if (dx != 0)
12185   {
12186     x = (dx == 1 ? BX1 : BX2);
12187     for (y = BY1; y <= BY2; y++)
12188       DrawScreenField(x, y);
12189   }
12190
12191   if (dy != 0)
12192   {
12193     y = (dy == 1 ? BY1 : BY2);
12194     for (x = BX1; x <= BX2; x++)
12195       DrawScreenField(x, y);
12196   }
12197
12198   redraw_mask |= REDRAW_FIELD;
12199 }
12200
12201 static boolean canFallDown(struct PlayerInfo *player)
12202 {
12203   int jx = player->jx, jy = player->jy;
12204
12205   return (IN_LEV_FIELD(jx, jy + 1) &&
12206           (IS_FREE(jx, jy + 1) ||
12207            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12208           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12209           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12210 }
12211
12212 static boolean canPassField(int x, int y, int move_dir)
12213 {
12214   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12215   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12216   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12217   int nextx = x + dx;
12218   int nexty = y + dy;
12219   int element = Feld[x][y];
12220
12221   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12222           !CAN_MOVE(element) &&
12223           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12224           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12225           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12226 }
12227
12228 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12229 {
12230   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12231   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12232   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12233   int newx = x + dx;
12234   int newy = y + dy;
12235
12236   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12237           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12238           (IS_DIGGABLE(Feld[newx][newy]) ||
12239            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12240            canPassField(newx, newy, move_dir)));
12241 }
12242
12243 static void CheckGravityMovement(struct PlayerInfo *player)
12244 {
12245   if (player->gravity && !player->programmed_action)
12246   {
12247     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12248     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12249     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12250     int jx = player->jx, jy = player->jy;
12251     boolean player_is_moving_to_valid_field =
12252       (!player_is_snapping &&
12253        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12254         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12255     boolean player_can_fall_down = canFallDown(player);
12256
12257     if (player_can_fall_down &&
12258         !player_is_moving_to_valid_field)
12259       player->programmed_action = MV_DOWN;
12260   }
12261 }
12262
12263 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12264 {
12265   return CheckGravityMovement(player);
12266
12267   if (player->gravity && !player->programmed_action)
12268   {
12269     int jx = player->jx, jy = player->jy;
12270     boolean field_under_player_is_free =
12271       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12272     boolean player_is_standing_on_valid_field =
12273       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12274        (IS_WALKABLE(Feld[jx][jy]) &&
12275         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12276
12277     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12278       player->programmed_action = MV_DOWN;
12279   }
12280 }
12281
12282 /*
12283   MovePlayerOneStep()
12284   -----------------------------------------------------------------------------
12285   dx, dy:               direction (non-diagonal) to try to move the player to
12286   real_dx, real_dy:     direction as read from input device (can be diagonal)
12287 */
12288
12289 boolean MovePlayerOneStep(struct PlayerInfo *player,
12290                           int dx, int dy, int real_dx, int real_dy)
12291 {
12292   int jx = player->jx, jy = player->jy;
12293   int new_jx = jx + dx, new_jy = jy + dy;
12294   int can_move;
12295   boolean player_can_move = !player->cannot_move;
12296
12297   if (!player->active || (!dx && !dy))
12298     return MP_NO_ACTION;
12299
12300   player->MovDir = (dx < 0 ? MV_LEFT :
12301                     dx > 0 ? MV_RIGHT :
12302                     dy < 0 ? MV_UP :
12303                     dy > 0 ? MV_DOWN :  MV_NONE);
12304
12305   if (!IN_LEV_FIELD(new_jx, new_jy))
12306     return MP_NO_ACTION;
12307
12308   if (!player_can_move)
12309   {
12310     if (player->MovPos == 0)
12311     {
12312       player->is_moving = FALSE;
12313       player->is_digging = FALSE;
12314       player->is_collecting = FALSE;
12315       player->is_snapping = FALSE;
12316       player->is_pushing = FALSE;
12317     }
12318   }
12319
12320   if (!network.enabled && game.centered_player_nr == -1 &&
12321       !AllPlayersInSight(player, new_jx, new_jy))
12322     return MP_NO_ACTION;
12323
12324   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12325   if (can_move != MP_MOVING)
12326     return can_move;
12327
12328   /* check if DigField() has caused relocation of the player */
12329   if (player->jx != jx || player->jy != jy)
12330     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12331
12332   StorePlayer[jx][jy] = 0;
12333   player->last_jx = jx;
12334   player->last_jy = jy;
12335   player->jx = new_jx;
12336   player->jy = new_jy;
12337   StorePlayer[new_jx][new_jy] = player->element_nr;
12338
12339   if (player->move_delay_value_next != -1)
12340   {
12341     player->move_delay_value = player->move_delay_value_next;
12342     player->move_delay_value_next = -1;
12343   }
12344
12345   player->MovPos =
12346     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12347
12348   player->step_counter++;
12349
12350   PlayerVisit[jx][jy] = FrameCounter;
12351
12352   player->is_moving = TRUE;
12353
12354 #if 1
12355   /* should better be called in MovePlayer(), but this breaks some tapes */
12356   ScrollPlayer(player, SCROLL_INIT);
12357 #endif
12358
12359   return MP_MOVING;
12360 }
12361
12362 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12363 {
12364   int jx = player->jx, jy = player->jy;
12365   int old_jx = jx, old_jy = jy;
12366   int moved = MP_NO_ACTION;
12367
12368   if (!player->active)
12369     return FALSE;
12370
12371   if (!dx && !dy)
12372   {
12373     if (player->MovPos == 0)
12374     {
12375       player->is_moving = FALSE;
12376       player->is_digging = FALSE;
12377       player->is_collecting = FALSE;
12378       player->is_snapping = FALSE;
12379       player->is_pushing = FALSE;
12380     }
12381
12382     return FALSE;
12383   }
12384
12385   if (player->move_delay > 0)
12386     return FALSE;
12387
12388   player->move_delay = -1;              /* set to "uninitialized" value */
12389
12390   /* store if player is automatically moved to next field */
12391   player->is_auto_moving = (player->programmed_action != MV_NONE);
12392
12393   /* remove the last programmed player action */
12394   player->programmed_action = 0;
12395
12396   if (player->MovPos)
12397   {
12398     /* should only happen if pre-1.2 tape recordings are played */
12399     /* this is only for backward compatibility */
12400
12401     int original_move_delay_value = player->move_delay_value;
12402
12403 #if DEBUG
12404     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12405            tape.counter);
12406 #endif
12407
12408     /* scroll remaining steps with finest movement resolution */
12409     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12410
12411     while (player->MovPos)
12412     {
12413       ScrollPlayer(player, SCROLL_GO_ON);
12414       ScrollScreen(NULL, SCROLL_GO_ON);
12415
12416       AdvanceFrameAndPlayerCounters(player->index_nr);
12417
12418       DrawAllPlayers();
12419       BackToFront_WithFrameDelay(0);
12420     }
12421
12422     player->move_delay_value = original_move_delay_value;
12423   }
12424
12425   player->is_active = FALSE;
12426
12427   if (player->last_move_dir & MV_HORIZONTAL)
12428   {
12429     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12430       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12431   }
12432   else
12433   {
12434     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12435       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12436   }
12437
12438   if (!moved && !player->is_active)
12439   {
12440     player->is_moving = FALSE;
12441     player->is_digging = FALSE;
12442     player->is_collecting = FALSE;
12443     player->is_snapping = FALSE;
12444     player->is_pushing = FALSE;
12445   }
12446
12447   jx = player->jx;
12448   jy = player->jy;
12449
12450   if (moved & MP_MOVING && !ScreenMovPos &&
12451       (player->index_nr == game.centered_player_nr ||
12452        game.centered_player_nr == -1))
12453   {
12454     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12455     int offset = game.scroll_delay_value;
12456
12457     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12458     {
12459       /* actual player has left the screen -- scroll in that direction */
12460       if (jx != old_jx)         /* player has moved horizontally */
12461         scroll_x += (jx - old_jx);
12462       else                      /* player has moved vertically */
12463         scroll_y += (jy - old_jy);
12464     }
12465     else
12466     {
12467       if (jx != old_jx)         /* player has moved horizontally */
12468       {
12469         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12470             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12471           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12472
12473         /* don't scroll over playfield boundaries */
12474         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12475           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12476
12477         /* don't scroll more than one field at a time */
12478         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12479
12480         /* don't scroll against the player's moving direction */
12481         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12482             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12483           scroll_x = old_scroll_x;
12484       }
12485       else                      /* player has moved vertically */
12486       {
12487         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12488             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12489           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12490
12491         /* don't scroll over playfield boundaries */
12492         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12493           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12494
12495         /* don't scroll more than one field at a time */
12496         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12497
12498         /* don't scroll against the player's moving direction */
12499         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12500             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12501           scroll_y = old_scroll_y;
12502       }
12503     }
12504
12505     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12506     {
12507       if (!network.enabled && game.centered_player_nr == -1 &&
12508           !AllPlayersInVisibleScreen())
12509       {
12510         scroll_x = old_scroll_x;
12511         scroll_y = old_scroll_y;
12512       }
12513       else
12514       {
12515         ScrollScreen(player, SCROLL_INIT);
12516         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12517       }
12518     }
12519   }
12520
12521   player->StepFrame = 0;
12522
12523   if (moved & MP_MOVING)
12524   {
12525     if (old_jx != jx && old_jy == jy)
12526       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12527     else if (old_jx == jx && old_jy != jy)
12528       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12529
12530     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12531
12532     player->last_move_dir = player->MovDir;
12533     player->is_moving = TRUE;
12534     player->is_snapping = FALSE;
12535     player->is_switching = FALSE;
12536     player->is_dropping = FALSE;
12537     player->is_dropping_pressed = FALSE;
12538     player->drop_pressed_delay = 0;
12539
12540 #if 0
12541     /* should better be called here than above, but this breaks some tapes */
12542     ScrollPlayer(player, SCROLL_INIT);
12543 #endif
12544   }
12545   else
12546   {
12547     CheckGravityMovementWhenNotMoving(player);
12548
12549     player->is_moving = FALSE;
12550
12551     /* at this point, the player is allowed to move, but cannot move right now
12552        (e.g. because of something blocking the way) -- ensure that the player
12553        is also allowed to move in the next frame (in old versions before 3.1.1,
12554        the player was forced to wait again for eight frames before next try) */
12555
12556     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12557       player->move_delay = 0;   /* allow direct movement in the next frame */
12558   }
12559
12560   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12561     player->move_delay = player->move_delay_value;
12562
12563   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12564   {
12565     TestIfPlayerTouchesBadThing(jx, jy);
12566     TestIfPlayerTouchesCustomElement(jx, jy);
12567   }
12568
12569   if (!player->active)
12570     RemovePlayer(player);
12571
12572   return moved;
12573 }
12574
12575 void ScrollPlayer(struct PlayerInfo *player, int mode)
12576 {
12577   int jx = player->jx, jy = player->jy;
12578   int last_jx = player->last_jx, last_jy = player->last_jy;
12579   int move_stepsize = TILEX / player->move_delay_value;
12580
12581   if (!player->active)
12582     return;
12583
12584   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12585     return;
12586
12587   if (mode == SCROLL_INIT)
12588   {
12589     player->actual_frame_counter = FrameCounter;
12590     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12591
12592     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12593         Feld[last_jx][last_jy] == EL_EMPTY)
12594     {
12595       int last_field_block_delay = 0;   /* start with no blocking at all */
12596       int block_delay_adjustment = player->block_delay_adjustment;
12597
12598       /* if player blocks last field, add delay for exactly one move */
12599       if (player->block_last_field)
12600       {
12601         last_field_block_delay += player->move_delay_value;
12602
12603         /* when blocking enabled, prevent moving up despite gravity */
12604         if (player->gravity && player->MovDir == MV_UP)
12605           block_delay_adjustment = -1;
12606       }
12607
12608       /* add block delay adjustment (also possible when not blocking) */
12609       last_field_block_delay += block_delay_adjustment;
12610
12611       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12612       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12613     }
12614
12615     if (player->MovPos != 0)    /* player has not yet reached destination */
12616       return;
12617   }
12618   else if (!FrameReached(&player->actual_frame_counter, 1))
12619     return;
12620
12621   if (player->MovPos != 0)
12622   {
12623     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12624     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12625
12626     /* before DrawPlayer() to draw correct player graphic for this case */
12627     if (player->MovPos == 0)
12628       CheckGravityMovement(player);
12629   }
12630
12631   if (player->MovPos == 0)      /* player reached destination field */
12632   {
12633     if (player->move_delay_reset_counter > 0)
12634     {
12635       player->move_delay_reset_counter--;
12636
12637       if (player->move_delay_reset_counter == 0)
12638       {
12639         /* continue with normal speed after quickly moving through gate */
12640         HALVE_PLAYER_SPEED(player);
12641
12642         /* be able to make the next move without delay */
12643         player->move_delay = 0;
12644       }
12645     }
12646
12647     player->last_jx = jx;
12648     player->last_jy = jy;
12649
12650     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12651         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12652         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12653         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12654         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12655         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12656         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12657         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12658     {
12659       ExitPlayer(player);
12660
12661       if ((local_player->friends_still_needed == 0 ||
12662            IS_SP_ELEMENT(Feld[jx][jy])) &&
12663           AllPlayersGone)
12664         PlayerWins(local_player);
12665     }
12666
12667     /* this breaks one level: "machine", level 000 */
12668     {
12669       int move_direction = player->MovDir;
12670       int enter_side = MV_DIR_OPPOSITE(move_direction);
12671       int leave_side = move_direction;
12672       int old_jx = last_jx;
12673       int old_jy = last_jy;
12674       int old_element = Feld[old_jx][old_jy];
12675       int new_element = Feld[jx][jy];
12676
12677       if (IS_CUSTOM_ELEMENT(old_element))
12678         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12679                                    CE_LEFT_BY_PLAYER,
12680                                    player->index_bit, leave_side);
12681
12682       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12683                                           CE_PLAYER_LEAVES_X,
12684                                           player->index_bit, leave_side);
12685
12686       if (IS_CUSTOM_ELEMENT(new_element))
12687         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12688                                    player->index_bit, enter_side);
12689
12690       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12691                                           CE_PLAYER_ENTERS_X,
12692                                           player->index_bit, enter_side);
12693
12694       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12695                                         CE_MOVE_OF_X, move_direction);
12696     }
12697
12698     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12699     {
12700       TestIfPlayerTouchesBadThing(jx, jy);
12701       TestIfPlayerTouchesCustomElement(jx, jy);
12702
12703       /* needed because pushed element has not yet reached its destination,
12704          so it would trigger a change event at its previous field location */
12705       if (!player->is_pushing)
12706         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12707
12708       if (!player->active)
12709         RemovePlayer(player);
12710     }
12711
12712     if (!local_player->LevelSolved && level.use_step_counter)
12713     {
12714       int i;
12715
12716       TimePlayed++;
12717
12718       if (TimeLeft > 0)
12719       {
12720         TimeLeft--;
12721
12722         if (TimeLeft <= 10 && setup.time_limit)
12723           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12724
12725         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12726
12727         DisplayGameControlValues();
12728
12729         if (!TimeLeft && setup.time_limit)
12730           for (i = 0; i < MAX_PLAYERS; i++)
12731             KillPlayer(&stored_player[i]);
12732       }
12733       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12734       {
12735         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12736
12737         DisplayGameControlValues();
12738       }
12739     }
12740
12741     if (tape.single_step && tape.recording && !tape.pausing &&
12742         !player->programmed_action)
12743       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12744
12745     if (!player->programmed_action)
12746       CheckSaveEngineSnapshot(player);
12747   }
12748 }
12749
12750 void ScrollScreen(struct PlayerInfo *player, int mode)
12751 {
12752   static unsigned int screen_frame_counter = 0;
12753
12754   if (mode == SCROLL_INIT)
12755   {
12756     /* set scrolling step size according to actual player's moving speed */
12757     ScrollStepSize = TILEX / player->move_delay_value;
12758
12759     screen_frame_counter = FrameCounter;
12760     ScreenMovDir = player->MovDir;
12761     ScreenMovPos = player->MovPos;
12762     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12763     return;
12764   }
12765   else if (!FrameReached(&screen_frame_counter, 1))
12766     return;
12767
12768   if (ScreenMovPos)
12769   {
12770     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12771     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12772     redraw_mask |= REDRAW_FIELD;
12773   }
12774   else
12775     ScreenMovDir = MV_NONE;
12776 }
12777
12778 void TestIfPlayerTouchesCustomElement(int x, int y)
12779 {
12780   static int xy[4][2] =
12781   {
12782     { 0, -1 },
12783     { -1, 0 },
12784     { +1, 0 },
12785     { 0, +1 }
12786   };
12787   static int trigger_sides[4][2] =
12788   {
12789     /* center side       border side */
12790     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12791     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12792     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12793     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12794   };
12795   static int touch_dir[4] =
12796   {
12797     MV_LEFT | MV_RIGHT,
12798     MV_UP   | MV_DOWN,
12799     MV_UP   | MV_DOWN,
12800     MV_LEFT | MV_RIGHT
12801   };
12802   int center_element = Feld[x][y];      /* should always be non-moving! */
12803   int i;
12804
12805   for (i = 0; i < NUM_DIRECTIONS; i++)
12806   {
12807     int xx = x + xy[i][0];
12808     int yy = y + xy[i][1];
12809     int center_side = trigger_sides[i][0];
12810     int border_side = trigger_sides[i][1];
12811     int border_element;
12812
12813     if (!IN_LEV_FIELD(xx, yy))
12814       continue;
12815
12816     if (IS_PLAYER(x, y))                /* player found at center element */
12817     {
12818       struct PlayerInfo *player = PLAYERINFO(x, y);
12819
12820       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12821         border_element = Feld[xx][yy];          /* may be moving! */
12822       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12823         border_element = Feld[xx][yy];
12824       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12825         border_element = MovingOrBlocked2Element(xx, yy);
12826       else
12827         continue;               /* center and border element do not touch */
12828
12829       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12830                                  player->index_bit, border_side);
12831       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12832                                           CE_PLAYER_TOUCHES_X,
12833                                           player->index_bit, border_side);
12834
12835       {
12836         /* use player element that is initially defined in the level playfield,
12837            not the player element that corresponds to the runtime player number
12838            (example: a level that contains EL_PLAYER_3 as the only player would
12839            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12840         int player_element = PLAYERINFO(x, y)->initial_element;
12841
12842         CheckElementChangeBySide(xx, yy, border_element, player_element,
12843                                  CE_TOUCHING_X, border_side);
12844       }
12845     }
12846     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12847     {
12848       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12849
12850       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12851       {
12852         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12853           continue;             /* center and border element do not touch */
12854       }
12855
12856       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12857                                  player->index_bit, center_side);
12858       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12859                                           CE_PLAYER_TOUCHES_X,
12860                                           player->index_bit, center_side);
12861
12862       {
12863         /* use player element that is initially defined in the level playfield,
12864            not the player element that corresponds to the runtime player number
12865            (example: a level that contains EL_PLAYER_3 as the only player would
12866            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12867         int player_element = PLAYERINFO(xx, yy)->initial_element;
12868
12869         CheckElementChangeBySide(x, y, center_element, player_element,
12870                                  CE_TOUCHING_X, center_side);
12871       }
12872
12873       break;
12874     }
12875   }
12876 }
12877
12878 void TestIfElementTouchesCustomElement(int x, int y)
12879 {
12880   static int xy[4][2] =
12881   {
12882     { 0, -1 },
12883     { -1, 0 },
12884     { +1, 0 },
12885     { 0, +1 }
12886   };
12887   static int trigger_sides[4][2] =
12888   {
12889     /* center side      border side */
12890     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12891     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12892     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12893     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12894   };
12895   static int touch_dir[4] =
12896   {
12897     MV_LEFT | MV_RIGHT,
12898     MV_UP   | MV_DOWN,
12899     MV_UP   | MV_DOWN,
12900     MV_LEFT | MV_RIGHT
12901   };
12902   boolean change_center_element = FALSE;
12903   int center_element = Feld[x][y];      /* should always be non-moving! */
12904   int border_element_old[NUM_DIRECTIONS];
12905   int i;
12906
12907   for (i = 0; i < NUM_DIRECTIONS; i++)
12908   {
12909     int xx = x + xy[i][0];
12910     int yy = y + xy[i][1];
12911     int border_element;
12912
12913     border_element_old[i] = -1;
12914
12915     if (!IN_LEV_FIELD(xx, yy))
12916       continue;
12917
12918     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12919       border_element = Feld[xx][yy];    /* may be moving! */
12920     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12921       border_element = Feld[xx][yy];
12922     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12923       border_element = MovingOrBlocked2Element(xx, yy);
12924     else
12925       continue;                 /* center and border element do not touch */
12926
12927     border_element_old[i] = border_element;
12928   }
12929
12930   for (i = 0; i < NUM_DIRECTIONS; i++)
12931   {
12932     int xx = x + xy[i][0];
12933     int yy = y + xy[i][1];
12934     int center_side = trigger_sides[i][0];
12935     int border_element = border_element_old[i];
12936
12937     if (border_element == -1)
12938       continue;
12939
12940     /* check for change of border element */
12941     CheckElementChangeBySide(xx, yy, border_element, center_element,
12942                              CE_TOUCHING_X, center_side);
12943
12944     /* (center element cannot be player, so we dont have to check this here) */
12945   }
12946
12947   for (i = 0; i < NUM_DIRECTIONS; i++)
12948   {
12949     int xx = x + xy[i][0];
12950     int yy = y + xy[i][1];
12951     int border_side = trigger_sides[i][1];
12952     int border_element = border_element_old[i];
12953
12954     if (border_element == -1)
12955       continue;
12956
12957     /* check for change of center element (but change it only once) */
12958     if (!change_center_element)
12959       change_center_element =
12960         CheckElementChangeBySide(x, y, center_element, border_element,
12961                                  CE_TOUCHING_X, border_side);
12962
12963     if (IS_PLAYER(xx, yy))
12964     {
12965       /* use player element that is initially defined in the level playfield,
12966          not the player element that corresponds to the runtime player number
12967          (example: a level that contains EL_PLAYER_3 as the only player would
12968          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12969       int player_element = PLAYERINFO(xx, yy)->initial_element;
12970
12971       CheckElementChangeBySide(x, y, center_element, player_element,
12972                                CE_TOUCHING_X, border_side);
12973     }
12974   }
12975 }
12976
12977 void TestIfElementHitsCustomElement(int x, int y, int direction)
12978 {
12979   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12980   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12981   int hitx = x + dx, hity = y + dy;
12982   int hitting_element = Feld[x][y];
12983   int touched_element;
12984
12985   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12986     return;
12987
12988   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12989                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12990
12991   if (IN_LEV_FIELD(hitx, hity))
12992   {
12993     int opposite_direction = MV_DIR_OPPOSITE(direction);
12994     int hitting_side = direction;
12995     int touched_side = opposite_direction;
12996     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12997                           MovDir[hitx][hity] != direction ||
12998                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12999
13000     object_hit = TRUE;
13001
13002     if (object_hit)
13003     {
13004       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13005                                CE_HITTING_X, touched_side);
13006
13007       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13008                                CE_HIT_BY_X, hitting_side);
13009
13010       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13011                                CE_HIT_BY_SOMETHING, opposite_direction);
13012
13013       if (IS_PLAYER(hitx, hity))
13014       {
13015         /* use player element that is initially defined in the level playfield,
13016            not the player element that corresponds to the runtime player number
13017            (example: a level that contains EL_PLAYER_3 as the only player would
13018            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13019         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13020
13021         CheckElementChangeBySide(x, y, hitting_element, player_element,
13022                                  CE_HITTING_X, touched_side);
13023       }
13024     }
13025   }
13026
13027   /* "hitting something" is also true when hitting the playfield border */
13028   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13029                            CE_HITTING_SOMETHING, direction);
13030 }
13031
13032 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13033 {
13034   int i, kill_x = -1, kill_y = -1;
13035
13036   int bad_element = -1;
13037   static int test_xy[4][2] =
13038   {
13039     { 0, -1 },
13040     { -1, 0 },
13041     { +1, 0 },
13042     { 0, +1 }
13043   };
13044   static int test_dir[4] =
13045   {
13046     MV_UP,
13047     MV_LEFT,
13048     MV_RIGHT,
13049     MV_DOWN
13050   };
13051
13052   for (i = 0; i < NUM_DIRECTIONS; i++)
13053   {
13054     int test_x, test_y, test_move_dir, test_element;
13055
13056     test_x = good_x + test_xy[i][0];
13057     test_y = good_y + test_xy[i][1];
13058
13059     if (!IN_LEV_FIELD(test_x, test_y))
13060       continue;
13061
13062     test_move_dir =
13063       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13064
13065     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13066
13067     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13068        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13069     */
13070     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13071         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13072     {
13073       kill_x = test_x;
13074       kill_y = test_y;
13075       bad_element = test_element;
13076
13077       break;
13078     }
13079   }
13080
13081   if (kill_x != -1 || kill_y != -1)
13082   {
13083     if (IS_PLAYER(good_x, good_y))
13084     {
13085       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13086
13087       if (player->shield_deadly_time_left > 0 &&
13088           !IS_INDESTRUCTIBLE(bad_element))
13089         Bang(kill_x, kill_y);
13090       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13091         KillPlayer(player);
13092     }
13093     else
13094       Bang(good_x, good_y);
13095   }
13096 }
13097
13098 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13099 {
13100   int i, kill_x = -1, kill_y = -1;
13101   int bad_element = Feld[bad_x][bad_y];
13102   static int test_xy[4][2] =
13103   {
13104     { 0, -1 },
13105     { -1, 0 },
13106     { +1, 0 },
13107     { 0, +1 }
13108   };
13109   static int touch_dir[4] =
13110   {
13111     MV_LEFT | MV_RIGHT,
13112     MV_UP   | MV_DOWN,
13113     MV_UP   | MV_DOWN,
13114     MV_LEFT | MV_RIGHT
13115   };
13116   static int test_dir[4] =
13117   {
13118     MV_UP,
13119     MV_LEFT,
13120     MV_RIGHT,
13121     MV_DOWN
13122   };
13123
13124   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13125     return;
13126
13127   for (i = 0; i < NUM_DIRECTIONS; i++)
13128   {
13129     int test_x, test_y, test_move_dir, test_element;
13130
13131     test_x = bad_x + test_xy[i][0];
13132     test_y = bad_y + test_xy[i][1];
13133
13134     if (!IN_LEV_FIELD(test_x, test_y))
13135       continue;
13136
13137     test_move_dir =
13138       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13139
13140     test_element = Feld[test_x][test_y];
13141
13142     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13143        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13144     */
13145     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13146         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13147     {
13148       /* good thing is player or penguin that does not move away */
13149       if (IS_PLAYER(test_x, test_y))
13150       {
13151         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13152
13153         if (bad_element == EL_ROBOT && player->is_moving)
13154           continue;     /* robot does not kill player if he is moving */
13155
13156         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13157         {
13158           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13159             continue;           /* center and border element do not touch */
13160         }
13161
13162         kill_x = test_x;
13163         kill_y = test_y;
13164
13165         break;
13166       }
13167       else if (test_element == EL_PENGUIN)
13168       {
13169         kill_x = test_x;
13170         kill_y = test_y;
13171
13172         break;
13173       }
13174     }
13175   }
13176
13177   if (kill_x != -1 || kill_y != -1)
13178   {
13179     if (IS_PLAYER(kill_x, kill_y))
13180     {
13181       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13182
13183       if (player->shield_deadly_time_left > 0 &&
13184           !IS_INDESTRUCTIBLE(bad_element))
13185         Bang(bad_x, bad_y);
13186       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13187         KillPlayer(player);
13188     }
13189     else
13190       Bang(kill_x, kill_y);
13191   }
13192 }
13193
13194 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13195 {
13196   int bad_element = Feld[bad_x][bad_y];
13197   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13198   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13199   int test_x = bad_x + dx, test_y = bad_y + dy;
13200   int test_move_dir, test_element;
13201   int kill_x = -1, kill_y = -1;
13202
13203   if (!IN_LEV_FIELD(test_x, test_y))
13204     return;
13205
13206   test_move_dir =
13207     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13208
13209   test_element = Feld[test_x][test_y];
13210
13211   if (test_move_dir != bad_move_dir)
13212   {
13213     /* good thing can be player or penguin that does not move away */
13214     if (IS_PLAYER(test_x, test_y))
13215     {
13216       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13217
13218       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13219          player as being hit when he is moving towards the bad thing, because
13220          the "get hit by" condition would be lost after the player stops) */
13221       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13222         return;         /* player moves away from bad thing */
13223
13224       kill_x = test_x;
13225       kill_y = test_y;
13226     }
13227     else if (test_element == EL_PENGUIN)
13228     {
13229       kill_x = test_x;
13230       kill_y = test_y;
13231     }
13232   }
13233
13234   if (kill_x != -1 || kill_y != -1)
13235   {
13236     if (IS_PLAYER(kill_x, kill_y))
13237     {
13238       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13239
13240       if (player->shield_deadly_time_left > 0 &&
13241           !IS_INDESTRUCTIBLE(bad_element))
13242         Bang(bad_x, bad_y);
13243       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13244         KillPlayer(player);
13245     }
13246     else
13247       Bang(kill_x, kill_y);
13248   }
13249 }
13250
13251 void TestIfPlayerTouchesBadThing(int x, int y)
13252 {
13253   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13254 }
13255
13256 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13257 {
13258   TestIfGoodThingHitsBadThing(x, y, move_dir);
13259 }
13260
13261 void TestIfBadThingTouchesPlayer(int x, int y)
13262 {
13263   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13264 }
13265
13266 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13267 {
13268   TestIfBadThingHitsGoodThing(x, y, move_dir);
13269 }
13270
13271 void TestIfFriendTouchesBadThing(int x, int y)
13272 {
13273   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13274 }
13275
13276 void TestIfBadThingTouchesFriend(int x, int y)
13277 {
13278   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13279 }
13280
13281 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13282 {
13283   int i, kill_x = bad_x, kill_y = bad_y;
13284   static int xy[4][2] =
13285   {
13286     { 0, -1 },
13287     { -1, 0 },
13288     { +1, 0 },
13289     { 0, +1 }
13290   };
13291
13292   for (i = 0; i < NUM_DIRECTIONS; i++)
13293   {
13294     int x, y, element;
13295
13296     x = bad_x + xy[i][0];
13297     y = bad_y + xy[i][1];
13298     if (!IN_LEV_FIELD(x, y))
13299       continue;
13300
13301     element = Feld[x][y];
13302     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13303         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13304     {
13305       kill_x = x;
13306       kill_y = y;
13307       break;
13308     }
13309   }
13310
13311   if (kill_x != bad_x || kill_y != bad_y)
13312     Bang(bad_x, bad_y);
13313 }
13314
13315 void KillPlayer(struct PlayerInfo *player)
13316 {
13317   int jx = player->jx, jy = player->jy;
13318
13319   if (!player->active)
13320     return;
13321
13322 #if 0
13323   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13324          player->killed, player->active, player->reanimated);
13325 #endif
13326
13327   /* the following code was introduced to prevent an infinite loop when calling
13328      -> Bang()
13329      -> CheckTriggeredElementChangeExt()
13330      -> ExecuteCustomElementAction()
13331      -> KillPlayer()
13332      -> (infinitely repeating the above sequence of function calls)
13333      which occurs when killing the player while having a CE with the setting
13334      "kill player X when explosion of <player X>"; the solution using a new
13335      field "player->killed" was chosen for backwards compatibility, although
13336      clever use of the fields "player->active" etc. would probably also work */
13337 #if 1
13338   if (player->killed)
13339     return;
13340 #endif
13341
13342   player->killed = TRUE;
13343
13344   /* remove accessible field at the player's position */
13345   Feld[jx][jy] = EL_EMPTY;
13346
13347   /* deactivate shield (else Bang()/Explode() would not work right) */
13348   player->shield_normal_time_left = 0;
13349   player->shield_deadly_time_left = 0;
13350
13351 #if 0
13352   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13353          player->killed, player->active, player->reanimated);
13354 #endif
13355
13356   Bang(jx, jy);
13357
13358 #if 0
13359   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13360          player->killed, player->active, player->reanimated);
13361 #endif
13362
13363   if (player->reanimated)       /* killed player may have been reanimated */
13364     player->killed = player->reanimated = FALSE;
13365   else
13366     BuryPlayer(player);
13367 }
13368
13369 static void KillPlayerUnlessEnemyProtected(int x, int y)
13370 {
13371   if (!PLAYER_ENEMY_PROTECTED(x, y))
13372     KillPlayer(PLAYERINFO(x, y));
13373 }
13374
13375 static void KillPlayerUnlessExplosionProtected(int x, int y)
13376 {
13377   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13378     KillPlayer(PLAYERINFO(x, y));
13379 }
13380
13381 void BuryPlayer(struct PlayerInfo *player)
13382 {
13383   int jx = player->jx, jy = player->jy;
13384
13385   if (!player->active)
13386     return;
13387
13388   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13389   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13390
13391   player->GameOver = TRUE;
13392   RemovePlayer(player);
13393 }
13394
13395 void RemovePlayer(struct PlayerInfo *player)
13396 {
13397   int jx = player->jx, jy = player->jy;
13398   int i, found = FALSE;
13399
13400   player->present = FALSE;
13401   player->active = FALSE;
13402
13403   if (!ExplodeField[jx][jy])
13404     StorePlayer[jx][jy] = 0;
13405
13406   if (player->is_moving)
13407     TEST_DrawLevelField(player->last_jx, player->last_jy);
13408
13409   for (i = 0; i < MAX_PLAYERS; i++)
13410     if (stored_player[i].active)
13411       found = TRUE;
13412
13413   if (!found)
13414     AllPlayersGone = TRUE;
13415
13416   ExitX = ZX = jx;
13417   ExitY = ZY = jy;
13418 }
13419
13420 void ExitPlayer(struct PlayerInfo *player)
13421 {
13422   DrawPlayer(player);   /* needed here only to cleanup last field */
13423   RemovePlayer(player);
13424
13425   local_player->players_still_needed--;
13426 }
13427
13428 static void setFieldForSnapping(int x, int y, int element, int direction)
13429 {
13430   struct ElementInfo *ei = &element_info[element];
13431   int direction_bit = MV_DIR_TO_BIT(direction);
13432   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13433   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13434                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13435
13436   Feld[x][y] = EL_ELEMENT_SNAPPING;
13437   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13438
13439   ResetGfxAnimation(x, y);
13440
13441   GfxElement[x][y] = element;
13442   GfxAction[x][y] = action;
13443   GfxDir[x][y] = direction;
13444   GfxFrame[x][y] = -1;
13445 }
13446
13447 /*
13448   =============================================================================
13449   checkDiagonalPushing()
13450   -----------------------------------------------------------------------------
13451   check if diagonal input device direction results in pushing of object
13452   (by checking if the alternative direction is walkable, diggable, ...)
13453   =============================================================================
13454 */
13455
13456 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13457                                     int x, int y, int real_dx, int real_dy)
13458 {
13459   int jx, jy, dx, dy, xx, yy;
13460
13461   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13462     return TRUE;
13463
13464   /* diagonal direction: check alternative direction */
13465   jx = player->jx;
13466   jy = player->jy;
13467   dx = x - jx;
13468   dy = y - jy;
13469   xx = jx + (dx == 0 ? real_dx : 0);
13470   yy = jy + (dy == 0 ? real_dy : 0);
13471
13472   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13473 }
13474
13475 /*
13476   =============================================================================
13477   DigField()
13478   -----------------------------------------------------------------------------
13479   x, y:                 field next to player (non-diagonal) to try to dig to
13480   real_dx, real_dy:     direction as read from input device (can be diagonal)
13481   =============================================================================
13482 */
13483
13484 static int DigField(struct PlayerInfo *player,
13485                     int oldx, int oldy, int x, int y,
13486                     int real_dx, int real_dy, int mode)
13487 {
13488   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13489   boolean player_was_pushing = player->is_pushing;
13490   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13491   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13492   int jx = oldx, jy = oldy;
13493   int dx = x - jx, dy = y - jy;
13494   int nextx = x + dx, nexty = y + dy;
13495   int move_direction = (dx == -1 ? MV_LEFT  :
13496                         dx == +1 ? MV_RIGHT :
13497                         dy == -1 ? MV_UP    :
13498                         dy == +1 ? MV_DOWN  : MV_NONE);
13499   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13500   int dig_side = MV_DIR_OPPOSITE(move_direction);
13501   int old_element = Feld[jx][jy];
13502   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13503   int collect_count;
13504
13505   if (is_player)                /* function can also be called by EL_PENGUIN */
13506   {
13507     if (player->MovPos == 0)
13508     {
13509       player->is_digging = FALSE;
13510       player->is_collecting = FALSE;
13511     }
13512
13513     if (player->MovPos == 0)    /* last pushing move finished */
13514       player->is_pushing = FALSE;
13515
13516     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13517     {
13518       player->is_switching = FALSE;
13519       player->push_delay = -1;
13520
13521       return MP_NO_ACTION;
13522     }
13523   }
13524
13525   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13526     old_element = Back[jx][jy];
13527
13528   /* in case of element dropped at player position, check background */
13529   else if (Back[jx][jy] != EL_EMPTY &&
13530            game.engine_version >= VERSION_IDENT(2,2,0,0))
13531     old_element = Back[jx][jy];
13532
13533   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13534     return MP_NO_ACTION;        /* field has no opening in this direction */
13535
13536   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13537     return MP_NO_ACTION;        /* field has no opening in this direction */
13538
13539   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13540   {
13541     SplashAcid(x, y);
13542
13543     Feld[jx][jy] = player->artwork_element;
13544     InitMovingField(jx, jy, MV_DOWN);
13545     Store[jx][jy] = EL_ACID;
13546     ContinueMoving(jx, jy);
13547     BuryPlayer(player);
13548
13549     return MP_DONT_RUN_INTO;
13550   }
13551
13552   if (player_can_move && DONT_RUN_INTO(element))
13553   {
13554     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13555
13556     return MP_DONT_RUN_INTO;
13557   }
13558
13559   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13560     return MP_NO_ACTION;
13561
13562   collect_count = element_info[element].collect_count_initial;
13563
13564   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13565     return MP_NO_ACTION;
13566
13567   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13568     player_can_move = player_can_move_or_snap;
13569
13570   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13571       game.engine_version >= VERSION_IDENT(2,2,0,0))
13572   {
13573     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13574                                player->index_bit, dig_side);
13575     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13576                                         player->index_bit, dig_side);
13577
13578     if (element == EL_DC_LANDMINE)
13579       Bang(x, y);
13580
13581     if (Feld[x][y] != element)          /* field changed by snapping */
13582       return MP_ACTION;
13583
13584     return MP_NO_ACTION;
13585   }
13586
13587   if (player->gravity && is_player && !player->is_auto_moving &&
13588       canFallDown(player) && move_direction != MV_DOWN &&
13589       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13590     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13591
13592   if (player_can_move &&
13593       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13594   {
13595     int sound_element = SND_ELEMENT(element);
13596     int sound_action = ACTION_WALKING;
13597
13598     if (IS_RND_GATE(element))
13599     {
13600       if (!player->key[RND_GATE_NR(element)])
13601         return MP_NO_ACTION;
13602     }
13603     else if (IS_RND_GATE_GRAY(element))
13604     {
13605       if (!player->key[RND_GATE_GRAY_NR(element)])
13606         return MP_NO_ACTION;
13607     }
13608     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13609     {
13610       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13611         return MP_NO_ACTION;
13612     }
13613     else if (element == EL_EXIT_OPEN ||
13614              element == EL_EM_EXIT_OPEN ||
13615              element == EL_EM_EXIT_OPENING ||
13616              element == EL_STEEL_EXIT_OPEN ||
13617              element == EL_EM_STEEL_EXIT_OPEN ||
13618              element == EL_EM_STEEL_EXIT_OPENING ||
13619              element == EL_SP_EXIT_OPEN ||
13620              element == EL_SP_EXIT_OPENING)
13621     {
13622       sound_action = ACTION_PASSING;    /* player is passing exit */
13623     }
13624     else if (element == EL_EMPTY)
13625     {
13626       sound_action = ACTION_MOVING;             /* nothing to walk on */
13627     }
13628
13629     /* play sound from background or player, whatever is available */
13630     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13631       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13632     else
13633       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13634   }
13635   else if (player_can_move &&
13636            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13637   {
13638     if (!ACCESS_FROM(element, opposite_direction))
13639       return MP_NO_ACTION;      /* field not accessible from this direction */
13640
13641     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13642       return MP_NO_ACTION;
13643
13644     if (IS_EM_GATE(element))
13645     {
13646       if (!player->key[EM_GATE_NR(element)])
13647         return MP_NO_ACTION;
13648     }
13649     else if (IS_EM_GATE_GRAY(element))
13650     {
13651       if (!player->key[EM_GATE_GRAY_NR(element)])
13652         return MP_NO_ACTION;
13653     }
13654     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13655     {
13656       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13657         return MP_NO_ACTION;
13658     }
13659     else if (IS_EMC_GATE(element))
13660     {
13661       if (!player->key[EMC_GATE_NR(element)])
13662         return MP_NO_ACTION;
13663     }
13664     else if (IS_EMC_GATE_GRAY(element))
13665     {
13666       if (!player->key[EMC_GATE_GRAY_NR(element)])
13667         return MP_NO_ACTION;
13668     }
13669     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13670     {
13671       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13672         return MP_NO_ACTION;
13673     }
13674     else if (element == EL_DC_GATE_WHITE ||
13675              element == EL_DC_GATE_WHITE_GRAY ||
13676              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13677     {
13678       if (player->num_white_keys == 0)
13679         return MP_NO_ACTION;
13680
13681       player->num_white_keys--;
13682     }
13683     else if (IS_SP_PORT(element))
13684     {
13685       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13686           element == EL_SP_GRAVITY_PORT_RIGHT ||
13687           element == EL_SP_GRAVITY_PORT_UP ||
13688           element == EL_SP_GRAVITY_PORT_DOWN)
13689         player->gravity = !player->gravity;
13690       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13691                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13692                element == EL_SP_GRAVITY_ON_PORT_UP ||
13693                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13694         player->gravity = TRUE;
13695       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13696                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13697                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13698                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13699         player->gravity = FALSE;
13700     }
13701
13702     /* automatically move to the next field with double speed */
13703     player->programmed_action = move_direction;
13704
13705     if (player->move_delay_reset_counter == 0)
13706     {
13707       player->move_delay_reset_counter = 2;     /* two double speed steps */
13708
13709       DOUBLE_PLAYER_SPEED(player);
13710     }
13711
13712     PlayLevelSoundAction(x, y, ACTION_PASSING);
13713   }
13714   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13715   {
13716     RemoveField(x, y);
13717
13718     if (mode != DF_SNAP)
13719     {
13720       GfxElement[x][y] = GFX_ELEMENT(element);
13721       player->is_digging = TRUE;
13722     }
13723
13724     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13725
13726     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13727                                         player->index_bit, dig_side);
13728
13729     if (mode == DF_SNAP)
13730     {
13731       if (level.block_snap_field)
13732         setFieldForSnapping(x, y, element, move_direction);
13733       else
13734         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13735
13736       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13737                                           player->index_bit, dig_side);
13738     }
13739   }
13740   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13741   {
13742     RemoveField(x, y);
13743
13744     if (is_player && mode != DF_SNAP)
13745     {
13746       GfxElement[x][y] = element;
13747       player->is_collecting = TRUE;
13748     }
13749
13750     if (element == EL_SPEED_PILL)
13751     {
13752       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13753     }
13754     else if (element == EL_EXTRA_TIME && level.time > 0)
13755     {
13756       TimeLeft += level.extra_time;
13757
13758       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13759
13760       DisplayGameControlValues();
13761     }
13762     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13763     {
13764       player->shield_normal_time_left += level.shield_normal_time;
13765       if (element == EL_SHIELD_DEADLY)
13766         player->shield_deadly_time_left += level.shield_deadly_time;
13767     }
13768     else if (element == EL_DYNAMITE ||
13769              element == EL_EM_DYNAMITE ||
13770              element == EL_SP_DISK_RED)
13771     {
13772       if (player->inventory_size < MAX_INVENTORY_SIZE)
13773         player->inventory_element[player->inventory_size++] = element;
13774
13775       DrawGameDoorValues();
13776     }
13777     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13778     {
13779       player->dynabomb_count++;
13780       player->dynabombs_left++;
13781     }
13782     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13783     {
13784       player->dynabomb_size++;
13785     }
13786     else if (element == EL_DYNABOMB_INCREASE_POWER)
13787     {
13788       player->dynabomb_xl = TRUE;
13789     }
13790     else if (IS_KEY(element))
13791     {
13792       player->key[KEY_NR(element)] = TRUE;
13793
13794       DrawGameDoorValues();
13795     }
13796     else if (element == EL_DC_KEY_WHITE)
13797     {
13798       player->num_white_keys++;
13799
13800       /* display white keys? */
13801       /* DrawGameDoorValues(); */
13802     }
13803     else if (IS_ENVELOPE(element))
13804     {
13805       player->show_envelope = element;
13806     }
13807     else if (element == EL_EMC_LENSES)
13808     {
13809       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13810
13811       RedrawAllInvisibleElementsForLenses();
13812     }
13813     else if (element == EL_EMC_MAGNIFIER)
13814     {
13815       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13816
13817       RedrawAllInvisibleElementsForMagnifier();
13818     }
13819     else if (IS_DROPPABLE(element) ||
13820              IS_THROWABLE(element))     /* can be collected and dropped */
13821     {
13822       int i;
13823
13824       if (collect_count == 0)
13825         player->inventory_infinite_element = element;
13826       else
13827         for (i = 0; i < collect_count; i++)
13828           if (player->inventory_size < MAX_INVENTORY_SIZE)
13829             player->inventory_element[player->inventory_size++] = element;
13830
13831       DrawGameDoorValues();
13832     }
13833     else if (collect_count > 0)
13834     {
13835       local_player->gems_still_needed -= collect_count;
13836       if (local_player->gems_still_needed < 0)
13837         local_player->gems_still_needed = 0;
13838
13839       game.snapshot.collected_item = TRUE;
13840
13841       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13842
13843       DisplayGameControlValues();
13844     }
13845
13846     RaiseScoreElement(element);
13847     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13848
13849     if (is_player)
13850       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13851                                           player->index_bit, dig_side);
13852
13853     if (mode == DF_SNAP)
13854     {
13855       if (level.block_snap_field)
13856         setFieldForSnapping(x, y, element, move_direction);
13857       else
13858         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13859
13860       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13861                                           player->index_bit, dig_side);
13862     }
13863   }
13864   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13865   {
13866     if (mode == DF_SNAP && element != EL_BD_ROCK)
13867       return MP_NO_ACTION;
13868
13869     if (CAN_FALL(element) && dy)
13870       return MP_NO_ACTION;
13871
13872     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13873         !(element == EL_SPRING && level.use_spring_bug))
13874       return MP_NO_ACTION;
13875
13876     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13877         ((move_direction & MV_VERTICAL &&
13878           ((element_info[element].move_pattern & MV_LEFT &&
13879             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13880            (element_info[element].move_pattern & MV_RIGHT &&
13881             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13882          (move_direction & MV_HORIZONTAL &&
13883           ((element_info[element].move_pattern & MV_UP &&
13884             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13885            (element_info[element].move_pattern & MV_DOWN &&
13886             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13887       return MP_NO_ACTION;
13888
13889     /* do not push elements already moving away faster than player */
13890     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13891         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13892       return MP_NO_ACTION;
13893
13894     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13895     {
13896       if (player->push_delay_value == -1 || !player_was_pushing)
13897         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13898     }
13899     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13900     {
13901       if (player->push_delay_value == -1)
13902         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13903     }
13904     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13905     {
13906       if (!player->is_pushing)
13907         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13908     }
13909
13910     player->is_pushing = TRUE;
13911     player->is_active = TRUE;
13912
13913     if (!(IN_LEV_FIELD(nextx, nexty) &&
13914           (IS_FREE(nextx, nexty) ||
13915            (IS_SB_ELEMENT(element) &&
13916             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13917            (IS_CUSTOM_ELEMENT(element) &&
13918             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13919       return MP_NO_ACTION;
13920
13921     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13922       return MP_NO_ACTION;
13923
13924     if (player->push_delay == -1)       /* new pushing; restart delay */
13925       player->push_delay = 0;
13926
13927     if (player->push_delay < player->push_delay_value &&
13928         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13929         element != EL_SPRING && element != EL_BALLOON)
13930     {
13931       /* make sure that there is no move delay before next try to push */
13932       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13933         player->move_delay = 0;
13934
13935       return MP_NO_ACTION;
13936     }
13937
13938     if (IS_CUSTOM_ELEMENT(element) &&
13939         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13940     {
13941       if (!DigFieldByCE(nextx, nexty, element))
13942         return MP_NO_ACTION;
13943     }
13944
13945     if (IS_SB_ELEMENT(element))
13946     {
13947       if (element == EL_SOKOBAN_FIELD_FULL)
13948       {
13949         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13950         local_player->sokobanfields_still_needed++;
13951       }
13952
13953       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13954       {
13955         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13956         local_player->sokobanfields_still_needed--;
13957       }
13958
13959       Feld[x][y] = EL_SOKOBAN_OBJECT;
13960
13961       if (Back[x][y] == Back[nextx][nexty])
13962         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13963       else if (Back[x][y] != 0)
13964         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13965                                     ACTION_EMPTYING);
13966       else
13967         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13968                                     ACTION_FILLING);
13969
13970       if (local_player->sokobanfields_still_needed == 0 &&
13971           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13972       {
13973         local_player->players_still_needed = 0;
13974
13975         PlayerWins(player);
13976
13977         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13978       }
13979     }
13980     else
13981       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13982
13983     InitMovingField(x, y, move_direction);
13984     GfxAction[x][y] = ACTION_PUSHING;
13985
13986     if (mode == DF_SNAP)
13987       ContinueMoving(x, y);
13988     else
13989       MovPos[x][y] = (dx != 0 ? dx : dy);
13990
13991     Pushed[x][y] = TRUE;
13992     Pushed[nextx][nexty] = TRUE;
13993
13994     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13995       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13996     else
13997       player->push_delay_value = -1;    /* get new value later */
13998
13999     /* check for element change _after_ element has been pushed */
14000     if (game.use_change_when_pushing_bug)
14001     {
14002       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14003                                  player->index_bit, dig_side);
14004       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14005                                           player->index_bit, dig_side);
14006     }
14007   }
14008   else if (IS_SWITCHABLE(element))
14009   {
14010     if (PLAYER_SWITCHING(player, x, y))
14011     {
14012       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14013                                           player->index_bit, dig_side);
14014
14015       return MP_ACTION;
14016     }
14017
14018     player->is_switching = TRUE;
14019     player->switch_x = x;
14020     player->switch_y = y;
14021
14022     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14023
14024     if (element == EL_ROBOT_WHEEL)
14025     {
14026       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14027       ZX = x;
14028       ZY = y;
14029
14030       game.robot_wheel_active = TRUE;
14031
14032       TEST_DrawLevelField(x, y);
14033     }
14034     else if (element == EL_SP_TERMINAL)
14035     {
14036       int xx, yy;
14037
14038       SCAN_PLAYFIELD(xx, yy)
14039       {
14040         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14041         {
14042           Bang(xx, yy);
14043         }
14044         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14045         {
14046           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14047
14048           ResetGfxAnimation(xx, yy);
14049           TEST_DrawLevelField(xx, yy);
14050         }
14051       }
14052     }
14053     else if (IS_BELT_SWITCH(element))
14054     {
14055       ToggleBeltSwitch(x, y);
14056     }
14057     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14058              element == EL_SWITCHGATE_SWITCH_DOWN ||
14059              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14060              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14061     {
14062       ToggleSwitchgateSwitch(x, y);
14063     }
14064     else if (element == EL_LIGHT_SWITCH ||
14065              element == EL_LIGHT_SWITCH_ACTIVE)
14066     {
14067       ToggleLightSwitch(x, y);
14068     }
14069     else if (element == EL_TIMEGATE_SWITCH ||
14070              element == EL_DC_TIMEGATE_SWITCH)
14071     {
14072       ActivateTimegateSwitch(x, y);
14073     }
14074     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14075              element == EL_BALLOON_SWITCH_RIGHT ||
14076              element == EL_BALLOON_SWITCH_UP    ||
14077              element == EL_BALLOON_SWITCH_DOWN  ||
14078              element == EL_BALLOON_SWITCH_NONE  ||
14079              element == EL_BALLOON_SWITCH_ANY)
14080     {
14081       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14082                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14083                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14084                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14085                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14086                              move_direction);
14087     }
14088     else if (element == EL_LAMP)
14089     {
14090       Feld[x][y] = EL_LAMP_ACTIVE;
14091       local_player->lights_still_needed--;
14092
14093       ResetGfxAnimation(x, y);
14094       TEST_DrawLevelField(x, y);
14095     }
14096     else if (element == EL_TIME_ORB_FULL)
14097     {
14098       Feld[x][y] = EL_TIME_ORB_EMPTY;
14099
14100       if (level.time > 0 || level.use_time_orb_bug)
14101       {
14102         TimeLeft += level.time_orb_time;
14103         game.no_time_limit = FALSE;
14104
14105         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14106
14107         DisplayGameControlValues();
14108       }
14109
14110       ResetGfxAnimation(x, y);
14111       TEST_DrawLevelField(x, y);
14112     }
14113     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14114              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14115     {
14116       int xx, yy;
14117
14118       game.ball_state = !game.ball_state;
14119
14120       SCAN_PLAYFIELD(xx, yy)
14121       {
14122         int e = Feld[xx][yy];
14123
14124         if (game.ball_state)
14125         {
14126           if (e == EL_EMC_MAGIC_BALL)
14127             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14128           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14129             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14130         }
14131         else
14132         {
14133           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14134             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14135           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14136             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14137         }
14138       }
14139     }
14140
14141     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14142                                         player->index_bit, dig_side);
14143
14144     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14145                                         player->index_bit, dig_side);
14146
14147     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14148                                         player->index_bit, dig_side);
14149
14150     return MP_ACTION;
14151   }
14152   else
14153   {
14154     if (!PLAYER_SWITCHING(player, x, y))
14155     {
14156       player->is_switching = TRUE;
14157       player->switch_x = x;
14158       player->switch_y = y;
14159
14160       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14161                                  player->index_bit, dig_side);
14162       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14163                                           player->index_bit, dig_side);
14164
14165       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14166                                  player->index_bit, dig_side);
14167       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14168                                           player->index_bit, dig_side);
14169     }
14170
14171     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14172                                player->index_bit, dig_side);
14173     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14174                                         player->index_bit, dig_side);
14175
14176     return MP_NO_ACTION;
14177   }
14178
14179   player->push_delay = -1;
14180
14181   if (is_player)                /* function can also be called by EL_PENGUIN */
14182   {
14183     if (Feld[x][y] != element)          /* really digged/collected something */
14184     {
14185       player->is_collecting = !player->is_digging;
14186       player->is_active = TRUE;
14187     }
14188   }
14189
14190   return MP_MOVING;
14191 }
14192
14193 static boolean DigFieldByCE(int x, int y, int digging_element)
14194 {
14195   int element = Feld[x][y];
14196
14197   if (!IS_FREE(x, y))
14198   {
14199     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14200                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14201                   ACTION_BREAKING);
14202
14203     /* no element can dig solid indestructible elements */
14204     if (IS_INDESTRUCTIBLE(element) &&
14205         !IS_DIGGABLE(element) &&
14206         !IS_COLLECTIBLE(element))
14207       return FALSE;
14208
14209     if (AmoebaNr[x][y] &&
14210         (element == EL_AMOEBA_FULL ||
14211          element == EL_BD_AMOEBA ||
14212          element == EL_AMOEBA_GROWING))
14213     {
14214       AmoebaCnt[AmoebaNr[x][y]]--;
14215       AmoebaCnt2[AmoebaNr[x][y]]--;
14216     }
14217
14218     if (IS_MOVING(x, y))
14219       RemoveMovingField(x, y);
14220     else
14221     {
14222       RemoveField(x, y);
14223       TEST_DrawLevelField(x, y);
14224     }
14225
14226     /* if digged element was about to explode, prevent the explosion */
14227     ExplodeField[x][y] = EX_TYPE_NONE;
14228
14229     PlayLevelSoundAction(x, y, action);
14230   }
14231
14232   Store[x][y] = EL_EMPTY;
14233
14234   /* this makes it possible to leave the removed element again */
14235   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14236     Store[x][y] = element;
14237
14238   return TRUE;
14239 }
14240
14241 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14242 {
14243   int jx = player->jx, jy = player->jy;
14244   int x = jx + dx, y = jy + dy;
14245   int snap_direction = (dx == -1 ? MV_LEFT  :
14246                         dx == +1 ? MV_RIGHT :
14247                         dy == -1 ? MV_UP    :
14248                         dy == +1 ? MV_DOWN  : MV_NONE);
14249   boolean can_continue_snapping = (level.continuous_snapping &&
14250                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14251
14252   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14253     return FALSE;
14254
14255   if (!player->active || !IN_LEV_FIELD(x, y))
14256     return FALSE;
14257
14258   if (dx && dy)
14259     return FALSE;
14260
14261   if (!dx && !dy)
14262   {
14263     if (player->MovPos == 0)
14264       player->is_pushing = FALSE;
14265
14266     player->is_snapping = FALSE;
14267
14268     if (player->MovPos == 0)
14269     {
14270       player->is_moving = FALSE;
14271       player->is_digging = FALSE;
14272       player->is_collecting = FALSE;
14273     }
14274
14275     return FALSE;
14276   }
14277
14278   /* prevent snapping with already pressed snap key when not allowed */
14279   if (player->is_snapping && !can_continue_snapping)
14280     return FALSE;
14281
14282   player->MovDir = snap_direction;
14283
14284   if (player->MovPos == 0)
14285   {
14286     player->is_moving = FALSE;
14287     player->is_digging = FALSE;
14288     player->is_collecting = FALSE;
14289   }
14290
14291   player->is_dropping = FALSE;
14292   player->is_dropping_pressed = FALSE;
14293   player->drop_pressed_delay = 0;
14294
14295   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14296     return FALSE;
14297
14298   player->is_snapping = TRUE;
14299   player->is_active = TRUE;
14300
14301   if (player->MovPos == 0)
14302   {
14303     player->is_moving = FALSE;
14304     player->is_digging = FALSE;
14305     player->is_collecting = FALSE;
14306   }
14307
14308   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14309     TEST_DrawLevelField(player->last_jx, player->last_jy);
14310
14311   TEST_DrawLevelField(x, y);
14312
14313   return TRUE;
14314 }
14315
14316 static boolean DropElement(struct PlayerInfo *player)
14317 {
14318   int old_element, new_element;
14319   int dropx = player->jx, dropy = player->jy;
14320   int drop_direction = player->MovDir;
14321   int drop_side = drop_direction;
14322   int drop_element = get_next_dropped_element(player);
14323
14324   /* do not drop an element on top of another element; when holding drop key
14325      pressed without moving, dropped element must move away before the next
14326      element can be dropped (this is especially important if the next element
14327      is dynamite, which can be placed on background for historical reasons) */
14328   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14329     return MP_ACTION;
14330
14331   if (IS_THROWABLE(drop_element))
14332   {
14333     dropx += GET_DX_FROM_DIR(drop_direction);
14334     dropy += GET_DY_FROM_DIR(drop_direction);
14335
14336     if (!IN_LEV_FIELD(dropx, dropy))
14337       return FALSE;
14338   }
14339
14340   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14341   new_element = drop_element;           /* default: no change when dropping */
14342
14343   /* check if player is active, not moving and ready to drop */
14344   if (!player->active || player->MovPos || player->drop_delay > 0)
14345     return FALSE;
14346
14347   /* check if player has anything that can be dropped */
14348   if (new_element == EL_UNDEFINED)
14349     return FALSE;
14350
14351   /* only set if player has anything that can be dropped */
14352   player->is_dropping_pressed = TRUE;
14353
14354   /* check if drop key was pressed long enough for EM style dynamite */
14355   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14356     return FALSE;
14357
14358   /* check if anything can be dropped at the current position */
14359   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14360     return FALSE;
14361
14362   /* collected custom elements can only be dropped on empty fields */
14363   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14364     return FALSE;
14365
14366   if (old_element != EL_EMPTY)
14367     Back[dropx][dropy] = old_element;   /* store old element on this field */
14368
14369   ResetGfxAnimation(dropx, dropy);
14370   ResetRandomAnimationValue(dropx, dropy);
14371
14372   if (player->inventory_size > 0 ||
14373       player->inventory_infinite_element != EL_UNDEFINED)
14374   {
14375     if (player->inventory_size > 0)
14376     {
14377       player->inventory_size--;
14378
14379       DrawGameDoorValues();
14380
14381       if (new_element == EL_DYNAMITE)
14382         new_element = EL_DYNAMITE_ACTIVE;
14383       else if (new_element == EL_EM_DYNAMITE)
14384         new_element = EL_EM_DYNAMITE_ACTIVE;
14385       else if (new_element == EL_SP_DISK_RED)
14386         new_element = EL_SP_DISK_RED_ACTIVE;
14387     }
14388
14389     Feld[dropx][dropy] = new_element;
14390
14391     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14392       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14393                           el2img(Feld[dropx][dropy]), 0);
14394
14395     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14396
14397     /* needed if previous element just changed to "empty" in the last frame */
14398     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14399
14400     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14401                                player->index_bit, drop_side);
14402     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14403                                         CE_PLAYER_DROPS_X,
14404                                         player->index_bit, drop_side);
14405
14406     TestIfElementTouchesCustomElement(dropx, dropy);
14407   }
14408   else          /* player is dropping a dyna bomb */
14409   {
14410     player->dynabombs_left--;
14411
14412     Feld[dropx][dropy] = new_element;
14413
14414     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14415       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14416                           el2img(Feld[dropx][dropy]), 0);
14417
14418     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14419   }
14420
14421   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14422     InitField_WithBug1(dropx, dropy, FALSE);
14423
14424   new_element = Feld[dropx][dropy];     /* element might have changed */
14425
14426   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14427       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14428   {
14429     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14430       MovDir[dropx][dropy] = drop_direction;
14431
14432     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14433
14434     /* do not cause impact style collision by dropping elements that can fall */
14435     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14436   }
14437
14438   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14439   player->is_dropping = TRUE;
14440
14441   player->drop_pressed_delay = 0;
14442   player->is_dropping_pressed = FALSE;
14443
14444   player->drop_x = dropx;
14445   player->drop_y = dropy;
14446
14447   return TRUE;
14448 }
14449
14450 /* ------------------------------------------------------------------------- */
14451 /* game sound playing functions                                              */
14452 /* ------------------------------------------------------------------------- */
14453
14454 static int *loop_sound_frame = NULL;
14455 static int *loop_sound_volume = NULL;
14456
14457 void InitPlayLevelSound()
14458 {
14459   int num_sounds = getSoundListSize();
14460
14461   checked_free(loop_sound_frame);
14462   checked_free(loop_sound_volume);
14463
14464   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14465   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14466 }
14467
14468 static void PlayLevelSound(int x, int y, int nr)
14469 {
14470   int sx = SCREENX(x), sy = SCREENY(y);
14471   int volume, stereo_position;
14472   int max_distance = 8;
14473   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14474
14475   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14476       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14477     return;
14478
14479   if (!IN_LEV_FIELD(x, y) ||
14480       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14481       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14482     return;
14483
14484   volume = SOUND_MAX_VOLUME;
14485
14486   if (!IN_SCR_FIELD(sx, sy))
14487   {
14488     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14489     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14490
14491     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14492   }
14493
14494   stereo_position = (SOUND_MAX_LEFT +
14495                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14496                      (SCR_FIELDX + 2 * max_distance));
14497
14498   if (IS_LOOP_SOUND(nr))
14499   {
14500     /* This assures that quieter loop sounds do not overwrite louder ones,
14501        while restarting sound volume comparison with each new game frame. */
14502
14503     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14504       return;
14505
14506     loop_sound_volume[nr] = volume;
14507     loop_sound_frame[nr] = FrameCounter;
14508   }
14509
14510   PlaySoundExt(nr, volume, stereo_position, type);
14511 }
14512
14513 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14514 {
14515   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14516                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14517                  y < LEVELY(BY1) ? LEVELY(BY1) :
14518                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14519                  sound_action);
14520 }
14521
14522 static void PlayLevelSoundAction(int x, int y, int action)
14523 {
14524   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14525 }
14526
14527 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14528 {
14529   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14530
14531   if (sound_effect != SND_UNDEFINED)
14532     PlayLevelSound(x, y, sound_effect);
14533 }
14534
14535 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14536                                               int action)
14537 {
14538   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14539
14540   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14541     PlayLevelSound(x, y, sound_effect);
14542 }
14543
14544 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14545 {
14546   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14547
14548   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14549     PlayLevelSound(x, y, sound_effect);
14550 }
14551
14552 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14553 {
14554   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14555
14556   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14557     StopSound(sound_effect);
14558 }
14559
14560 static int getLevelMusicNr()
14561 {
14562   if (levelset.music[level_nr] != MUS_UNDEFINED)
14563     return levelset.music[level_nr];            /* from config file */
14564   else
14565     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14566 }
14567
14568 static void FadeLevelSounds()
14569 {
14570   FadeSounds();
14571 }
14572
14573 static void FadeLevelMusic()
14574 {
14575   int music_nr = getLevelMusicNr();
14576   char *curr_music = getCurrentlyPlayingMusicFilename();
14577   char *next_music = getMusicInfoEntryFilename(music_nr);
14578
14579   if (!strEqual(curr_music, next_music))
14580     FadeMusic();
14581 }
14582
14583 void FadeLevelSoundsAndMusic()
14584 {
14585   FadeLevelSounds();
14586   FadeLevelMusic();
14587 }
14588
14589 static void PlayLevelMusic()
14590 {
14591   int music_nr = getLevelMusicNr();
14592   char *curr_music = getCurrentlyPlayingMusicFilename();
14593   char *next_music = getMusicInfoEntryFilename(music_nr);
14594
14595   if (!strEqual(curr_music, next_music))
14596     PlayMusic(music_nr);
14597 }
14598
14599 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14600 {
14601   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14602   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14603   int x = xx - 1 - offset;
14604   int y = yy - 1 - offset;
14605
14606   switch (sample)
14607   {
14608     case SAMPLE_blank:
14609       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14610       break;
14611
14612     case SAMPLE_roll:
14613       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14614       break;
14615
14616     case SAMPLE_stone:
14617       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14618       break;
14619
14620     case SAMPLE_nut:
14621       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14622       break;
14623
14624     case SAMPLE_crack:
14625       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14626       break;
14627
14628     case SAMPLE_bug:
14629       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14630       break;
14631
14632     case SAMPLE_tank:
14633       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14634       break;
14635
14636     case SAMPLE_android_clone:
14637       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14638       break;
14639
14640     case SAMPLE_android_move:
14641       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14642       break;
14643
14644     case SAMPLE_spring:
14645       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14646       break;
14647
14648     case SAMPLE_slurp:
14649       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14650       break;
14651
14652     case SAMPLE_eater:
14653       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14654       break;
14655
14656     case SAMPLE_eater_eat:
14657       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14658       break;
14659
14660     case SAMPLE_alien:
14661       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14662       break;
14663
14664     case SAMPLE_collect:
14665       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14666       break;
14667
14668     case SAMPLE_diamond:
14669       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14670       break;
14671
14672     case SAMPLE_squash:
14673       /* !!! CHECK THIS !!! */
14674 #if 1
14675       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14676 #else
14677       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14678 #endif
14679       break;
14680
14681     case SAMPLE_wonderfall:
14682       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14683       break;
14684
14685     case SAMPLE_drip:
14686       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14687       break;
14688
14689     case SAMPLE_push:
14690       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14691       break;
14692
14693     case SAMPLE_dirt:
14694       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14695       break;
14696
14697     case SAMPLE_acid:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14699       break;
14700
14701     case SAMPLE_ball:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14703       break;
14704
14705     case SAMPLE_grow:
14706       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14707       break;
14708
14709     case SAMPLE_wonder:
14710       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14711       break;
14712
14713     case SAMPLE_door:
14714       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14715       break;
14716
14717     case SAMPLE_exit_open:
14718       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14719       break;
14720
14721     case SAMPLE_exit_leave:
14722       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14723       break;
14724
14725     case SAMPLE_dynamite:
14726       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14727       break;
14728
14729     case SAMPLE_tick:
14730       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14731       break;
14732
14733     case SAMPLE_press:
14734       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14735       break;
14736
14737     case SAMPLE_wheel:
14738       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14739       break;
14740
14741     case SAMPLE_boom:
14742       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14743       break;
14744
14745     case SAMPLE_die:
14746       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14747       break;
14748
14749     case SAMPLE_time:
14750       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14751       break;
14752
14753     default:
14754       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14755       break;
14756   }
14757 }
14758
14759 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14760 {
14761   int element = map_element_SP_to_RND(element_sp);
14762   int action = map_action_SP_to_RND(action_sp);
14763   int offset = (setup.sp_show_border_elements ? 0 : 1);
14764   int x = xx - offset;
14765   int y = yy - offset;
14766
14767   PlayLevelSoundElementAction(x, y, element, action);
14768 }
14769
14770 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14771 {
14772   int element = map_element_MM_to_RND(element_mm);
14773   int action = map_action_MM_to_RND(action_mm);
14774   int offset = 0;
14775   int x = xx - offset;
14776   int y = yy - offset;
14777
14778   if (!IS_MM_ELEMENT(element))
14779     element = EL_MM_DEFAULT;
14780
14781   PlayLevelSoundElementAction(x, y, element, action);
14782 }
14783
14784 void PlaySound_MM(int sound_mm)
14785 {
14786   int sound = map_sound_MM_to_RND(sound_mm);
14787
14788   if (sound == SND_UNDEFINED)
14789     return;
14790
14791   PlaySound(sound);
14792 }
14793
14794 void PlaySoundLoop_MM(int sound_mm)
14795 {
14796   int sound = map_sound_MM_to_RND(sound_mm);
14797
14798   if (sound == SND_UNDEFINED)
14799     return;
14800
14801   PlaySoundLoop(sound);
14802 }
14803
14804 void StopSound_MM(int sound_mm)
14805 {
14806   int sound = map_sound_MM_to_RND(sound_mm);
14807
14808   if (sound == SND_UNDEFINED)
14809     return;
14810
14811   StopSound(sound);
14812 }
14813
14814 void RaiseScore(int value)
14815 {
14816   local_player->score += value;
14817
14818   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14819
14820   DisplayGameControlValues();
14821 }
14822
14823 void RaiseScoreElement(int element)
14824 {
14825   switch (element)
14826   {
14827     case EL_EMERALD:
14828     case EL_BD_DIAMOND:
14829     case EL_EMERALD_YELLOW:
14830     case EL_EMERALD_RED:
14831     case EL_EMERALD_PURPLE:
14832     case EL_SP_INFOTRON:
14833       RaiseScore(level.score[SC_EMERALD]);
14834       break;
14835     case EL_DIAMOND:
14836       RaiseScore(level.score[SC_DIAMOND]);
14837       break;
14838     case EL_CRYSTAL:
14839       RaiseScore(level.score[SC_CRYSTAL]);
14840       break;
14841     case EL_PEARL:
14842       RaiseScore(level.score[SC_PEARL]);
14843       break;
14844     case EL_BUG:
14845     case EL_BD_BUTTERFLY:
14846     case EL_SP_ELECTRON:
14847       RaiseScore(level.score[SC_BUG]);
14848       break;
14849     case EL_SPACESHIP:
14850     case EL_BD_FIREFLY:
14851     case EL_SP_SNIKSNAK:
14852       RaiseScore(level.score[SC_SPACESHIP]);
14853       break;
14854     case EL_YAMYAM:
14855     case EL_DARK_YAMYAM:
14856       RaiseScore(level.score[SC_YAMYAM]);
14857       break;
14858     case EL_ROBOT:
14859       RaiseScore(level.score[SC_ROBOT]);
14860       break;
14861     case EL_PACMAN:
14862       RaiseScore(level.score[SC_PACMAN]);
14863       break;
14864     case EL_NUT:
14865       RaiseScore(level.score[SC_NUT]);
14866       break;
14867     case EL_DYNAMITE:
14868     case EL_EM_DYNAMITE:
14869     case EL_SP_DISK_RED:
14870     case EL_DYNABOMB_INCREASE_NUMBER:
14871     case EL_DYNABOMB_INCREASE_SIZE:
14872     case EL_DYNABOMB_INCREASE_POWER:
14873       RaiseScore(level.score[SC_DYNAMITE]);
14874       break;
14875     case EL_SHIELD_NORMAL:
14876     case EL_SHIELD_DEADLY:
14877       RaiseScore(level.score[SC_SHIELD]);
14878       break;
14879     case EL_EXTRA_TIME:
14880       RaiseScore(level.extra_time_score);
14881       break;
14882     case EL_KEY_1:
14883     case EL_KEY_2:
14884     case EL_KEY_3:
14885     case EL_KEY_4:
14886     case EL_EM_KEY_1:
14887     case EL_EM_KEY_2:
14888     case EL_EM_KEY_3:
14889     case EL_EM_KEY_4:
14890     case EL_EMC_KEY_5:
14891     case EL_EMC_KEY_6:
14892     case EL_EMC_KEY_7:
14893     case EL_EMC_KEY_8:
14894     case EL_DC_KEY_WHITE:
14895       RaiseScore(level.score[SC_KEY]);
14896       break;
14897     default:
14898       RaiseScore(element_info[element].collect_score);
14899       break;
14900   }
14901 }
14902
14903 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14904 {
14905   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14906   {
14907     /* closing door required in case of envelope style request dialogs */
14908     if (!skip_request)
14909       CloseDoor(DOOR_CLOSE_1);
14910
14911     if (network.enabled)
14912       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14913     else
14914     {
14915       if (quick_quit)
14916         FadeSkipNextFadeIn();
14917
14918       SetGameStatus(GAME_MODE_MAIN);
14919
14920       DrawMainMenu();
14921     }
14922   }
14923   else          /* continue playing the game */
14924   {
14925     if (tape.playing && tape.deactivate_display)
14926       TapeDeactivateDisplayOff(TRUE);
14927
14928     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14929
14930     if (tape.playing && tape.deactivate_display)
14931       TapeDeactivateDisplayOn();
14932   }
14933 }
14934
14935 void RequestQuitGame(boolean ask_if_really_quit)
14936 {
14937   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14938   boolean skip_request = AllPlayersGone || quick_quit;
14939
14940   RequestQuitGameExt(skip_request, quick_quit,
14941                      "Do you really want to quit the game?");
14942 }
14943
14944 void RequestRestartGame(char *message)
14945 {
14946   game.restart_game_message = NULL;
14947
14948   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14949   {
14950     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14951   }
14952   else
14953   {
14954     SetGameStatus(GAME_MODE_MAIN);
14955
14956     DrawMainMenu();
14957   }
14958 }
14959
14960
14961 /* ------------------------------------------------------------------------- */
14962 /* random generator functions                                                */
14963 /* ------------------------------------------------------------------------- */
14964
14965 unsigned int InitEngineRandom_RND(int seed)
14966 {
14967   game.num_random_calls = 0;
14968
14969   return InitEngineRandom(seed);
14970 }
14971
14972 unsigned int RND(int max)
14973 {
14974   if (max > 0)
14975   {
14976     game.num_random_calls++;
14977
14978     return GetEngineRandom(max);
14979   }
14980
14981   return 0;
14982 }
14983
14984
14985 /* ------------------------------------------------------------------------- */
14986 /* game engine snapshot handling functions                                   */
14987 /* ------------------------------------------------------------------------- */
14988
14989 struct EngineSnapshotInfo
14990 {
14991   /* runtime values for custom element collect score */
14992   int collect_score[NUM_CUSTOM_ELEMENTS];
14993
14994   /* runtime values for group element choice position */
14995   int choice_pos[NUM_GROUP_ELEMENTS];
14996
14997   /* runtime values for belt position animations */
14998   int belt_graphic[4][NUM_BELT_PARTS];
14999   int belt_anim_mode[4][NUM_BELT_PARTS];
15000 };
15001
15002 static struct EngineSnapshotInfo engine_snapshot_rnd;
15003 static char *snapshot_level_identifier = NULL;
15004 static int snapshot_level_nr = -1;
15005
15006 static void SaveEngineSnapshotValues_RND()
15007 {
15008   static int belt_base_active_element[4] =
15009   {
15010     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15011     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15012     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15013     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15014   };
15015   int i, j;
15016
15017   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15018   {
15019     int element = EL_CUSTOM_START + i;
15020
15021     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15022   }
15023
15024   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15025   {
15026     int element = EL_GROUP_START + i;
15027
15028     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15029   }
15030
15031   for (i = 0; i < 4; i++)
15032   {
15033     for (j = 0; j < NUM_BELT_PARTS; j++)
15034     {
15035       int element = belt_base_active_element[i] + j;
15036       int graphic = el2img(element);
15037       int anim_mode = graphic_info[graphic].anim_mode;
15038
15039       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15040       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15041     }
15042   }
15043 }
15044
15045 static void LoadEngineSnapshotValues_RND()
15046 {
15047   unsigned int num_random_calls = game.num_random_calls;
15048   int i, j;
15049
15050   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15051   {
15052     int element = EL_CUSTOM_START + i;
15053
15054     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15055   }
15056
15057   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15058   {
15059     int element = EL_GROUP_START + i;
15060
15061     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15062   }
15063
15064   for (i = 0; i < 4; i++)
15065   {
15066     for (j = 0; j < NUM_BELT_PARTS; j++)
15067     {
15068       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15069       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15070
15071       graphic_info[graphic].anim_mode = anim_mode;
15072     }
15073   }
15074
15075   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15076   {
15077     InitRND(tape.random_seed);
15078     for (i = 0; i < num_random_calls; i++)
15079       RND(1);
15080   }
15081
15082   if (game.num_random_calls != num_random_calls)
15083   {
15084     Error(ERR_INFO, "number of random calls out of sync");
15085     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15086     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15087     Error(ERR_EXIT, "this should not happen -- please debug");
15088   }
15089 }
15090
15091 void FreeEngineSnapshotSingle()
15092 {
15093   FreeSnapshotSingle();
15094
15095   setString(&snapshot_level_identifier, NULL);
15096   snapshot_level_nr = -1;
15097 }
15098
15099 void FreeEngineSnapshotList()
15100 {
15101   FreeSnapshotList();
15102 }
15103
15104 ListNode *SaveEngineSnapshotBuffers()
15105 {
15106   ListNode *buffers = NULL;
15107
15108   /* copy some special values to a structure better suited for the snapshot */
15109
15110   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15111     SaveEngineSnapshotValues_RND();
15112   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15113     SaveEngineSnapshotValues_EM();
15114   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15115     SaveEngineSnapshotValues_SP(&buffers);
15116   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15117     SaveEngineSnapshotValues_MM(&buffers);
15118
15119   /* save values stored in special snapshot structure */
15120
15121   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15122     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15123   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15124     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15125   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15126     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15127   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15128     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15129
15130   /* save further RND engine values */
15131
15132   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15135
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15137   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15140
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15146
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15150
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15152
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15154
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15157
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15176
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15179
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15183
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15186
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15192
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15195
15196 #if 0
15197   ListNode *node = engine_snapshot_list_rnd;
15198   int num_bytes = 0;
15199
15200   while (node != NULL)
15201   {
15202     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15203
15204     node = node->next;
15205   }
15206
15207   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15208 #endif
15209
15210   return buffers;
15211 }
15212
15213 void SaveEngineSnapshotSingle()
15214 {
15215   ListNode *buffers = SaveEngineSnapshotBuffers();
15216
15217   /* finally save all snapshot buffers to single snapshot */
15218   SaveSnapshotSingle(buffers);
15219
15220   /* save level identification information */
15221   setString(&snapshot_level_identifier, leveldir_current->identifier);
15222   snapshot_level_nr = level_nr;
15223 }
15224
15225 boolean CheckSaveEngineSnapshotToList()
15226 {
15227   boolean save_snapshot =
15228     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15229      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15230       game.snapshot.changed_action) ||
15231      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15232       game.snapshot.collected_item));
15233
15234   game.snapshot.changed_action = FALSE;
15235   game.snapshot.collected_item = FALSE;
15236   game.snapshot.save_snapshot = save_snapshot;
15237
15238   return save_snapshot;
15239 }
15240
15241 void SaveEngineSnapshotToList()
15242 {
15243   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15244       tape.quick_resume)
15245     return;
15246
15247   ListNode *buffers = SaveEngineSnapshotBuffers();
15248
15249   /* finally save all snapshot buffers to snapshot list */
15250   SaveSnapshotToList(buffers);
15251 }
15252
15253 void SaveEngineSnapshotToListInitial()
15254 {
15255   FreeEngineSnapshotList();
15256
15257   SaveEngineSnapshotToList();
15258 }
15259
15260 void LoadEngineSnapshotValues()
15261 {
15262   /* restore special values from snapshot structure */
15263
15264   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15265     LoadEngineSnapshotValues_RND();
15266   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15267     LoadEngineSnapshotValues_EM();
15268   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15269     LoadEngineSnapshotValues_SP();
15270   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15271     LoadEngineSnapshotValues_MM();
15272 }
15273
15274 void LoadEngineSnapshotSingle()
15275 {
15276   LoadSnapshotSingle();
15277
15278   LoadEngineSnapshotValues();
15279 }
15280
15281 void LoadEngineSnapshot_Undo(int steps)
15282 {
15283   LoadSnapshotFromList_Older(steps);
15284
15285   LoadEngineSnapshotValues();
15286 }
15287
15288 void LoadEngineSnapshot_Redo(int steps)
15289 {
15290   LoadSnapshotFromList_Newer(steps);
15291
15292   LoadEngineSnapshotValues();
15293 }
15294
15295 boolean CheckEngineSnapshotSingle()
15296 {
15297   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15298           snapshot_level_nr == level_nr);
15299 }
15300
15301 boolean CheckEngineSnapshotList()
15302 {
15303   return CheckSnapshotList();
15304 }
15305
15306
15307 /* ---------- new game button stuff ---------------------------------------- */
15308
15309 static struct
15310 {
15311   int graphic;
15312   struct XY *pos;
15313   int gadget_id;
15314   boolean *setup_value;
15315   boolean allowed_on_tape;
15316   char *infotext;
15317 } gamebutton_info[NUM_GAME_BUTTONS] =
15318 {
15319   {
15320     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15321     GAME_CTRL_ID_STOP,                          NULL,
15322     TRUE,                                       "stop game"
15323   },
15324   {
15325     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15326     GAME_CTRL_ID_PAUSE,                         NULL,
15327     TRUE,                                       "pause game"
15328   },
15329   {
15330     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15331     GAME_CTRL_ID_PLAY,                          NULL,
15332     TRUE,                                       "play game"
15333   },
15334   {
15335     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15336     GAME_CTRL_ID_UNDO,                          NULL,
15337     TRUE,                                       "undo step"
15338   },
15339   {
15340     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15341     GAME_CTRL_ID_REDO,                          NULL,
15342     TRUE,                                       "redo step"
15343   },
15344   {
15345     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15346     GAME_CTRL_ID_SAVE,                          NULL,
15347     TRUE,                                       "save game"
15348   },
15349   {
15350     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15351     GAME_CTRL_ID_PAUSE2,                        NULL,
15352     TRUE,                                       "pause game"
15353   },
15354   {
15355     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15356     GAME_CTRL_ID_LOAD,                          NULL,
15357     TRUE,                                       "load game"
15358   },
15359   {
15360     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15361     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15362     FALSE,                                      "stop game"
15363   },
15364   {
15365     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15366     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15367     FALSE,                                      "pause game"
15368   },
15369   {
15370     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15371     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15372     FALSE,                                      "play game"
15373   },
15374   {
15375     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15376     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15377     TRUE,                                       "background music on/off"
15378   },
15379   {
15380     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15381     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15382     TRUE,                                       "sound loops on/off"
15383   },
15384   {
15385     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15386     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15387     TRUE,                                       "normal sounds on/off"
15388   },
15389   {
15390     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15391     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15392     FALSE,                                      "background music on/off"
15393   },
15394   {
15395     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15396     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15397     FALSE,                                      "sound loops on/off"
15398   },
15399   {
15400     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15401     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15402     FALSE,                                      "normal sounds on/off"
15403   }
15404 };
15405
15406 void CreateGameButtons()
15407 {
15408   int i;
15409
15410   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15411   {
15412     int graphic = gamebutton_info[i].graphic;
15413     struct GraphicInfo *gfx = &graphic_info[graphic];
15414     struct XY *pos = gamebutton_info[i].pos;
15415     struct GadgetInfo *gi;
15416     int button_type;
15417     boolean checked;
15418     unsigned int event_mask;
15419     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15420     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15421     int base_x = (on_tape ? VX : DX);
15422     int base_y = (on_tape ? VY : DY);
15423     int gd_x   = gfx->src_x;
15424     int gd_y   = gfx->src_y;
15425     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15426     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15427     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15428     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15429     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15430     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15431     int id = i;
15432
15433     if (gfx->bitmap == NULL)
15434     {
15435       game_gadget[id] = NULL;
15436
15437       continue;
15438     }
15439
15440     if (id == GAME_CTRL_ID_STOP ||
15441         id == GAME_CTRL_ID_PANEL_STOP ||
15442         id == GAME_CTRL_ID_PLAY ||
15443         id == GAME_CTRL_ID_PANEL_PLAY ||
15444         id == GAME_CTRL_ID_SAVE ||
15445         id == GAME_CTRL_ID_LOAD)
15446     {
15447       button_type = GD_TYPE_NORMAL_BUTTON;
15448       checked = FALSE;
15449       event_mask = GD_EVENT_RELEASED;
15450     }
15451     else if (id == GAME_CTRL_ID_UNDO ||
15452              id == GAME_CTRL_ID_REDO)
15453     {
15454       button_type = GD_TYPE_NORMAL_BUTTON;
15455       checked = FALSE;
15456       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15457     }
15458     else
15459     {
15460       button_type = GD_TYPE_CHECK_BUTTON;
15461       checked = (gamebutton_info[i].setup_value != NULL ?
15462                  *gamebutton_info[i].setup_value : FALSE);
15463       event_mask = GD_EVENT_PRESSED;
15464     }
15465
15466     gi = CreateGadget(GDI_CUSTOM_ID, id,
15467                       GDI_IMAGE_ID, graphic,
15468                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15469                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15470                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15471                       GDI_WIDTH, gfx->width,
15472                       GDI_HEIGHT, gfx->height,
15473                       GDI_TYPE, button_type,
15474                       GDI_STATE, GD_BUTTON_UNPRESSED,
15475                       GDI_CHECKED, checked,
15476                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15477                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15478                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15479                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15480                       GDI_DIRECT_DRAW, FALSE,
15481                       GDI_EVENT_MASK, event_mask,
15482                       GDI_CALLBACK_ACTION, HandleGameButtons,
15483                       GDI_END);
15484
15485     if (gi == NULL)
15486       Error(ERR_EXIT, "cannot create gadget");
15487
15488     game_gadget[id] = gi;
15489   }
15490 }
15491
15492 void FreeGameButtons()
15493 {
15494   int i;
15495
15496   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15497     FreeGadget(game_gadget[i]);
15498 }
15499
15500 static void UnmapGameButtonsAtSamePosition(int id)
15501 {
15502   int i;
15503
15504   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15505     if (i != id &&
15506         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15507         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15508       UnmapGadget(game_gadget[i]);
15509 }
15510
15511 static void UnmapGameButtonsAtSamePosition_All()
15512 {
15513   if (setup.show_snapshot_buttons)
15514   {
15515     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15516     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15517     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15518   }
15519   else
15520   {
15521     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15522     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15523     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15524
15525     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15526     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15527     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15528   }
15529 }
15530
15531 static void MapGameButtonsAtSamePosition(int id)
15532 {
15533   int i;
15534
15535   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15536     if (i != id &&
15537         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15538         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15539       MapGadget(game_gadget[i]);
15540
15541   UnmapGameButtonsAtSamePosition_All();
15542 }
15543
15544 void MapUndoRedoButtons()
15545 {
15546   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15547   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15548
15549   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15550   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15551
15552   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15553 }
15554
15555 void UnmapUndoRedoButtons()
15556 {
15557   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15558   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15559
15560   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15561   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15562
15563   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15564 }
15565
15566 void MapGameButtonsExt(boolean on_tape)
15567 {
15568   int i;
15569
15570   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15571     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15572         i != GAME_CTRL_ID_UNDO &&
15573         i != GAME_CTRL_ID_REDO)
15574       MapGadget(game_gadget[i]);
15575
15576   UnmapGameButtonsAtSamePosition_All();
15577
15578   RedrawGameButtons();
15579 }
15580
15581 void UnmapGameButtonsExt(boolean on_tape)
15582 {
15583   int i;
15584
15585   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15586     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15587       UnmapGadget(game_gadget[i]);
15588 }
15589
15590 void RedrawGameButtonsExt(boolean on_tape)
15591 {
15592   int i;
15593
15594   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15595     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15596       RedrawGadget(game_gadget[i]);
15597
15598   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15599   redraw_mask &= ~REDRAW_ALL;
15600 }
15601
15602 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15603 {
15604   if (gi == NULL)
15605     return;
15606
15607   gi->checked = state;
15608 }
15609
15610 void RedrawSoundButtonGadget(int id)
15611 {
15612   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15613              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15614              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15615              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15616              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15617              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15618              id);
15619
15620   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15621   RedrawGadget(game_gadget[id2]);
15622 }
15623
15624 void MapGameButtons()
15625 {
15626   MapGameButtonsExt(FALSE);
15627 }
15628
15629 void UnmapGameButtons()
15630 {
15631   UnmapGameButtonsExt(FALSE);
15632 }
15633
15634 void RedrawGameButtons()
15635 {
15636   RedrawGameButtonsExt(FALSE);
15637 }
15638
15639 void MapGameButtonsOnTape()
15640 {
15641   MapGameButtonsExt(TRUE);
15642 }
15643
15644 void UnmapGameButtonsOnTape()
15645 {
15646   UnmapGameButtonsExt(TRUE);
15647 }
15648
15649 void RedrawGameButtonsOnTape()
15650 {
15651   RedrawGameButtonsExt(TRUE);
15652 }
15653
15654 void GameUndoRedoExt()
15655 {
15656   ClearPlayerAction();
15657
15658   tape.pausing = TRUE;
15659
15660   RedrawPlayfield();
15661   UpdateAndDisplayGameControlValues();
15662
15663   DrawCompleteVideoDisplay();
15664   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15665   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15666   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15667
15668   BackToFront();
15669 }
15670
15671 void GameUndo(int steps)
15672 {
15673   if (!CheckEngineSnapshotList())
15674     return;
15675
15676   LoadEngineSnapshot_Undo(steps);
15677
15678   GameUndoRedoExt();
15679 }
15680
15681 void GameRedo(int steps)
15682 {
15683   if (!CheckEngineSnapshotList())
15684     return;
15685
15686   LoadEngineSnapshot_Redo(steps);
15687
15688   GameUndoRedoExt();
15689 }
15690
15691 static void HandleGameButtonsExt(int id, int button)
15692 {
15693   static boolean game_undo_executed = FALSE;
15694   int steps = BUTTON_STEPSIZE(button);
15695   boolean handle_game_buttons =
15696     (game_status == GAME_MODE_PLAYING ||
15697      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15698
15699   if (!handle_game_buttons)
15700     return;
15701
15702   switch (id)
15703   {
15704     case GAME_CTRL_ID_STOP:
15705     case GAME_CTRL_ID_PANEL_STOP:
15706       if (game_status == GAME_MODE_MAIN)
15707         break;
15708
15709       if (tape.playing)
15710         TapeStop();
15711       else
15712         RequestQuitGame(TRUE);
15713
15714       break;
15715
15716     case GAME_CTRL_ID_PAUSE:
15717     case GAME_CTRL_ID_PAUSE2:
15718     case GAME_CTRL_ID_PANEL_PAUSE:
15719       if (network.enabled && game_status == GAME_MODE_PLAYING)
15720       {
15721         if (tape.pausing)
15722           SendToServer_ContinuePlaying();
15723         else
15724           SendToServer_PausePlaying();
15725       }
15726       else
15727         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15728
15729       game_undo_executed = FALSE;
15730
15731       break;
15732
15733     case GAME_CTRL_ID_PLAY:
15734     case GAME_CTRL_ID_PANEL_PLAY:
15735       if (game_status == GAME_MODE_MAIN)
15736       {
15737         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15738       }
15739       else if (tape.pausing)
15740       {
15741         if (network.enabled)
15742           SendToServer_ContinuePlaying();
15743         else
15744           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15745       }
15746       break;
15747
15748     case GAME_CTRL_ID_UNDO:
15749       // Important: When using "save snapshot when collecting an item" mode,
15750       // load last (current) snapshot for first "undo" after pressing "pause"
15751       // (else the last-but-one snapshot would be loaded, because the snapshot
15752       // pointer already points to the last snapshot when pressing "pause",
15753       // which is fine for "every step/move" mode, but not for "every collect")
15754       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15755           !game_undo_executed)
15756         steps--;
15757
15758       game_undo_executed = TRUE;
15759
15760       GameUndo(steps);
15761       break;
15762
15763     case GAME_CTRL_ID_REDO:
15764       GameRedo(steps);
15765       break;
15766
15767     case GAME_CTRL_ID_SAVE:
15768       TapeQuickSave();
15769       break;
15770
15771     case GAME_CTRL_ID_LOAD:
15772       TapeQuickLoad();
15773       break;
15774
15775     case SOUND_CTRL_ID_MUSIC:
15776     case SOUND_CTRL_ID_PANEL_MUSIC:
15777       if (setup.sound_music)
15778       { 
15779         setup.sound_music = FALSE;
15780
15781         FadeMusic();
15782       }
15783       else if (audio.music_available)
15784       { 
15785         setup.sound = setup.sound_music = TRUE;
15786
15787         SetAudioMode(setup.sound);
15788
15789         if (game_status == GAME_MODE_PLAYING)
15790           PlayLevelMusic();
15791       }
15792
15793       RedrawSoundButtonGadget(id);
15794
15795       break;
15796
15797     case SOUND_CTRL_ID_LOOPS:
15798     case SOUND_CTRL_ID_PANEL_LOOPS:
15799       if (setup.sound_loops)
15800         setup.sound_loops = FALSE;
15801       else if (audio.loops_available)
15802       {
15803         setup.sound = setup.sound_loops = TRUE;
15804
15805         SetAudioMode(setup.sound);
15806       }
15807
15808       RedrawSoundButtonGadget(id);
15809
15810       break;
15811
15812     case SOUND_CTRL_ID_SIMPLE:
15813     case SOUND_CTRL_ID_PANEL_SIMPLE:
15814       if (setup.sound_simple)
15815         setup.sound_simple = FALSE;
15816       else if (audio.sound_available)
15817       {
15818         setup.sound = setup.sound_simple = TRUE;
15819
15820         SetAudioMode(setup.sound);
15821       }
15822
15823       RedrawSoundButtonGadget(id);
15824
15825       break;
15826
15827     default:
15828       break;
15829   }
15830 }
15831
15832 static void HandleGameButtons(struct GadgetInfo *gi)
15833 {
15834   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15835 }
15836
15837 void HandleSoundButtonKeys(Key key)
15838 {
15839   if (key == setup.shortcut.sound_simple)
15840     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15841   else if (key == setup.shortcut.sound_loops)
15842     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15843   else if (key == setup.shortcut.sound_music)
15844     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15845 }