added counting remaining health to score after solved game (MM engine)
[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 SOUND_CTRL_ID_MUSIC             8
1017 #define SOUND_CTRL_ID_LOOPS             9
1018 #define SOUND_CTRL_ID_SIMPLE            10
1019
1020 #define NUM_GAME_BUTTONS                11
1021
1022
1023 /* forward declaration for internal use */
1024
1025 static void CreateField(int, int, int);
1026
1027 static void ResetGfxAnimation(int, int);
1028
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1031
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1036
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1041
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1048
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1052
1053 static void HandleElementChange(int, int, int);
1054 static void ExecuteCustomElementAction(int, int, int, int);
1055 static boolean ChangeElement(int, int, int, int);
1056
1057 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1058 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1059         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1060 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1061         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1062 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1063         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1064 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1066
1067 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1068 #define CheckElementChange(x, y, e, te, ev)                             \
1069         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1070 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1071         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1072 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1073         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1074
1075 static void PlayLevelSound(int, int, int);
1076 static void PlayLevelSoundNearest(int, int, int);
1077 static void PlayLevelSoundAction(int, int, int);
1078 static void PlayLevelSoundElementAction(int, int, int, int);
1079 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1080 static void PlayLevelSoundActionIfLoop(int, int, int);
1081 static void StopLevelSoundActionIfLoop(int, int, int);
1082 static void PlayLevelMusic();
1083 static void FadeLevelSoundsAndMusic();
1084
1085 static void HandleGameButtons(struct GadgetInfo *);
1086
1087 int AmoebeNachbarNr(int, int);
1088 void AmoebeUmwandeln(int, int);
1089 void ContinueMoving(int, int);
1090 void Bang(int, int);
1091 void InitMovDir(int, int);
1092 void InitAmoebaNr(int, int);
1093 int NewHiScore(void);
1094
1095 void TestIfGoodThingHitsBadThing(int, int, int);
1096 void TestIfBadThingHitsGoodThing(int, int, int);
1097 void TestIfPlayerTouchesBadThing(int, int);
1098 void TestIfPlayerRunsIntoBadThing(int, int, int);
1099 void TestIfBadThingTouchesPlayer(int, int);
1100 void TestIfBadThingRunsIntoPlayer(int, int, int);
1101 void TestIfFriendTouchesBadThing(int, int);
1102 void TestIfBadThingTouchesFriend(int, int);
1103 void TestIfBadThingTouchesOtherBadThing(int, int);
1104 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1105
1106 void KillPlayer(struct PlayerInfo *);
1107 void BuryPlayer(struct PlayerInfo *);
1108 void RemovePlayer(struct PlayerInfo *);
1109
1110 static int getInvisibleActiveFromInvisibleElement(int);
1111 static int getInvisibleFromInvisibleActiveElement(int);
1112
1113 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1114
1115 /* for detection of endless loops, caused by custom element programming */
1116 /* (using maximal playfield width x 10 is just a rough approximation) */
1117 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1118
1119 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1120 {                                                                       \
1121   if (recursion_loop_detected)                                          \
1122     return (rc);                                                        \
1123                                                                         \
1124   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1125   {                                                                     \
1126     recursion_loop_detected = TRUE;                                     \
1127     recursion_loop_element = (e);                                       \
1128   }                                                                     \
1129                                                                         \
1130   recursion_loop_depth++;                                               \
1131 }
1132
1133 #define RECURSION_LOOP_DETECTION_END()                                  \
1134 {                                                                       \
1135   recursion_loop_depth--;                                               \
1136 }
1137
1138 static int recursion_loop_depth;
1139 static boolean recursion_loop_detected;
1140 static boolean recursion_loop_element;
1141
1142 static int map_player_action[MAX_PLAYERS];
1143
1144
1145 /* ------------------------------------------------------------------------- */
1146 /* definition of elements that automatically change to other elements after  */
1147 /* a specified time, eventually calling a function when changing             */
1148 /* ------------------------------------------------------------------------- */
1149
1150 /* forward declaration for changer functions */
1151 static void InitBuggyBase(int, int);
1152 static void WarnBuggyBase(int, int);
1153
1154 static void InitTrap(int, int);
1155 static void ActivateTrap(int, int);
1156 static void ChangeActiveTrap(int, int);
1157
1158 static void InitRobotWheel(int, int);
1159 static void RunRobotWheel(int, int);
1160 static void StopRobotWheel(int, int);
1161
1162 static void InitTimegateWheel(int, int);
1163 static void RunTimegateWheel(int, int);
1164
1165 static void InitMagicBallDelay(int, int);
1166 static void ActivateMagicBall(int, int);
1167
1168 struct ChangingElementInfo
1169 {
1170   int element;
1171   int target_element;
1172   int change_delay;
1173   void (*pre_change_function)(int x, int y);
1174   void (*change_function)(int x, int y);
1175   void (*post_change_function)(int x, int y);
1176 };
1177
1178 static struct ChangingElementInfo change_delay_list[] =
1179 {
1180   {
1181     EL_NUT_BREAKING,
1182     EL_EMERALD,
1183     6,
1184     NULL,
1185     NULL,
1186     NULL
1187   },
1188   {
1189     EL_PEARL_BREAKING,
1190     EL_EMPTY,
1191     8,
1192     NULL,
1193     NULL,
1194     NULL
1195   },
1196   {
1197     EL_EXIT_OPENING,
1198     EL_EXIT_OPEN,
1199     29,
1200     NULL,
1201     NULL,
1202     NULL
1203   },
1204   {
1205     EL_EXIT_CLOSING,
1206     EL_EXIT_CLOSED,
1207     29,
1208     NULL,
1209     NULL,
1210     NULL
1211   },
1212   {
1213     EL_STEEL_EXIT_OPENING,
1214     EL_STEEL_EXIT_OPEN,
1215     29,
1216     NULL,
1217     NULL,
1218     NULL
1219   },
1220   {
1221     EL_STEEL_EXIT_CLOSING,
1222     EL_STEEL_EXIT_CLOSED,
1223     29,
1224     NULL,
1225     NULL,
1226     NULL
1227   },
1228   {
1229     EL_EM_EXIT_OPENING,
1230     EL_EM_EXIT_OPEN,
1231     29,
1232     NULL,
1233     NULL,
1234     NULL
1235   },
1236   {
1237     EL_EM_EXIT_CLOSING,
1238     EL_EMPTY,
1239     29,
1240     NULL,
1241     NULL,
1242     NULL
1243   },
1244   {
1245     EL_EM_STEEL_EXIT_OPENING,
1246     EL_EM_STEEL_EXIT_OPEN,
1247     29,
1248     NULL,
1249     NULL,
1250     NULL
1251   },
1252   {
1253     EL_EM_STEEL_EXIT_CLOSING,
1254     EL_STEELWALL,
1255     29,
1256     NULL,
1257     NULL,
1258     NULL
1259   },
1260   {
1261     EL_SP_EXIT_OPENING,
1262     EL_SP_EXIT_OPEN,
1263     29,
1264     NULL,
1265     NULL,
1266     NULL
1267   },
1268   {
1269     EL_SP_EXIT_CLOSING,
1270     EL_SP_EXIT_CLOSED,
1271     29,
1272     NULL,
1273     NULL,
1274     NULL
1275   },
1276   {
1277     EL_SWITCHGATE_OPENING,
1278     EL_SWITCHGATE_OPEN,
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_SWITCHGATE_CLOSING,
1286     EL_SWITCHGATE_CLOSED,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_TIMEGATE_OPENING,
1294     EL_TIMEGATE_OPEN,
1295     29,
1296     NULL,
1297     NULL,
1298     NULL
1299   },
1300   {
1301     EL_TIMEGATE_CLOSING,
1302     EL_TIMEGATE_CLOSED,
1303     29,
1304     NULL,
1305     NULL,
1306     NULL
1307   },
1308
1309   {
1310     EL_ACID_SPLASH_LEFT,
1311     EL_EMPTY,
1312     8,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317   {
1318     EL_ACID_SPLASH_RIGHT,
1319     EL_EMPTY,
1320     8,
1321     NULL,
1322     NULL,
1323     NULL
1324   },
1325   {
1326     EL_SP_BUGGY_BASE,
1327     EL_SP_BUGGY_BASE_ACTIVATING,
1328     0,
1329     InitBuggyBase,
1330     NULL,
1331     NULL
1332   },
1333   {
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     EL_SP_BUGGY_BASE_ACTIVE,
1336     0,
1337     InitBuggyBase,
1338     NULL,
1339     NULL
1340   },
1341   {
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     EL_SP_BUGGY_BASE,
1344     0,
1345     InitBuggyBase,
1346     WarnBuggyBase,
1347     NULL
1348   },
1349   {
1350     EL_TRAP,
1351     EL_TRAP_ACTIVE,
1352     0,
1353     InitTrap,
1354     NULL,
1355     ActivateTrap
1356   },
1357   {
1358     EL_TRAP_ACTIVE,
1359     EL_TRAP,
1360     31,
1361     NULL,
1362     ChangeActiveTrap,
1363     NULL
1364   },
1365   {
1366     EL_ROBOT_WHEEL_ACTIVE,
1367     EL_ROBOT_WHEEL,
1368     0,
1369     InitRobotWheel,
1370     RunRobotWheel,
1371     StopRobotWheel
1372   },
1373   {
1374     EL_TIMEGATE_SWITCH_ACTIVE,
1375     EL_TIMEGATE_SWITCH,
1376     0,
1377     InitTimegateWheel,
1378     RunTimegateWheel,
1379     NULL
1380   },
1381   {
1382     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1383     EL_DC_TIMEGATE_SWITCH,
1384     0,
1385     InitTimegateWheel,
1386     RunTimegateWheel,
1387     NULL
1388   },
1389   {
1390     EL_EMC_MAGIC_BALL_ACTIVE,
1391     EL_EMC_MAGIC_BALL_ACTIVE,
1392     0,
1393     InitMagicBallDelay,
1394     NULL,
1395     ActivateMagicBall
1396   },
1397   {
1398     EL_EMC_SPRING_BUMPER_ACTIVE,
1399     EL_EMC_SPRING_BUMPER,
1400     8,
1401     NULL,
1402     NULL,
1403     NULL
1404   },
1405   {
1406     EL_DIAGONAL_SHRINKING,
1407     EL_UNDEFINED,
1408     0,
1409     NULL,
1410     NULL,
1411     NULL
1412   },
1413   {
1414     EL_DIAGONAL_GROWING,
1415     EL_UNDEFINED,
1416     0,
1417     NULL,
1418     NULL,
1419     NULL,
1420   },
1421
1422   {
1423     EL_UNDEFINED,
1424     EL_UNDEFINED,
1425     -1,
1426     NULL,
1427     NULL,
1428     NULL
1429   }
1430 };
1431
1432 struct
1433 {
1434   int element;
1435   int push_delay_fixed, push_delay_random;
1436 }
1437 push_delay_list[] =
1438 {
1439   { EL_SPRING,                  0, 0 },
1440   { EL_BALLOON,                 0, 0 },
1441
1442   { EL_SOKOBAN_OBJECT,          2, 0 },
1443   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1444   { EL_SATELLITE,               2, 0 },
1445   { EL_SP_DISK_YELLOW,          2, 0 },
1446
1447   { EL_UNDEFINED,               0, 0 },
1448 };
1449
1450 struct
1451 {
1452   int element;
1453   int move_stepsize;
1454 }
1455 move_stepsize_list[] =
1456 {
1457   { EL_AMOEBA_DROP,             2 },
1458   { EL_AMOEBA_DROPPING,         2 },
1459   { EL_QUICKSAND_FILLING,       1 },
1460   { EL_QUICKSAND_EMPTYING,      1 },
1461   { EL_QUICKSAND_FAST_FILLING,  2 },
1462   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1463   { EL_MAGIC_WALL_FILLING,      2 },
1464   { EL_MAGIC_WALL_EMPTYING,     2 },
1465   { EL_BD_MAGIC_WALL_FILLING,   2 },
1466   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1467   { EL_DC_MAGIC_WALL_FILLING,   2 },
1468   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1469
1470   { EL_UNDEFINED,               0 },
1471 };
1472
1473 struct
1474 {
1475   int element;
1476   int count;
1477 }
1478 collect_count_list[] =
1479 {
1480   { EL_EMERALD,                 1 },
1481   { EL_BD_DIAMOND,              1 },
1482   { EL_EMERALD_YELLOW,          1 },
1483   { EL_EMERALD_RED,             1 },
1484   { EL_EMERALD_PURPLE,          1 },
1485   { EL_DIAMOND,                 3 },
1486   { EL_SP_INFOTRON,             1 },
1487   { EL_PEARL,                   5 },
1488   { EL_CRYSTAL,                 8 },
1489
1490   { EL_UNDEFINED,               0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int direction;
1497 }
1498 access_direction_list[] =
1499 {
1500   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1501   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1502   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1503   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1504   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1505   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1506   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1507   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1508   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1509   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1510   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1511
1512   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1513   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1514   { EL_SP_PORT_UP,                                                   MV_DOWN },
1515   { EL_SP_PORT_DOWN,                                         MV_UP           },
1516   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1517   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1518   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1519   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1520   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1521   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1522   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1523   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1524   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1525   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1526   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1527   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1528   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1529   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1530   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1531
1532   { EL_UNDEFINED,                       MV_NONE                              }
1533 };
1534
1535 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1536
1537 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1538 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1539 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1540                                  IS_JUST_CHANGING(x, y))
1541
1542 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1543
1544 /* static variables for playfield scan mode (scanning forward or backward) */
1545 static int playfield_scan_start_x = 0;
1546 static int playfield_scan_start_y = 0;
1547 static int playfield_scan_delta_x = 1;
1548 static int playfield_scan_delta_y = 1;
1549
1550 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1551                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1552                                      (y) += playfield_scan_delta_y)     \
1553                                 for ((x) = playfield_scan_start_x;      \
1554                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1555                                      (x) += playfield_scan_delta_x)
1556
1557 #ifdef DEBUG
1558 void DEBUG_SetMaximumDynamite()
1559 {
1560   int i;
1561
1562   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1563     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1564       local_player->inventory_element[local_player->inventory_size++] =
1565         EL_DYNAMITE;
1566 }
1567 #endif
1568
1569 static void InitPlayfieldScanModeVars()
1570 {
1571   if (game.use_reverse_scan_direction)
1572   {
1573     playfield_scan_start_x = lev_fieldx - 1;
1574     playfield_scan_start_y = lev_fieldy - 1;
1575
1576     playfield_scan_delta_x = -1;
1577     playfield_scan_delta_y = -1;
1578   }
1579   else
1580   {
1581     playfield_scan_start_x = 0;
1582     playfield_scan_start_y = 0;
1583
1584     playfield_scan_delta_x = 1;
1585     playfield_scan_delta_y = 1;
1586   }
1587 }
1588
1589 static void InitPlayfieldScanMode(int mode)
1590 {
1591   game.use_reverse_scan_direction =
1592     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1593
1594   InitPlayfieldScanModeVars();
1595 }
1596
1597 static int get_move_delay_from_stepsize(int move_stepsize)
1598 {
1599   move_stepsize =
1600     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1601
1602   /* make sure that stepsize value is always a power of 2 */
1603   move_stepsize = (1 << log_2(move_stepsize));
1604
1605   return TILEX / move_stepsize;
1606 }
1607
1608 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1609                                boolean init_game)
1610 {
1611   int player_nr = player->index_nr;
1612   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1613   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1614
1615   /* do no immediately change move delay -- the player might just be moving */
1616   player->move_delay_value_next = move_delay;
1617
1618   /* information if player can move must be set separately */
1619   player->cannot_move = cannot_move;
1620
1621   if (init_game)
1622   {
1623     player->move_delay       = game.initial_move_delay[player_nr];
1624     player->move_delay_value = game.initial_move_delay_value[player_nr];
1625
1626     player->move_delay_value_next = -1;
1627
1628     player->move_delay_reset_counter = 0;
1629   }
1630 }
1631
1632 void GetPlayerConfig()
1633 {
1634   GameFrameDelay = setup.game_frame_delay;
1635
1636   if (!audio.sound_available)
1637     setup.sound_simple = FALSE;
1638
1639   if (!audio.loops_available)
1640     setup.sound_loops = FALSE;
1641
1642   if (!audio.music_available)
1643     setup.sound_music = FALSE;
1644
1645   if (!video.fullscreen_available)
1646     setup.fullscreen = FALSE;
1647
1648   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1649
1650   SetAudioMode(setup.sound);
1651 }
1652
1653 int GetElementFromGroupElement(int element)
1654 {
1655   if (IS_GROUP_ELEMENT(element))
1656   {
1657     struct ElementGroupInfo *group = element_info[element].group;
1658     int last_anim_random_frame = gfx.anim_random_frame;
1659     int element_pos;
1660
1661     if (group->choice_mode == ANIM_RANDOM)
1662       gfx.anim_random_frame = RND(group->num_elements_resolved);
1663
1664     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1665                                     group->choice_mode, 0,
1666                                     group->choice_pos);
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = last_anim_random_frame;
1670
1671     group->choice_pos++;
1672
1673     element = group->element_resolved[element_pos];
1674   }
1675
1676   return element;
1677 }
1678
1679 static void InitPlayerField(int x, int y, int element, boolean init_game)
1680 {
1681   if (element == EL_SP_MURPHY)
1682   {
1683     if (init_game)
1684     {
1685       if (stored_player[0].present)
1686       {
1687         Feld[x][y] = EL_SP_MURPHY_CLONE;
1688
1689         return;
1690       }
1691       else
1692       {
1693         stored_player[0].initial_element = element;
1694         stored_player[0].use_murphy = TRUE;
1695
1696         if (!level.use_artwork_element[0])
1697           stored_player[0].artwork_element = EL_SP_MURPHY;
1698       }
1699
1700       Feld[x][y] = EL_PLAYER_1;
1701     }
1702   }
1703
1704   if (init_game)
1705   {
1706     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1707     int jx = player->jx, jy = player->jy;
1708
1709     player->present = TRUE;
1710
1711     player->block_last_field = (element == EL_SP_MURPHY ?
1712                                 level.sp_block_last_field :
1713                                 level.block_last_field);
1714
1715     /* ---------- initialize player's last field block delay --------------- */
1716
1717     /* always start with reliable default value (no adjustment needed) */
1718     player->block_delay_adjustment = 0;
1719
1720     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1721     if (player->block_last_field && element == EL_SP_MURPHY)
1722       player->block_delay_adjustment = 1;
1723
1724     /* special case 2: in game engines before 3.1.1, blocking was different */
1725     if (game.use_block_last_field_bug)
1726       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1727
1728     if (!options.network || player->connected)
1729     {
1730       player->active = TRUE;
1731
1732       /* remove potentially duplicate players */
1733       if (StorePlayer[jx][jy] == Feld[x][y])
1734         StorePlayer[jx][jy] = 0;
1735
1736       StorePlayer[x][y] = Feld[x][y];
1737
1738 #if DEBUG_INIT_PLAYER
1739       if (options.debug)
1740       {
1741         printf("- player element %d activated", player->element_nr);
1742         printf(" (local player is %d and currently %s)\n",
1743                local_player->element_nr,
1744                local_player->active ? "active" : "not active");
1745       }
1746     }
1747 #endif
1748
1749     Feld[x][y] = EL_EMPTY;
1750
1751     player->jx = player->last_jx = x;
1752     player->jy = player->last_jy = y;
1753   }
1754
1755   if (!init_game)
1756   {
1757     int player_nr = GET_PLAYER_NR(element);
1758     struct PlayerInfo *player = &stored_player[player_nr];
1759
1760     if (player->active && player->killed)
1761       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1762   }
1763 }
1764
1765 static void InitField(int x, int y, boolean init_game)
1766 {
1767   int element = Feld[x][y];
1768
1769   switch (element)
1770   {
1771     case EL_SP_MURPHY:
1772     case EL_PLAYER_1:
1773     case EL_PLAYER_2:
1774     case EL_PLAYER_3:
1775     case EL_PLAYER_4:
1776       InitPlayerField(x, y, element, init_game);
1777       break;
1778
1779     case EL_SOKOBAN_FIELD_PLAYER:
1780       element = Feld[x][y] = EL_PLAYER_1;
1781       InitField(x, y, init_game);
1782
1783       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1784       InitField(x, y, init_game);
1785       break;
1786
1787     case EL_SOKOBAN_FIELD_EMPTY:
1788       local_player->sokobanfields_still_needed++;
1789       break;
1790
1791     case EL_STONEBLOCK:
1792       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1793         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1794       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1795         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1796       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1797         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1798       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1800       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1801         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1802       break;
1803
1804     case EL_BUG:
1805     case EL_BUG_RIGHT:
1806     case EL_BUG_UP:
1807     case EL_BUG_LEFT:
1808     case EL_BUG_DOWN:
1809     case EL_SPACESHIP:
1810     case EL_SPACESHIP_RIGHT:
1811     case EL_SPACESHIP_UP:
1812     case EL_SPACESHIP_LEFT:
1813     case EL_SPACESHIP_DOWN:
1814     case EL_BD_BUTTERFLY:
1815     case EL_BD_BUTTERFLY_RIGHT:
1816     case EL_BD_BUTTERFLY_UP:
1817     case EL_BD_BUTTERFLY_LEFT:
1818     case EL_BD_BUTTERFLY_DOWN:
1819     case EL_BD_FIREFLY:
1820     case EL_BD_FIREFLY_RIGHT:
1821     case EL_BD_FIREFLY_UP:
1822     case EL_BD_FIREFLY_LEFT:
1823     case EL_BD_FIREFLY_DOWN:
1824     case EL_PACMAN_RIGHT:
1825     case EL_PACMAN_UP:
1826     case EL_PACMAN_LEFT:
1827     case EL_PACMAN_DOWN:
1828     case EL_YAMYAM:
1829     case EL_YAMYAM_LEFT:
1830     case EL_YAMYAM_RIGHT:
1831     case EL_YAMYAM_UP:
1832     case EL_YAMYAM_DOWN:
1833     case EL_DARK_YAMYAM:
1834     case EL_ROBOT:
1835     case EL_PACMAN:
1836     case EL_SP_SNIKSNAK:
1837     case EL_SP_ELECTRON:
1838     case EL_MOLE:
1839     case EL_MOLE_LEFT:
1840     case EL_MOLE_RIGHT:
1841     case EL_MOLE_UP:
1842     case EL_MOLE_DOWN:
1843       InitMovDir(x, y);
1844       break;
1845
1846     case EL_AMOEBA_FULL:
1847     case EL_BD_AMOEBA:
1848       InitAmoebaNr(x, y);
1849       break;
1850
1851     case EL_AMOEBA_DROP:
1852       if (y == lev_fieldy - 1)
1853       {
1854         Feld[x][y] = EL_AMOEBA_GROWING;
1855         Store[x][y] = EL_AMOEBA_WET;
1856       }
1857       break;
1858
1859     case EL_DYNAMITE_ACTIVE:
1860     case EL_SP_DISK_RED_ACTIVE:
1861     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1862     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1863     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1864     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1865       MovDelay[x][y] = 96;
1866       break;
1867
1868     case EL_EM_DYNAMITE_ACTIVE:
1869       MovDelay[x][y] = 32;
1870       break;
1871
1872     case EL_LAMP:
1873       local_player->lights_still_needed++;
1874       break;
1875
1876     case EL_PENGUIN:
1877       local_player->friends_still_needed++;
1878       break;
1879
1880     case EL_PIG:
1881     case EL_DRAGON:
1882       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1883       break;
1884
1885     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1886     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1887     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1888     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1889     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1890     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1891     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1897       if (init_game)
1898       {
1899         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1900         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1901         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1902
1903         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1904         {
1905           game.belt_dir[belt_nr] = belt_dir;
1906           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1907         }
1908         else    /* more than one switch -- set it like the first switch */
1909         {
1910           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1911         }
1912       }
1913       break;
1914
1915     case EL_LIGHT_SWITCH_ACTIVE:
1916       if (init_game)
1917         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1918       break;
1919
1920     case EL_INVISIBLE_STEELWALL:
1921     case EL_INVISIBLE_WALL:
1922     case EL_INVISIBLE_SAND:
1923       if (game.light_time_left > 0 ||
1924           game.lenses_time_left > 0)
1925         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1926       break;
1927
1928     case EL_EMC_MAGIC_BALL:
1929       if (game.ball_state)
1930         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1931       break;
1932
1933     case EL_EMC_MAGIC_BALL_SWITCH:
1934       if (game.ball_state)
1935         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1936       break;
1937
1938     case EL_TRIGGER_PLAYER:
1939     case EL_TRIGGER_ELEMENT:
1940     case EL_TRIGGER_CE_VALUE:
1941     case EL_TRIGGER_CE_SCORE:
1942     case EL_SELF:
1943     case EL_ANY_ELEMENT:
1944     case EL_CURRENT_CE_VALUE:
1945     case EL_CURRENT_CE_SCORE:
1946     case EL_PREV_CE_1:
1947     case EL_PREV_CE_2:
1948     case EL_PREV_CE_3:
1949     case EL_PREV_CE_4:
1950     case EL_PREV_CE_5:
1951     case EL_PREV_CE_6:
1952     case EL_PREV_CE_7:
1953     case EL_PREV_CE_8:
1954     case EL_NEXT_CE_1:
1955     case EL_NEXT_CE_2:
1956     case EL_NEXT_CE_3:
1957     case EL_NEXT_CE_4:
1958     case EL_NEXT_CE_5:
1959     case EL_NEXT_CE_6:
1960     case EL_NEXT_CE_7:
1961     case EL_NEXT_CE_8:
1962       /* reference elements should not be used on the playfield */
1963       Feld[x][y] = EL_EMPTY;
1964       break;
1965
1966     default:
1967       if (IS_CUSTOM_ELEMENT(element))
1968       {
1969         if (CAN_MOVE(element))
1970           InitMovDir(x, y);
1971
1972         if (!element_info[element].use_last_ce_value || init_game)
1973           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1974       }
1975       else if (IS_GROUP_ELEMENT(element))
1976       {
1977         Feld[x][y] = GetElementFromGroupElement(element);
1978
1979         InitField(x, y, init_game);
1980       }
1981
1982       break;
1983   }
1984
1985   if (!init_game)
1986     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1987 }
1988
1989 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1990 {
1991   InitField(x, y, init_game);
1992
1993   /* not needed to call InitMovDir() -- already done by InitField()! */
1994   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1995       CAN_MOVE(Feld[x][y]))
1996     InitMovDir(x, y);
1997 }
1998
1999 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2000 {
2001   int old_element = Feld[x][y];
2002
2003   InitField(x, y, init_game);
2004
2005   /* not needed to call InitMovDir() -- already done by InitField()! */
2006   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2007       CAN_MOVE(old_element) &&
2008       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2009     InitMovDir(x, y);
2010
2011   /* this case is in fact a combination of not less than three bugs:
2012      first, it calls InitMovDir() for elements that can move, although this is
2013      already done by InitField(); then, it checks the element that was at this
2014      field _before_ the call to InitField() (which can change it); lastly, it
2015      was not called for "mole with direction" elements, which were treated as
2016      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2017   */
2018 }
2019
2020 static int get_key_element_from_nr(int key_nr)
2021 {
2022   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2023                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2024                           EL_EM_KEY_1 : EL_KEY_1);
2025
2026   return key_base_element + key_nr;
2027 }
2028
2029 static int get_next_dropped_element(struct PlayerInfo *player)
2030 {
2031   return (player->inventory_size > 0 ?
2032           player->inventory_element[player->inventory_size - 1] :
2033           player->inventory_infinite_element != EL_UNDEFINED ?
2034           player->inventory_infinite_element :
2035           player->dynabombs_left > 0 ?
2036           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2037           EL_UNDEFINED);
2038 }
2039
2040 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2041 {
2042   /* pos >= 0: get element from bottom of the stack;
2043      pos <  0: get element from top of the stack */
2044
2045   if (pos < 0)
2046   {
2047     int min_inventory_size = -pos;
2048     int inventory_pos = player->inventory_size - min_inventory_size;
2049     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2050
2051     return (player->inventory_size >= min_inventory_size ?
2052             player->inventory_element[inventory_pos] :
2053             player->inventory_infinite_element != EL_UNDEFINED ?
2054             player->inventory_infinite_element :
2055             player->dynabombs_left >= min_dynabombs_left ?
2056             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2057             EL_UNDEFINED);
2058   }
2059   else
2060   {
2061     int min_dynabombs_left = pos + 1;
2062     int min_inventory_size = pos + 1 - player->dynabombs_left;
2063     int inventory_pos = pos - player->dynabombs_left;
2064
2065     return (player->inventory_infinite_element != EL_UNDEFINED ?
2066             player->inventory_infinite_element :
2067             player->dynabombs_left >= min_dynabombs_left ?
2068             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2069             player->inventory_size >= min_inventory_size ?
2070             player->inventory_element[inventory_pos] :
2071             EL_UNDEFINED);
2072   }
2073 }
2074
2075 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2076 {
2077   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2078   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2079   int compare_result;
2080
2081   if (gpo1->sort_priority != gpo2->sort_priority)
2082     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2083   else
2084     compare_result = gpo1->nr - gpo2->nr;
2085
2086   return compare_result;
2087 }
2088
2089 int getPlayerInventorySize(int player_nr)
2090 {
2091   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2092     return level.native_em_level->ply[player_nr]->dynamite;
2093   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2094     return level.native_sp_level->game_sp->red_disk_count;
2095   else
2096     return stored_player[player_nr].inventory_size;
2097 }
2098
2099 void InitGameControlValues()
2100 {
2101   int i;
2102
2103   for (i = 0; game_panel_controls[i].nr != -1; i++)
2104   {
2105     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2106     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2107     struct TextPosInfo *pos = gpc->pos;
2108     int nr = gpc->nr;
2109     int type = gpc->type;
2110
2111     if (nr != i)
2112     {
2113       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2114       Error(ERR_EXIT, "this should not happen -- please debug");
2115     }
2116
2117     /* force update of game controls after initialization */
2118     gpc->value = gpc->last_value = -1;
2119     gpc->frame = gpc->last_frame = -1;
2120     gpc->gfx_frame = -1;
2121
2122     /* determine panel value width for later calculation of alignment */
2123     if (type == TYPE_INTEGER || type == TYPE_STRING)
2124     {
2125       pos->width = pos->size * getFontWidth(pos->font);
2126       pos->height = getFontHeight(pos->font);
2127     }
2128     else if (type == TYPE_ELEMENT)
2129     {
2130       pos->width = pos->size;
2131       pos->height = pos->size;
2132     }
2133
2134     /* fill structure for game panel draw order */
2135     gpo->nr = gpc->nr;
2136     gpo->sort_priority = pos->sort_priority;
2137   }
2138
2139   /* sort game panel controls according to sort_priority and control number */
2140   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2141         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2142 }
2143
2144 void UpdatePlayfieldElementCount()
2145 {
2146   boolean use_element_count = FALSE;
2147   int i, j, x, y;
2148
2149   /* first check if it is needed at all to calculate playfield element count */
2150   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2151     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2152       use_element_count = TRUE;
2153
2154   if (!use_element_count)
2155     return;
2156
2157   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2158     element_info[i].element_count = 0;
2159
2160   SCAN_PLAYFIELD(x, y)
2161   {
2162     element_info[Feld[x][y]].element_count++;
2163   }
2164
2165   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2166     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2167       if (IS_IN_GROUP(j, i))
2168         element_info[EL_GROUP_START + i].element_count +=
2169           element_info[j].element_count;
2170 }
2171
2172 void UpdateGameControlValues()
2173 {
2174   int i, k;
2175   int time = (local_player->LevelSolved ?
2176               local_player->LevelSolved_CountingTime :
2177               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2178               level.native_em_level->lev->time :
2179               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2180               level.native_sp_level->game_sp->time_played :
2181               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2182               game_mm.energy_left :
2183               game.no_time_limit ? TimePlayed : TimeLeft);
2184   int score = (local_player->LevelSolved ?
2185                local_player->LevelSolved_CountingScore :
2186                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187                level.native_em_level->lev->score :
2188                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189                level.native_sp_level->game_sp->score :
2190                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2191                game_mm.score :
2192                local_player->score);
2193   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194               level.native_em_level->lev->required :
2195               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196               level.native_sp_level->game_sp->infotrons_still_needed :
2197               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198               game_mm.kettles_still_needed :
2199               local_player->gems_still_needed);
2200   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201                      level.native_em_level->lev->required > 0 :
2202                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2204                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205                      game_mm.kettles_still_needed > 0 ||
2206                      game_mm.lights_still_needed > 0 :
2207                      local_player->gems_still_needed > 0 ||
2208                      local_player->sokobanfields_still_needed > 0 ||
2209                      local_player->lights_still_needed > 0);
2210   int health = (local_player->LevelSolved ?
2211                 local_player->LevelSolved_CountingHealth :
2212                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2213                 MM_HEALTH(game_mm.laser_overload_value) :
2214                 local_player->health);
2215
2216   UpdatePlayfieldElementCount();
2217
2218   /* update game panel control values */
2219
2220   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2221   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2222
2223   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2224   for (i = 0; i < MAX_NUM_KEYS; i++)
2225     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2226   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2227   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2228
2229   if (game.centered_player_nr == -1)
2230   {
2231     for (i = 0; i < MAX_PLAYERS; i++)
2232     {
2233       /* only one player in Supaplex game engine */
2234       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2235         break;
2236
2237       for (k = 0; k < MAX_NUM_KEYS; k++)
2238       {
2239         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2240         {
2241           if (level.native_em_level->ply[i]->keys & (1 << k))
2242             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2243               get_key_element_from_nr(k);
2244         }
2245         else if (stored_player[i].key[k])
2246           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247             get_key_element_from_nr(k);
2248       }
2249
2250       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2251         getPlayerInventorySize(i);
2252
2253       if (stored_player[i].num_white_keys > 0)
2254         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2255           EL_DC_KEY_WHITE;
2256
2257       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2258         stored_player[i].num_white_keys;
2259     }
2260   }
2261   else
2262   {
2263     int player_nr = game.centered_player_nr;
2264
2265     for (k = 0; k < MAX_NUM_KEYS; k++)
2266     {
2267       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2268       {
2269         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2270           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2271             get_key_element_from_nr(k);
2272       }
2273       else if (stored_player[player_nr].key[k])
2274         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2275           get_key_element_from_nr(k);
2276     }
2277
2278     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2279       getPlayerInventorySize(player_nr);
2280
2281     if (stored_player[player_nr].num_white_keys > 0)
2282       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2283
2284     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2285       stored_player[player_nr].num_white_keys;
2286   }
2287
2288   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2289   {
2290     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2291       get_inventory_element_from_pos(local_player, i);
2292     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2293       get_inventory_element_from_pos(local_player, -i - 1);
2294   }
2295
2296   game_panel_controls[GAME_PANEL_SCORE].value = score;
2297   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2298
2299   game_panel_controls[GAME_PANEL_TIME].value = time;
2300
2301   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2302   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2303   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2304
2305   if (game.no_time_limit)
2306     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2307   else
2308     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2309
2310   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2311   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2312
2313   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2314
2315   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2316     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2317      EL_EMPTY);
2318   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2319     local_player->shield_normal_time_left;
2320   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2321     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2322      EL_EMPTY);
2323   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2324     local_player->shield_deadly_time_left;
2325
2326   game_panel_controls[GAME_PANEL_EXIT].value =
2327     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2328
2329   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2330     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2331   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2332     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2333      EL_EMC_MAGIC_BALL_SWITCH);
2334
2335   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2336     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2337   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2338     game.light_time_left;
2339
2340   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2341     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2342   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2343     game.timegate_time_left;
2344
2345   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2346     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2347
2348   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2349     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2350   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2351     game.lenses_time_left;
2352
2353   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2354     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2356     game.magnify_time_left;
2357
2358   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2359     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2360      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2361      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2362      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2363      EL_BALLOON_SWITCH_NONE);
2364
2365   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2366     local_player->dynabomb_count;
2367   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2368     local_player->dynabomb_size;
2369   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2370     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2371
2372   game_panel_controls[GAME_PANEL_PENGUINS].value =
2373     local_player->friends_still_needed;
2374
2375   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2376     local_player->sokobanfields_still_needed;
2377   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2378     local_player->sokobanfields_still_needed;
2379
2380   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2381     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2382
2383   for (i = 0; i < NUM_BELTS; i++)
2384   {
2385     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2386       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2387        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2388     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2389       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2390   }
2391
2392   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2393     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2394   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2395     game.magic_wall_time_left;
2396
2397   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2398     local_player->gravity;
2399
2400   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2401     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2402
2403   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2405       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2406        game.panel.element[i].id : EL_UNDEFINED);
2407
2408   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2410       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2411        element_info[game.panel.element_count[i].id].element_count : 0);
2412
2413   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2415       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2416        element_info[game.panel.ce_score[i].id].collect_score : 0);
2417
2418   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2420       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2421        element_info[game.panel.ce_score_element[i].id].collect_score :
2422        EL_UNDEFINED);
2423
2424   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2425   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2426   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2427
2428   /* update game panel control frames */
2429
2430   for (i = 0; game_panel_controls[i].nr != -1; i++)
2431   {
2432     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2433
2434     if (gpc->type == TYPE_ELEMENT)
2435     {
2436       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2437       {
2438         int last_anim_random_frame = gfx.anim_random_frame;
2439         int element = gpc->value;
2440         int graphic = el2panelimg(element);
2441
2442         if (gpc->value != gpc->last_value)
2443         {
2444           gpc->gfx_frame = 0;
2445           gpc->gfx_random = INIT_GFX_RANDOM();
2446         }
2447         else
2448         {
2449           gpc->gfx_frame++;
2450
2451           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2452               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2453             gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455
2456         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2457           gfx.anim_random_frame = gpc->gfx_random;
2458
2459         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2460           gpc->gfx_frame = element_info[element].collect_score;
2461
2462         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2463                                               gpc->gfx_frame);
2464
2465         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2466           gfx.anim_random_frame = last_anim_random_frame;
2467       }
2468     }
2469     else if (gpc->type == TYPE_GRAPHIC)
2470     {
2471       if (gpc->graphic != IMG_UNDEFINED)
2472       {
2473         int last_anim_random_frame = gfx.anim_random_frame;
2474         int graphic = gpc->graphic;
2475
2476         if (gpc->value != gpc->last_value)
2477         {
2478           gpc->gfx_frame = 0;
2479           gpc->gfx_random = INIT_GFX_RANDOM();
2480         }
2481         else
2482         {
2483           gpc->gfx_frame++;
2484
2485           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2486               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2487             gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489
2490         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491           gfx.anim_random_frame = gpc->gfx_random;
2492
2493         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = last_anim_random_frame;
2497       }
2498     }
2499   }
2500 }
2501
2502 void DisplayGameControlValues()
2503 {
2504   boolean redraw_panel = FALSE;
2505   int i;
2506
2507   for (i = 0; game_panel_controls[i].nr != -1; i++)
2508   {
2509     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2510
2511     if (PANEL_DEACTIVATED(gpc->pos))
2512       continue;
2513
2514     if (gpc->value == gpc->last_value &&
2515         gpc->frame == gpc->last_frame)
2516       continue;
2517
2518     redraw_panel = TRUE;
2519   }
2520
2521   if (!redraw_panel)
2522     return;
2523
2524   /* copy default game door content to main double buffer */
2525
2526   /* !!! CHECK AGAIN !!! */
2527   SetPanelBackground();
2528   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2529   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2530
2531   /* redraw game control buttons */
2532   RedrawGameButtons();
2533
2534   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2535
2536   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2537   {
2538     int nr = game_panel_order[i].nr;
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2540     struct TextPosInfo *pos = gpc->pos;
2541     int type = gpc->type;
2542     int value = gpc->value;
2543     int frame = gpc->frame;
2544     int size = pos->size;
2545     int font = pos->font;
2546     boolean draw_masked = pos->draw_masked;
2547     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2548
2549     if (PANEL_DEACTIVATED(pos))
2550       continue;
2551
2552     gpc->last_value = value;
2553     gpc->last_frame = frame;
2554
2555     if (type == TYPE_INTEGER)
2556     {
2557       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2558           nr == GAME_PANEL_TIME)
2559       {
2560         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2561
2562         if (use_dynamic_size)           /* use dynamic number of digits */
2563         {
2564           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2565           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2566           int size2 = size1 + 1;
2567           int font1 = pos->font;
2568           int font2 = pos->font_alt;
2569
2570           size = (value < value_change ? size1 : size2);
2571           font = (value < value_change ? font1 : font2);
2572         }
2573       }
2574
2575       /* correct text size if "digits" is zero or less */
2576       if (size <= 0)
2577         size = strlen(int2str(value, size));
2578
2579       /* dynamically correct text alignment */
2580       pos->width = size * getFontWidth(font);
2581
2582       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2583                   int2str(value, size), font, mask_mode);
2584     }
2585     else if (type == TYPE_ELEMENT)
2586     {
2587       int element, graphic;
2588       Bitmap *src_bitmap;
2589       int src_x, src_y;
2590       int width, height;
2591       int dst_x = PANEL_XPOS(pos);
2592       int dst_y = PANEL_YPOS(pos);
2593
2594       if (value != EL_UNDEFINED && value != EL_EMPTY)
2595       {
2596         element = value;
2597         graphic = el2panelimg(value);
2598
2599         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2600
2601         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2602           size = TILESIZE;
2603
2604         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2605                               &src_x, &src_y);
2606
2607         width  = graphic_info[graphic].width  * size / TILESIZE;
2608         height = graphic_info[graphic].height * size / TILESIZE;
2609
2610         if (draw_masked)
2611           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2612                            dst_x, dst_y);
2613         else
2614           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2615                      dst_x, dst_y);
2616       }
2617     }
2618     else if (type == TYPE_GRAPHIC)
2619     {
2620       int graphic        = gpc->graphic;
2621       int graphic_active = gpc->graphic_active;
2622       Bitmap *src_bitmap;
2623       int src_x, src_y;
2624       int width, height;
2625       int dst_x = PANEL_XPOS(pos);
2626       int dst_y = PANEL_YPOS(pos);
2627       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2628                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2629
2630       if (graphic != IMG_UNDEFINED && !skip)
2631       {
2632         if (pos->style == STYLE_REVERSE)
2633           value = 100 - value;
2634
2635         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2636
2637         if (pos->direction & MV_HORIZONTAL)
2638         {
2639           width  = graphic_info[graphic_active].width * value / 100;
2640           height = graphic_info[graphic_active].height;
2641
2642           if (pos->direction == MV_LEFT)
2643           {
2644             src_x += graphic_info[graphic_active].width - width;
2645             dst_x += graphic_info[graphic_active].width - width;
2646           }
2647         }
2648         else
2649         {
2650           width  = graphic_info[graphic_active].width;
2651           height = graphic_info[graphic_active].height * value / 100;
2652
2653           if (pos->direction == MV_UP)
2654           {
2655             src_y += graphic_info[graphic_active].height - height;
2656             dst_y += graphic_info[graphic_active].height - height;
2657           }
2658         }
2659
2660         if (draw_masked)
2661           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2662                            dst_x, dst_y);
2663         else
2664           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2665                      dst_x, dst_y);
2666
2667         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2668
2669         if (pos->direction & MV_HORIZONTAL)
2670         {
2671           if (pos->direction == MV_RIGHT)
2672           {
2673             src_x += width;
2674             dst_x += width;
2675           }
2676           else
2677           {
2678             dst_x = PANEL_XPOS(pos);
2679           }
2680
2681           width = graphic_info[graphic].width - width;
2682         }
2683         else
2684         {
2685           if (pos->direction == MV_DOWN)
2686           {
2687             src_y += height;
2688             dst_y += height;
2689           }
2690           else
2691           {
2692             dst_y = PANEL_YPOS(pos);
2693           }
2694
2695           height = graphic_info[graphic].height - height;
2696         }
2697
2698         if (draw_masked)
2699           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2700                            dst_x, dst_y);
2701         else
2702           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2703                      dst_x, dst_y);
2704       }
2705     }
2706     else if (type == TYPE_STRING)
2707     {
2708       boolean active = (value != 0);
2709       char *state_normal = "off";
2710       char *state_active = "on";
2711       char *state = (active ? state_active : state_normal);
2712       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2713                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2714                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2715                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2716
2717       if (nr == GAME_PANEL_GRAVITY_STATE)
2718       {
2719         int font1 = pos->font;          /* (used for normal state) */
2720         int font2 = pos->font_alt;      /* (used for active state) */
2721
2722         font = (active ? font2 : font1);
2723       }
2724
2725       if (s != NULL)
2726       {
2727         char *s_cut;
2728
2729         if (size <= 0)
2730         {
2731           /* don't truncate output if "chars" is zero or less */
2732           size = strlen(s);
2733
2734           /* dynamically correct text alignment */
2735           pos->width = size * getFontWidth(font);
2736         }
2737
2738         s_cut = getStringCopyN(s, size);
2739
2740         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2741                     s_cut, font, mask_mode);
2742
2743         free(s_cut);
2744       }
2745     }
2746
2747     redraw_mask |= REDRAW_DOOR_1;
2748   }
2749
2750   SetGameStatus(GAME_MODE_PLAYING);
2751 }
2752
2753 void UpdateAndDisplayGameControlValues()
2754 {
2755   if (tape.deactivate_display)
2756     return;
2757
2758   UpdateGameControlValues();
2759   DisplayGameControlValues();
2760 }
2761
2762 void UpdateGameDoorValues()
2763 {
2764   UpdateGameControlValues();
2765 }
2766
2767 void DrawGameDoorValues()
2768 {
2769   DisplayGameControlValues();
2770 }
2771
2772
2773 /*
2774   =============================================================================
2775   InitGameEngine()
2776   -----------------------------------------------------------------------------
2777   initialize game engine due to level / tape version number
2778   =============================================================================
2779 */
2780
2781 static void InitGameEngine()
2782 {
2783   int i, j, k, l, x, y;
2784
2785   /* set game engine from tape file when re-playing, else from level file */
2786   game.engine_version = (tape.playing ? tape.engine_version :
2787                          level.game_version);
2788
2789   /* set single or multi-player game mode (needed for re-playing tapes) */
2790   game.team_mode = setup.team_mode;
2791
2792   if (tape.playing)
2793   {
2794     int num_players = 0;
2795
2796     for (i = 0; i < MAX_PLAYERS; i++)
2797       if (tape.player_participates[i])
2798         num_players++;
2799
2800     /* multi-player tapes contain input data for more than one player */
2801     game.team_mode = (num_players > 1);
2802   }
2803
2804   /* ---------------------------------------------------------------------- */
2805   /* set flags for bugs and changes according to active game engine version */
2806   /* ---------------------------------------------------------------------- */
2807
2808   /*
2809     Summary of bugfix/change:
2810     Fixed handling for custom elements that change when pushed by the player.
2811
2812     Fixed/changed in version:
2813     3.1.0
2814
2815     Description:
2816     Before 3.1.0, custom elements that "change when pushing" changed directly
2817     after the player started pushing them (until then handled in "DigField()").
2818     Since 3.1.0, these custom elements are not changed until the "pushing"
2819     move of the element is finished (now handled in "ContinueMoving()").
2820
2821     Affected levels/tapes:
2822     The first condition is generally needed for all levels/tapes before version
2823     3.1.0, which might use the old behaviour before it was changed; known tapes
2824     that are affected are some tapes from the level set "Walpurgis Gardens" by
2825     Jamie Cullen.
2826     The second condition is an exception from the above case and is needed for
2827     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2828     above (including some development versions of 3.1.0), but before it was
2829     known that this change would break tapes like the above and was fixed in
2830     3.1.1, so that the changed behaviour was active although the engine version
2831     while recording maybe was before 3.1.0. There is at least one tape that is
2832     affected by this exception, which is the tape for the one-level set "Bug
2833     Machine" by Juergen Bonhagen.
2834   */
2835
2836   game.use_change_when_pushing_bug =
2837     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2838      !(tape.playing &&
2839        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2840        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2841
2842   /*
2843     Summary of bugfix/change:
2844     Fixed handling for blocking the field the player leaves when moving.
2845
2846     Fixed/changed in version:
2847     3.1.1
2848
2849     Description:
2850     Before 3.1.1, when "block last field when moving" was enabled, the field
2851     the player is leaving when moving was blocked for the time of the move,
2852     and was directly unblocked afterwards. This resulted in the last field
2853     being blocked for exactly one less than the number of frames of one player
2854     move. Additionally, even when blocking was disabled, the last field was
2855     blocked for exactly one frame.
2856     Since 3.1.1, due to changes in player movement handling, the last field
2857     is not blocked at all when blocking is disabled. When blocking is enabled,
2858     the last field is blocked for exactly the number of frames of one player
2859     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2860     last field is blocked for exactly one more than the number of frames of
2861     one player move.
2862
2863     Affected levels/tapes:
2864     (!!! yet to be determined -- probably many !!!)
2865   */
2866
2867   game.use_block_last_field_bug =
2868     (game.engine_version < VERSION_IDENT(3,1,1,0));
2869
2870   game_em.use_single_button =
2871     (game.engine_version > VERSION_IDENT(4,0,0,2));
2872
2873   game_em.use_snap_key_bug =
2874     (game.engine_version < VERSION_IDENT(4,0,1,0));
2875
2876   /* ---------------------------------------------------------------------- */
2877
2878   /* set maximal allowed number of custom element changes per game frame */
2879   game.max_num_changes_per_frame = 1;
2880
2881   /* default scan direction: scan playfield from top/left to bottom/right */
2882   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2883
2884   /* dynamically adjust element properties according to game engine version */
2885   InitElementPropertiesEngine(game.engine_version);
2886
2887 #if 0
2888   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2889   printf("          tape version == %06d [%s] [file: %06d]\n",
2890          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2891          tape.file_version);
2892   printf("       => game.engine_version == %06d\n", game.engine_version);
2893 #endif
2894
2895   /* ---------- initialize player's initial move delay --------------------- */
2896
2897   /* dynamically adjust player properties according to level information */
2898   for (i = 0; i < MAX_PLAYERS; i++)
2899     game.initial_move_delay_value[i] =
2900       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2901
2902   /* dynamically adjust player properties according to game engine version */
2903   for (i = 0; i < MAX_PLAYERS; i++)
2904     game.initial_move_delay[i] =
2905       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2906        game.initial_move_delay_value[i] : 0);
2907
2908   /* ---------- initialize player's initial push delay --------------------- */
2909
2910   /* dynamically adjust player properties according to game engine version */
2911   game.initial_push_delay_value =
2912     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2913
2914   /* ---------- initialize changing elements ------------------------------- */
2915
2916   /* initialize changing elements information */
2917   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2918   {
2919     struct ElementInfo *ei = &element_info[i];
2920
2921     /* this pointer might have been changed in the level editor */
2922     ei->change = &ei->change_page[0];
2923
2924     if (!IS_CUSTOM_ELEMENT(i))
2925     {
2926       ei->change->target_element = EL_EMPTY_SPACE;
2927       ei->change->delay_fixed = 0;
2928       ei->change->delay_random = 0;
2929       ei->change->delay_frames = 1;
2930     }
2931
2932     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2933     {
2934       ei->has_change_event[j] = FALSE;
2935
2936       ei->event_page_nr[j] = 0;
2937       ei->event_page[j] = &ei->change_page[0];
2938     }
2939   }
2940
2941   /* add changing elements from pre-defined list */
2942   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2943   {
2944     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2945     struct ElementInfo *ei = &element_info[ch_delay->element];
2946
2947     ei->change->target_element       = ch_delay->target_element;
2948     ei->change->delay_fixed          = ch_delay->change_delay;
2949
2950     ei->change->pre_change_function  = ch_delay->pre_change_function;
2951     ei->change->change_function      = ch_delay->change_function;
2952     ei->change->post_change_function = ch_delay->post_change_function;
2953
2954     ei->change->can_change = TRUE;
2955     ei->change->can_change_or_has_action = TRUE;
2956
2957     ei->has_change_event[CE_DELAY] = TRUE;
2958
2959     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2960     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2961   }
2962
2963   /* ---------- initialize internal run-time variables --------------------- */
2964
2965   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2966   {
2967     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2968
2969     for (j = 0; j < ei->num_change_pages; j++)
2970     {
2971       ei->change_page[j].can_change_or_has_action =
2972         (ei->change_page[j].can_change |
2973          ei->change_page[j].has_action);
2974     }
2975   }
2976
2977   /* add change events from custom element configuration */
2978   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2979   {
2980     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2981
2982     for (j = 0; j < ei->num_change_pages; j++)
2983     {
2984       if (!ei->change_page[j].can_change_or_has_action)
2985         continue;
2986
2987       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2988       {
2989         /* only add event page for the first page found with this event */
2990         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2991         {
2992           ei->has_change_event[k] = TRUE;
2993
2994           ei->event_page_nr[k] = j;
2995           ei->event_page[k] = &ei->change_page[j];
2996         }
2997       }
2998     }
2999   }
3000
3001   /* ---------- initialize reference elements in change conditions --------- */
3002
3003   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3004   {
3005     int element = EL_CUSTOM_START + i;
3006     struct ElementInfo *ei = &element_info[element];
3007
3008     for (j = 0; j < ei->num_change_pages; j++)
3009     {
3010       int trigger_element = ei->change_page[j].initial_trigger_element;
3011
3012       if (trigger_element >= EL_PREV_CE_8 &&
3013           trigger_element <= EL_NEXT_CE_8)
3014         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3015
3016       ei->change_page[j].trigger_element = trigger_element;
3017     }
3018   }
3019
3020   /* ---------- initialize run-time trigger player and element ------------- */
3021
3022   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3023   {
3024     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3025
3026     for (j = 0; j < ei->num_change_pages; j++)
3027     {
3028       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3029       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3030       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3031       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3032       ei->change_page[j].actual_trigger_ce_value = 0;
3033       ei->change_page[j].actual_trigger_ce_score = 0;
3034     }
3035   }
3036
3037   /* ---------- initialize trigger events ---------------------------------- */
3038
3039   /* initialize trigger events information */
3040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3041     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3042       trigger_events[i][j] = FALSE;
3043
3044   /* add trigger events from element change event properties */
3045   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3046   {
3047     struct ElementInfo *ei = &element_info[i];
3048
3049     for (j = 0; j < ei->num_change_pages; j++)
3050     {
3051       if (!ei->change_page[j].can_change_or_has_action)
3052         continue;
3053
3054       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3055       {
3056         int trigger_element = ei->change_page[j].trigger_element;
3057
3058         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3059         {
3060           if (ei->change_page[j].has_event[k])
3061           {
3062             if (IS_GROUP_ELEMENT(trigger_element))
3063             {
3064               struct ElementGroupInfo *group =
3065                 element_info[trigger_element].group;
3066
3067               for (l = 0; l < group->num_elements_resolved; l++)
3068                 trigger_events[group->element_resolved[l]][k] = TRUE;
3069             }
3070             else if (trigger_element == EL_ANY_ELEMENT)
3071               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3072                 trigger_events[l][k] = TRUE;
3073             else
3074               trigger_events[trigger_element][k] = TRUE;
3075           }
3076         }
3077       }
3078     }
3079   }
3080
3081   /* ---------- initialize push delay -------------------------------------- */
3082
3083   /* initialize push delay values to default */
3084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3085   {
3086     if (!IS_CUSTOM_ELEMENT(i))
3087     {
3088       /* set default push delay values (corrected since version 3.0.7-1) */
3089       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3090       {
3091         element_info[i].push_delay_fixed = 2;
3092         element_info[i].push_delay_random = 8;
3093       }
3094       else
3095       {
3096         element_info[i].push_delay_fixed = 8;
3097         element_info[i].push_delay_random = 8;
3098       }
3099     }
3100   }
3101
3102   /* set push delay value for certain elements from pre-defined list */
3103   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3104   {
3105     int e = push_delay_list[i].element;
3106
3107     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3108     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3109   }
3110
3111   /* set push delay value for Supaplex elements for newer engine versions */
3112   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3113   {
3114     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3115     {
3116       if (IS_SP_ELEMENT(i))
3117       {
3118         /* set SP push delay to just enough to push under a falling zonk */
3119         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3120
3121         element_info[i].push_delay_fixed  = delay;
3122         element_info[i].push_delay_random = 0;
3123       }
3124     }
3125   }
3126
3127   /* ---------- initialize move stepsize ----------------------------------- */
3128
3129   /* initialize move stepsize values to default */
3130   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131     if (!IS_CUSTOM_ELEMENT(i))
3132       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3133
3134   /* set move stepsize value for certain elements from pre-defined list */
3135   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3136   {
3137     int e = move_stepsize_list[i].element;
3138
3139     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3140   }
3141
3142   /* ---------- initialize collect score ----------------------------------- */
3143
3144   /* initialize collect score values for custom elements from initial value */
3145   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3146     if (IS_CUSTOM_ELEMENT(i))
3147       element_info[i].collect_score = element_info[i].collect_score_initial;
3148
3149   /* ---------- initialize collect count ----------------------------------- */
3150
3151   /* initialize collect count values for non-custom elements */
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153     if (!IS_CUSTOM_ELEMENT(i))
3154       element_info[i].collect_count_initial = 0;
3155
3156   /* add collect count values for all elements from pre-defined list */
3157   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3158     element_info[collect_count_list[i].element].collect_count_initial =
3159       collect_count_list[i].count;
3160
3161   /* ---------- initialize access direction -------------------------------- */
3162
3163   /* initialize access direction values to default (access from every side) */
3164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3165     if (!IS_CUSTOM_ELEMENT(i))
3166       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3167
3168   /* set access direction value for certain elements from pre-defined list */
3169   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3170     element_info[access_direction_list[i].element].access_direction =
3171       access_direction_list[i].direction;
3172
3173   /* ---------- initialize explosion content ------------------------------- */
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175   {
3176     if (IS_CUSTOM_ELEMENT(i))
3177       continue;
3178
3179     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3180     {
3181       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3182
3183       element_info[i].content.e[x][y] =
3184         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3185          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3186          i == EL_PLAYER_3 ? EL_EMERALD :
3187          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3188          i == EL_MOLE ? EL_EMERALD_RED :
3189          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3190          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3191          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3192          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3193          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3194          i == EL_WALL_EMERALD ? EL_EMERALD :
3195          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3196          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3197          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3198          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3199          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3200          i == EL_WALL_PEARL ? EL_PEARL :
3201          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3202          EL_EMPTY);
3203     }
3204   }
3205
3206   /* ---------- initialize recursion detection ------------------------------ */
3207   recursion_loop_depth = 0;
3208   recursion_loop_detected = FALSE;
3209   recursion_loop_element = EL_UNDEFINED;
3210
3211   /* ---------- initialize graphics engine ---------------------------------- */
3212   game.scroll_delay_value =
3213     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3214      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3215   game.scroll_delay_value =
3216     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3217
3218   /* ---------- initialize game engine snapshots ---------------------------- */
3219   for (i = 0; i < MAX_PLAYERS; i++)
3220     game.snapshot.last_action[i] = 0;
3221   game.snapshot.changed_action = FALSE;
3222   game.snapshot.collected_item = FALSE;
3223   game.snapshot.mode =
3224     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3225      SNAPSHOT_MODE_EVERY_STEP :
3226      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3227      SNAPSHOT_MODE_EVERY_MOVE :
3228      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3229      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3230   game.snapshot.save_snapshot = FALSE;
3231
3232   /* ---------- initialize level time for Supaplex engine ------------------- */
3233   /* Supaplex levels with time limit currently unsupported -- should be added */
3234   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3235     level.time = 0;
3236 }
3237
3238 int get_num_special_action(int element, int action_first, int action_last)
3239 {
3240   int num_special_action = 0;
3241   int i, j;
3242
3243   for (i = action_first; i <= action_last; i++)
3244   {
3245     boolean found = FALSE;
3246
3247     for (j = 0; j < NUM_DIRECTIONS; j++)
3248       if (el_act_dir2img(element, i, j) !=
3249           el_act_dir2img(element, ACTION_DEFAULT, j))
3250         found = TRUE;
3251
3252     if (found)
3253       num_special_action++;
3254     else
3255       break;
3256   }
3257
3258   return num_special_action;
3259 }
3260
3261
3262 /*
3263   =============================================================================
3264   InitGame()
3265   -----------------------------------------------------------------------------
3266   initialize and start new game
3267   =============================================================================
3268 */
3269
3270 void InitGame()
3271 {
3272   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3273   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3274   int fade_mask = REDRAW_FIELD;
3275
3276   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3277   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3278   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3279   int initial_move_dir = MV_DOWN;
3280   int i, j, x, y;
3281
3282   // required here to update video display before fading (FIX THIS)
3283   DrawMaskedBorder(REDRAW_DOOR_2);
3284
3285   if (!game.restart_level)
3286     CloseDoor(DOOR_CLOSE_1);
3287
3288   SetGameStatus(GAME_MODE_PLAYING);
3289
3290   if (level_editor_test_game)
3291     FadeSkipNextFadeIn();
3292   else
3293     FadeSetEnterScreen();
3294
3295   if (CheckIfGlobalBorderHasChanged())
3296     fade_mask = REDRAW_ALL;
3297
3298   FadeLevelSoundsAndMusic();
3299
3300   ExpireSoundLoops(TRUE);
3301
3302   FadeOut(fade_mask);
3303
3304   /* needed if different viewport properties defined for playing */
3305   ChangeViewportPropertiesIfNeeded();
3306
3307   ClearField();
3308
3309   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3310
3311   DrawCompleteVideoDisplay();
3312
3313   InitGameEngine();
3314   InitGameControlValues();
3315
3316   /* don't play tapes over network */
3317   network_playing = (options.network && !tape.playing);
3318
3319   for (i = 0; i < MAX_PLAYERS; i++)
3320   {
3321     struct PlayerInfo *player = &stored_player[i];
3322
3323     player->index_nr = i;
3324     player->index_bit = (1 << i);
3325     player->element_nr = EL_PLAYER_1 + i;
3326
3327     player->present = FALSE;
3328     player->active = FALSE;
3329     player->mapped = FALSE;
3330
3331     player->killed = FALSE;
3332     player->reanimated = FALSE;
3333
3334     player->action = 0;
3335     player->effective_action = 0;
3336     player->programmed_action = 0;
3337
3338     player->mouse_action.lx = 0;
3339     player->mouse_action.ly = 0;
3340     player->mouse_action.button = 0;
3341
3342     player->effective_mouse_action.lx = 0;
3343     player->effective_mouse_action.ly = 0;
3344     player->effective_mouse_action.button = 0;
3345
3346     player->score = 0;
3347     player->score_final = 0;
3348
3349     player->health = MAX_HEALTH;
3350     player->health_final = MAX_HEALTH;
3351
3352     player->gems_still_needed = level.gems_needed;
3353     player->sokobanfields_still_needed = 0;
3354     player->lights_still_needed = 0;
3355     player->friends_still_needed = 0;
3356
3357     for (j = 0; j < MAX_NUM_KEYS; j++)
3358       player->key[j] = FALSE;
3359
3360     player->num_white_keys = 0;
3361
3362     player->dynabomb_count = 0;
3363     player->dynabomb_size = 1;
3364     player->dynabombs_left = 0;
3365     player->dynabomb_xl = FALSE;
3366
3367     player->MovDir = initial_move_dir;
3368     player->MovPos = 0;
3369     player->GfxPos = 0;
3370     player->GfxDir = initial_move_dir;
3371     player->GfxAction = ACTION_DEFAULT;
3372     player->Frame = 0;
3373     player->StepFrame = 0;
3374
3375     player->initial_element = player->element_nr;
3376     player->artwork_element =
3377       (level.use_artwork_element[i] ? level.artwork_element[i] :
3378        player->element_nr);
3379     player->use_murphy = FALSE;
3380
3381     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3382     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3383
3384     player->gravity = level.initial_player_gravity[i];
3385
3386     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3387
3388     player->actual_frame_counter = 0;
3389
3390     player->step_counter = 0;
3391
3392     player->last_move_dir = initial_move_dir;
3393
3394     player->is_active = FALSE;
3395
3396     player->is_waiting = FALSE;
3397     player->is_moving = FALSE;
3398     player->is_auto_moving = FALSE;
3399     player->is_digging = FALSE;
3400     player->is_snapping = FALSE;
3401     player->is_collecting = FALSE;
3402     player->is_pushing = FALSE;
3403     player->is_switching = FALSE;
3404     player->is_dropping = FALSE;
3405     player->is_dropping_pressed = FALSE;
3406
3407     player->is_bored = FALSE;
3408     player->is_sleeping = FALSE;
3409
3410     player->was_waiting = TRUE;
3411     player->was_moving = FALSE;
3412     player->was_snapping = FALSE;
3413     player->was_dropping = FALSE;
3414
3415     player->force_dropping = FALSE;
3416
3417     player->frame_counter_bored = -1;
3418     player->frame_counter_sleeping = -1;
3419
3420     player->anim_delay_counter = 0;
3421     player->post_delay_counter = 0;
3422
3423     player->dir_waiting = initial_move_dir;
3424     player->action_waiting = ACTION_DEFAULT;
3425     player->last_action_waiting = ACTION_DEFAULT;
3426     player->special_action_bored = ACTION_DEFAULT;
3427     player->special_action_sleeping = ACTION_DEFAULT;
3428
3429     player->switch_x = -1;
3430     player->switch_y = -1;
3431
3432     player->drop_x = -1;
3433     player->drop_y = -1;
3434
3435     player->show_envelope = 0;
3436
3437     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3438
3439     player->push_delay       = -1;      /* initialized when pushing starts */
3440     player->push_delay_value = game.initial_push_delay_value;
3441
3442     player->drop_delay = 0;
3443     player->drop_pressed_delay = 0;
3444
3445     player->last_jx = -1;
3446     player->last_jy = -1;
3447     player->jx = -1;
3448     player->jy = -1;
3449
3450     player->shield_normal_time_left = 0;
3451     player->shield_deadly_time_left = 0;
3452
3453     player->inventory_infinite_element = EL_UNDEFINED;
3454     player->inventory_size = 0;
3455
3456     if (level.use_initial_inventory[i])
3457     {
3458       for (j = 0; j < level.initial_inventory_size[i]; j++)
3459       {
3460         int element = level.initial_inventory_content[i][j];
3461         int collect_count = element_info[element].collect_count_initial;
3462         int k;
3463
3464         if (!IS_CUSTOM_ELEMENT(element))
3465           collect_count = 1;
3466
3467         if (collect_count == 0)
3468           player->inventory_infinite_element = element;
3469         else
3470           for (k = 0; k < collect_count; k++)
3471             if (player->inventory_size < MAX_INVENTORY_SIZE)
3472               player->inventory_element[player->inventory_size++] = element;
3473       }
3474     }
3475
3476     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3477     SnapField(player, 0, 0);
3478
3479     player->LevelSolved = FALSE;
3480     player->GameOver = FALSE;
3481
3482     player->LevelSolved_GameWon = FALSE;
3483     player->LevelSolved_GameEnd = FALSE;
3484     player->LevelSolved_PanelOff = FALSE;
3485     player->LevelSolved_SaveTape = FALSE;
3486     player->LevelSolved_SaveScore = FALSE;
3487
3488     player->LevelSolved_CountingTime = 0;
3489     player->LevelSolved_CountingScore = 0;
3490     player->LevelSolved_CountingHealth = 0;
3491
3492     map_player_action[i] = i;
3493   }
3494
3495   network_player_action_received = FALSE;
3496
3497 #if defined(NETWORK_AVALIABLE)
3498   /* initial null action */
3499   if (network_playing)
3500     SendToServer_MovePlayer(MV_NONE);
3501 #endif
3502
3503   ZX = ZY = -1;
3504   ExitX = ExitY = -1;
3505
3506   FrameCounter = 0;
3507   TimeFrames = 0;
3508   TimePlayed = 0;
3509   TimeLeft = level.time;
3510   TapeTime = 0;
3511
3512   ScreenMovDir = MV_NONE;
3513   ScreenMovPos = 0;
3514   ScreenGfxPos = 0;
3515
3516   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3517
3518   AllPlayersGone = FALSE;
3519
3520   game.no_time_limit = (level.time == 0);
3521
3522   game.yamyam_content_nr = 0;
3523   game.robot_wheel_active = FALSE;
3524   game.magic_wall_active = FALSE;
3525   game.magic_wall_time_left = 0;
3526   game.light_time_left = 0;
3527   game.timegate_time_left = 0;
3528   game.switchgate_pos = 0;
3529   game.wind_direction = level.wind_direction_initial;
3530
3531   game.lenses_time_left = 0;
3532   game.magnify_time_left = 0;
3533
3534   game.ball_state = level.ball_state_initial;
3535   game.ball_content_nr = 0;
3536
3537   game.envelope_active = FALSE;
3538
3539   /* set focus to local player for network games, else to all players */
3540   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3541   game.centered_player_nr_next = game.centered_player_nr;
3542   game.set_centered_player = FALSE;
3543
3544   if (network_playing && tape.recording)
3545   {
3546     /* store client dependent player focus when recording network games */
3547     tape.centered_player_nr_next = game.centered_player_nr_next;
3548     tape.set_centered_player = TRUE;
3549   }
3550
3551   for (i = 0; i < NUM_BELTS; i++)
3552   {
3553     game.belt_dir[i] = MV_NONE;
3554     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3555   }
3556
3557   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3558     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3559
3560 #if DEBUG_INIT_PLAYER
3561   if (options.debug)
3562   {
3563     printf("Player status at level initialization:\n");
3564   }
3565 #endif
3566
3567   SCAN_PLAYFIELD(x, y)
3568   {
3569     Feld[x][y] = level.field[x][y];
3570     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3571     ChangeDelay[x][y] = 0;
3572     ChangePage[x][y] = -1;
3573     CustomValue[x][y] = 0;              /* initialized in InitField() */
3574     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3575     AmoebaNr[x][y] = 0;
3576     WasJustMoving[x][y] = 0;
3577     WasJustFalling[x][y] = 0;
3578     CheckCollision[x][y] = 0;
3579     CheckImpact[x][y] = 0;
3580     Stop[x][y] = FALSE;
3581     Pushed[x][y] = FALSE;
3582
3583     ChangeCount[x][y] = 0;
3584     ChangeEvent[x][y] = -1;
3585
3586     ExplodePhase[x][y] = 0;
3587     ExplodeDelay[x][y] = 0;
3588     ExplodeField[x][y] = EX_TYPE_NONE;
3589
3590     RunnerVisit[x][y] = 0;
3591     PlayerVisit[x][y] = 0;
3592
3593     GfxFrame[x][y] = 0;
3594     GfxRandom[x][y] = INIT_GFX_RANDOM();
3595     GfxElement[x][y] = EL_UNDEFINED;
3596     GfxAction[x][y] = ACTION_DEFAULT;
3597     GfxDir[x][y] = MV_NONE;
3598     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3599   }
3600
3601   SCAN_PLAYFIELD(x, y)
3602   {
3603     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3604       emulate_bd = FALSE;
3605     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3606       emulate_sb = FALSE;
3607     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3608       emulate_sp = FALSE;
3609
3610     InitField(x, y, TRUE);
3611
3612     ResetGfxAnimation(x, y);
3613   }
3614
3615   InitBeltMovement();
3616
3617   for (i = 0; i < MAX_PLAYERS; i++)
3618   {
3619     struct PlayerInfo *player = &stored_player[i];
3620
3621     /* set number of special actions for bored and sleeping animation */
3622     player->num_special_action_bored =
3623       get_num_special_action(player->artwork_element,
3624                              ACTION_BORING_1, ACTION_BORING_LAST);
3625     player->num_special_action_sleeping =
3626       get_num_special_action(player->artwork_element,
3627                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3628   }
3629
3630   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3631                     emulate_sb ? EMU_SOKOBAN :
3632                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3633
3634   /* initialize type of slippery elements */
3635   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3636   {
3637     if (!IS_CUSTOM_ELEMENT(i))
3638     {
3639       /* default: elements slip down either to the left or right randomly */
3640       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3641
3642       /* SP style elements prefer to slip down on the left side */
3643       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3644         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3645
3646       /* BD style elements prefer to slip down on the left side */
3647       if (game.emulation == EMU_BOULDERDASH)
3648         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3649     }
3650   }
3651
3652   /* initialize explosion and ignition delay */
3653   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3654   {
3655     if (!IS_CUSTOM_ELEMENT(i))
3656     {
3657       int num_phase = 8;
3658       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3659                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3660                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3661       int last_phase = (num_phase + 1) * delay;
3662       int half_phase = (num_phase / 2) * delay;
3663
3664       element_info[i].explosion_delay = last_phase - 1;
3665       element_info[i].ignition_delay = half_phase;
3666
3667       if (i == EL_BLACK_ORB)
3668         element_info[i].ignition_delay = 1;
3669     }
3670   }
3671
3672   /* correct non-moving belts to start moving left */
3673   for (i = 0; i < NUM_BELTS; i++)
3674     if (game.belt_dir[i] == MV_NONE)
3675       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3676
3677 #if USE_NEW_PLAYER_ASSIGNMENTS
3678   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3679   /* choose default local player */
3680   local_player = &stored_player[0];
3681
3682   for (i = 0; i < MAX_PLAYERS; i++)
3683     stored_player[i].connected = FALSE;
3684
3685   local_player->connected = TRUE;
3686   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3687
3688   if (tape.playing)
3689   {
3690     for (i = 0; i < MAX_PLAYERS; i++)
3691       stored_player[i].connected = tape.player_participates[i];
3692   }
3693   else if (game.team_mode && !options.network)
3694   {
3695     /* try to guess locally connected team mode players (needed for correct
3696        assignment of player figures from level to locally playing players) */
3697
3698     for (i = 0; i < MAX_PLAYERS; i++)
3699       if (setup.input[i].use_joystick ||
3700           setup.input[i].key.left != KSYM_UNDEFINED)
3701         stored_player[i].connected = TRUE;
3702   }
3703
3704 #if DEBUG_INIT_PLAYER
3705   if (options.debug)
3706   {
3707     printf("Player status after level initialization:\n");
3708
3709     for (i = 0; i < MAX_PLAYERS; i++)
3710     {
3711       struct PlayerInfo *player = &stored_player[i];
3712
3713       printf("- player %d: present == %d, connected == %d, active == %d",
3714              i + 1,
3715              player->present,
3716              player->connected,
3717              player->active);
3718
3719       if (local_player == player)
3720         printf(" (local player)");
3721
3722       printf("\n");
3723     }
3724   }
3725 #endif
3726
3727 #if DEBUG_INIT_PLAYER
3728   if (options.debug)
3729     printf("Reassigning players ...\n");
3730 #endif
3731
3732   /* check if any connected player was not found in playfield */
3733   for (i = 0; i < MAX_PLAYERS; i++)
3734   {
3735     struct PlayerInfo *player = &stored_player[i];
3736
3737     if (player->connected && !player->present)
3738     {
3739       struct PlayerInfo *field_player = NULL;
3740
3741 #if DEBUG_INIT_PLAYER
3742       if (options.debug)
3743         printf("- looking for field player for player %d ...\n", i + 1);
3744 #endif
3745
3746       /* assign first free player found that is present in the playfield */
3747
3748       /* first try: look for unmapped playfield player that is not connected */
3749       for (j = 0; j < MAX_PLAYERS; j++)
3750         if (field_player == NULL &&
3751             stored_player[j].present &&
3752             !stored_player[j].mapped &&
3753             !stored_player[j].connected)
3754           field_player = &stored_player[j];
3755
3756       /* second try: look for *any* unmapped playfield player */
3757       for (j = 0; j < MAX_PLAYERS; j++)
3758         if (field_player == NULL &&
3759             stored_player[j].present &&
3760             !stored_player[j].mapped)
3761           field_player = &stored_player[j];
3762
3763       if (field_player != NULL)
3764       {
3765         int jx = field_player->jx, jy = field_player->jy;
3766
3767 #if DEBUG_INIT_PLAYER
3768         if (options.debug)
3769           printf("- found player %d\n", field_player->index_nr + 1);
3770 #endif
3771
3772         player->present = FALSE;
3773         player->active = FALSE;
3774
3775         field_player->present = TRUE;
3776         field_player->active = TRUE;
3777
3778         /*
3779         player->initial_element = field_player->initial_element;
3780         player->artwork_element = field_player->artwork_element;
3781
3782         player->block_last_field       = field_player->block_last_field;
3783         player->block_delay_adjustment = field_player->block_delay_adjustment;
3784         */
3785
3786         StorePlayer[jx][jy] = field_player->element_nr;
3787
3788         field_player->jx = field_player->last_jx = jx;
3789         field_player->jy = field_player->last_jy = jy;
3790
3791         if (local_player == player)
3792           local_player = field_player;
3793
3794         map_player_action[field_player->index_nr] = i;
3795
3796         field_player->mapped = TRUE;
3797
3798 #if DEBUG_INIT_PLAYER
3799         if (options.debug)
3800           printf("- map_player_action[%d] == %d\n",
3801                  field_player->index_nr + 1, i + 1);
3802 #endif
3803       }
3804     }
3805
3806     if (player->connected && player->present)
3807       player->mapped = TRUE;
3808   }
3809
3810 #if DEBUG_INIT_PLAYER
3811   if (options.debug)
3812   {
3813     printf("Player status after player assignment (first stage):\n");
3814
3815     for (i = 0; i < MAX_PLAYERS; i++)
3816     {
3817       struct PlayerInfo *player = &stored_player[i];
3818
3819       printf("- player %d: present == %d, connected == %d, active == %d",
3820              i + 1,
3821              player->present,
3822              player->connected,
3823              player->active);
3824
3825       if (local_player == player)
3826         printf(" (local player)");
3827
3828       printf("\n");
3829     }
3830   }
3831 #endif
3832
3833 #else
3834
3835   /* check if any connected player was not found in playfield */
3836   for (i = 0; i < MAX_PLAYERS; i++)
3837   {
3838     struct PlayerInfo *player = &stored_player[i];
3839
3840     if (player->connected && !player->present)
3841     {
3842       for (j = 0; j < MAX_PLAYERS; j++)
3843       {
3844         struct PlayerInfo *field_player = &stored_player[j];
3845         int jx = field_player->jx, jy = field_player->jy;
3846
3847         /* assign first free player found that is present in the playfield */
3848         if (field_player->present && !field_player->connected)
3849         {
3850           player->present = TRUE;
3851           player->active = TRUE;
3852
3853           field_player->present = FALSE;
3854           field_player->active = FALSE;
3855
3856           player->initial_element = field_player->initial_element;
3857           player->artwork_element = field_player->artwork_element;
3858
3859           player->block_last_field       = field_player->block_last_field;
3860           player->block_delay_adjustment = field_player->block_delay_adjustment;
3861
3862           StorePlayer[jx][jy] = player->element_nr;
3863
3864           player->jx = player->last_jx = jx;
3865           player->jy = player->last_jy = jy;
3866
3867           break;
3868         }
3869       }
3870     }
3871   }
3872 #endif
3873
3874 #if 0
3875   printf("::: local_player->present == %d\n", local_player->present);
3876 #endif
3877
3878   if (tape.playing)
3879   {
3880     /* when playing a tape, eliminate all players who do not participate */
3881
3882 #if USE_NEW_PLAYER_ASSIGNMENTS
3883
3884     if (!game.team_mode)
3885     {
3886       for (i = 0; i < MAX_PLAYERS; i++)
3887       {
3888         if (stored_player[i].active &&
3889             !tape.player_participates[map_player_action[i]])
3890         {
3891           struct PlayerInfo *player = &stored_player[i];
3892           int jx = player->jx, jy = player->jy;
3893
3894 #if DEBUG_INIT_PLAYER
3895           if (options.debug)
3896             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3897 #endif
3898
3899           player->active = FALSE;
3900           StorePlayer[jx][jy] = 0;
3901           Feld[jx][jy] = EL_EMPTY;
3902         }
3903       }
3904     }
3905
3906 #else
3907
3908     for (i = 0; i < MAX_PLAYERS; i++)
3909     {
3910       if (stored_player[i].active &&
3911           !tape.player_participates[i])
3912       {
3913         struct PlayerInfo *player = &stored_player[i];
3914         int jx = player->jx, jy = player->jy;
3915
3916         player->active = FALSE;
3917         StorePlayer[jx][jy] = 0;
3918         Feld[jx][jy] = EL_EMPTY;
3919       }
3920     }
3921 #endif
3922   }
3923   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3924   {
3925     /* when in single player mode, eliminate all but the first active player */
3926
3927     for (i = 0; i < MAX_PLAYERS; i++)
3928     {
3929       if (stored_player[i].active)
3930       {
3931         for (j = i + 1; j < MAX_PLAYERS; j++)
3932         {
3933           if (stored_player[j].active)
3934           {
3935             struct PlayerInfo *player = &stored_player[j];
3936             int jx = player->jx, jy = player->jy;
3937
3938             player->active = FALSE;
3939             player->present = FALSE;
3940
3941             StorePlayer[jx][jy] = 0;
3942             Feld[jx][jy] = EL_EMPTY;
3943           }
3944         }
3945       }
3946     }
3947   }
3948
3949   /* when recording the game, store which players take part in the game */
3950   if (tape.recording)
3951   {
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953     for (i = 0; i < MAX_PLAYERS; i++)
3954       if (stored_player[i].connected)
3955         tape.player_participates[i] = TRUE;
3956 #else
3957     for (i = 0; i < MAX_PLAYERS; i++)
3958       if (stored_player[i].active)
3959         tape.player_participates[i] = TRUE;
3960 #endif
3961   }
3962
3963 #if DEBUG_INIT_PLAYER
3964   if (options.debug)
3965   {
3966     printf("Player status after player assignment (final stage):\n");
3967
3968     for (i = 0; i < MAX_PLAYERS; i++)
3969     {
3970       struct PlayerInfo *player = &stored_player[i];
3971
3972       printf("- player %d: present == %d, connected == %d, active == %d",
3973              i + 1,
3974              player->present,
3975              player->connected,
3976              player->active);
3977
3978       if (local_player == player)
3979         printf(" (local player)");
3980
3981       printf("\n");
3982     }
3983   }
3984 #endif
3985
3986   if (BorderElement == EL_EMPTY)
3987   {
3988     SBX_Left = 0;
3989     SBX_Right = lev_fieldx - SCR_FIELDX;
3990     SBY_Upper = 0;
3991     SBY_Lower = lev_fieldy - SCR_FIELDY;
3992   }
3993   else
3994   {
3995     SBX_Left = -1;
3996     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3997     SBY_Upper = -1;
3998     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3999   }
4000
4001   if (full_lev_fieldx <= SCR_FIELDX)
4002     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4003   if (full_lev_fieldy <= SCR_FIELDY)
4004     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4005
4006   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4007     SBX_Left--;
4008   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4009     SBY_Upper--;
4010
4011   /* if local player not found, look for custom element that might create
4012      the player (make some assumptions about the right custom element) */
4013   if (!local_player->present)
4014   {
4015     int start_x = 0, start_y = 0;
4016     int found_rating = 0;
4017     int found_element = EL_UNDEFINED;
4018     int player_nr = local_player->index_nr;
4019
4020     SCAN_PLAYFIELD(x, y)
4021     {
4022       int element = Feld[x][y];
4023       int content;
4024       int xx, yy;
4025       boolean is_player;
4026
4027       if (level.use_start_element[player_nr] &&
4028           level.start_element[player_nr] == element &&
4029           found_rating < 4)
4030       {
4031         start_x = x;
4032         start_y = y;
4033
4034         found_rating = 4;
4035         found_element = element;
4036       }
4037
4038       if (!IS_CUSTOM_ELEMENT(element))
4039         continue;
4040
4041       if (CAN_CHANGE(element))
4042       {
4043         for (i = 0; i < element_info[element].num_change_pages; i++)
4044         {
4045           /* check for player created from custom element as single target */
4046           content = element_info[element].change_page[i].target_element;
4047           is_player = ELEM_IS_PLAYER(content);
4048
4049           if (is_player && (found_rating < 3 ||
4050                             (found_rating == 3 && element < found_element)))
4051           {
4052             start_x = x;
4053             start_y = y;
4054
4055             found_rating = 3;
4056             found_element = element;
4057           }
4058         }
4059       }
4060
4061       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4062       {
4063         /* check for player created from custom element as explosion content */
4064         content = element_info[element].content.e[xx][yy];
4065         is_player = ELEM_IS_PLAYER(content);
4066
4067         if (is_player && (found_rating < 2 ||
4068                           (found_rating == 2 && element < found_element)))
4069         {
4070           start_x = x + xx - 1;
4071           start_y = y + yy - 1;
4072
4073           found_rating = 2;
4074           found_element = element;
4075         }
4076
4077         if (!CAN_CHANGE(element))
4078           continue;
4079
4080         for (i = 0; i < element_info[element].num_change_pages; i++)
4081         {
4082           /* check for player created from custom element as extended target */
4083           content =
4084             element_info[element].change_page[i].target_content.e[xx][yy];
4085
4086           is_player = ELEM_IS_PLAYER(content);
4087
4088           if (is_player && (found_rating < 1 ||
4089                             (found_rating == 1 && element < found_element)))
4090           {
4091             start_x = x + xx - 1;
4092             start_y = y + yy - 1;
4093
4094             found_rating = 1;
4095             found_element = element;
4096           }
4097         }
4098       }
4099     }
4100
4101     scroll_x = SCROLL_POSITION_X(start_x);
4102     scroll_y = SCROLL_POSITION_Y(start_y);
4103   }
4104   else
4105   {
4106     scroll_x = SCROLL_POSITION_X(local_player->jx);
4107     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4108   }
4109
4110   /* !!! FIX THIS (START) !!! */
4111   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4112   {
4113     InitGameEngine_EM();
4114   }
4115   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4116   {
4117     InitGameEngine_SP();
4118   }
4119   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4120   {
4121     InitGameEngine_MM();
4122   }
4123   else
4124   {
4125     DrawLevel(REDRAW_FIELD);
4126     DrawAllPlayers();
4127
4128     /* after drawing the level, correct some elements */
4129     if (game.timegate_time_left == 0)
4130       CloseAllOpenTimegates();
4131   }
4132
4133   /* blit playfield from scroll buffer to normal back buffer for fading in */
4134   BlitScreenToBitmap(backbuffer);
4135   /* !!! FIX THIS (END) !!! */
4136
4137   DrawMaskedBorder(fade_mask);
4138
4139   FadeIn(fade_mask);
4140
4141 #if 1
4142   // full screen redraw is required at this point in the following cases:
4143   // - special editor door undrawn when game was started from level editor
4144   // - drawing area (playfield) was changed and has to be removed completely
4145   redraw_mask = REDRAW_ALL;
4146   BackToFront();
4147 #endif
4148
4149   if (!game.restart_level)
4150   {
4151     /* copy default game door content to main double buffer */
4152
4153     /* !!! CHECK AGAIN !!! */
4154     SetPanelBackground();
4155     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4156     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4157   }
4158
4159   SetPanelBackground();
4160   SetDrawBackgroundMask(REDRAW_DOOR_1);
4161
4162   UpdateAndDisplayGameControlValues();
4163
4164   if (!game.restart_level)
4165   {
4166     UnmapGameButtons();
4167     UnmapTapeButtons();
4168
4169     FreeGameButtons();
4170     CreateGameButtons();
4171
4172     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4173     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4174     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4175
4176     MapGameButtons();
4177     MapTapeButtons();
4178
4179     /* copy actual game door content to door double buffer for OpenDoor() */
4180     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4181
4182     OpenDoor(DOOR_OPEN_ALL);
4183
4184     PlaySound(SND_GAME_STARTING);
4185
4186     if (setup.sound_music)
4187       PlayLevelMusic();
4188
4189     KeyboardAutoRepeatOffUnlessAutoplay();
4190
4191 #if DEBUG_INIT_PLAYER
4192     if (options.debug)
4193     {
4194       printf("Player status (final):\n");
4195
4196       for (i = 0; i < MAX_PLAYERS; i++)
4197       {
4198         struct PlayerInfo *player = &stored_player[i];
4199
4200         printf("- player %d: present == %d, connected == %d, active == %d",
4201                i + 1,
4202                player->present,
4203                player->connected,
4204                player->active);
4205
4206         if (local_player == player)
4207           printf(" (local player)");
4208
4209         printf("\n");
4210       }
4211     }
4212 #endif
4213   }
4214
4215   UnmapAllGadgets();
4216
4217   MapGameButtons();
4218   MapTapeButtons();
4219
4220   if (!game.restart_level && !tape.playing)
4221   {
4222     LevelStats_incPlayed(level_nr);
4223
4224     SaveLevelSetup_SeriesInfo();
4225   }
4226
4227   game.restart_level = FALSE;
4228
4229   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4230     InitGameActions_MM();
4231
4232   SaveEngineSnapshotToListInitial();
4233 }
4234
4235 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4236                         int actual_player_x, int actual_player_y)
4237 {
4238   /* this is used for non-R'n'D game engines to update certain engine values */
4239
4240   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4241   {
4242     actual_player_x = correctLevelPosX_EM(actual_player_x);
4243     actual_player_y = correctLevelPosY_EM(actual_player_y);
4244   }
4245
4246   /* needed to determine if sounds are played within the visible screen area */
4247   scroll_x = actual_scroll_x;
4248   scroll_y = actual_scroll_y;
4249
4250   /* needed to get player position for "follow finger" playing input method */
4251   local_player->jx = actual_player_x;
4252   local_player->jy = actual_player_y;
4253 }
4254
4255 void InitMovDir(int x, int y)
4256 {
4257   int i, element = Feld[x][y];
4258   static int xy[4][2] =
4259   {
4260     {  0, +1 },
4261     { +1,  0 },
4262     {  0, -1 },
4263     { -1,  0 }
4264   };
4265   static int direction[3][4] =
4266   {
4267     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4268     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4269     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4270   };
4271
4272   switch (element)
4273   {
4274     case EL_BUG_RIGHT:
4275     case EL_BUG_UP:
4276     case EL_BUG_LEFT:
4277     case EL_BUG_DOWN:
4278       Feld[x][y] = EL_BUG;
4279       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4280       break;
4281
4282     case EL_SPACESHIP_RIGHT:
4283     case EL_SPACESHIP_UP:
4284     case EL_SPACESHIP_LEFT:
4285     case EL_SPACESHIP_DOWN:
4286       Feld[x][y] = EL_SPACESHIP;
4287       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4288       break;
4289
4290     case EL_BD_BUTTERFLY_RIGHT:
4291     case EL_BD_BUTTERFLY_UP:
4292     case EL_BD_BUTTERFLY_LEFT:
4293     case EL_BD_BUTTERFLY_DOWN:
4294       Feld[x][y] = EL_BD_BUTTERFLY;
4295       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4296       break;
4297
4298     case EL_BD_FIREFLY_RIGHT:
4299     case EL_BD_FIREFLY_UP:
4300     case EL_BD_FIREFLY_LEFT:
4301     case EL_BD_FIREFLY_DOWN:
4302       Feld[x][y] = EL_BD_FIREFLY;
4303       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4304       break;
4305
4306     case EL_PACMAN_RIGHT:
4307     case EL_PACMAN_UP:
4308     case EL_PACMAN_LEFT:
4309     case EL_PACMAN_DOWN:
4310       Feld[x][y] = EL_PACMAN;
4311       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4312       break;
4313
4314     case EL_YAMYAM_LEFT:
4315     case EL_YAMYAM_RIGHT:
4316     case EL_YAMYAM_UP:
4317     case EL_YAMYAM_DOWN:
4318       Feld[x][y] = EL_YAMYAM;
4319       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4320       break;
4321
4322     case EL_SP_SNIKSNAK:
4323       MovDir[x][y] = MV_UP;
4324       break;
4325
4326     case EL_SP_ELECTRON:
4327       MovDir[x][y] = MV_LEFT;
4328       break;
4329
4330     case EL_MOLE_LEFT:
4331     case EL_MOLE_RIGHT:
4332     case EL_MOLE_UP:
4333     case EL_MOLE_DOWN:
4334       Feld[x][y] = EL_MOLE;
4335       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4336       break;
4337
4338     default:
4339       if (IS_CUSTOM_ELEMENT(element))
4340       {
4341         struct ElementInfo *ei = &element_info[element];
4342         int move_direction_initial = ei->move_direction_initial;
4343         int move_pattern = ei->move_pattern;
4344
4345         if (move_direction_initial == MV_START_PREVIOUS)
4346         {
4347           if (MovDir[x][y] != MV_NONE)
4348             return;
4349
4350           move_direction_initial = MV_START_AUTOMATIC;
4351         }
4352
4353         if (move_direction_initial == MV_START_RANDOM)
4354           MovDir[x][y] = 1 << RND(4);
4355         else if (move_direction_initial & MV_ANY_DIRECTION)
4356           MovDir[x][y] = move_direction_initial;
4357         else if (move_pattern == MV_ALL_DIRECTIONS ||
4358                  move_pattern == MV_TURNING_LEFT ||
4359                  move_pattern == MV_TURNING_RIGHT ||
4360                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4361                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4362                  move_pattern == MV_TURNING_RANDOM)
4363           MovDir[x][y] = 1 << RND(4);
4364         else if (move_pattern == MV_HORIZONTAL)
4365           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4366         else if (move_pattern == MV_VERTICAL)
4367           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4368         else if (move_pattern & MV_ANY_DIRECTION)
4369           MovDir[x][y] = element_info[element].move_pattern;
4370         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4371                  move_pattern == MV_ALONG_RIGHT_SIDE)
4372         {
4373           /* use random direction as default start direction */
4374           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4375             MovDir[x][y] = 1 << RND(4);
4376
4377           for (i = 0; i < NUM_DIRECTIONS; i++)
4378           {
4379             int x1 = x + xy[i][0];
4380             int y1 = y + xy[i][1];
4381
4382             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4383             {
4384               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4385                 MovDir[x][y] = direction[0][i];
4386               else
4387                 MovDir[x][y] = direction[1][i];
4388
4389               break;
4390             }
4391           }
4392         }                
4393       }
4394       else
4395       {
4396         MovDir[x][y] = 1 << RND(4);
4397
4398         if (element != EL_BUG &&
4399             element != EL_SPACESHIP &&
4400             element != EL_BD_BUTTERFLY &&
4401             element != EL_BD_FIREFLY)
4402           break;
4403
4404         for (i = 0; i < NUM_DIRECTIONS; i++)
4405         {
4406           int x1 = x + xy[i][0];
4407           int y1 = y + xy[i][1];
4408
4409           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4410           {
4411             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4412             {
4413               MovDir[x][y] = direction[0][i];
4414               break;
4415             }
4416             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4417                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4418             {
4419               MovDir[x][y] = direction[1][i];
4420               break;
4421             }
4422           }
4423         }
4424       }
4425       break;
4426   }
4427
4428   GfxDir[x][y] = MovDir[x][y];
4429 }
4430
4431 void InitAmoebaNr(int x, int y)
4432 {
4433   int i;
4434   int group_nr = AmoebeNachbarNr(x, y);
4435
4436   if (group_nr == 0)
4437   {
4438     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4439     {
4440       if (AmoebaCnt[i] == 0)
4441       {
4442         group_nr = i;
4443         break;
4444       }
4445     }
4446   }
4447
4448   AmoebaNr[x][y] = group_nr;
4449   AmoebaCnt[group_nr]++;
4450   AmoebaCnt2[group_nr]++;
4451 }
4452
4453 static void PlayerWins(struct PlayerInfo *player)
4454 {
4455   player->LevelSolved = TRUE;
4456   player->GameOver = TRUE;
4457
4458   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4459                          level.native_em_level->lev->score :
4460                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4461                          game_mm.score :
4462                          player->score);
4463   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4464                           MM_HEALTH(game_mm.laser_overload_value) :
4465                           player->health);
4466
4467   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4468                                       TimeLeft);
4469   player->LevelSolved_CountingScore = player->score_final;
4470   player->LevelSolved_CountingHealth = player->health_final;
4471 }
4472
4473 void GameWon()
4474 {
4475   static int time, time_final;
4476   static int score, score_final;
4477   static int health, health_final;
4478   static int game_over_delay_1 = 0;
4479   static int game_over_delay_2 = 0;
4480   static int game_over_delay_3 = 0;
4481   int game_over_delay_value_1 = 50;
4482   int game_over_delay_value_2 = 25;
4483   int game_over_delay_value_3 = 50;
4484
4485   if (!local_player->LevelSolved_GameWon)
4486   {
4487     int i;
4488
4489     /* do not start end game actions before the player stops moving (to exit) */
4490     if (local_player->MovPos)
4491       return;
4492
4493     local_player->LevelSolved_GameWon = TRUE;
4494     local_player->LevelSolved_SaveTape = tape.recording;
4495     local_player->LevelSolved_SaveScore = !tape.playing;
4496
4497     if (!tape.playing)
4498     {
4499       LevelStats_incSolved(level_nr);
4500
4501       SaveLevelSetup_SeriesInfo();
4502     }
4503
4504     if (tape.auto_play)         /* tape might already be stopped here */
4505       tape.auto_play_level_solved = TRUE;
4506
4507     TapeStop();
4508
4509     game_over_delay_1 = game_over_delay_value_1;
4510     game_over_delay_2 = 0;
4511     game_over_delay_3 = game_over_delay_value_3;
4512
4513     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4514     score = score_final = local_player->score_final;
4515     health = health_final = local_player->health_final;
4516
4517     if (TimeLeft > 0)
4518     {
4519       time_final = 0;
4520       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4521     }
4522     else if (game.no_time_limit && TimePlayed < 999)
4523     {
4524       time_final = 999;
4525       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4526     }
4527
4528     if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4529     {
4530       health_final = 0;
4531       score_final += health * level.score[SC_TIME_BONUS];
4532
4533       game_over_delay_2 = game_over_delay_value_2;
4534     }
4535
4536     local_player->score_final = score_final;
4537     local_player->health_final = health_final;
4538
4539     if (level_editor_test_game)
4540     {
4541       time = time_final;
4542       score = score_final;
4543
4544       local_player->LevelSolved_CountingTime = time;
4545       local_player->LevelSolved_CountingScore = score;
4546
4547       game_panel_controls[GAME_PANEL_TIME].value = time;
4548       game_panel_controls[GAME_PANEL_SCORE].value = score;
4549
4550       DisplayGameControlValues();
4551     }
4552
4553     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4554     {
4555       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4556       {
4557         /* close exit door after last player */
4558         if ((AllPlayersGone &&
4559              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4560               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4561               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4562             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4563             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4564         {
4565           int element = Feld[ExitX][ExitY];
4566
4567           Feld[ExitX][ExitY] =
4568             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4569              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4570              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4571              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4572              EL_EM_STEEL_EXIT_CLOSING);
4573
4574           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4575         }
4576
4577         /* player disappears */
4578         DrawLevelField(ExitX, ExitY);
4579       }
4580
4581       for (i = 0; i < MAX_PLAYERS; i++)
4582       {
4583         struct PlayerInfo *player = &stored_player[i];
4584
4585         if (player->present)
4586         {
4587           RemovePlayer(player);
4588
4589           /* player disappears */
4590           DrawLevelField(player->jx, player->jy);
4591         }
4592       }
4593     }
4594
4595     PlaySound(SND_GAME_WINNING);
4596   }
4597
4598   if (game_over_delay_1 > 0)
4599   {
4600     game_over_delay_1--;
4601
4602     return;
4603   }
4604
4605   if (time != time_final)
4606   {
4607     int time_to_go = ABS(time_final - time);
4608     int time_count_dir = (time < time_final ? +1 : -1);
4609     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4610
4611     time  += time_count_steps * time_count_dir;
4612     score += time_count_steps * level.score[SC_TIME_BONUS];
4613
4614     local_player->LevelSolved_CountingTime = time;
4615     local_player->LevelSolved_CountingScore = score;
4616
4617     game_panel_controls[GAME_PANEL_TIME].value = time;
4618     game_panel_controls[GAME_PANEL_SCORE].value = score;
4619
4620     DisplayGameControlValues();
4621
4622     if (time == time_final)
4623       StopSound(SND_GAME_LEVELTIME_BONUS);
4624     else if (setup.sound_loops)
4625       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4626     else
4627       PlaySound(SND_GAME_LEVELTIME_BONUS);
4628
4629     return;
4630   }
4631
4632   if (game_over_delay_2 > 0)
4633   {
4634     game_over_delay_2--;
4635
4636     return;
4637   }
4638
4639   if (health != health_final)
4640   {
4641     int health_count_dir = (health < health_final ? +1 : -1);
4642
4643     health += health_count_dir;
4644     score  += level.score[SC_TIME_BONUS];
4645
4646     local_player->LevelSolved_CountingHealth = health;
4647     local_player->LevelSolved_CountingScore = score;
4648
4649     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4650     game_panel_controls[GAME_PANEL_SCORE].value = score;
4651
4652     DisplayGameControlValues();
4653
4654     if (health == health_final)
4655       StopSound(SND_GAME_LEVELTIME_BONUS);
4656     else if (setup.sound_loops)
4657       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4658     else
4659       PlaySound(SND_GAME_LEVELTIME_BONUS);
4660
4661     return;
4662   }
4663
4664   local_player->LevelSolved_PanelOff = TRUE;
4665
4666   if (game_over_delay_3 > 0)
4667   {
4668     game_over_delay_3--;
4669
4670     return;
4671   }
4672
4673   GameEnd();
4674 }
4675
4676 void GameEnd()
4677 {
4678   int hi_pos;
4679   boolean raise_level = FALSE;
4680
4681   local_player->LevelSolved_GameEnd = TRUE;
4682
4683   if (!global.use_envelope_request)
4684     CloseDoor(DOOR_CLOSE_1);
4685
4686   if (local_player->LevelSolved_SaveTape)
4687   {
4688     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4689   }
4690
4691   CloseDoor(DOOR_CLOSE_ALL);
4692
4693   if (level_editor_test_game)
4694   {
4695     SetGameStatus(GAME_MODE_MAIN);
4696
4697     DrawMainMenu();
4698
4699     return;
4700   }
4701
4702   if (!local_player->LevelSolved_SaveScore)
4703   {
4704     SetGameStatus(GAME_MODE_MAIN);
4705
4706     DrawMainMenu();
4707
4708     return;
4709   }
4710
4711   if (level_nr == leveldir_current->handicap_level)
4712   {
4713     leveldir_current->handicap_level++;
4714
4715     SaveLevelSetup_SeriesInfo();
4716   }
4717
4718   if (setup.increment_levels &&
4719       level_nr < leveldir_current->last_level)
4720     raise_level = TRUE;                 /* advance to next level */
4721
4722   if ((hi_pos = NewHiScore()) >= 0) 
4723   {
4724     SetGameStatus(GAME_MODE_SCORES);
4725
4726     DrawHallOfFame(hi_pos);
4727
4728     if (raise_level)
4729     {
4730       level_nr++;
4731       TapeErase();
4732     }
4733   }
4734   else
4735   {
4736     SetGameStatus(GAME_MODE_MAIN);
4737
4738     if (raise_level)
4739     {
4740       level_nr++;
4741       TapeErase();
4742     }
4743
4744     DrawMainMenu();
4745   }
4746 }
4747
4748 int NewHiScore()
4749 {
4750   int k, l;
4751   int position = -1;
4752   boolean one_score_entry_per_name = !program.many_scores_per_name;
4753
4754   LoadScore(level_nr);
4755
4756   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4757       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4758     return -1;
4759
4760   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4761   {
4762     if (local_player->score_final > highscore[k].Score)
4763     {
4764       /* player has made it to the hall of fame */
4765
4766       if (k < MAX_SCORE_ENTRIES - 1)
4767       {
4768         int m = MAX_SCORE_ENTRIES - 1;
4769
4770         if (one_score_entry_per_name)
4771         {
4772           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4773             if (strEqual(setup.player_name, highscore[l].Name))
4774               m = l;
4775
4776           if (m == k)   /* player's new highscore overwrites his old one */
4777             goto put_into_list;
4778         }
4779
4780         for (l = m; l > k; l--)
4781         {
4782           strcpy(highscore[l].Name, highscore[l - 1].Name);
4783           highscore[l].Score = highscore[l - 1].Score;
4784         }
4785       }
4786
4787       put_into_list:
4788
4789       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4790       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4791       highscore[k].Score = local_player->score_final; 
4792       position = k;
4793
4794       break;
4795     }
4796     else if (one_score_entry_per_name &&
4797              !strncmp(setup.player_name, highscore[k].Name,
4798                       MAX_PLAYER_NAME_LEN))
4799       break;    /* player already there with a higher score */
4800   }
4801
4802   if (position >= 0) 
4803     SaveScore(level_nr);
4804
4805   return position;
4806 }
4807
4808 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4809 {
4810   int element = Feld[x][y];
4811   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4812   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4813   int horiz_move = (dx != 0);
4814   int sign = (horiz_move ? dx : dy);
4815   int step = sign * element_info[element].move_stepsize;
4816
4817   /* special values for move stepsize for spring and things on conveyor belt */
4818   if (horiz_move)
4819   {
4820     if (CAN_FALL(element) &&
4821         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4822       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4823     else if (element == EL_SPRING)
4824       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4825   }
4826
4827   return step;
4828 }
4829
4830 inline static int getElementMoveStepsize(int x, int y)
4831 {
4832   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4833 }
4834
4835 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4836 {
4837   if (player->GfxAction != action || player->GfxDir != dir)
4838   {
4839     player->GfxAction = action;
4840     player->GfxDir = dir;
4841     player->Frame = 0;
4842     player->StepFrame = 0;
4843   }
4844 }
4845
4846 static void ResetGfxFrame(int x, int y)
4847 {
4848   // profiling showed that "autotest" spends 10~20% of its time in this function
4849   if (DrawingDeactivatedField())
4850     return;
4851
4852   int element = Feld[x][y];
4853   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4854
4855   if (graphic_info[graphic].anim_global_sync)
4856     GfxFrame[x][y] = FrameCounter;
4857   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4858     GfxFrame[x][y] = CustomValue[x][y];
4859   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4860     GfxFrame[x][y] = element_info[element].collect_score;
4861   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4862     GfxFrame[x][y] = ChangeDelay[x][y];
4863 }
4864
4865 static void ResetGfxAnimation(int x, int y)
4866 {
4867   GfxAction[x][y] = ACTION_DEFAULT;
4868   GfxDir[x][y] = MovDir[x][y];
4869   GfxFrame[x][y] = 0;
4870
4871   ResetGfxFrame(x, y);
4872 }
4873
4874 static void ResetRandomAnimationValue(int x, int y)
4875 {
4876   GfxRandom[x][y] = INIT_GFX_RANDOM();
4877 }
4878
4879 void InitMovingField(int x, int y, int direction)
4880 {
4881   int element = Feld[x][y];
4882   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4883   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4884   int newx = x + dx;
4885   int newy = y + dy;
4886   boolean is_moving_before, is_moving_after;
4887
4888   /* check if element was/is moving or being moved before/after mode change */
4889   is_moving_before = (WasJustMoving[x][y] != 0);
4890   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4891
4892   /* reset animation only for moving elements which change direction of moving
4893      or which just started or stopped moving
4894      (else CEs with property "can move" / "not moving" are reset each frame) */
4895   if (is_moving_before != is_moving_after ||
4896       direction != MovDir[x][y])
4897     ResetGfxAnimation(x, y);
4898
4899   MovDir[x][y] = direction;
4900   GfxDir[x][y] = direction;
4901
4902   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4903                      direction == MV_DOWN && CAN_FALL(element) ?
4904                      ACTION_FALLING : ACTION_MOVING);
4905
4906   /* this is needed for CEs with property "can move" / "not moving" */
4907
4908   if (is_moving_after)
4909   {
4910     if (Feld[newx][newy] == EL_EMPTY)
4911       Feld[newx][newy] = EL_BLOCKED;
4912
4913     MovDir[newx][newy] = MovDir[x][y];
4914
4915     CustomValue[newx][newy] = CustomValue[x][y];
4916
4917     GfxFrame[newx][newy] = GfxFrame[x][y];
4918     GfxRandom[newx][newy] = GfxRandom[x][y];
4919     GfxAction[newx][newy] = GfxAction[x][y];
4920     GfxDir[newx][newy] = GfxDir[x][y];
4921   }
4922 }
4923
4924 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4925 {
4926   int direction = MovDir[x][y];
4927   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4928   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4929
4930   *goes_to_x = newx;
4931   *goes_to_y = newy;
4932 }
4933
4934 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4935 {
4936   int oldx = x, oldy = y;
4937   int direction = MovDir[x][y];
4938
4939   if (direction == MV_LEFT)
4940     oldx++;
4941   else if (direction == MV_RIGHT)
4942     oldx--;
4943   else if (direction == MV_UP)
4944     oldy++;
4945   else if (direction == MV_DOWN)
4946     oldy--;
4947
4948   *comes_from_x = oldx;
4949   *comes_from_y = oldy;
4950 }
4951
4952 int MovingOrBlocked2Element(int x, int y)
4953 {
4954   int element = Feld[x][y];
4955
4956   if (element == EL_BLOCKED)
4957   {
4958     int oldx, oldy;
4959
4960     Blocked2Moving(x, y, &oldx, &oldy);
4961     return Feld[oldx][oldy];
4962   }
4963   else
4964     return element;
4965 }
4966
4967 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4968 {
4969   /* like MovingOrBlocked2Element(), but if element is moving
4970      and (x,y) is the field the moving element is just leaving,
4971      return EL_BLOCKED instead of the element value */
4972   int element = Feld[x][y];
4973
4974   if (IS_MOVING(x, y))
4975   {
4976     if (element == EL_BLOCKED)
4977     {
4978       int oldx, oldy;
4979
4980       Blocked2Moving(x, y, &oldx, &oldy);
4981       return Feld[oldx][oldy];
4982     }
4983     else
4984       return EL_BLOCKED;
4985   }
4986   else
4987     return element;
4988 }
4989
4990 static void RemoveField(int x, int y)
4991 {
4992   Feld[x][y] = EL_EMPTY;
4993
4994   MovPos[x][y] = 0;
4995   MovDir[x][y] = 0;
4996   MovDelay[x][y] = 0;
4997
4998   CustomValue[x][y] = 0;
4999
5000   AmoebaNr[x][y] = 0;
5001   ChangeDelay[x][y] = 0;
5002   ChangePage[x][y] = -1;
5003   Pushed[x][y] = FALSE;
5004
5005   GfxElement[x][y] = EL_UNDEFINED;
5006   GfxAction[x][y] = ACTION_DEFAULT;
5007   GfxDir[x][y] = MV_NONE;
5008 }
5009
5010 void RemoveMovingField(int x, int y)
5011 {
5012   int oldx = x, oldy = y, newx = x, newy = y;
5013   int element = Feld[x][y];
5014   int next_element = EL_UNDEFINED;
5015
5016   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5017     return;
5018
5019   if (IS_MOVING(x, y))
5020   {
5021     Moving2Blocked(x, y, &newx, &newy);
5022
5023     if (Feld[newx][newy] != EL_BLOCKED)
5024     {
5025       /* element is moving, but target field is not free (blocked), but
5026          already occupied by something different (example: acid pool);
5027          in this case, only remove the moving field, but not the target */
5028
5029       RemoveField(oldx, oldy);
5030
5031       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5032
5033       TEST_DrawLevelField(oldx, oldy);
5034
5035       return;
5036     }
5037   }
5038   else if (element == EL_BLOCKED)
5039   {
5040     Blocked2Moving(x, y, &oldx, &oldy);
5041     if (!IS_MOVING(oldx, oldy))
5042       return;
5043   }
5044
5045   if (element == EL_BLOCKED &&
5046       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5047        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5048        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5049        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5050        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5051        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5052     next_element = get_next_element(Feld[oldx][oldy]);
5053
5054   RemoveField(oldx, oldy);
5055   RemoveField(newx, newy);
5056
5057   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5058
5059   if (next_element != EL_UNDEFINED)
5060     Feld[oldx][oldy] = next_element;
5061
5062   TEST_DrawLevelField(oldx, oldy);
5063   TEST_DrawLevelField(newx, newy);
5064 }
5065
5066 void DrawDynamite(int x, int y)
5067 {
5068   int sx = SCREENX(x), sy = SCREENY(y);
5069   int graphic = el2img(Feld[x][y]);
5070   int frame;
5071
5072   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5073     return;
5074
5075   if (IS_WALKABLE_INSIDE(Back[x][y]))
5076     return;
5077
5078   if (Back[x][y])
5079     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5080   else if (Store[x][y])
5081     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5082
5083   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5084
5085   if (Back[x][y] || Store[x][y])
5086     DrawGraphicThruMask(sx, sy, graphic, frame);
5087   else
5088     DrawGraphic(sx, sy, graphic, frame);
5089 }
5090
5091 void CheckDynamite(int x, int y)
5092 {
5093   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5094   {
5095     MovDelay[x][y]--;
5096
5097     if (MovDelay[x][y] != 0)
5098     {
5099       DrawDynamite(x, y);
5100       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5101
5102       return;
5103     }
5104   }
5105
5106   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5107
5108   Bang(x, y);
5109 }
5110
5111 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5112 {
5113   boolean num_checked_players = 0;
5114   int i;
5115
5116   for (i = 0; i < MAX_PLAYERS; i++)
5117   {
5118     if (stored_player[i].active)
5119     {
5120       int sx = stored_player[i].jx;
5121       int sy = stored_player[i].jy;
5122
5123       if (num_checked_players == 0)
5124       {
5125         *sx1 = *sx2 = sx;
5126         *sy1 = *sy2 = sy;
5127       }
5128       else
5129       {
5130         *sx1 = MIN(*sx1, sx);
5131         *sy1 = MIN(*sy1, sy);
5132         *sx2 = MAX(*sx2, sx);
5133         *sy2 = MAX(*sy2, sy);
5134       }
5135
5136       num_checked_players++;
5137     }
5138   }
5139 }
5140
5141 static boolean checkIfAllPlayersFitToScreen_RND()
5142 {
5143   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5144
5145   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5146
5147   return (sx2 - sx1 < SCR_FIELDX &&
5148           sy2 - sy1 < SCR_FIELDY);
5149 }
5150
5151 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5152 {
5153   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5154
5155   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5156
5157   *sx = (sx1 + sx2) / 2;
5158   *sy = (sy1 + sy2) / 2;
5159 }
5160
5161 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5162                         boolean center_screen, boolean quick_relocation)
5163 {
5164   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5165   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5166   boolean no_delay = (tape.warp_forward);
5167   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5168   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5169   int new_scroll_x, new_scroll_y;
5170
5171   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5172   {
5173     /* case 1: quick relocation inside visible screen (without scrolling) */
5174
5175     RedrawPlayfield();
5176
5177     return;
5178   }
5179
5180   if (!level.shifted_relocation || center_screen)
5181   {
5182     /* relocation _with_ centering of screen */
5183
5184     new_scroll_x = SCROLL_POSITION_X(x);
5185     new_scroll_y = SCROLL_POSITION_Y(y);
5186   }
5187   else
5188   {
5189     /* relocation _without_ centering of screen */
5190
5191     int center_scroll_x = SCROLL_POSITION_X(old_x);
5192     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5193     int offset_x = x + (scroll_x - center_scroll_x);
5194     int offset_y = y + (scroll_y - center_scroll_y);
5195
5196     /* for new screen position, apply previous offset to center position */
5197     new_scroll_x = SCROLL_POSITION_X(offset_x);
5198     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5199   }
5200
5201   if (quick_relocation)
5202   {
5203     /* case 2: quick relocation (redraw without visible scrolling) */
5204
5205     scroll_x = new_scroll_x;
5206     scroll_y = new_scroll_y;
5207
5208     RedrawPlayfield();
5209
5210     return;
5211   }
5212
5213   /* case 3: visible relocation (with scrolling to new position) */
5214
5215   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5216
5217   SetVideoFrameDelay(wait_delay_value);
5218
5219   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5220   {
5221     int dx = 0, dy = 0;
5222     int fx = FX, fy = FY;
5223
5224     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5225     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5226
5227     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5228       break;
5229
5230     scroll_x -= dx;
5231     scroll_y -= dy;
5232
5233     fx += dx * TILEX / 2;
5234     fy += dy * TILEY / 2;
5235
5236     ScrollLevel(dx, dy);
5237     DrawAllPlayers();
5238
5239     /* scroll in two steps of half tile size to make things smoother */
5240     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5241
5242     /* scroll second step to align at full tile size */
5243     BlitScreenToBitmap(window);
5244   }
5245
5246   DrawAllPlayers();
5247   BackToFront();
5248
5249   SetVideoFrameDelay(frame_delay_value_old);
5250 }
5251
5252 void RelocatePlayer(int jx, int jy, int el_player_raw)
5253 {
5254   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5255   int player_nr = GET_PLAYER_NR(el_player);
5256   struct PlayerInfo *player = &stored_player[player_nr];
5257   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5258   boolean no_delay = (tape.warp_forward);
5259   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5260   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5261   int old_jx = player->jx;
5262   int old_jy = player->jy;
5263   int old_element = Feld[old_jx][old_jy];
5264   int element = Feld[jx][jy];
5265   boolean player_relocated = (old_jx != jx || old_jy != jy);
5266
5267   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5268   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5269   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5270   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5271   int leave_side_horiz = move_dir_horiz;
5272   int leave_side_vert  = move_dir_vert;
5273   int enter_side = enter_side_horiz | enter_side_vert;
5274   int leave_side = leave_side_horiz | leave_side_vert;
5275
5276   if (player->GameOver)         /* do not reanimate dead player */
5277     return;
5278
5279   if (!player_relocated)        /* no need to relocate the player */
5280     return;
5281
5282   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5283   {
5284     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5285     DrawLevelField(jx, jy);
5286   }
5287
5288   if (player->present)
5289   {
5290     while (player->MovPos)
5291     {
5292       ScrollPlayer(player, SCROLL_GO_ON);
5293       ScrollScreen(NULL, SCROLL_GO_ON);
5294
5295       AdvanceFrameAndPlayerCounters(player->index_nr);
5296
5297       DrawPlayer(player);
5298
5299       BackToFront_WithFrameDelay(wait_delay_value);
5300     }
5301
5302     DrawPlayer(player);         /* needed here only to cleanup last field */
5303     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5304
5305     player->is_moving = FALSE;
5306   }
5307
5308   if (IS_CUSTOM_ELEMENT(old_element))
5309     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5310                                CE_LEFT_BY_PLAYER,
5311                                player->index_bit, leave_side);
5312
5313   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5314                                       CE_PLAYER_LEAVES_X,
5315                                       player->index_bit, leave_side);
5316
5317   Feld[jx][jy] = el_player;
5318   InitPlayerField(jx, jy, el_player, TRUE);
5319
5320   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5321      possible that the relocation target field did not contain a player element,
5322      but a walkable element, to which the new player was relocated -- in this
5323      case, restore that (already initialized!) element on the player field */
5324   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5325   {
5326     Feld[jx][jy] = element;     /* restore previously existing element */
5327   }
5328
5329   /* only visually relocate centered player */
5330   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5331                      FALSE, level.instant_relocation);
5332
5333   TestIfPlayerTouchesBadThing(jx, jy);
5334   TestIfPlayerTouchesCustomElement(jx, jy);
5335
5336   if (IS_CUSTOM_ELEMENT(element))
5337     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5338                                player->index_bit, enter_side);
5339
5340   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5341                                       player->index_bit, enter_side);
5342
5343   if (player->is_switching)
5344   {
5345     /* ensure that relocation while still switching an element does not cause
5346        a new element to be treated as also switched directly after relocation
5347        (this is important for teleporter switches that teleport the player to
5348        a place where another teleporter switch is in the same direction, which
5349        would then incorrectly be treated as immediately switched before the
5350        direction key that caused the switch was released) */
5351
5352     player->switch_x += jx - old_jx;
5353     player->switch_y += jy - old_jy;
5354   }
5355 }
5356
5357 void Explode(int ex, int ey, int phase, int mode)
5358 {
5359   int x, y;
5360   int last_phase;
5361   int border_element;
5362
5363   /* !!! eliminate this variable !!! */
5364   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5365
5366   if (game.explosions_delayed)
5367   {
5368     ExplodeField[ex][ey] = mode;
5369     return;
5370   }
5371
5372   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5373   {
5374     int center_element = Feld[ex][ey];
5375     int artwork_element, explosion_element;     /* set these values later */
5376
5377     /* remove things displayed in background while burning dynamite */
5378     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5379       Back[ex][ey] = 0;
5380
5381     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5382     {
5383       /* put moving element to center field (and let it explode there) */
5384       center_element = MovingOrBlocked2Element(ex, ey);
5385       RemoveMovingField(ex, ey);
5386       Feld[ex][ey] = center_element;
5387     }
5388
5389     /* now "center_element" is finally determined -- set related values now */
5390     artwork_element = center_element;           /* for custom player artwork */
5391     explosion_element = center_element;         /* for custom player artwork */
5392
5393     if (IS_PLAYER(ex, ey))
5394     {
5395       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5396
5397       artwork_element = stored_player[player_nr].artwork_element;
5398
5399       if (level.use_explosion_element[player_nr])
5400       {
5401         explosion_element = level.explosion_element[player_nr];
5402         artwork_element = explosion_element;
5403       }
5404     }
5405
5406     if (mode == EX_TYPE_NORMAL ||
5407         mode == EX_TYPE_CENTER ||
5408         mode == EX_TYPE_CROSS)
5409       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5410
5411     last_phase = element_info[explosion_element].explosion_delay + 1;
5412
5413     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5414     {
5415       int xx = x - ex + 1;
5416       int yy = y - ey + 1;
5417       int element;
5418
5419       if (!IN_LEV_FIELD(x, y) ||
5420           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5421           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5422         continue;
5423
5424       element = Feld[x][y];
5425
5426       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5427       {
5428         element = MovingOrBlocked2Element(x, y);
5429
5430         if (!IS_EXPLOSION_PROOF(element))
5431           RemoveMovingField(x, y);
5432       }
5433
5434       /* indestructible elements can only explode in center (but not flames) */
5435       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5436                                            mode == EX_TYPE_BORDER)) ||
5437           element == EL_FLAMES)
5438         continue;
5439
5440       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5441          behaviour, for example when touching a yamyam that explodes to rocks
5442          with active deadly shield, a rock is created under the player !!! */
5443       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5444 #if 0
5445       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5446           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5447            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5448 #else
5449       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5450 #endif
5451       {
5452         if (IS_ACTIVE_BOMB(element))
5453         {
5454           /* re-activate things under the bomb like gate or penguin */
5455           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5456           Back[x][y] = 0;
5457         }
5458
5459         continue;
5460       }
5461
5462       /* save walkable background elements while explosion on same tile */
5463       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5464           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5465         Back[x][y] = element;
5466
5467       /* ignite explodable elements reached by other explosion */
5468       if (element == EL_EXPLOSION)
5469         element = Store2[x][y];
5470
5471       if (AmoebaNr[x][y] &&
5472           (element == EL_AMOEBA_FULL ||
5473            element == EL_BD_AMOEBA ||
5474            element == EL_AMOEBA_GROWING))
5475       {
5476         AmoebaCnt[AmoebaNr[x][y]]--;
5477         AmoebaCnt2[AmoebaNr[x][y]]--;
5478       }
5479
5480       RemoveField(x, y);
5481
5482       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5483       {
5484         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5485
5486         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5487
5488         if (PLAYERINFO(ex, ey)->use_murphy)
5489           Store[x][y] = EL_EMPTY;
5490       }
5491
5492       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5493          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5494       else if (ELEM_IS_PLAYER(center_element))
5495         Store[x][y] = EL_EMPTY;
5496       else if (center_element == EL_YAMYAM)
5497         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5498       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5499         Store[x][y] = element_info[center_element].content.e[xx][yy];
5500 #if 1
5501       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5502          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5503          otherwise) -- FIX THIS !!! */
5504       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5505         Store[x][y] = element_info[element].content.e[1][1];
5506 #else
5507       else if (!CAN_EXPLODE(element))
5508         Store[x][y] = element_info[element].content.e[1][1];
5509 #endif
5510       else
5511         Store[x][y] = EL_EMPTY;
5512
5513       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5514           center_element == EL_AMOEBA_TO_DIAMOND)
5515         Store2[x][y] = element;
5516
5517       Feld[x][y] = EL_EXPLOSION;
5518       GfxElement[x][y] = artwork_element;
5519
5520       ExplodePhase[x][y] = 1;
5521       ExplodeDelay[x][y] = last_phase;
5522
5523       Stop[x][y] = TRUE;
5524     }
5525
5526     if (center_element == EL_YAMYAM)
5527       game.yamyam_content_nr =
5528         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5529
5530     return;
5531   }
5532
5533   if (Stop[ex][ey])
5534     return;
5535
5536   x = ex;
5537   y = ey;
5538
5539   if (phase == 1)
5540     GfxFrame[x][y] = 0;         /* restart explosion animation */
5541
5542   last_phase = ExplodeDelay[x][y];
5543
5544   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5545
5546   /* this can happen if the player leaves an explosion just in time */
5547   if (GfxElement[x][y] == EL_UNDEFINED)
5548     GfxElement[x][y] = EL_EMPTY;
5549
5550   border_element = Store2[x][y];
5551   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5552     border_element = StorePlayer[x][y];
5553
5554   if (phase == element_info[border_element].ignition_delay ||
5555       phase == last_phase)
5556   {
5557     boolean border_explosion = FALSE;
5558
5559     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5560         !PLAYER_EXPLOSION_PROTECTED(x, y))
5561     {
5562       KillPlayerUnlessExplosionProtected(x, y);
5563       border_explosion = TRUE;
5564     }
5565     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5566     {
5567       Feld[x][y] = Store2[x][y];
5568       Store2[x][y] = 0;
5569       Bang(x, y);
5570       border_explosion = TRUE;
5571     }
5572     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5573     {
5574       AmoebeUmwandeln(x, y);
5575       Store2[x][y] = 0;
5576       border_explosion = TRUE;
5577     }
5578
5579     /* if an element just explodes due to another explosion (chain-reaction),
5580        do not immediately end the new explosion when it was the last frame of
5581        the explosion (as it would be done in the following "if"-statement!) */
5582     if (border_explosion && phase == last_phase)
5583       return;
5584   }
5585
5586   if (phase == last_phase)
5587   {
5588     int element;
5589
5590     element = Feld[x][y] = Store[x][y];
5591     Store[x][y] = Store2[x][y] = 0;
5592     GfxElement[x][y] = EL_UNDEFINED;
5593
5594     /* player can escape from explosions and might therefore be still alive */
5595     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5596         element <= EL_PLAYER_IS_EXPLODING_4)
5597     {
5598       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5599       int explosion_element = EL_PLAYER_1 + player_nr;
5600       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5601       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5602
5603       if (level.use_explosion_element[player_nr])
5604         explosion_element = level.explosion_element[player_nr];
5605
5606       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5607                     element_info[explosion_element].content.e[xx][yy]);
5608     }
5609
5610     /* restore probably existing indestructible background element */
5611     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5612       element = Feld[x][y] = Back[x][y];
5613     Back[x][y] = 0;
5614
5615     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5616     GfxDir[x][y] = MV_NONE;
5617     ChangeDelay[x][y] = 0;
5618     ChangePage[x][y] = -1;
5619
5620     CustomValue[x][y] = 0;
5621
5622     InitField_WithBug2(x, y, FALSE);
5623
5624     TEST_DrawLevelField(x, y);
5625
5626     TestIfElementTouchesCustomElement(x, y);
5627
5628     if (GFX_CRUMBLED(element))
5629       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5630
5631     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5632       StorePlayer[x][y] = 0;
5633
5634     if (ELEM_IS_PLAYER(element))
5635       RelocatePlayer(x, y, element);
5636   }
5637   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5638   {
5639     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5640     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5641
5642     if (phase == delay)
5643       TEST_DrawLevelFieldCrumbled(x, y);
5644
5645     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5646     {
5647       DrawLevelElement(x, y, Back[x][y]);
5648       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5649     }
5650     else if (IS_WALKABLE_UNDER(Back[x][y]))
5651     {
5652       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5653       DrawLevelElementThruMask(x, y, Back[x][y]);
5654     }
5655     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5656       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5657   }
5658 }
5659
5660 void DynaExplode(int ex, int ey)
5661 {
5662   int i, j;
5663   int dynabomb_element = Feld[ex][ey];
5664   int dynabomb_size = 1;
5665   boolean dynabomb_xl = FALSE;
5666   struct PlayerInfo *player;
5667   static int xy[4][2] =
5668   {
5669     { 0, -1 },
5670     { -1, 0 },
5671     { +1, 0 },
5672     { 0, +1 }
5673   };
5674
5675   if (IS_ACTIVE_BOMB(dynabomb_element))
5676   {
5677     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5678     dynabomb_size = player->dynabomb_size;
5679     dynabomb_xl = player->dynabomb_xl;
5680     player->dynabombs_left++;
5681   }
5682
5683   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5684
5685   for (i = 0; i < NUM_DIRECTIONS; i++)
5686   {
5687     for (j = 1; j <= dynabomb_size; j++)
5688     {
5689       int x = ex + j * xy[i][0];
5690       int y = ey + j * xy[i][1];
5691       int element;
5692
5693       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5694         break;
5695
5696       element = Feld[x][y];
5697
5698       /* do not restart explosions of fields with active bombs */
5699       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5700         continue;
5701
5702       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5703
5704       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5705           !IS_DIGGABLE(element) && !dynabomb_xl)
5706         break;
5707     }
5708   }
5709 }
5710
5711 void Bang(int x, int y)
5712 {
5713   int element = MovingOrBlocked2Element(x, y);
5714   int explosion_type = EX_TYPE_NORMAL;
5715
5716   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5717   {
5718     struct PlayerInfo *player = PLAYERINFO(x, y);
5719
5720     element = Feld[x][y] = player->initial_element;
5721
5722     if (level.use_explosion_element[player->index_nr])
5723     {
5724       int explosion_element = level.explosion_element[player->index_nr];
5725
5726       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5727         explosion_type = EX_TYPE_CROSS;
5728       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5729         explosion_type = EX_TYPE_CENTER;
5730     }
5731   }
5732
5733   switch (element)
5734   {
5735     case EL_BUG:
5736     case EL_SPACESHIP:
5737     case EL_BD_BUTTERFLY:
5738     case EL_BD_FIREFLY:
5739     case EL_YAMYAM:
5740     case EL_DARK_YAMYAM:
5741     case EL_ROBOT:
5742     case EL_PACMAN:
5743     case EL_MOLE:
5744       RaiseScoreElement(element);
5745       break;
5746
5747     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5748     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5749     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5750     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5751     case EL_DYNABOMB_INCREASE_NUMBER:
5752     case EL_DYNABOMB_INCREASE_SIZE:
5753     case EL_DYNABOMB_INCREASE_POWER:
5754       explosion_type = EX_TYPE_DYNA;
5755       break;
5756
5757     case EL_DC_LANDMINE:
5758       explosion_type = EX_TYPE_CENTER;
5759       break;
5760
5761     case EL_PENGUIN:
5762     case EL_LAMP:
5763     case EL_LAMP_ACTIVE:
5764     case EL_AMOEBA_TO_DIAMOND:
5765       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5766         explosion_type = EX_TYPE_CENTER;
5767       break;
5768
5769     default:
5770       if (element_info[element].explosion_type == EXPLODES_CROSS)
5771         explosion_type = EX_TYPE_CROSS;
5772       else if (element_info[element].explosion_type == EXPLODES_1X1)
5773         explosion_type = EX_TYPE_CENTER;
5774       break;
5775   }
5776
5777   if (explosion_type == EX_TYPE_DYNA)
5778     DynaExplode(x, y);
5779   else
5780     Explode(x, y, EX_PHASE_START, explosion_type);
5781
5782   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5783 }
5784
5785 void SplashAcid(int x, int y)
5786 {
5787   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5788       (!IN_LEV_FIELD(x - 1, y - 2) ||
5789        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5790     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5791
5792   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5793       (!IN_LEV_FIELD(x + 1, y - 2) ||
5794        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5795     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5796
5797   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5798 }
5799
5800 static void InitBeltMovement()
5801 {
5802   static int belt_base_element[4] =
5803   {
5804     EL_CONVEYOR_BELT_1_LEFT,
5805     EL_CONVEYOR_BELT_2_LEFT,
5806     EL_CONVEYOR_BELT_3_LEFT,
5807     EL_CONVEYOR_BELT_4_LEFT
5808   };
5809   static int belt_base_active_element[4] =
5810   {
5811     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5812     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5813     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5814     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5815   };
5816
5817   int x, y, i, j;
5818
5819   /* set frame order for belt animation graphic according to belt direction */
5820   for (i = 0; i < NUM_BELTS; i++)
5821   {
5822     int belt_nr = i;
5823
5824     for (j = 0; j < NUM_BELT_PARTS; j++)
5825     {
5826       int element = belt_base_active_element[belt_nr] + j;
5827       int graphic_1 = el2img(element);
5828       int graphic_2 = el2panelimg(element);
5829
5830       if (game.belt_dir[i] == MV_LEFT)
5831       {
5832         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5833         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5834       }
5835       else
5836       {
5837         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5838         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5839       }
5840     }
5841   }
5842
5843   SCAN_PLAYFIELD(x, y)
5844   {
5845     int element = Feld[x][y];
5846
5847     for (i = 0; i < NUM_BELTS; i++)
5848     {
5849       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5850       {
5851         int e_belt_nr = getBeltNrFromBeltElement(element);
5852         int belt_nr = i;
5853
5854         if (e_belt_nr == belt_nr)
5855         {
5856           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5857
5858           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5859         }
5860       }
5861     }
5862   }
5863 }
5864
5865 static void ToggleBeltSwitch(int x, int y)
5866 {
5867   static int belt_base_element[4] =
5868   {
5869     EL_CONVEYOR_BELT_1_LEFT,
5870     EL_CONVEYOR_BELT_2_LEFT,
5871     EL_CONVEYOR_BELT_3_LEFT,
5872     EL_CONVEYOR_BELT_4_LEFT
5873   };
5874   static int belt_base_active_element[4] =
5875   {
5876     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5877     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5878     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5879     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5880   };
5881   static int belt_base_switch_element[4] =
5882   {
5883     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5884     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5885     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5886     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5887   };
5888   static int belt_move_dir[4] =
5889   {
5890     MV_LEFT,
5891     MV_NONE,
5892     MV_RIGHT,
5893     MV_NONE,
5894   };
5895
5896   int element = Feld[x][y];
5897   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5898   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5899   int belt_dir = belt_move_dir[belt_dir_nr];
5900   int xx, yy, i;
5901
5902   if (!IS_BELT_SWITCH(element))
5903     return;
5904
5905   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5906   game.belt_dir[belt_nr] = belt_dir;
5907
5908   if (belt_dir_nr == 3)
5909     belt_dir_nr = 1;
5910
5911   /* set frame order for belt animation graphic according to belt direction */
5912   for (i = 0; i < NUM_BELT_PARTS; i++)
5913   {
5914     int element = belt_base_active_element[belt_nr] + i;
5915     int graphic_1 = el2img(element);
5916     int graphic_2 = el2panelimg(element);
5917
5918     if (belt_dir == MV_LEFT)
5919     {
5920       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5921       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5922     }
5923     else
5924     {
5925       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5926       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5927     }
5928   }
5929
5930   SCAN_PLAYFIELD(xx, yy)
5931   {
5932     int element = Feld[xx][yy];
5933
5934     if (IS_BELT_SWITCH(element))
5935     {
5936       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5937
5938       if (e_belt_nr == belt_nr)
5939       {
5940         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5941         TEST_DrawLevelField(xx, yy);
5942       }
5943     }
5944     else if (IS_BELT(element) && belt_dir != MV_NONE)
5945     {
5946       int e_belt_nr = getBeltNrFromBeltElement(element);
5947
5948       if (e_belt_nr == belt_nr)
5949       {
5950         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5951
5952         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5953         TEST_DrawLevelField(xx, yy);
5954       }
5955     }
5956     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5957     {
5958       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5959
5960       if (e_belt_nr == belt_nr)
5961       {
5962         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5963
5964         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5965         TEST_DrawLevelField(xx, yy);
5966       }
5967     }
5968   }
5969 }
5970
5971 static void ToggleSwitchgateSwitch(int x, int y)
5972 {
5973   int xx, yy;
5974
5975   game.switchgate_pos = !game.switchgate_pos;
5976
5977   SCAN_PLAYFIELD(xx, yy)
5978   {
5979     int element = Feld[xx][yy];
5980
5981     if (element == EL_SWITCHGATE_SWITCH_UP)
5982     {
5983       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5984       TEST_DrawLevelField(xx, yy);
5985     }
5986     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5987     {
5988       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5989       TEST_DrawLevelField(xx, yy);
5990     }
5991     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5992     {
5993       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5994       TEST_DrawLevelField(xx, yy);
5995     }
5996     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5997     {
5998       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5999       TEST_DrawLevelField(xx, yy);
6000     }
6001     else if (element == EL_SWITCHGATE_OPEN ||
6002              element == EL_SWITCHGATE_OPENING)
6003     {
6004       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6005
6006       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6007     }
6008     else if (element == EL_SWITCHGATE_CLOSED ||
6009              element == EL_SWITCHGATE_CLOSING)
6010     {
6011       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6012
6013       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6014     }
6015   }
6016 }
6017
6018 static int getInvisibleActiveFromInvisibleElement(int element)
6019 {
6020   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6021           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6022           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6023           element);
6024 }
6025
6026 static int getInvisibleFromInvisibleActiveElement(int element)
6027 {
6028   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6029           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6030           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6031           element);
6032 }
6033
6034 static void RedrawAllLightSwitchesAndInvisibleElements()
6035 {
6036   int x, y;
6037
6038   SCAN_PLAYFIELD(x, y)
6039   {
6040     int element = Feld[x][y];
6041
6042     if (element == EL_LIGHT_SWITCH &&
6043         game.light_time_left > 0)
6044     {
6045       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6046       TEST_DrawLevelField(x, y);
6047     }
6048     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6049              game.light_time_left == 0)
6050     {
6051       Feld[x][y] = EL_LIGHT_SWITCH;
6052       TEST_DrawLevelField(x, y);
6053     }
6054     else if (element == EL_EMC_DRIPPER &&
6055              game.light_time_left > 0)
6056     {
6057       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6058       TEST_DrawLevelField(x, y);
6059     }
6060     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6061              game.light_time_left == 0)
6062     {
6063       Feld[x][y] = EL_EMC_DRIPPER;
6064       TEST_DrawLevelField(x, y);
6065     }
6066     else if (element == EL_INVISIBLE_STEELWALL ||
6067              element == EL_INVISIBLE_WALL ||
6068              element == EL_INVISIBLE_SAND)
6069     {
6070       if (game.light_time_left > 0)
6071         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6072
6073       TEST_DrawLevelField(x, y);
6074
6075       /* uncrumble neighbour fields, if needed */
6076       if (element == EL_INVISIBLE_SAND)
6077         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6078     }
6079     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6080              element == EL_INVISIBLE_WALL_ACTIVE ||
6081              element == EL_INVISIBLE_SAND_ACTIVE)
6082     {
6083       if (game.light_time_left == 0)
6084         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6085
6086       TEST_DrawLevelField(x, y);
6087
6088       /* re-crumble neighbour fields, if needed */
6089       if (element == EL_INVISIBLE_SAND)
6090         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6091     }
6092   }
6093 }
6094
6095 static void RedrawAllInvisibleElementsForLenses()
6096 {
6097   int x, y;
6098
6099   SCAN_PLAYFIELD(x, y)
6100   {
6101     int element = Feld[x][y];
6102
6103     if (element == EL_EMC_DRIPPER &&
6104         game.lenses_time_left > 0)
6105     {
6106       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6107       TEST_DrawLevelField(x, y);
6108     }
6109     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6110              game.lenses_time_left == 0)
6111     {
6112       Feld[x][y] = EL_EMC_DRIPPER;
6113       TEST_DrawLevelField(x, y);
6114     }
6115     else if (element == EL_INVISIBLE_STEELWALL ||
6116              element == EL_INVISIBLE_WALL ||
6117              element == EL_INVISIBLE_SAND)
6118     {
6119       if (game.lenses_time_left > 0)
6120         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6121
6122       TEST_DrawLevelField(x, y);
6123
6124       /* uncrumble neighbour fields, if needed */
6125       if (element == EL_INVISIBLE_SAND)
6126         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6127     }
6128     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6129              element == EL_INVISIBLE_WALL_ACTIVE ||
6130              element == EL_INVISIBLE_SAND_ACTIVE)
6131     {
6132       if (game.lenses_time_left == 0)
6133         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6134
6135       TEST_DrawLevelField(x, y);
6136
6137       /* re-crumble neighbour fields, if needed */
6138       if (element == EL_INVISIBLE_SAND)
6139         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6140     }
6141   }
6142 }
6143
6144 static void RedrawAllInvisibleElementsForMagnifier()
6145 {
6146   int x, y;
6147
6148   SCAN_PLAYFIELD(x, y)
6149   {
6150     int element = Feld[x][y];
6151
6152     if (element == EL_EMC_FAKE_GRASS &&
6153         game.magnify_time_left > 0)
6154     {
6155       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6156       TEST_DrawLevelField(x, y);
6157     }
6158     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6159              game.magnify_time_left == 0)
6160     {
6161       Feld[x][y] = EL_EMC_FAKE_GRASS;
6162       TEST_DrawLevelField(x, y);
6163     }
6164     else if (IS_GATE_GRAY(element) &&
6165              game.magnify_time_left > 0)
6166     {
6167       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6168                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6169                     IS_EM_GATE_GRAY(element) ?
6170                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6171                     IS_EMC_GATE_GRAY(element) ?
6172                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6173                     IS_DC_GATE_GRAY(element) ?
6174                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6175                     element);
6176       TEST_DrawLevelField(x, y);
6177     }
6178     else if (IS_GATE_GRAY_ACTIVE(element) &&
6179              game.magnify_time_left == 0)
6180     {
6181       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6182                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6183                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6184                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6185                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6186                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6187                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6188                     EL_DC_GATE_WHITE_GRAY :
6189                     element);
6190       TEST_DrawLevelField(x, y);
6191     }
6192   }
6193 }
6194
6195 static void ToggleLightSwitch(int x, int y)
6196 {
6197   int element = Feld[x][y];
6198
6199   game.light_time_left =
6200     (element == EL_LIGHT_SWITCH ?
6201      level.time_light * FRAMES_PER_SECOND : 0);
6202
6203   RedrawAllLightSwitchesAndInvisibleElements();
6204 }
6205
6206 static void ActivateTimegateSwitch(int x, int y)
6207 {
6208   int xx, yy;
6209
6210   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6211
6212   SCAN_PLAYFIELD(xx, yy)
6213   {
6214     int element = Feld[xx][yy];
6215
6216     if (element == EL_TIMEGATE_CLOSED ||
6217         element == EL_TIMEGATE_CLOSING)
6218     {
6219       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6220       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6221     }
6222
6223     /*
6224     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6225     {
6226       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6227       TEST_DrawLevelField(xx, yy);
6228     }
6229     */
6230
6231   }
6232
6233   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6234                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6235 }
6236
6237 void Impact(int x, int y)
6238 {
6239   boolean last_line = (y == lev_fieldy - 1);
6240   boolean object_hit = FALSE;
6241   boolean impact = (last_line || object_hit);
6242   int element = Feld[x][y];
6243   int smashed = EL_STEELWALL;
6244
6245   if (!last_line)       /* check if element below was hit */
6246   {
6247     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6248       return;
6249
6250     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6251                                          MovDir[x][y + 1] != MV_DOWN ||
6252                                          MovPos[x][y + 1] <= TILEY / 2));
6253
6254     /* do not smash moving elements that left the smashed field in time */
6255     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6256         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6257       object_hit = FALSE;
6258
6259 #if USE_QUICKSAND_IMPACT_BUGFIX
6260     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6261     {
6262       RemoveMovingField(x, y + 1);
6263       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6264       Feld[x][y + 2] = EL_ROCK;
6265       TEST_DrawLevelField(x, y + 2);
6266
6267       object_hit = TRUE;
6268     }
6269
6270     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6271     {
6272       RemoveMovingField(x, y + 1);
6273       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6274       Feld[x][y + 2] = EL_ROCK;
6275       TEST_DrawLevelField(x, y + 2);
6276
6277       object_hit = TRUE;
6278     }
6279 #endif
6280
6281     if (object_hit)
6282       smashed = MovingOrBlocked2Element(x, y + 1);
6283
6284     impact = (last_line || object_hit);
6285   }
6286
6287   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6288   {
6289     SplashAcid(x, y + 1);
6290     return;
6291   }
6292
6293   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6294   /* only reset graphic animation if graphic really changes after impact */
6295   if (impact &&
6296       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6297   {
6298     ResetGfxAnimation(x, y);
6299     TEST_DrawLevelField(x, y);
6300   }
6301
6302   if (impact && CAN_EXPLODE_IMPACT(element))
6303   {
6304     Bang(x, y);
6305     return;
6306   }
6307   else if (impact && element == EL_PEARL &&
6308            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6309   {
6310     ResetGfxAnimation(x, y);
6311
6312     Feld[x][y] = EL_PEARL_BREAKING;
6313     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6314     return;
6315   }
6316   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6317   {
6318     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6319
6320     return;
6321   }
6322
6323   if (impact && element == EL_AMOEBA_DROP)
6324   {
6325     if (object_hit && IS_PLAYER(x, y + 1))
6326       KillPlayerUnlessEnemyProtected(x, y + 1);
6327     else if (object_hit && smashed == EL_PENGUIN)
6328       Bang(x, y + 1);
6329     else
6330     {
6331       Feld[x][y] = EL_AMOEBA_GROWING;
6332       Store[x][y] = EL_AMOEBA_WET;
6333
6334       ResetRandomAnimationValue(x, y);
6335     }
6336     return;
6337   }
6338
6339   if (object_hit)               /* check which object was hit */
6340   {
6341     if ((CAN_PASS_MAGIC_WALL(element) && 
6342          (smashed == EL_MAGIC_WALL ||
6343           smashed == EL_BD_MAGIC_WALL)) ||
6344         (CAN_PASS_DC_MAGIC_WALL(element) &&
6345          smashed == EL_DC_MAGIC_WALL))
6346     {
6347       int xx, yy;
6348       int activated_magic_wall =
6349         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6350          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6351          EL_DC_MAGIC_WALL_ACTIVE);
6352
6353       /* activate magic wall / mill */
6354       SCAN_PLAYFIELD(xx, yy)
6355       {
6356         if (Feld[xx][yy] == smashed)
6357           Feld[xx][yy] = activated_magic_wall;
6358       }
6359
6360       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6361       game.magic_wall_active = TRUE;
6362
6363       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6364                             SND_MAGIC_WALL_ACTIVATING :
6365                             smashed == EL_BD_MAGIC_WALL ?
6366                             SND_BD_MAGIC_WALL_ACTIVATING :
6367                             SND_DC_MAGIC_WALL_ACTIVATING));
6368     }
6369
6370     if (IS_PLAYER(x, y + 1))
6371     {
6372       if (CAN_SMASH_PLAYER(element))
6373       {
6374         KillPlayerUnlessEnemyProtected(x, y + 1);
6375         return;
6376       }
6377     }
6378     else if (smashed == EL_PENGUIN)
6379     {
6380       if (CAN_SMASH_PLAYER(element))
6381       {
6382         Bang(x, y + 1);
6383         return;
6384       }
6385     }
6386     else if (element == EL_BD_DIAMOND)
6387     {
6388       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6389       {
6390         Bang(x, y + 1);
6391         return;
6392       }
6393     }
6394     else if (((element == EL_SP_INFOTRON ||
6395                element == EL_SP_ZONK) &&
6396               (smashed == EL_SP_SNIKSNAK ||
6397                smashed == EL_SP_ELECTRON ||
6398                smashed == EL_SP_DISK_ORANGE)) ||
6399              (element == EL_SP_INFOTRON &&
6400               smashed == EL_SP_DISK_YELLOW))
6401     {
6402       Bang(x, y + 1);
6403       return;
6404     }
6405     else if (CAN_SMASH_EVERYTHING(element))
6406     {
6407       if (IS_CLASSIC_ENEMY(smashed) ||
6408           CAN_EXPLODE_SMASHED(smashed))
6409       {
6410         Bang(x, y + 1);
6411         return;
6412       }
6413       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6414       {
6415         if (smashed == EL_LAMP ||
6416             smashed == EL_LAMP_ACTIVE)
6417         {
6418           Bang(x, y + 1);
6419           return;
6420         }
6421         else if (smashed == EL_NUT)
6422         {
6423           Feld[x][y + 1] = EL_NUT_BREAKING;
6424           PlayLevelSound(x, y, SND_NUT_BREAKING);
6425           RaiseScoreElement(EL_NUT);
6426           return;
6427         }
6428         else if (smashed == EL_PEARL)
6429         {
6430           ResetGfxAnimation(x, y);
6431
6432           Feld[x][y + 1] = EL_PEARL_BREAKING;
6433           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6434           return;
6435         }
6436         else if (smashed == EL_DIAMOND)
6437         {
6438           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6439           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6440           return;
6441         }
6442         else if (IS_BELT_SWITCH(smashed))
6443         {
6444           ToggleBeltSwitch(x, y + 1);
6445         }
6446         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6447                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6448                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6449                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6450         {
6451           ToggleSwitchgateSwitch(x, y + 1);
6452         }
6453         else if (smashed == EL_LIGHT_SWITCH ||
6454                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6455         {
6456           ToggleLightSwitch(x, y + 1);
6457         }
6458         else
6459         {
6460           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6461
6462           CheckElementChangeBySide(x, y + 1, smashed, element,
6463                                    CE_SWITCHED, CH_SIDE_TOP);
6464           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6465                                             CH_SIDE_TOP);
6466         }
6467       }
6468       else
6469       {
6470         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6471       }
6472     }
6473   }
6474
6475   /* play sound of magic wall / mill */
6476   if (!last_line &&
6477       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6478        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6479        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6480   {
6481     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6482       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6483     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6484       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6485     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6486       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6487
6488     return;
6489   }
6490
6491   /* play sound of object that hits the ground */
6492   if (last_line || object_hit)
6493     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6494 }
6495
6496 inline static void TurnRoundExt(int x, int y)
6497 {
6498   static struct
6499   {
6500     int dx, dy;
6501   } move_xy[] =
6502   {
6503     {  0,  0 },
6504     { -1,  0 },
6505     { +1,  0 },
6506     {  0,  0 },
6507     {  0, -1 },
6508     {  0,  0 }, { 0, 0 }, { 0, 0 },
6509     {  0, +1 }
6510   };
6511   static struct
6512   {
6513     int left, right, back;
6514   } turn[] =
6515   {
6516     { 0,        0,              0        },
6517     { MV_DOWN,  MV_UP,          MV_RIGHT },
6518     { MV_UP,    MV_DOWN,        MV_LEFT  },
6519     { 0,        0,              0        },
6520     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6521     { 0,        0,              0        },
6522     { 0,        0,              0        },
6523     { 0,        0,              0        },
6524     { MV_RIGHT, MV_LEFT,        MV_UP    }
6525   };
6526
6527   int element = Feld[x][y];
6528   int move_pattern = element_info[element].move_pattern;
6529
6530   int old_move_dir = MovDir[x][y];
6531   int left_dir  = turn[old_move_dir].left;
6532   int right_dir = turn[old_move_dir].right;
6533   int back_dir  = turn[old_move_dir].back;
6534
6535   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6536   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6537   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6538   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6539
6540   int left_x  = x + left_dx,  left_y  = y + left_dy;
6541   int right_x = x + right_dx, right_y = y + right_dy;
6542   int move_x  = x + move_dx,  move_y  = y + move_dy;
6543
6544   int xx, yy;
6545
6546   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6547   {
6548     TestIfBadThingTouchesOtherBadThing(x, y);
6549
6550     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6551       MovDir[x][y] = right_dir;
6552     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6553       MovDir[x][y] = left_dir;
6554
6555     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6556       MovDelay[x][y] = 9;
6557     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6558       MovDelay[x][y] = 1;
6559   }
6560   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6561   {
6562     TestIfBadThingTouchesOtherBadThing(x, y);
6563
6564     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6565       MovDir[x][y] = left_dir;
6566     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6567       MovDir[x][y] = right_dir;
6568
6569     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6570       MovDelay[x][y] = 9;
6571     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6572       MovDelay[x][y] = 1;
6573   }
6574   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6575   {
6576     TestIfBadThingTouchesOtherBadThing(x, y);
6577
6578     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6579       MovDir[x][y] = left_dir;
6580     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6581       MovDir[x][y] = right_dir;
6582
6583     if (MovDir[x][y] != old_move_dir)
6584       MovDelay[x][y] = 9;
6585   }
6586   else if (element == EL_YAMYAM)
6587   {
6588     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6589     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6590
6591     if (can_turn_left && can_turn_right)
6592       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6593     else if (can_turn_left)
6594       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6595     else if (can_turn_right)
6596       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6597     else
6598       MovDir[x][y] = back_dir;
6599
6600     MovDelay[x][y] = 16 + 16 * RND(3);
6601   }
6602   else if (element == EL_DARK_YAMYAM)
6603   {
6604     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6605                                                          left_x, left_y);
6606     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6607                                                          right_x, right_y);
6608
6609     if (can_turn_left && can_turn_right)
6610       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6611     else if (can_turn_left)
6612       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6613     else if (can_turn_right)
6614       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6615     else
6616       MovDir[x][y] = back_dir;
6617
6618     MovDelay[x][y] = 16 + 16 * RND(3);
6619   }
6620   else if (element == EL_PACMAN)
6621   {
6622     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6623     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6624
6625     if (can_turn_left && can_turn_right)
6626       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6627     else if (can_turn_left)
6628       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6629     else if (can_turn_right)
6630       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6631     else
6632       MovDir[x][y] = back_dir;
6633
6634     MovDelay[x][y] = 6 + RND(40);
6635   }
6636   else if (element == EL_PIG)
6637   {
6638     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6639     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6640     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6641     boolean should_turn_left, should_turn_right, should_move_on;
6642     int rnd_value = 24;
6643     int rnd = RND(rnd_value);
6644
6645     should_turn_left = (can_turn_left &&
6646                         (!can_move_on ||
6647                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6648                                                    y + back_dy + left_dy)));
6649     should_turn_right = (can_turn_right &&
6650                          (!can_move_on ||
6651                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6652                                                     y + back_dy + right_dy)));
6653     should_move_on = (can_move_on &&
6654                       (!can_turn_left ||
6655                        !can_turn_right ||
6656                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6657                                                  y + move_dy + left_dy) ||
6658                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6659                                                  y + move_dy + right_dy)));
6660
6661     if (should_turn_left || should_turn_right || should_move_on)
6662     {
6663       if (should_turn_left && should_turn_right && should_move_on)
6664         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6665                         rnd < 2 * rnd_value / 3 ? right_dir :
6666                         old_move_dir);
6667       else if (should_turn_left && should_turn_right)
6668         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6669       else if (should_turn_left && should_move_on)
6670         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6671       else if (should_turn_right && should_move_on)
6672         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6673       else if (should_turn_left)
6674         MovDir[x][y] = left_dir;
6675       else if (should_turn_right)
6676         MovDir[x][y] = right_dir;
6677       else if (should_move_on)
6678         MovDir[x][y] = old_move_dir;
6679     }
6680     else if (can_move_on && rnd > rnd_value / 8)
6681       MovDir[x][y] = old_move_dir;
6682     else if (can_turn_left && can_turn_right)
6683       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6684     else if (can_turn_left && rnd > rnd_value / 8)
6685       MovDir[x][y] = left_dir;
6686     else if (can_turn_right && rnd > rnd_value/8)
6687       MovDir[x][y] = right_dir;
6688     else
6689       MovDir[x][y] = back_dir;
6690
6691     xx = x + move_xy[MovDir[x][y]].dx;
6692     yy = y + move_xy[MovDir[x][y]].dy;
6693
6694     if (!IN_LEV_FIELD(xx, yy) ||
6695         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6696       MovDir[x][y] = old_move_dir;
6697
6698     MovDelay[x][y] = 0;
6699   }
6700   else if (element == EL_DRAGON)
6701   {
6702     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6703     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6704     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6705     int rnd_value = 24;
6706     int rnd = RND(rnd_value);
6707
6708     if (can_move_on && rnd > rnd_value / 8)
6709       MovDir[x][y] = old_move_dir;
6710     else if (can_turn_left && can_turn_right)
6711       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6712     else if (can_turn_left && rnd > rnd_value / 8)
6713       MovDir[x][y] = left_dir;
6714     else if (can_turn_right && rnd > rnd_value / 8)
6715       MovDir[x][y] = right_dir;
6716     else
6717       MovDir[x][y] = back_dir;
6718
6719     xx = x + move_xy[MovDir[x][y]].dx;
6720     yy = y + move_xy[MovDir[x][y]].dy;
6721
6722     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6723       MovDir[x][y] = old_move_dir;
6724
6725     MovDelay[x][y] = 0;
6726   }
6727   else if (element == EL_MOLE)
6728   {
6729     boolean can_move_on =
6730       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6731                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6732                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6733     if (!can_move_on)
6734     {
6735       boolean can_turn_left =
6736         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6737                               IS_AMOEBOID(Feld[left_x][left_y])));
6738
6739       boolean can_turn_right =
6740         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6741                               IS_AMOEBOID(Feld[right_x][right_y])));
6742
6743       if (can_turn_left && can_turn_right)
6744         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6745       else if (can_turn_left)
6746         MovDir[x][y] = left_dir;
6747       else
6748         MovDir[x][y] = right_dir;
6749     }
6750
6751     if (MovDir[x][y] != old_move_dir)
6752       MovDelay[x][y] = 9;
6753   }
6754   else if (element == EL_BALLOON)
6755   {
6756     MovDir[x][y] = game.wind_direction;
6757     MovDelay[x][y] = 0;
6758   }
6759   else if (element == EL_SPRING)
6760   {
6761     if (MovDir[x][y] & MV_HORIZONTAL)
6762     {
6763       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6764           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6765       {
6766         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6767         ResetGfxAnimation(move_x, move_y);
6768         TEST_DrawLevelField(move_x, move_y);
6769
6770         MovDir[x][y] = back_dir;
6771       }
6772       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6773                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6774         MovDir[x][y] = MV_NONE;
6775     }
6776
6777     MovDelay[x][y] = 0;
6778   }
6779   else if (element == EL_ROBOT ||
6780            element == EL_SATELLITE ||
6781            element == EL_PENGUIN ||
6782            element == EL_EMC_ANDROID)
6783   {
6784     int attr_x = -1, attr_y = -1;
6785
6786     if (AllPlayersGone)
6787     {
6788       attr_x = ExitX;
6789       attr_y = ExitY;
6790     }
6791     else
6792     {
6793       int i;
6794
6795       for (i = 0; i < MAX_PLAYERS; i++)
6796       {
6797         struct PlayerInfo *player = &stored_player[i];
6798         int jx = player->jx, jy = player->jy;
6799
6800         if (!player->active)
6801           continue;
6802
6803         if (attr_x == -1 ||
6804             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6805         {
6806           attr_x = jx;
6807           attr_y = jy;
6808         }
6809       }
6810     }
6811
6812     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6813         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6814          game.engine_version < VERSION_IDENT(3,1,0,0)))
6815     {
6816       attr_x = ZX;
6817       attr_y = ZY;
6818     }
6819
6820     if (element == EL_PENGUIN)
6821     {
6822       int i;
6823       static int xy[4][2] =
6824       {
6825         { 0, -1 },
6826         { -1, 0 },
6827         { +1, 0 },
6828         { 0, +1 }
6829       };
6830
6831       for (i = 0; i < NUM_DIRECTIONS; i++)
6832       {
6833         int ex = x + xy[i][0];
6834         int ey = y + xy[i][1];
6835
6836         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6837                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6838                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6839                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6840         {
6841           attr_x = ex;
6842           attr_y = ey;
6843           break;
6844         }
6845       }
6846     }
6847
6848     MovDir[x][y] = MV_NONE;
6849     if (attr_x < x)
6850       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6851     else if (attr_x > x)
6852       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6853     if (attr_y < y)
6854       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6855     else if (attr_y > y)
6856       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6857
6858     if (element == EL_ROBOT)
6859     {
6860       int newx, newy;
6861
6862       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6863         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6864       Moving2Blocked(x, y, &newx, &newy);
6865
6866       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6867         MovDelay[x][y] = 8 + 8 * !RND(3);
6868       else
6869         MovDelay[x][y] = 16;
6870     }
6871     else if (element == EL_PENGUIN)
6872     {
6873       int newx, newy;
6874
6875       MovDelay[x][y] = 1;
6876
6877       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6878       {
6879         boolean first_horiz = RND(2);
6880         int new_move_dir = MovDir[x][y];
6881
6882         MovDir[x][y] =
6883           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6884         Moving2Blocked(x, y, &newx, &newy);
6885
6886         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6887           return;
6888
6889         MovDir[x][y] =
6890           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6891         Moving2Blocked(x, y, &newx, &newy);
6892
6893         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6894           return;
6895
6896         MovDir[x][y] = old_move_dir;
6897         return;
6898       }
6899     }
6900     else if (element == EL_SATELLITE)
6901     {
6902       int newx, newy;
6903
6904       MovDelay[x][y] = 1;
6905
6906       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6907       {
6908         boolean first_horiz = RND(2);
6909         int new_move_dir = MovDir[x][y];
6910
6911         MovDir[x][y] =
6912           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6913         Moving2Blocked(x, y, &newx, &newy);
6914
6915         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6916           return;
6917
6918         MovDir[x][y] =
6919           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6920         Moving2Blocked(x, y, &newx, &newy);
6921
6922         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6923           return;
6924
6925         MovDir[x][y] = old_move_dir;
6926         return;
6927       }
6928     }
6929     else if (element == EL_EMC_ANDROID)
6930     {
6931       static int check_pos[16] =
6932       {
6933         -1,             /*  0 => (invalid)          */
6934         7,              /*  1 => MV_LEFT            */
6935         3,              /*  2 => MV_RIGHT           */
6936         -1,             /*  3 => (invalid)          */
6937         1,              /*  4 =>            MV_UP   */
6938         0,              /*  5 => MV_LEFT  | MV_UP   */
6939         2,              /*  6 => MV_RIGHT | MV_UP   */
6940         -1,             /*  7 => (invalid)          */
6941         5,              /*  8 =>            MV_DOWN */
6942         6,              /*  9 => MV_LEFT  | MV_DOWN */
6943         4,              /* 10 => MV_RIGHT | MV_DOWN */
6944         -1,             /* 11 => (invalid)          */
6945         -1,             /* 12 => (invalid)          */
6946         -1,             /* 13 => (invalid)          */
6947         -1,             /* 14 => (invalid)          */
6948         -1,             /* 15 => (invalid)          */
6949       };
6950       static struct
6951       {
6952         int dx, dy;
6953         int dir;
6954       } check_xy[8] =
6955       {
6956         { -1, -1,       MV_LEFT  | MV_UP   },
6957         {  0, -1,                  MV_UP   },
6958         { +1, -1,       MV_RIGHT | MV_UP   },
6959         { +1,  0,       MV_RIGHT           },
6960         { +1, +1,       MV_RIGHT | MV_DOWN },
6961         {  0, +1,                  MV_DOWN },
6962         { -1, +1,       MV_LEFT  | MV_DOWN },
6963         { -1,  0,       MV_LEFT            },
6964       };
6965       int start_pos, check_order;
6966       boolean can_clone = FALSE;
6967       int i;
6968
6969       /* check if there is any free field around current position */
6970       for (i = 0; i < 8; i++)
6971       {
6972         int newx = x + check_xy[i].dx;
6973         int newy = y + check_xy[i].dy;
6974
6975         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6976         {
6977           can_clone = TRUE;
6978
6979           break;
6980         }
6981       }
6982
6983       if (can_clone)            /* randomly find an element to clone */
6984       {
6985         can_clone = FALSE;
6986
6987         start_pos = check_pos[RND(8)];
6988         check_order = (RND(2) ? -1 : +1);
6989
6990         for (i = 0; i < 8; i++)
6991         {
6992           int pos_raw = start_pos + i * check_order;
6993           int pos = (pos_raw + 8) % 8;
6994           int newx = x + check_xy[pos].dx;
6995           int newy = y + check_xy[pos].dy;
6996
6997           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6998           {
6999             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7000             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7001
7002             Store[x][y] = Feld[newx][newy];
7003
7004             can_clone = TRUE;
7005
7006             break;
7007           }
7008         }
7009       }
7010
7011       if (can_clone)            /* randomly find a direction to move */
7012       {
7013         can_clone = FALSE;
7014
7015         start_pos = check_pos[RND(8)];
7016         check_order = (RND(2) ? -1 : +1);
7017
7018         for (i = 0; i < 8; i++)
7019         {
7020           int pos_raw = start_pos + i * check_order;
7021           int pos = (pos_raw + 8) % 8;
7022           int newx = x + check_xy[pos].dx;
7023           int newy = y + check_xy[pos].dy;
7024           int new_move_dir = check_xy[pos].dir;
7025
7026           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7027           {
7028             MovDir[x][y] = new_move_dir;
7029             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7030
7031             can_clone = TRUE;
7032
7033             break;
7034           }
7035         }
7036       }
7037
7038       if (can_clone)            /* cloning and moving successful */
7039         return;
7040
7041       /* cannot clone -- try to move towards player */
7042
7043       start_pos = check_pos[MovDir[x][y] & 0x0f];
7044       check_order = (RND(2) ? -1 : +1);
7045
7046       for (i = 0; i < 3; i++)
7047       {
7048         /* first check start_pos, then previous/next or (next/previous) pos */
7049         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7050         int pos = (pos_raw + 8) % 8;
7051         int newx = x + check_xy[pos].dx;
7052         int newy = y + check_xy[pos].dy;
7053         int new_move_dir = check_xy[pos].dir;
7054
7055         if (IS_PLAYER(newx, newy))
7056           break;
7057
7058         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7059         {
7060           MovDir[x][y] = new_move_dir;
7061           MovDelay[x][y] = level.android_move_time * 8 + 1;
7062
7063           break;
7064         }
7065       }
7066     }
7067   }
7068   else if (move_pattern == MV_TURNING_LEFT ||
7069            move_pattern == MV_TURNING_RIGHT ||
7070            move_pattern == MV_TURNING_LEFT_RIGHT ||
7071            move_pattern == MV_TURNING_RIGHT_LEFT ||
7072            move_pattern == MV_TURNING_RANDOM ||
7073            move_pattern == MV_ALL_DIRECTIONS)
7074   {
7075     boolean can_turn_left =
7076       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7077     boolean can_turn_right =
7078       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7079
7080     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7081       return;
7082
7083     if (move_pattern == MV_TURNING_LEFT)
7084       MovDir[x][y] = left_dir;
7085     else if (move_pattern == MV_TURNING_RIGHT)
7086       MovDir[x][y] = right_dir;
7087     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7088       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7089     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7090       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7091     else if (move_pattern == MV_TURNING_RANDOM)
7092       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7093                       can_turn_right && !can_turn_left ? right_dir :
7094                       RND(2) ? left_dir : right_dir);
7095     else if (can_turn_left && can_turn_right)
7096       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7097     else if (can_turn_left)
7098       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7099     else if (can_turn_right)
7100       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7101     else
7102       MovDir[x][y] = back_dir;
7103
7104     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7105   }
7106   else if (move_pattern == MV_HORIZONTAL ||
7107            move_pattern == MV_VERTICAL)
7108   {
7109     if (move_pattern & old_move_dir)
7110       MovDir[x][y] = back_dir;
7111     else if (move_pattern == MV_HORIZONTAL)
7112       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7113     else if (move_pattern == MV_VERTICAL)
7114       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7115
7116     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7117   }
7118   else if (move_pattern & MV_ANY_DIRECTION)
7119   {
7120     MovDir[x][y] = move_pattern;
7121     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7122   }
7123   else if (move_pattern & MV_WIND_DIRECTION)
7124   {
7125     MovDir[x][y] = game.wind_direction;
7126     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7127   }
7128   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7129   {
7130     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7131       MovDir[x][y] = left_dir;
7132     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7133       MovDir[x][y] = right_dir;
7134
7135     if (MovDir[x][y] != old_move_dir)
7136       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7137   }
7138   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7139   {
7140     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7141       MovDir[x][y] = right_dir;
7142     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7143       MovDir[x][y] = left_dir;
7144
7145     if (MovDir[x][y] != old_move_dir)
7146       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7147   }
7148   else if (move_pattern == MV_TOWARDS_PLAYER ||
7149            move_pattern == MV_AWAY_FROM_PLAYER)
7150   {
7151     int attr_x = -1, attr_y = -1;
7152     int newx, newy;
7153     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7154
7155     if (AllPlayersGone)
7156     {
7157       attr_x = ExitX;
7158       attr_y = ExitY;
7159     }
7160     else
7161     {
7162       int i;
7163
7164       for (i = 0; i < MAX_PLAYERS; i++)
7165       {
7166         struct PlayerInfo *player = &stored_player[i];
7167         int jx = player->jx, jy = player->jy;
7168
7169         if (!player->active)
7170           continue;
7171
7172         if (attr_x == -1 ||
7173             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7174         {
7175           attr_x = jx;
7176           attr_y = jy;
7177         }
7178       }
7179     }
7180
7181     MovDir[x][y] = MV_NONE;
7182     if (attr_x < x)
7183       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7184     else if (attr_x > x)
7185       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7186     if (attr_y < y)
7187       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7188     else if (attr_y > y)
7189       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7190
7191     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7192
7193     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7194     {
7195       boolean first_horiz = RND(2);
7196       int new_move_dir = MovDir[x][y];
7197
7198       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7199       {
7200         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7201         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7202
7203         return;
7204       }
7205
7206       MovDir[x][y] =
7207         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7208       Moving2Blocked(x, y, &newx, &newy);
7209
7210       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7211         return;
7212
7213       MovDir[x][y] =
7214         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7215       Moving2Blocked(x, y, &newx, &newy);
7216
7217       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7218         return;
7219
7220       MovDir[x][y] = old_move_dir;
7221     }
7222   }
7223   else if (move_pattern == MV_WHEN_PUSHED ||
7224            move_pattern == MV_WHEN_DROPPED)
7225   {
7226     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7227       MovDir[x][y] = MV_NONE;
7228
7229     MovDelay[x][y] = 0;
7230   }
7231   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7232   {
7233     static int test_xy[7][2] =
7234     {
7235       { 0, -1 },
7236       { -1, 0 },
7237       { +1, 0 },
7238       { 0, +1 },
7239       { 0, -1 },
7240       { -1, 0 },
7241       { +1, 0 },
7242     };
7243     static int test_dir[7] =
7244     {
7245       MV_UP,
7246       MV_LEFT,
7247       MV_RIGHT,
7248       MV_DOWN,
7249       MV_UP,
7250       MV_LEFT,
7251       MV_RIGHT,
7252     };
7253     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7254     int move_preference = -1000000;     /* start with very low preference */
7255     int new_move_dir = MV_NONE;
7256     int start_test = RND(4);
7257     int i;
7258
7259     for (i = 0; i < NUM_DIRECTIONS; i++)
7260     {
7261       int move_dir = test_dir[start_test + i];
7262       int move_dir_preference;
7263
7264       xx = x + test_xy[start_test + i][0];
7265       yy = y + test_xy[start_test + i][1];
7266
7267       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7268           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7269       {
7270         new_move_dir = move_dir;
7271
7272         break;
7273       }
7274
7275       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7276         continue;
7277
7278       move_dir_preference = -1 * RunnerVisit[xx][yy];
7279       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7280         move_dir_preference = PlayerVisit[xx][yy];
7281
7282       if (move_dir_preference > move_preference)
7283       {
7284         /* prefer field that has not been visited for the longest time */
7285         move_preference = move_dir_preference;
7286         new_move_dir = move_dir;
7287       }
7288       else if (move_dir_preference == move_preference &&
7289                move_dir == old_move_dir)
7290       {
7291         /* prefer last direction when all directions are preferred equally */
7292         move_preference = move_dir_preference;
7293         new_move_dir = move_dir;
7294       }
7295     }
7296
7297     MovDir[x][y] = new_move_dir;
7298     if (old_move_dir != new_move_dir)
7299       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7300   }
7301 }
7302
7303 static void TurnRound(int x, int y)
7304 {
7305   int direction = MovDir[x][y];
7306
7307   TurnRoundExt(x, y);
7308
7309   GfxDir[x][y] = MovDir[x][y];
7310
7311   if (direction != MovDir[x][y])
7312     GfxFrame[x][y] = 0;
7313
7314   if (MovDelay[x][y])
7315     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7316
7317   ResetGfxFrame(x, y);
7318 }
7319
7320 static boolean JustBeingPushed(int x, int y)
7321 {
7322   int i;
7323
7324   for (i = 0; i < MAX_PLAYERS; i++)
7325   {
7326     struct PlayerInfo *player = &stored_player[i];
7327
7328     if (player->active && player->is_pushing && player->MovPos)
7329     {
7330       int next_jx = player->jx + (player->jx - player->last_jx);
7331       int next_jy = player->jy + (player->jy - player->last_jy);
7332
7333       if (x == next_jx && y == next_jy)
7334         return TRUE;
7335     }
7336   }
7337
7338   return FALSE;
7339 }
7340
7341 void StartMoving(int x, int y)
7342 {
7343   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7344   int element = Feld[x][y];
7345
7346   if (Stop[x][y])
7347     return;
7348
7349   if (MovDelay[x][y] == 0)
7350     GfxAction[x][y] = ACTION_DEFAULT;
7351
7352   if (CAN_FALL(element) && y < lev_fieldy - 1)
7353   {
7354     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7355         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7356       if (JustBeingPushed(x, y))
7357         return;
7358
7359     if (element == EL_QUICKSAND_FULL)
7360     {
7361       if (IS_FREE(x, y + 1))
7362       {
7363         InitMovingField(x, y, MV_DOWN);
7364         started_moving = TRUE;
7365
7366         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7367 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7368         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7369           Store[x][y] = EL_ROCK;
7370 #else
7371         Store[x][y] = EL_ROCK;
7372 #endif
7373
7374         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7375       }
7376       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7377       {
7378         if (!MovDelay[x][y])
7379         {
7380           MovDelay[x][y] = TILEY + 1;
7381
7382           ResetGfxAnimation(x, y);
7383           ResetGfxAnimation(x, y + 1);
7384         }
7385
7386         if (MovDelay[x][y])
7387         {
7388           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7389           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7390
7391           MovDelay[x][y]--;
7392           if (MovDelay[x][y])
7393             return;
7394         }
7395
7396         Feld[x][y] = EL_QUICKSAND_EMPTY;
7397         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7398         Store[x][y + 1] = Store[x][y];
7399         Store[x][y] = 0;
7400
7401         PlayLevelSoundAction(x, y, ACTION_FILLING);
7402       }
7403       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7404       {
7405         if (!MovDelay[x][y])
7406         {
7407           MovDelay[x][y] = TILEY + 1;
7408
7409           ResetGfxAnimation(x, y);
7410           ResetGfxAnimation(x, y + 1);
7411         }
7412
7413         if (MovDelay[x][y])
7414         {
7415           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7416           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7417
7418           MovDelay[x][y]--;
7419           if (MovDelay[x][y])
7420             return;
7421         }
7422
7423         Feld[x][y] = EL_QUICKSAND_EMPTY;
7424         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7425         Store[x][y + 1] = Store[x][y];
7426         Store[x][y] = 0;
7427
7428         PlayLevelSoundAction(x, y, ACTION_FILLING);
7429       }
7430     }
7431     else if (element == EL_QUICKSAND_FAST_FULL)
7432     {
7433       if (IS_FREE(x, y + 1))
7434       {
7435         InitMovingField(x, y, MV_DOWN);
7436         started_moving = TRUE;
7437
7438         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7439 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7440         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7441           Store[x][y] = EL_ROCK;
7442 #else
7443         Store[x][y] = EL_ROCK;
7444 #endif
7445
7446         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7447       }
7448       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7449       {
7450         if (!MovDelay[x][y])
7451         {
7452           MovDelay[x][y] = TILEY + 1;
7453
7454           ResetGfxAnimation(x, y);
7455           ResetGfxAnimation(x, y + 1);
7456         }
7457
7458         if (MovDelay[x][y])
7459         {
7460           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7461           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7462
7463           MovDelay[x][y]--;
7464           if (MovDelay[x][y])
7465             return;
7466         }
7467
7468         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7469         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7470         Store[x][y + 1] = Store[x][y];
7471         Store[x][y] = 0;
7472
7473         PlayLevelSoundAction(x, y, ACTION_FILLING);
7474       }
7475       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7476       {
7477         if (!MovDelay[x][y])
7478         {
7479           MovDelay[x][y] = TILEY + 1;
7480
7481           ResetGfxAnimation(x, y);
7482           ResetGfxAnimation(x, y + 1);
7483         }
7484
7485         if (MovDelay[x][y])
7486         {
7487           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7488           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7489
7490           MovDelay[x][y]--;
7491           if (MovDelay[x][y])
7492             return;
7493         }
7494
7495         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7496         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7497         Store[x][y + 1] = Store[x][y];
7498         Store[x][y] = 0;
7499
7500         PlayLevelSoundAction(x, y, ACTION_FILLING);
7501       }
7502     }
7503     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7504              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7505     {
7506       InitMovingField(x, y, MV_DOWN);
7507       started_moving = TRUE;
7508
7509       Feld[x][y] = EL_QUICKSAND_FILLING;
7510       Store[x][y] = element;
7511
7512       PlayLevelSoundAction(x, y, ACTION_FILLING);
7513     }
7514     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7515              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7516     {
7517       InitMovingField(x, y, MV_DOWN);
7518       started_moving = TRUE;
7519
7520       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7521       Store[x][y] = element;
7522
7523       PlayLevelSoundAction(x, y, ACTION_FILLING);
7524     }
7525     else if (element == EL_MAGIC_WALL_FULL)
7526     {
7527       if (IS_FREE(x, y + 1))
7528       {
7529         InitMovingField(x, y, MV_DOWN);
7530         started_moving = TRUE;
7531
7532         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7533         Store[x][y] = EL_CHANGED(Store[x][y]);
7534       }
7535       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7536       {
7537         if (!MovDelay[x][y])
7538           MovDelay[x][y] = TILEY / 4 + 1;
7539
7540         if (MovDelay[x][y])
7541         {
7542           MovDelay[x][y]--;
7543           if (MovDelay[x][y])
7544             return;
7545         }
7546
7547         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7548         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7549         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7550         Store[x][y] = 0;
7551       }
7552     }
7553     else if (element == EL_BD_MAGIC_WALL_FULL)
7554     {
7555       if (IS_FREE(x, y + 1))
7556       {
7557         InitMovingField(x, y, MV_DOWN);
7558         started_moving = TRUE;
7559
7560         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7561         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7562       }
7563       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7564       {
7565         if (!MovDelay[x][y])
7566           MovDelay[x][y] = TILEY / 4 + 1;
7567
7568         if (MovDelay[x][y])
7569         {
7570           MovDelay[x][y]--;
7571           if (MovDelay[x][y])
7572             return;
7573         }
7574
7575         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7576         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7577         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7578         Store[x][y] = 0;
7579       }
7580     }
7581     else if (element == EL_DC_MAGIC_WALL_FULL)
7582     {
7583       if (IS_FREE(x, y + 1))
7584       {
7585         InitMovingField(x, y, MV_DOWN);
7586         started_moving = TRUE;
7587
7588         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7589         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7590       }
7591       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7592       {
7593         if (!MovDelay[x][y])
7594           MovDelay[x][y] = TILEY / 4 + 1;
7595
7596         if (MovDelay[x][y])
7597         {
7598           MovDelay[x][y]--;
7599           if (MovDelay[x][y])
7600             return;
7601         }
7602
7603         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7604         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7605         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7606         Store[x][y] = 0;
7607       }
7608     }
7609     else if ((CAN_PASS_MAGIC_WALL(element) &&
7610               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7611                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7612              (CAN_PASS_DC_MAGIC_WALL(element) &&
7613               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7614
7615     {
7616       InitMovingField(x, y, MV_DOWN);
7617       started_moving = TRUE;
7618
7619       Feld[x][y] =
7620         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7621          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7622          EL_DC_MAGIC_WALL_FILLING);
7623       Store[x][y] = element;
7624     }
7625     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7626     {
7627       SplashAcid(x, y + 1);
7628
7629       InitMovingField(x, y, MV_DOWN);
7630       started_moving = TRUE;
7631
7632       Store[x][y] = EL_ACID;
7633     }
7634     else if (
7635              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7636               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7637              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7638               CAN_FALL(element) && WasJustFalling[x][y] &&
7639               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7640
7641              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7642               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7643               (Feld[x][y + 1] == EL_BLOCKED)))
7644     {
7645       /* this is needed for a special case not covered by calling "Impact()"
7646          from "ContinueMoving()": if an element moves to a tile directly below
7647          another element which was just falling on that tile (which was empty
7648          in the previous frame), the falling element above would just stop
7649          instead of smashing the element below (in previous version, the above
7650          element was just checked for "moving" instead of "falling", resulting
7651          in incorrect smashes caused by horizontal movement of the above
7652          element; also, the case of the player being the element to smash was
7653          simply not covered here... :-/ ) */
7654
7655       CheckCollision[x][y] = 0;
7656       CheckImpact[x][y] = 0;
7657
7658       Impact(x, y);
7659     }
7660     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7661     {
7662       if (MovDir[x][y] == MV_NONE)
7663       {
7664         InitMovingField(x, y, MV_DOWN);
7665         started_moving = TRUE;
7666       }
7667     }
7668     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7669     {
7670       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7671         MovDir[x][y] = MV_DOWN;
7672
7673       InitMovingField(x, y, MV_DOWN);
7674       started_moving = TRUE;
7675     }
7676     else if (element == EL_AMOEBA_DROP)
7677     {
7678       Feld[x][y] = EL_AMOEBA_GROWING;
7679       Store[x][y] = EL_AMOEBA_WET;
7680     }
7681     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7682               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7683              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7684              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7685     {
7686       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7687                                 (IS_FREE(x - 1, y + 1) ||
7688                                  Feld[x - 1][y + 1] == EL_ACID));
7689       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7690                                 (IS_FREE(x + 1, y + 1) ||
7691                                  Feld[x + 1][y + 1] == EL_ACID));
7692       boolean can_fall_any  = (can_fall_left || can_fall_right);
7693       boolean can_fall_both = (can_fall_left && can_fall_right);
7694       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7695
7696       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7697       {
7698         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7699           can_fall_right = FALSE;
7700         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7701           can_fall_left = FALSE;
7702         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7703           can_fall_right = FALSE;
7704         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7705           can_fall_left = FALSE;
7706
7707         can_fall_any  = (can_fall_left || can_fall_right);
7708         can_fall_both = FALSE;
7709       }
7710
7711       if (can_fall_both)
7712       {
7713         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7714           can_fall_right = FALSE;       /* slip down on left side */
7715         else
7716           can_fall_left = !(can_fall_right = RND(2));
7717
7718         can_fall_both = FALSE;
7719       }
7720
7721       if (can_fall_any)
7722       {
7723         /* if not determined otherwise, prefer left side for slipping down */
7724         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7725         started_moving = TRUE;
7726       }
7727     }
7728     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7729     {
7730       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7731       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7732       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7733       int belt_dir = game.belt_dir[belt_nr];
7734
7735       if ((belt_dir == MV_LEFT  && left_is_free) ||
7736           (belt_dir == MV_RIGHT && right_is_free))
7737       {
7738         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7739
7740         InitMovingField(x, y, belt_dir);
7741         started_moving = TRUE;
7742
7743         Pushed[x][y] = TRUE;
7744         Pushed[nextx][y] = TRUE;
7745
7746         GfxAction[x][y] = ACTION_DEFAULT;
7747       }
7748       else
7749       {
7750         MovDir[x][y] = 0;       /* if element was moving, stop it */
7751       }
7752     }
7753   }
7754
7755   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7756   if (CAN_MOVE(element) && !started_moving)
7757   {
7758     int move_pattern = element_info[element].move_pattern;
7759     int newx, newy;
7760
7761     Moving2Blocked(x, y, &newx, &newy);
7762
7763     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7764       return;
7765
7766     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7767         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7768     {
7769       WasJustMoving[x][y] = 0;
7770       CheckCollision[x][y] = 0;
7771
7772       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7773
7774       if (Feld[x][y] != element)        /* element has changed */
7775         return;
7776     }
7777
7778     if (!MovDelay[x][y])        /* start new movement phase */
7779     {
7780       /* all objects that can change their move direction after each step
7781          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7782
7783       if (element != EL_YAMYAM &&
7784           element != EL_DARK_YAMYAM &&
7785           element != EL_PACMAN &&
7786           !(move_pattern & MV_ANY_DIRECTION) &&
7787           move_pattern != MV_TURNING_LEFT &&
7788           move_pattern != MV_TURNING_RIGHT &&
7789           move_pattern != MV_TURNING_LEFT_RIGHT &&
7790           move_pattern != MV_TURNING_RIGHT_LEFT &&
7791           move_pattern != MV_TURNING_RANDOM)
7792       {
7793         TurnRound(x, y);
7794
7795         if (MovDelay[x][y] && (element == EL_BUG ||
7796                                element == EL_SPACESHIP ||
7797                                element == EL_SP_SNIKSNAK ||
7798                                element == EL_SP_ELECTRON ||
7799                                element == EL_MOLE))
7800           TEST_DrawLevelField(x, y);
7801       }
7802     }
7803
7804     if (MovDelay[x][y])         /* wait some time before next movement */
7805     {
7806       MovDelay[x][y]--;
7807
7808       if (element == EL_ROBOT ||
7809           element == EL_YAMYAM ||
7810           element == EL_DARK_YAMYAM)
7811       {
7812         DrawLevelElementAnimationIfNeeded(x, y, element);
7813         PlayLevelSoundAction(x, y, ACTION_WAITING);
7814       }
7815       else if (element == EL_SP_ELECTRON)
7816         DrawLevelElementAnimationIfNeeded(x, y, element);
7817       else if (element == EL_DRAGON)
7818       {
7819         int i;
7820         int dir = MovDir[x][y];
7821         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7822         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7823         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7824                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7825                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7826                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7827         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7828
7829         GfxAction[x][y] = ACTION_ATTACKING;
7830
7831         if (IS_PLAYER(x, y))
7832           DrawPlayerField(x, y);
7833         else
7834           TEST_DrawLevelField(x, y);
7835
7836         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7837
7838         for (i = 1; i <= 3; i++)
7839         {
7840           int xx = x + i * dx;
7841           int yy = y + i * dy;
7842           int sx = SCREENX(xx);
7843           int sy = SCREENY(yy);
7844           int flame_graphic = graphic + (i - 1);
7845
7846           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7847             break;
7848
7849           if (MovDelay[x][y])
7850           {
7851             int flamed = MovingOrBlocked2Element(xx, yy);
7852
7853             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7854               Bang(xx, yy);
7855             else
7856               RemoveMovingField(xx, yy);
7857
7858             ChangeDelay[xx][yy] = 0;
7859
7860             Feld[xx][yy] = EL_FLAMES;
7861
7862             if (IN_SCR_FIELD(sx, sy))
7863             {
7864               TEST_DrawLevelFieldCrumbled(xx, yy);
7865               DrawGraphic(sx, sy, flame_graphic, frame);
7866             }
7867           }
7868           else
7869           {
7870             if (Feld[xx][yy] == EL_FLAMES)
7871               Feld[xx][yy] = EL_EMPTY;
7872             TEST_DrawLevelField(xx, yy);
7873           }
7874         }
7875       }
7876
7877       if (MovDelay[x][y])       /* element still has to wait some time */
7878       {
7879         PlayLevelSoundAction(x, y, ACTION_WAITING);
7880
7881         return;
7882       }
7883     }
7884
7885     /* now make next step */
7886
7887     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7888
7889     if (DONT_COLLIDE_WITH(element) &&
7890         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7891         !PLAYER_ENEMY_PROTECTED(newx, newy))
7892     {
7893       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7894
7895       return;
7896     }
7897
7898     else if (CAN_MOVE_INTO_ACID(element) &&
7899              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7900              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7901              (MovDir[x][y] == MV_DOWN ||
7902               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7903     {
7904       SplashAcid(newx, newy);
7905       Store[x][y] = EL_ACID;
7906     }
7907     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7908     {
7909       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7910           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7911           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7912           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7913       {
7914         RemoveField(x, y);
7915         TEST_DrawLevelField(x, y);
7916
7917         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7918         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7919           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7920
7921         local_player->friends_still_needed--;
7922         if (!local_player->friends_still_needed &&
7923             !local_player->GameOver && AllPlayersGone)
7924           PlayerWins(local_player);
7925
7926         return;
7927       }
7928       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7929       {
7930         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7931           TEST_DrawLevelField(newx, newy);
7932         else
7933           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7934       }
7935       else if (!IS_FREE(newx, newy))
7936       {
7937         GfxAction[x][y] = ACTION_WAITING;
7938
7939         if (IS_PLAYER(x, y))
7940           DrawPlayerField(x, y);
7941         else
7942           TEST_DrawLevelField(x, y);
7943
7944         return;
7945       }
7946     }
7947     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7948     {
7949       if (IS_FOOD_PIG(Feld[newx][newy]))
7950       {
7951         if (IS_MOVING(newx, newy))
7952           RemoveMovingField(newx, newy);
7953         else
7954         {
7955           Feld[newx][newy] = EL_EMPTY;
7956           TEST_DrawLevelField(newx, newy);
7957         }
7958
7959         PlayLevelSound(x, y, SND_PIG_DIGGING);
7960       }
7961       else if (!IS_FREE(newx, newy))
7962       {
7963         if (IS_PLAYER(x, y))
7964           DrawPlayerField(x, y);
7965         else
7966           TEST_DrawLevelField(x, y);
7967
7968         return;
7969       }
7970     }
7971     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7972     {
7973       if (Store[x][y] != EL_EMPTY)
7974       {
7975         boolean can_clone = FALSE;
7976         int xx, yy;
7977
7978         /* check if element to clone is still there */
7979         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7980         {
7981           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7982           {
7983             can_clone = TRUE;
7984
7985             break;
7986           }
7987         }
7988
7989         /* cannot clone or target field not free anymore -- do not clone */
7990         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7991           Store[x][y] = EL_EMPTY;
7992       }
7993
7994       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7995       {
7996         if (IS_MV_DIAGONAL(MovDir[x][y]))
7997         {
7998           int diagonal_move_dir = MovDir[x][y];
7999           int stored = Store[x][y];
8000           int change_delay = 8;
8001           int graphic;
8002
8003           /* android is moving diagonally */
8004
8005           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8006
8007           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8008           GfxElement[x][y] = EL_EMC_ANDROID;
8009           GfxAction[x][y] = ACTION_SHRINKING;
8010           GfxDir[x][y] = diagonal_move_dir;
8011           ChangeDelay[x][y] = change_delay;
8012
8013           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8014                                    GfxDir[x][y]);
8015
8016           DrawLevelGraphicAnimation(x, y, graphic);
8017           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8018
8019           if (Feld[newx][newy] == EL_ACID)
8020           {
8021             SplashAcid(newx, newy);
8022
8023             return;
8024           }
8025
8026           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8027
8028           Store[newx][newy] = EL_EMC_ANDROID;
8029           GfxElement[newx][newy] = EL_EMC_ANDROID;
8030           GfxAction[newx][newy] = ACTION_GROWING;
8031           GfxDir[newx][newy] = diagonal_move_dir;
8032           ChangeDelay[newx][newy] = change_delay;
8033
8034           graphic = el_act_dir2img(GfxElement[newx][newy],
8035                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8036
8037           DrawLevelGraphicAnimation(newx, newy, graphic);
8038           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8039
8040           return;
8041         }
8042         else
8043         {
8044           Feld[newx][newy] = EL_EMPTY;
8045           TEST_DrawLevelField(newx, newy);
8046
8047           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8048         }
8049       }
8050       else if (!IS_FREE(newx, newy))
8051       {
8052         return;
8053       }
8054     }
8055     else if (IS_CUSTOM_ELEMENT(element) &&
8056              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8057     {
8058       if (!DigFieldByCE(newx, newy, element))
8059         return;
8060
8061       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8062       {
8063         RunnerVisit[x][y] = FrameCounter;
8064         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8065       }
8066     }
8067     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8068     {
8069       if (!IS_FREE(newx, newy))
8070       {
8071         if (IS_PLAYER(x, y))
8072           DrawPlayerField(x, y);
8073         else
8074           TEST_DrawLevelField(x, y);
8075
8076         return;
8077       }
8078       else
8079       {
8080         boolean wanna_flame = !RND(10);
8081         int dx = newx - x, dy = newy - y;
8082         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8083         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8084         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8085                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8086         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8087                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8088
8089         if ((wanna_flame ||
8090              IS_CLASSIC_ENEMY(element1) ||
8091              IS_CLASSIC_ENEMY(element2)) &&
8092             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8093             element1 != EL_FLAMES && element2 != EL_FLAMES)
8094         {
8095           ResetGfxAnimation(x, y);
8096           GfxAction[x][y] = ACTION_ATTACKING;
8097
8098           if (IS_PLAYER(x, y))
8099             DrawPlayerField(x, y);
8100           else
8101             TEST_DrawLevelField(x, y);
8102
8103           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8104
8105           MovDelay[x][y] = 50;
8106
8107           Feld[newx][newy] = EL_FLAMES;
8108           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8109             Feld[newx1][newy1] = EL_FLAMES;
8110           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8111             Feld[newx2][newy2] = EL_FLAMES;
8112
8113           return;
8114         }
8115       }
8116     }
8117     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8118              Feld[newx][newy] == EL_DIAMOND)
8119     {
8120       if (IS_MOVING(newx, newy))
8121         RemoveMovingField(newx, newy);
8122       else
8123       {
8124         Feld[newx][newy] = EL_EMPTY;
8125         TEST_DrawLevelField(newx, newy);
8126       }
8127
8128       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8129     }
8130     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8131              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8132     {
8133       if (AmoebaNr[newx][newy])
8134       {
8135         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8136         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8137             Feld[newx][newy] == EL_BD_AMOEBA)
8138           AmoebaCnt[AmoebaNr[newx][newy]]--;
8139       }
8140
8141       if (IS_MOVING(newx, newy))
8142       {
8143         RemoveMovingField(newx, newy);
8144       }
8145       else
8146       {
8147         Feld[newx][newy] = EL_EMPTY;
8148         TEST_DrawLevelField(newx, newy);
8149       }
8150
8151       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8152     }
8153     else if ((element == EL_PACMAN || element == EL_MOLE)
8154              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8155     {
8156       if (AmoebaNr[newx][newy])
8157       {
8158         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8159         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8160             Feld[newx][newy] == EL_BD_AMOEBA)
8161           AmoebaCnt[AmoebaNr[newx][newy]]--;
8162       }
8163
8164       if (element == EL_MOLE)
8165       {
8166         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8167         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8168
8169         ResetGfxAnimation(x, y);
8170         GfxAction[x][y] = ACTION_DIGGING;
8171         TEST_DrawLevelField(x, y);
8172
8173         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8174
8175         return;                         /* wait for shrinking amoeba */
8176       }
8177       else      /* element == EL_PACMAN */
8178       {
8179         Feld[newx][newy] = EL_EMPTY;
8180         TEST_DrawLevelField(newx, newy);
8181         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8182       }
8183     }
8184     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8185              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8186               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8187     {
8188       /* wait for shrinking amoeba to completely disappear */
8189       return;
8190     }
8191     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8192     {
8193       /* object was running against a wall */
8194
8195       TurnRound(x, y);
8196
8197       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8198         DrawLevelElementAnimation(x, y, element);
8199
8200       if (DONT_TOUCH(element))
8201         TestIfBadThingTouchesPlayer(x, y);
8202
8203       return;
8204     }
8205
8206     InitMovingField(x, y, MovDir[x][y]);
8207
8208     PlayLevelSoundAction(x, y, ACTION_MOVING);
8209   }
8210
8211   if (MovDir[x][y])
8212     ContinueMoving(x, y);
8213 }
8214
8215 void ContinueMoving(int x, int y)
8216 {
8217   int element = Feld[x][y];
8218   struct ElementInfo *ei = &element_info[element];
8219   int direction = MovDir[x][y];
8220   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8221   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8222   int newx = x + dx, newy = y + dy;
8223   int stored = Store[x][y];
8224   int stored_new = Store[newx][newy];
8225   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8226   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8227   boolean last_line = (newy == lev_fieldy - 1);
8228
8229   MovPos[x][y] += getElementMoveStepsize(x, y);
8230
8231   if (pushed_by_player) /* special case: moving object pushed by player */
8232     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8233
8234   if (ABS(MovPos[x][y]) < TILEX)
8235   {
8236     TEST_DrawLevelField(x, y);
8237
8238     return;     /* element is still moving */
8239   }
8240
8241   /* element reached destination field */
8242
8243   Feld[x][y] = EL_EMPTY;
8244   Feld[newx][newy] = element;
8245   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8246
8247   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8248   {
8249     element = Feld[newx][newy] = EL_ACID;
8250   }
8251   else if (element == EL_MOLE)
8252   {
8253     Feld[x][y] = EL_SAND;
8254
8255     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8256   }
8257   else if (element == EL_QUICKSAND_FILLING)
8258   {
8259     element = Feld[newx][newy] = get_next_element(element);
8260     Store[newx][newy] = Store[x][y];
8261   }
8262   else if (element == EL_QUICKSAND_EMPTYING)
8263   {
8264     Feld[x][y] = get_next_element(element);
8265     element = Feld[newx][newy] = Store[x][y];
8266   }
8267   else if (element == EL_QUICKSAND_FAST_FILLING)
8268   {
8269     element = Feld[newx][newy] = get_next_element(element);
8270     Store[newx][newy] = Store[x][y];
8271   }
8272   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8273   {
8274     Feld[x][y] = get_next_element(element);
8275     element = Feld[newx][newy] = Store[x][y];
8276   }
8277   else if (element == EL_MAGIC_WALL_FILLING)
8278   {
8279     element = Feld[newx][newy] = get_next_element(element);
8280     if (!game.magic_wall_active)
8281       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8282     Store[newx][newy] = Store[x][y];
8283   }
8284   else if (element == EL_MAGIC_WALL_EMPTYING)
8285   {
8286     Feld[x][y] = get_next_element(element);
8287     if (!game.magic_wall_active)
8288       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8289     element = Feld[newx][newy] = Store[x][y];
8290
8291     InitField(newx, newy, FALSE);
8292   }
8293   else if (element == EL_BD_MAGIC_WALL_FILLING)
8294   {
8295     element = Feld[newx][newy] = get_next_element(element);
8296     if (!game.magic_wall_active)
8297       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8298     Store[newx][newy] = Store[x][y];
8299   }
8300   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8301   {
8302     Feld[x][y] = get_next_element(element);
8303     if (!game.magic_wall_active)
8304       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8305     element = Feld[newx][newy] = Store[x][y];
8306
8307     InitField(newx, newy, FALSE);
8308   }
8309   else if (element == EL_DC_MAGIC_WALL_FILLING)
8310   {
8311     element = Feld[newx][newy] = get_next_element(element);
8312     if (!game.magic_wall_active)
8313       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8314     Store[newx][newy] = Store[x][y];
8315   }
8316   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8317   {
8318     Feld[x][y] = get_next_element(element);
8319     if (!game.magic_wall_active)
8320       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8321     element = Feld[newx][newy] = Store[x][y];
8322
8323     InitField(newx, newy, FALSE);
8324   }
8325   else if (element == EL_AMOEBA_DROPPING)
8326   {
8327     Feld[x][y] = get_next_element(element);
8328     element = Feld[newx][newy] = Store[x][y];
8329   }
8330   else if (element == EL_SOKOBAN_OBJECT)
8331   {
8332     if (Back[x][y])
8333       Feld[x][y] = Back[x][y];
8334
8335     if (Back[newx][newy])
8336       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8337
8338     Back[x][y] = Back[newx][newy] = 0;
8339   }
8340
8341   Store[x][y] = EL_EMPTY;
8342   MovPos[x][y] = 0;
8343   MovDir[x][y] = 0;
8344   MovDelay[x][y] = 0;
8345
8346   MovDelay[newx][newy] = 0;
8347
8348   if (CAN_CHANGE_OR_HAS_ACTION(element))
8349   {
8350     /* copy element change control values to new field */
8351     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8352     ChangePage[newx][newy]  = ChangePage[x][y];
8353     ChangeCount[newx][newy] = ChangeCount[x][y];
8354     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8355   }
8356
8357   CustomValue[newx][newy] = CustomValue[x][y];
8358
8359   ChangeDelay[x][y] = 0;
8360   ChangePage[x][y] = -1;
8361   ChangeCount[x][y] = 0;
8362   ChangeEvent[x][y] = -1;
8363
8364   CustomValue[x][y] = 0;
8365
8366   /* copy animation control values to new field */
8367   GfxFrame[newx][newy]  = GfxFrame[x][y];
8368   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8369   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8370   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8371
8372   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8373
8374   /* some elements can leave other elements behind after moving */
8375   if (ei->move_leave_element != EL_EMPTY &&
8376       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8377       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8378   {
8379     int move_leave_element = ei->move_leave_element;
8380
8381     /* this makes it possible to leave the removed element again */
8382     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8383       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8384
8385     Feld[x][y] = move_leave_element;
8386
8387     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8388       MovDir[x][y] = direction;
8389
8390     InitField(x, y, FALSE);
8391
8392     if (GFX_CRUMBLED(Feld[x][y]))
8393       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8394
8395     if (ELEM_IS_PLAYER(move_leave_element))
8396       RelocatePlayer(x, y, move_leave_element);
8397   }
8398
8399   /* do this after checking for left-behind element */
8400   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8401
8402   if (!CAN_MOVE(element) ||
8403       (CAN_FALL(element) && direction == MV_DOWN &&
8404        (element == EL_SPRING ||
8405         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8406         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8407     GfxDir[x][y] = MovDir[newx][newy] = 0;
8408
8409   TEST_DrawLevelField(x, y);
8410   TEST_DrawLevelField(newx, newy);
8411
8412   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8413
8414   /* prevent pushed element from moving on in pushed direction */
8415   if (pushed_by_player && CAN_MOVE(element) &&
8416       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8417       !(element_info[element].move_pattern & direction))
8418     TurnRound(newx, newy);
8419
8420   /* prevent elements on conveyor belt from moving on in last direction */
8421   if (pushed_by_conveyor && CAN_FALL(element) &&
8422       direction & MV_HORIZONTAL)
8423     MovDir[newx][newy] = 0;
8424
8425   if (!pushed_by_player)
8426   {
8427     int nextx = newx + dx, nexty = newy + dy;
8428     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8429
8430     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8431
8432     if (CAN_FALL(element) && direction == MV_DOWN)
8433       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8434
8435     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8436       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8437
8438     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8439       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8440   }
8441
8442   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8443   {
8444     TestIfBadThingTouchesPlayer(newx, newy);
8445     TestIfBadThingTouchesFriend(newx, newy);
8446
8447     if (!IS_CUSTOM_ELEMENT(element))
8448       TestIfBadThingTouchesOtherBadThing(newx, newy);
8449   }
8450   else if (element == EL_PENGUIN)
8451     TestIfFriendTouchesBadThing(newx, newy);
8452
8453   if (DONT_GET_HIT_BY(element))
8454   {
8455     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8456   }
8457
8458   /* give the player one last chance (one more frame) to move away */
8459   if (CAN_FALL(element) && direction == MV_DOWN &&
8460       (last_line || (!IS_FREE(x, newy + 1) &&
8461                      (!IS_PLAYER(x, newy + 1) ||
8462                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8463     Impact(x, newy);
8464
8465   if (pushed_by_player && !game.use_change_when_pushing_bug)
8466   {
8467     int push_side = MV_DIR_OPPOSITE(direction);
8468     struct PlayerInfo *player = PLAYERINFO(x, y);
8469
8470     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8471                                player->index_bit, push_side);
8472     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8473                                         player->index_bit, push_side);
8474   }
8475
8476   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8477     MovDelay[newx][newy] = 1;
8478
8479   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8480
8481   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8482   TestIfElementHitsCustomElement(newx, newy, direction);
8483   TestIfPlayerTouchesCustomElement(newx, newy);
8484   TestIfElementTouchesCustomElement(newx, newy);
8485
8486   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8487       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8488     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8489                              MV_DIR_OPPOSITE(direction));
8490 }
8491
8492 int AmoebeNachbarNr(int ax, int ay)
8493 {
8494   int i;
8495   int element = Feld[ax][ay];
8496   int group_nr = 0;
8497   static int xy[4][2] =
8498   {
8499     { 0, -1 },
8500     { -1, 0 },
8501     { +1, 0 },
8502     { 0, +1 }
8503   };
8504
8505   for (i = 0; i < NUM_DIRECTIONS; i++)
8506   {
8507     int x = ax + xy[i][0];
8508     int y = ay + xy[i][1];
8509
8510     if (!IN_LEV_FIELD(x, y))
8511       continue;
8512
8513     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8514       group_nr = AmoebaNr[x][y];
8515   }
8516
8517   return group_nr;
8518 }
8519
8520 void AmoebenVereinigen(int ax, int ay)
8521 {
8522   int i, x, y, xx, yy;
8523   int new_group_nr = AmoebaNr[ax][ay];
8524   static int xy[4][2] =
8525   {
8526     { 0, -1 },
8527     { -1, 0 },
8528     { +1, 0 },
8529     { 0, +1 }
8530   };
8531
8532   if (new_group_nr == 0)
8533     return;
8534
8535   for (i = 0; i < NUM_DIRECTIONS; i++)
8536   {
8537     x = ax + xy[i][0];
8538     y = ay + xy[i][1];
8539
8540     if (!IN_LEV_FIELD(x, y))
8541       continue;
8542
8543     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8544          Feld[x][y] == EL_BD_AMOEBA ||
8545          Feld[x][y] == EL_AMOEBA_DEAD) &&
8546         AmoebaNr[x][y] != new_group_nr)
8547     {
8548       int old_group_nr = AmoebaNr[x][y];
8549
8550       if (old_group_nr == 0)
8551         return;
8552
8553       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8554       AmoebaCnt[old_group_nr] = 0;
8555       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8556       AmoebaCnt2[old_group_nr] = 0;
8557
8558       SCAN_PLAYFIELD(xx, yy)
8559       {
8560         if (AmoebaNr[xx][yy] == old_group_nr)
8561           AmoebaNr[xx][yy] = new_group_nr;
8562       }
8563     }
8564   }
8565 }
8566
8567 void AmoebeUmwandeln(int ax, int ay)
8568 {
8569   int i, x, y;
8570
8571   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8572   {
8573     int group_nr = AmoebaNr[ax][ay];
8574
8575 #ifdef DEBUG
8576     if (group_nr == 0)
8577     {
8578       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8579       printf("AmoebeUmwandeln(): This should never happen!\n");
8580       return;
8581     }
8582 #endif
8583
8584     SCAN_PLAYFIELD(x, y)
8585     {
8586       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8587       {
8588         AmoebaNr[x][y] = 0;
8589         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8590       }
8591     }
8592
8593     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8594                             SND_AMOEBA_TURNING_TO_GEM :
8595                             SND_AMOEBA_TURNING_TO_ROCK));
8596     Bang(ax, ay);
8597   }
8598   else
8599   {
8600     static int xy[4][2] =
8601     {
8602       { 0, -1 },
8603       { -1, 0 },
8604       { +1, 0 },
8605       { 0, +1 }
8606     };
8607
8608     for (i = 0; i < NUM_DIRECTIONS; i++)
8609     {
8610       x = ax + xy[i][0];
8611       y = ay + xy[i][1];
8612
8613       if (!IN_LEV_FIELD(x, y))
8614         continue;
8615
8616       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8617       {
8618         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8619                               SND_AMOEBA_TURNING_TO_GEM :
8620                               SND_AMOEBA_TURNING_TO_ROCK));
8621         Bang(x, y);
8622       }
8623     }
8624   }
8625 }
8626
8627 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8628 {
8629   int x, y;
8630   int group_nr = AmoebaNr[ax][ay];
8631   boolean done = FALSE;
8632
8633 #ifdef DEBUG
8634   if (group_nr == 0)
8635   {
8636     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8637     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8638     return;
8639   }
8640 #endif
8641
8642   SCAN_PLAYFIELD(x, y)
8643   {
8644     if (AmoebaNr[x][y] == group_nr &&
8645         (Feld[x][y] == EL_AMOEBA_DEAD ||
8646          Feld[x][y] == EL_BD_AMOEBA ||
8647          Feld[x][y] == EL_AMOEBA_GROWING))
8648     {
8649       AmoebaNr[x][y] = 0;
8650       Feld[x][y] = new_element;
8651       InitField(x, y, FALSE);
8652       TEST_DrawLevelField(x, y);
8653       done = TRUE;
8654     }
8655   }
8656
8657   if (done)
8658     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8659                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8660                             SND_BD_AMOEBA_TURNING_TO_GEM));
8661 }
8662
8663 void AmoebeWaechst(int x, int y)
8664 {
8665   static unsigned int sound_delay = 0;
8666   static unsigned int sound_delay_value = 0;
8667
8668   if (!MovDelay[x][y])          /* start new growing cycle */
8669   {
8670     MovDelay[x][y] = 7;
8671
8672     if (DelayReached(&sound_delay, sound_delay_value))
8673     {
8674       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8675       sound_delay_value = 30;
8676     }
8677   }
8678
8679   if (MovDelay[x][y])           /* wait some time before growing bigger */
8680   {
8681     MovDelay[x][y]--;
8682     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8683     {
8684       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8685                                            6 - MovDelay[x][y]);
8686
8687       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8688     }
8689
8690     if (!MovDelay[x][y])
8691     {
8692       Feld[x][y] = Store[x][y];
8693       Store[x][y] = 0;
8694       TEST_DrawLevelField(x, y);
8695     }
8696   }
8697 }
8698
8699 void AmoebaDisappearing(int x, int y)
8700 {
8701   static unsigned int sound_delay = 0;
8702   static unsigned int sound_delay_value = 0;
8703
8704   if (!MovDelay[x][y])          /* start new shrinking cycle */
8705   {
8706     MovDelay[x][y] = 7;
8707
8708     if (DelayReached(&sound_delay, sound_delay_value))
8709       sound_delay_value = 30;
8710   }
8711
8712   if (MovDelay[x][y])           /* wait some time before shrinking */
8713   {
8714     MovDelay[x][y]--;
8715     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8716     {
8717       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8718                                            6 - MovDelay[x][y]);
8719
8720       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8721     }
8722
8723     if (!MovDelay[x][y])
8724     {
8725       Feld[x][y] = EL_EMPTY;
8726       TEST_DrawLevelField(x, y);
8727
8728       /* don't let mole enter this field in this cycle;
8729          (give priority to objects falling to this field from above) */
8730       Stop[x][y] = TRUE;
8731     }
8732   }
8733 }
8734
8735 void AmoebeAbleger(int ax, int ay)
8736 {
8737   int i;
8738   int element = Feld[ax][ay];
8739   int graphic = el2img(element);
8740   int newax = ax, neway = ay;
8741   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8742   static int xy[4][2] =
8743   {
8744     { 0, -1 },
8745     { -1, 0 },
8746     { +1, 0 },
8747     { 0, +1 }
8748   };
8749
8750   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8751   {
8752     Feld[ax][ay] = EL_AMOEBA_DEAD;
8753     TEST_DrawLevelField(ax, ay);
8754     return;
8755   }
8756
8757   if (IS_ANIMATED(graphic))
8758     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8759
8760   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8761     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8762
8763   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8764   {
8765     MovDelay[ax][ay]--;
8766     if (MovDelay[ax][ay])
8767       return;
8768   }
8769
8770   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8771   {
8772     int start = RND(4);
8773     int x = ax + xy[start][0];
8774     int y = ay + xy[start][1];
8775
8776     if (!IN_LEV_FIELD(x, y))
8777       return;
8778
8779     if (IS_FREE(x, y) ||
8780         CAN_GROW_INTO(Feld[x][y]) ||
8781         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8782         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8783     {
8784       newax = x;
8785       neway = y;
8786     }
8787
8788     if (newax == ax && neway == ay)
8789       return;
8790   }
8791   else                          /* normal or "filled" (BD style) amoeba */
8792   {
8793     int start = RND(4);
8794     boolean waiting_for_player = FALSE;
8795
8796     for (i = 0; i < NUM_DIRECTIONS; i++)
8797     {
8798       int j = (start + i) % 4;
8799       int x = ax + xy[j][0];
8800       int y = ay + xy[j][1];
8801
8802       if (!IN_LEV_FIELD(x, y))
8803         continue;
8804
8805       if (IS_FREE(x, y) ||
8806           CAN_GROW_INTO(Feld[x][y]) ||
8807           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8808           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8809       {
8810         newax = x;
8811         neway = y;
8812         break;
8813       }
8814       else if (IS_PLAYER(x, y))
8815         waiting_for_player = TRUE;
8816     }
8817
8818     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8819     {
8820       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8821       {
8822         Feld[ax][ay] = EL_AMOEBA_DEAD;
8823         TEST_DrawLevelField(ax, ay);
8824         AmoebaCnt[AmoebaNr[ax][ay]]--;
8825
8826         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8827         {
8828           if (element == EL_AMOEBA_FULL)
8829             AmoebeUmwandeln(ax, ay);
8830           else if (element == EL_BD_AMOEBA)
8831             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8832         }
8833       }
8834       return;
8835     }
8836     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8837     {
8838       /* amoeba gets larger by growing in some direction */
8839
8840       int new_group_nr = AmoebaNr[ax][ay];
8841
8842 #ifdef DEBUG
8843   if (new_group_nr == 0)
8844   {
8845     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8846     printf("AmoebeAbleger(): This should never happen!\n");
8847     return;
8848   }
8849 #endif
8850
8851       AmoebaNr[newax][neway] = new_group_nr;
8852       AmoebaCnt[new_group_nr]++;
8853       AmoebaCnt2[new_group_nr]++;
8854
8855       /* if amoeba touches other amoeba(s) after growing, unify them */
8856       AmoebenVereinigen(newax, neway);
8857
8858       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8859       {
8860         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8861         return;
8862       }
8863     }
8864   }
8865
8866   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8867       (neway == lev_fieldy - 1 && newax != ax))
8868   {
8869     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8870     Store[newax][neway] = element;
8871   }
8872   else if (neway == ay || element == EL_EMC_DRIPPER)
8873   {
8874     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8875
8876     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8877   }
8878   else
8879   {
8880     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8881     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8882     Store[ax][ay] = EL_AMOEBA_DROP;
8883     ContinueMoving(ax, ay);
8884     return;
8885   }
8886
8887   TEST_DrawLevelField(newax, neway);
8888 }
8889
8890 void Life(int ax, int ay)
8891 {
8892   int x1, y1, x2, y2;
8893   int life_time = 40;
8894   int element = Feld[ax][ay];
8895   int graphic = el2img(element);
8896   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8897                          level.biomaze);
8898   boolean changed = FALSE;
8899
8900   if (IS_ANIMATED(graphic))
8901     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8902
8903   if (Stop[ax][ay])
8904     return;
8905
8906   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8907     MovDelay[ax][ay] = life_time;
8908
8909   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8910   {
8911     MovDelay[ax][ay]--;
8912     if (MovDelay[ax][ay])
8913       return;
8914   }
8915
8916   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8917   {
8918     int xx = ax+x1, yy = ay+y1;
8919     int nachbarn = 0;
8920
8921     if (!IN_LEV_FIELD(xx, yy))
8922       continue;
8923
8924     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8925     {
8926       int x = xx+x2, y = yy+y2;
8927
8928       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8929         continue;
8930
8931       if (((Feld[x][y] == element ||
8932             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8933            !Stop[x][y]) ||
8934           (IS_FREE(x, y) && Stop[x][y]))
8935         nachbarn++;
8936     }
8937
8938     if (xx == ax && yy == ay)           /* field in the middle */
8939     {
8940       if (nachbarn < life_parameter[0] ||
8941           nachbarn > life_parameter[1])
8942       {
8943         Feld[xx][yy] = EL_EMPTY;
8944         if (!Stop[xx][yy])
8945           TEST_DrawLevelField(xx, yy);
8946         Stop[xx][yy] = TRUE;
8947         changed = TRUE;
8948       }
8949     }
8950     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8951     {                                   /* free border field */
8952       if (nachbarn >= life_parameter[2] &&
8953           nachbarn <= life_parameter[3])
8954       {
8955         Feld[xx][yy] = element;
8956         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8957         if (!Stop[xx][yy])
8958           TEST_DrawLevelField(xx, yy);
8959         Stop[xx][yy] = TRUE;
8960         changed = TRUE;
8961       }
8962     }
8963   }
8964
8965   if (changed)
8966     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8967                    SND_GAME_OF_LIFE_GROWING);
8968 }
8969
8970 static void InitRobotWheel(int x, int y)
8971 {
8972   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8973 }
8974
8975 static void RunRobotWheel(int x, int y)
8976 {
8977   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8978 }
8979
8980 static void StopRobotWheel(int x, int y)
8981 {
8982   if (ZX == x && ZY == y)
8983   {
8984     ZX = ZY = -1;
8985
8986     game.robot_wheel_active = FALSE;
8987   }
8988 }
8989
8990 static void InitTimegateWheel(int x, int y)
8991 {
8992   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8993 }
8994
8995 static void RunTimegateWheel(int x, int y)
8996 {
8997   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8998 }
8999
9000 static void InitMagicBallDelay(int x, int y)
9001 {
9002   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9003 }
9004
9005 static void ActivateMagicBall(int bx, int by)
9006 {
9007   int x, y;
9008
9009   if (level.ball_random)
9010   {
9011     int pos_border = RND(8);    /* select one of the eight border elements */
9012     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9013     int xx = pos_content % 3;
9014     int yy = pos_content / 3;
9015
9016     x = bx - 1 + xx;
9017     y = by - 1 + yy;
9018
9019     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9020       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9021   }
9022   else
9023   {
9024     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9025     {
9026       int xx = x - bx + 1;
9027       int yy = y - by + 1;
9028
9029       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9030         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9031     }
9032   }
9033
9034   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9035 }
9036
9037 void CheckExit(int x, int y)
9038 {
9039   if (local_player->gems_still_needed > 0 ||
9040       local_player->sokobanfields_still_needed > 0 ||
9041       local_player->lights_still_needed > 0)
9042   {
9043     int element = Feld[x][y];
9044     int graphic = el2img(element);
9045
9046     if (IS_ANIMATED(graphic))
9047       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9048
9049     return;
9050   }
9051
9052   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9053     return;
9054
9055   Feld[x][y] = EL_EXIT_OPENING;
9056
9057   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9058 }
9059
9060 void CheckExitEM(int x, int y)
9061 {
9062   if (local_player->gems_still_needed > 0 ||
9063       local_player->sokobanfields_still_needed > 0 ||
9064       local_player->lights_still_needed > 0)
9065   {
9066     int element = Feld[x][y];
9067     int graphic = el2img(element);
9068
9069     if (IS_ANIMATED(graphic))
9070       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9071
9072     return;
9073   }
9074
9075   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9076     return;
9077
9078   Feld[x][y] = EL_EM_EXIT_OPENING;
9079
9080   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9081 }
9082
9083 void CheckExitSteel(int x, int y)
9084 {
9085   if (local_player->gems_still_needed > 0 ||
9086       local_player->sokobanfields_still_needed > 0 ||
9087       local_player->lights_still_needed > 0)
9088   {
9089     int element = Feld[x][y];
9090     int graphic = el2img(element);
9091
9092     if (IS_ANIMATED(graphic))
9093       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9094
9095     return;
9096   }
9097
9098   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9099     return;
9100
9101   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9102
9103   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9104 }
9105
9106 void CheckExitSteelEM(int x, int y)
9107 {
9108   if (local_player->gems_still_needed > 0 ||
9109       local_player->sokobanfields_still_needed > 0 ||
9110       local_player->lights_still_needed > 0)
9111   {
9112     int element = Feld[x][y];
9113     int graphic = el2img(element);
9114
9115     if (IS_ANIMATED(graphic))
9116       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9117
9118     return;
9119   }
9120
9121   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9122     return;
9123
9124   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9125
9126   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9127 }
9128
9129 void CheckExitSP(int x, int y)
9130 {
9131   if (local_player->gems_still_needed > 0)
9132   {
9133     int element = Feld[x][y];
9134     int graphic = el2img(element);
9135
9136     if (IS_ANIMATED(graphic))
9137       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9138
9139     return;
9140   }
9141
9142   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9143     return;
9144
9145   Feld[x][y] = EL_SP_EXIT_OPENING;
9146
9147   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9148 }
9149
9150 static void CloseAllOpenTimegates()
9151 {
9152   int x, y;
9153
9154   SCAN_PLAYFIELD(x, y)
9155   {
9156     int element = Feld[x][y];
9157
9158     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9159     {
9160       Feld[x][y] = EL_TIMEGATE_CLOSING;
9161
9162       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9163     }
9164   }
9165 }
9166
9167 void DrawTwinkleOnField(int x, int y)
9168 {
9169   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9170     return;
9171
9172   if (Feld[x][y] == EL_BD_DIAMOND)
9173     return;
9174
9175   if (MovDelay[x][y] == 0)      /* next animation frame */
9176     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9177
9178   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9179   {
9180     MovDelay[x][y]--;
9181
9182     DrawLevelElementAnimation(x, y, Feld[x][y]);
9183
9184     if (MovDelay[x][y] != 0)
9185     {
9186       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9187                                            10 - MovDelay[x][y]);
9188
9189       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9190     }
9191   }
9192 }
9193
9194 void MauerWaechst(int x, int y)
9195 {
9196   int delay = 6;
9197
9198   if (!MovDelay[x][y])          /* next animation frame */
9199     MovDelay[x][y] = 3 * delay;
9200
9201   if (MovDelay[x][y])           /* wait some time before next frame */
9202   {
9203     MovDelay[x][y]--;
9204
9205     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9206     {
9207       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9208       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9209
9210       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9211     }
9212
9213     if (!MovDelay[x][y])
9214     {
9215       if (MovDir[x][y] == MV_LEFT)
9216       {
9217         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9218           TEST_DrawLevelField(x - 1, y);
9219       }
9220       else if (MovDir[x][y] == MV_RIGHT)
9221       {
9222         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9223           TEST_DrawLevelField(x + 1, y);
9224       }
9225       else if (MovDir[x][y] == MV_UP)
9226       {
9227         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9228           TEST_DrawLevelField(x, y - 1);
9229       }
9230       else
9231       {
9232         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9233           TEST_DrawLevelField(x, y + 1);
9234       }
9235
9236       Feld[x][y] = Store[x][y];
9237       Store[x][y] = 0;
9238       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9239       TEST_DrawLevelField(x, y);
9240     }
9241   }
9242 }
9243
9244 void MauerAbleger(int ax, int ay)
9245 {
9246   int element = Feld[ax][ay];
9247   int graphic = el2img(element);
9248   boolean oben_frei = FALSE, unten_frei = FALSE;
9249   boolean links_frei = FALSE, rechts_frei = FALSE;
9250   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9251   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9252   boolean new_wall = FALSE;
9253
9254   if (IS_ANIMATED(graphic))
9255     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9256
9257   if (!MovDelay[ax][ay])        /* start building new wall */
9258     MovDelay[ax][ay] = 6;
9259
9260   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9261   {
9262     MovDelay[ax][ay]--;
9263     if (MovDelay[ax][ay])
9264       return;
9265   }
9266
9267   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9268     oben_frei = TRUE;
9269   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9270     unten_frei = TRUE;
9271   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9272     links_frei = TRUE;
9273   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9274     rechts_frei = TRUE;
9275
9276   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9277       element == EL_EXPANDABLE_WALL_ANY)
9278   {
9279     if (oben_frei)
9280     {
9281       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9282       Store[ax][ay-1] = element;
9283       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9284       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9285         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9286                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9287       new_wall = TRUE;
9288     }
9289     if (unten_frei)
9290     {
9291       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9292       Store[ax][ay+1] = element;
9293       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9294       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9295         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9296                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9297       new_wall = TRUE;
9298     }
9299   }
9300
9301   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9302       element == EL_EXPANDABLE_WALL_ANY ||
9303       element == EL_EXPANDABLE_WALL ||
9304       element == EL_BD_EXPANDABLE_WALL)
9305   {
9306     if (links_frei)
9307     {
9308       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9309       Store[ax-1][ay] = element;
9310       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9311       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9312         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9313                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9314       new_wall = TRUE;
9315     }
9316
9317     if (rechts_frei)
9318     {
9319       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9320       Store[ax+1][ay] = element;
9321       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9322       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9323         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9324                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9325       new_wall = TRUE;
9326     }
9327   }
9328
9329   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9330     TEST_DrawLevelField(ax, ay);
9331
9332   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9333     oben_massiv = TRUE;
9334   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9335     unten_massiv = TRUE;
9336   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9337     links_massiv = TRUE;
9338   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9339     rechts_massiv = TRUE;
9340
9341   if (((oben_massiv && unten_massiv) ||
9342        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9343        element == EL_EXPANDABLE_WALL) &&
9344       ((links_massiv && rechts_massiv) ||
9345        element == EL_EXPANDABLE_WALL_VERTICAL))
9346     Feld[ax][ay] = EL_WALL;
9347
9348   if (new_wall)
9349     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9350 }
9351
9352 void MauerAblegerStahl(int ax, int ay)
9353 {
9354   int element = Feld[ax][ay];
9355   int graphic = el2img(element);
9356   boolean oben_frei = FALSE, unten_frei = FALSE;
9357   boolean links_frei = FALSE, rechts_frei = FALSE;
9358   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9359   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9360   boolean new_wall = FALSE;
9361
9362   if (IS_ANIMATED(graphic))
9363     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9364
9365   if (!MovDelay[ax][ay])        /* start building new wall */
9366     MovDelay[ax][ay] = 6;
9367
9368   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9369   {
9370     MovDelay[ax][ay]--;
9371     if (MovDelay[ax][ay])
9372       return;
9373   }
9374
9375   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9376     oben_frei = TRUE;
9377   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9378     unten_frei = TRUE;
9379   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9380     links_frei = TRUE;
9381   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9382     rechts_frei = TRUE;
9383
9384   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9385       element == EL_EXPANDABLE_STEELWALL_ANY)
9386   {
9387     if (oben_frei)
9388     {
9389       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9390       Store[ax][ay-1] = element;
9391       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9392       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9393         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9394                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9395       new_wall = TRUE;
9396     }
9397     if (unten_frei)
9398     {
9399       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9400       Store[ax][ay+1] = element;
9401       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9402       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9403         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9404                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9405       new_wall = TRUE;
9406     }
9407   }
9408
9409   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9410       element == EL_EXPANDABLE_STEELWALL_ANY)
9411   {
9412     if (links_frei)
9413     {
9414       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9415       Store[ax-1][ay] = element;
9416       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9417       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9418         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9419                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9420       new_wall = TRUE;
9421     }
9422
9423     if (rechts_frei)
9424     {
9425       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9426       Store[ax+1][ay] = element;
9427       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9428       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9429         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9430                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9431       new_wall = TRUE;
9432     }
9433   }
9434
9435   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9436     oben_massiv = TRUE;
9437   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9438     unten_massiv = TRUE;
9439   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9440     links_massiv = TRUE;
9441   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9442     rechts_massiv = TRUE;
9443
9444   if (((oben_massiv && unten_massiv) ||
9445        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9446       ((links_massiv && rechts_massiv) ||
9447        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9448     Feld[ax][ay] = EL_STEELWALL;
9449
9450   if (new_wall)
9451     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9452 }
9453
9454 void CheckForDragon(int x, int y)
9455 {
9456   int i, j;
9457   boolean dragon_found = FALSE;
9458   static int xy[4][2] =
9459   {
9460     { 0, -1 },
9461     { -1, 0 },
9462     { +1, 0 },
9463     { 0, +1 }
9464   };
9465
9466   for (i = 0; i < NUM_DIRECTIONS; i++)
9467   {
9468     for (j = 0; j < 4; j++)
9469     {
9470       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9471
9472       if (IN_LEV_FIELD(xx, yy) &&
9473           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9474       {
9475         if (Feld[xx][yy] == EL_DRAGON)
9476           dragon_found = TRUE;
9477       }
9478       else
9479         break;
9480     }
9481   }
9482
9483   if (!dragon_found)
9484   {
9485     for (i = 0; i < NUM_DIRECTIONS; i++)
9486     {
9487       for (j = 0; j < 3; j++)
9488       {
9489         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9490   
9491         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9492         {
9493           Feld[xx][yy] = EL_EMPTY;
9494           TEST_DrawLevelField(xx, yy);
9495         }
9496         else
9497           break;
9498       }
9499     }
9500   }
9501 }
9502
9503 static void InitBuggyBase(int x, int y)
9504 {
9505   int element = Feld[x][y];
9506   int activating_delay = FRAMES_PER_SECOND / 4;
9507
9508   ChangeDelay[x][y] =
9509     (element == EL_SP_BUGGY_BASE ?
9510      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9511      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9512      activating_delay :
9513      element == EL_SP_BUGGY_BASE_ACTIVE ?
9514      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9515 }
9516
9517 static void WarnBuggyBase(int x, int y)
9518 {
9519   int i;
9520   static int xy[4][2] =
9521   {
9522     { 0, -1 },
9523     { -1, 0 },
9524     { +1, 0 },
9525     { 0, +1 }
9526   };
9527
9528   for (i = 0; i < NUM_DIRECTIONS; i++)
9529   {
9530     int xx = x + xy[i][0];
9531     int yy = y + xy[i][1];
9532
9533     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9534     {
9535       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9536
9537       break;
9538     }
9539   }
9540 }
9541
9542 static void InitTrap(int x, int y)
9543 {
9544   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9545 }
9546
9547 static void ActivateTrap(int x, int y)
9548 {
9549   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9550 }
9551
9552 static void ChangeActiveTrap(int x, int y)
9553 {
9554   int graphic = IMG_TRAP_ACTIVE;
9555
9556   /* if new animation frame was drawn, correct crumbled sand border */
9557   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9558     TEST_DrawLevelFieldCrumbled(x, y);
9559 }
9560
9561 static int getSpecialActionElement(int element, int number, int base_element)
9562 {
9563   return (element != EL_EMPTY ? element :
9564           number != -1 ? base_element + number - 1 :
9565           EL_EMPTY);
9566 }
9567
9568 static int getModifiedActionNumber(int value_old, int operator, int operand,
9569                                    int value_min, int value_max)
9570 {
9571   int value_new = (operator == CA_MODE_SET      ? operand :
9572                    operator == CA_MODE_ADD      ? value_old + operand :
9573                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9574                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9575                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9576                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9577                    value_old);
9578
9579   return (value_new < value_min ? value_min :
9580           value_new > value_max ? value_max :
9581           value_new);
9582 }
9583
9584 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9585 {
9586   struct ElementInfo *ei = &element_info[element];
9587   struct ElementChangeInfo *change = &ei->change_page[page];
9588   int target_element = change->target_element;
9589   int action_type = change->action_type;
9590   int action_mode = change->action_mode;
9591   int action_arg = change->action_arg;
9592   int action_element = change->action_element;
9593   int i;
9594
9595   if (!change->has_action)
9596     return;
9597
9598   /* ---------- determine action paramater values -------------------------- */
9599
9600   int level_time_value =
9601     (level.time > 0 ? TimeLeft :
9602      TimePlayed);
9603
9604   int action_arg_element_raw =
9605     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9606      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9607      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9608      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9609      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9610      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9611      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9612      EL_EMPTY);
9613   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9614
9615   int action_arg_direction =
9616     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9617      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9618      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9619      change->actual_trigger_side :
9620      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9621      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9622      MV_NONE);
9623
9624   int action_arg_number_min =
9625     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9626      CA_ARG_MIN);
9627
9628   int action_arg_number_max =
9629     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9630      action_type == CA_SET_LEVEL_GEMS ? 999 :
9631      action_type == CA_SET_LEVEL_TIME ? 9999 :
9632      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9633      action_type == CA_SET_CE_VALUE ? 9999 :
9634      action_type == CA_SET_CE_SCORE ? 9999 :
9635      CA_ARG_MAX);
9636
9637   int action_arg_number_reset =
9638     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9639      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9640      action_type == CA_SET_LEVEL_TIME ? level.time :
9641      action_type == CA_SET_LEVEL_SCORE ? 0 :
9642      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9643      action_type == CA_SET_CE_SCORE ? 0 :
9644      0);
9645
9646   int action_arg_number =
9647     (action_arg <= CA_ARG_MAX ? action_arg :
9648      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9649      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9650      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9651      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9652      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9653      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9654      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9655      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9656      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9657      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9658      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9659      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9660      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9661      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9662      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9663      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9664      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9665      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9666      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9667      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9668      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9669      -1);
9670
9671   int action_arg_number_old =
9672     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9673      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9674      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9675      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9676      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9677      0);
9678
9679   int action_arg_number_new =
9680     getModifiedActionNumber(action_arg_number_old,
9681                             action_mode, action_arg_number,
9682                             action_arg_number_min, action_arg_number_max);
9683
9684   int trigger_player_bits =
9685     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9686      change->actual_trigger_player_bits : change->trigger_player);
9687
9688   int action_arg_player_bits =
9689     (action_arg >= CA_ARG_PLAYER_1 &&
9690      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9691      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9692      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9693      PLAYER_BITS_ANY);
9694
9695   /* ---------- execute action  -------------------------------------------- */
9696
9697   switch (action_type)
9698   {
9699     case CA_NO_ACTION:
9700     {
9701       return;
9702     }
9703
9704     /* ---------- level actions  ------------------------------------------- */
9705
9706     case CA_RESTART_LEVEL:
9707     {
9708       game.restart_level = TRUE;
9709
9710       break;
9711     }
9712
9713     case CA_SHOW_ENVELOPE:
9714     {
9715       int element = getSpecialActionElement(action_arg_element,
9716                                             action_arg_number, EL_ENVELOPE_1);
9717
9718       if (IS_ENVELOPE(element))
9719         local_player->show_envelope = element;
9720
9721       break;
9722     }
9723
9724     case CA_SET_LEVEL_TIME:
9725     {
9726       if (level.time > 0)       /* only modify limited time value */
9727       {
9728         TimeLeft = action_arg_number_new;
9729
9730         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9731
9732         DisplayGameControlValues();
9733
9734         if (!TimeLeft && setup.time_limit)
9735           for (i = 0; i < MAX_PLAYERS; i++)
9736             KillPlayer(&stored_player[i]);
9737       }
9738
9739       break;
9740     }
9741
9742     case CA_SET_LEVEL_SCORE:
9743     {
9744       local_player->score = action_arg_number_new;
9745
9746       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9747
9748       DisplayGameControlValues();
9749
9750       break;
9751     }
9752
9753     case CA_SET_LEVEL_GEMS:
9754     {
9755       local_player->gems_still_needed = action_arg_number_new;
9756
9757       game.snapshot.collected_item = TRUE;
9758
9759       game_panel_controls[GAME_PANEL_GEMS].value =
9760         local_player->gems_still_needed;
9761
9762       DisplayGameControlValues();
9763
9764       break;
9765     }
9766
9767     case CA_SET_LEVEL_WIND:
9768     {
9769       game.wind_direction = action_arg_direction;
9770
9771       break;
9772     }
9773
9774     case CA_SET_LEVEL_RANDOM_SEED:
9775     {
9776       /* ensure that setting a new random seed while playing is predictable */
9777       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9778
9779       break;
9780     }
9781
9782     /* ---------- player actions  ------------------------------------------ */
9783
9784     case CA_MOVE_PLAYER:
9785     {
9786       /* automatically move to the next field in specified direction */
9787       for (i = 0; i < MAX_PLAYERS; i++)
9788         if (trigger_player_bits & (1 << i))
9789           stored_player[i].programmed_action = action_arg_direction;
9790
9791       break;
9792     }
9793
9794     case CA_EXIT_PLAYER:
9795     {
9796       for (i = 0; i < MAX_PLAYERS; i++)
9797         if (action_arg_player_bits & (1 << i))
9798           PlayerWins(&stored_player[i]);
9799
9800       break;
9801     }
9802
9803     case CA_KILL_PLAYER:
9804     {
9805       for (i = 0; i < MAX_PLAYERS; i++)
9806         if (action_arg_player_bits & (1 << i))
9807           KillPlayer(&stored_player[i]);
9808
9809       break;
9810     }
9811
9812     case CA_SET_PLAYER_KEYS:
9813     {
9814       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9815       int element = getSpecialActionElement(action_arg_element,
9816                                             action_arg_number, EL_KEY_1);
9817
9818       if (IS_KEY(element))
9819       {
9820         for (i = 0; i < MAX_PLAYERS; i++)
9821         {
9822           if (trigger_player_bits & (1 << i))
9823           {
9824             stored_player[i].key[KEY_NR(element)] = key_state;
9825
9826             DrawGameDoorValues();
9827           }
9828         }
9829       }
9830
9831       break;
9832     }
9833
9834     case CA_SET_PLAYER_SPEED:
9835     {
9836       for (i = 0; i < MAX_PLAYERS; i++)
9837       {
9838         if (trigger_player_bits & (1 << i))
9839         {
9840           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9841
9842           if (action_arg == CA_ARG_SPEED_FASTER &&
9843               stored_player[i].cannot_move)
9844           {
9845             action_arg_number = STEPSIZE_VERY_SLOW;
9846           }
9847           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9848                    action_arg == CA_ARG_SPEED_FASTER)
9849           {
9850             action_arg_number = 2;
9851             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9852                            CA_MODE_MULTIPLY);
9853           }
9854           else if (action_arg == CA_ARG_NUMBER_RESET)
9855           {
9856             action_arg_number = level.initial_player_stepsize[i];
9857           }
9858
9859           move_stepsize =
9860             getModifiedActionNumber(move_stepsize,
9861                                     action_mode,
9862                                     action_arg_number,
9863                                     action_arg_number_min,
9864                                     action_arg_number_max);
9865
9866           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9867         }
9868       }
9869
9870       break;
9871     }
9872
9873     case CA_SET_PLAYER_SHIELD:
9874     {
9875       for (i = 0; i < MAX_PLAYERS; i++)
9876       {
9877         if (trigger_player_bits & (1 << i))
9878         {
9879           if (action_arg == CA_ARG_SHIELD_OFF)
9880           {
9881             stored_player[i].shield_normal_time_left = 0;
9882             stored_player[i].shield_deadly_time_left = 0;
9883           }
9884           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9885           {
9886             stored_player[i].shield_normal_time_left = 999999;
9887           }
9888           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9889           {
9890             stored_player[i].shield_normal_time_left = 999999;
9891             stored_player[i].shield_deadly_time_left = 999999;
9892           }
9893         }
9894       }
9895
9896       break;
9897     }
9898
9899     case CA_SET_PLAYER_GRAVITY:
9900     {
9901       for (i = 0; i < MAX_PLAYERS; i++)
9902       {
9903         if (trigger_player_bits & (1 << i))
9904         {
9905           stored_player[i].gravity =
9906             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9907              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9908              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9909              stored_player[i].gravity);
9910         }
9911       }
9912
9913       break;
9914     }
9915
9916     case CA_SET_PLAYER_ARTWORK:
9917     {
9918       for (i = 0; i < MAX_PLAYERS; i++)
9919       {
9920         if (trigger_player_bits & (1 << i))
9921         {
9922           int artwork_element = action_arg_element;
9923
9924           if (action_arg == CA_ARG_ELEMENT_RESET)
9925             artwork_element =
9926               (level.use_artwork_element[i] ? level.artwork_element[i] :
9927                stored_player[i].element_nr);
9928
9929           if (stored_player[i].artwork_element != artwork_element)
9930             stored_player[i].Frame = 0;
9931
9932           stored_player[i].artwork_element = artwork_element;
9933
9934           SetPlayerWaiting(&stored_player[i], FALSE);
9935
9936           /* set number of special actions for bored and sleeping animation */
9937           stored_player[i].num_special_action_bored =
9938             get_num_special_action(artwork_element,
9939                                    ACTION_BORING_1, ACTION_BORING_LAST);
9940           stored_player[i].num_special_action_sleeping =
9941             get_num_special_action(artwork_element,
9942                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9943         }
9944       }
9945
9946       break;
9947     }
9948
9949     case CA_SET_PLAYER_INVENTORY:
9950     {
9951       for (i = 0; i < MAX_PLAYERS; i++)
9952       {
9953         struct PlayerInfo *player = &stored_player[i];
9954         int j, k;
9955
9956         if (trigger_player_bits & (1 << i))
9957         {
9958           int inventory_element = action_arg_element;
9959
9960           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9961               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9962               action_arg == CA_ARG_ELEMENT_ACTION)
9963           {
9964             int element = inventory_element;
9965             int collect_count = element_info[element].collect_count_initial;
9966
9967             if (!IS_CUSTOM_ELEMENT(element))
9968               collect_count = 1;
9969
9970             if (collect_count == 0)
9971               player->inventory_infinite_element = element;
9972             else
9973               for (k = 0; k < collect_count; k++)
9974                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9975                   player->inventory_element[player->inventory_size++] =
9976                     element;
9977           }
9978           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9979                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9980                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9981           {
9982             if (player->inventory_infinite_element != EL_UNDEFINED &&
9983                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9984                                      action_arg_element_raw))
9985               player->inventory_infinite_element = EL_UNDEFINED;
9986
9987             for (k = 0, j = 0; j < player->inventory_size; j++)
9988             {
9989               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9990                                         action_arg_element_raw))
9991                 player->inventory_element[k++] = player->inventory_element[j];
9992             }
9993
9994             player->inventory_size = k;
9995           }
9996           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9997           {
9998             if (player->inventory_size > 0)
9999             {
10000               for (j = 0; j < player->inventory_size - 1; j++)
10001                 player->inventory_element[j] = player->inventory_element[j + 1];
10002
10003               player->inventory_size--;
10004             }
10005           }
10006           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10007           {
10008             if (player->inventory_size > 0)
10009               player->inventory_size--;
10010           }
10011           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10012           {
10013             player->inventory_infinite_element = EL_UNDEFINED;
10014             player->inventory_size = 0;
10015           }
10016           else if (action_arg == CA_ARG_INVENTORY_RESET)
10017           {
10018             player->inventory_infinite_element = EL_UNDEFINED;
10019             player->inventory_size = 0;
10020
10021             if (level.use_initial_inventory[i])
10022             {
10023               for (j = 0; j < level.initial_inventory_size[i]; j++)
10024               {
10025                 int element = level.initial_inventory_content[i][j];
10026                 int collect_count = element_info[element].collect_count_initial;
10027
10028                 if (!IS_CUSTOM_ELEMENT(element))
10029                   collect_count = 1;
10030
10031                 if (collect_count == 0)
10032                   player->inventory_infinite_element = element;
10033                 else
10034                   for (k = 0; k < collect_count; k++)
10035                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10036                       player->inventory_element[player->inventory_size++] =
10037                         element;
10038               }
10039             }
10040           }
10041         }
10042       }
10043
10044       break;
10045     }
10046
10047     /* ---------- CE actions  ---------------------------------------------- */
10048
10049     case CA_SET_CE_VALUE:
10050     {
10051       int last_ce_value = CustomValue[x][y];
10052
10053       CustomValue[x][y] = action_arg_number_new;
10054
10055       if (CustomValue[x][y] != last_ce_value)
10056       {
10057         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10058         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10059
10060         if (CustomValue[x][y] == 0)
10061         {
10062           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10063           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10064         }
10065       }
10066
10067       break;
10068     }
10069
10070     case CA_SET_CE_SCORE:
10071     {
10072       int last_ce_score = ei->collect_score;
10073
10074       ei->collect_score = action_arg_number_new;
10075
10076       if (ei->collect_score != last_ce_score)
10077       {
10078         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10079         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10080
10081         if (ei->collect_score == 0)
10082         {
10083           int xx, yy;
10084
10085           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10086           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10087
10088           /*
10089             This is a very special case that seems to be a mixture between
10090             CheckElementChange() and CheckTriggeredElementChange(): while
10091             the first one only affects single elements that are triggered
10092             directly, the second one affects multiple elements in the playfield
10093             that are triggered indirectly by another element. This is a third
10094             case: Changing the CE score always affects multiple identical CEs,
10095             so every affected CE must be checked, not only the single CE for
10096             which the CE score was changed in the first place (as every instance
10097             of that CE shares the same CE score, and therefore also can change)!
10098           */
10099           SCAN_PLAYFIELD(xx, yy)
10100           {
10101             if (Feld[xx][yy] == element)
10102               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10103                                  CE_SCORE_GETS_ZERO);
10104           }
10105         }
10106       }
10107
10108       break;
10109     }
10110
10111     case CA_SET_CE_ARTWORK:
10112     {
10113       int artwork_element = action_arg_element;
10114       boolean reset_frame = FALSE;
10115       int xx, yy;
10116
10117       if (action_arg == CA_ARG_ELEMENT_RESET)
10118         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10119                            element);
10120
10121       if (ei->gfx_element != artwork_element)
10122         reset_frame = TRUE;
10123
10124       ei->gfx_element = artwork_element;
10125
10126       SCAN_PLAYFIELD(xx, yy)
10127       {
10128         if (Feld[xx][yy] == element)
10129         {
10130           if (reset_frame)
10131           {
10132             ResetGfxAnimation(xx, yy);
10133             ResetRandomAnimationValue(xx, yy);
10134           }
10135
10136           TEST_DrawLevelField(xx, yy);
10137         }
10138       }
10139
10140       break;
10141     }
10142
10143     /* ---------- engine actions  ------------------------------------------ */
10144
10145     case CA_SET_ENGINE_SCAN_MODE:
10146     {
10147       InitPlayfieldScanMode(action_arg);
10148
10149       break;
10150     }
10151
10152     default:
10153       break;
10154   }
10155 }
10156
10157 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10158 {
10159   int old_element = Feld[x][y];
10160   int new_element = GetElementFromGroupElement(element);
10161   int previous_move_direction = MovDir[x][y];
10162   int last_ce_value = CustomValue[x][y];
10163   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10164   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10165   boolean add_player_onto_element = (new_element_is_player &&
10166                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10167                                      IS_WALKABLE(old_element));
10168
10169   if (!add_player_onto_element)
10170   {
10171     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10172       RemoveMovingField(x, y);
10173     else
10174       RemoveField(x, y);
10175
10176     Feld[x][y] = new_element;
10177
10178     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10179       MovDir[x][y] = previous_move_direction;
10180
10181     if (element_info[new_element].use_last_ce_value)
10182       CustomValue[x][y] = last_ce_value;
10183
10184     InitField_WithBug1(x, y, FALSE);
10185
10186     new_element = Feld[x][y];   /* element may have changed */
10187
10188     ResetGfxAnimation(x, y);
10189     ResetRandomAnimationValue(x, y);
10190
10191     TEST_DrawLevelField(x, y);
10192
10193     if (GFX_CRUMBLED(new_element))
10194       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10195   }
10196
10197   /* check if element under the player changes from accessible to unaccessible
10198      (needed for special case of dropping element which then changes) */
10199   /* (must be checked after creating new element for walkable group elements) */
10200   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10201       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10202   {
10203     Bang(x, y);
10204
10205     return;
10206   }
10207
10208   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10209   if (new_element_is_player)
10210     RelocatePlayer(x, y, new_element);
10211
10212   if (is_change)
10213     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10214
10215   TestIfBadThingTouchesPlayer(x, y);
10216   TestIfPlayerTouchesCustomElement(x, y);
10217   TestIfElementTouchesCustomElement(x, y);
10218 }
10219
10220 static void CreateField(int x, int y, int element)
10221 {
10222   CreateFieldExt(x, y, element, FALSE);
10223 }
10224
10225 static void CreateElementFromChange(int x, int y, int element)
10226 {
10227   element = GET_VALID_RUNTIME_ELEMENT(element);
10228
10229   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10230   {
10231     int old_element = Feld[x][y];
10232
10233     /* prevent changed element from moving in same engine frame
10234        unless both old and new element can either fall or move */
10235     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10236         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10237       Stop[x][y] = TRUE;
10238   }
10239
10240   CreateFieldExt(x, y, element, TRUE);
10241 }
10242
10243 static boolean ChangeElement(int x, int y, int element, int page)
10244 {
10245   struct ElementInfo *ei = &element_info[element];
10246   struct ElementChangeInfo *change = &ei->change_page[page];
10247   int ce_value = CustomValue[x][y];
10248   int ce_score = ei->collect_score;
10249   int target_element;
10250   int old_element = Feld[x][y];
10251
10252   /* always use default change event to prevent running into a loop */
10253   if (ChangeEvent[x][y] == -1)
10254     ChangeEvent[x][y] = CE_DELAY;
10255
10256   if (ChangeEvent[x][y] == CE_DELAY)
10257   {
10258     /* reset actual trigger element, trigger player and action element */
10259     change->actual_trigger_element = EL_EMPTY;
10260     change->actual_trigger_player = EL_EMPTY;
10261     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10262     change->actual_trigger_side = CH_SIDE_NONE;
10263     change->actual_trigger_ce_value = 0;
10264     change->actual_trigger_ce_score = 0;
10265   }
10266
10267   /* do not change elements more than a specified maximum number of changes */
10268   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10269     return FALSE;
10270
10271   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10272
10273   if (change->explode)
10274   {
10275     Bang(x, y);
10276
10277     return TRUE;
10278   }
10279
10280   if (change->use_target_content)
10281   {
10282     boolean complete_replace = TRUE;
10283     boolean can_replace[3][3];
10284     int xx, yy;
10285
10286     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10287     {
10288       boolean is_empty;
10289       boolean is_walkable;
10290       boolean is_diggable;
10291       boolean is_collectible;
10292       boolean is_removable;
10293       boolean is_destructible;
10294       int ex = x + xx - 1;
10295       int ey = y + yy - 1;
10296       int content_element = change->target_content.e[xx][yy];
10297       int e;
10298
10299       can_replace[xx][yy] = TRUE;
10300
10301       if (ex == x && ey == y)   /* do not check changing element itself */
10302         continue;
10303
10304       if (content_element == EL_EMPTY_SPACE)
10305       {
10306         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10307
10308         continue;
10309       }
10310
10311       if (!IN_LEV_FIELD(ex, ey))
10312       {
10313         can_replace[xx][yy] = FALSE;
10314         complete_replace = FALSE;
10315
10316         continue;
10317       }
10318
10319       e = Feld[ex][ey];
10320
10321       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10322         e = MovingOrBlocked2Element(ex, ey);
10323
10324       is_empty = (IS_FREE(ex, ey) ||
10325                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10326
10327       is_walkable     = (is_empty || IS_WALKABLE(e));
10328       is_diggable     = (is_empty || IS_DIGGABLE(e));
10329       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10330       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10331       is_removable    = (is_diggable || is_collectible);
10332
10333       can_replace[xx][yy] =
10334         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10335           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10336           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10337           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10338           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10339           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10340          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10341
10342       if (!can_replace[xx][yy])
10343         complete_replace = FALSE;
10344     }
10345
10346     if (!change->only_if_complete || complete_replace)
10347     {
10348       boolean something_has_changed = FALSE;
10349
10350       if (change->only_if_complete && change->use_random_replace &&
10351           RND(100) < change->random_percentage)
10352         return FALSE;
10353
10354       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10355       {
10356         int ex = x + xx - 1;
10357         int ey = y + yy - 1;
10358         int content_element;
10359
10360         if (can_replace[xx][yy] && (!change->use_random_replace ||
10361                                     RND(100) < change->random_percentage))
10362         {
10363           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10364             RemoveMovingField(ex, ey);
10365
10366           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10367
10368           content_element = change->target_content.e[xx][yy];
10369           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10370                                               ce_value, ce_score);
10371
10372           CreateElementFromChange(ex, ey, target_element);
10373
10374           something_has_changed = TRUE;
10375
10376           /* for symmetry reasons, freeze newly created border elements */
10377           if (ex != x || ey != y)
10378             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10379         }
10380       }
10381
10382       if (something_has_changed)
10383       {
10384         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10385         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10386       }
10387     }
10388   }
10389   else
10390   {
10391     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10392                                         ce_value, ce_score);
10393
10394     if (element == EL_DIAGONAL_GROWING ||
10395         element == EL_DIAGONAL_SHRINKING)
10396     {
10397       target_element = Store[x][y];
10398
10399       Store[x][y] = EL_EMPTY;
10400     }
10401
10402     CreateElementFromChange(x, y, target_element);
10403
10404     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10405     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10406   }
10407
10408   /* this uses direct change before indirect change */
10409   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10410
10411   return TRUE;
10412 }
10413
10414 static void HandleElementChange(int x, int y, int page)
10415 {
10416   int element = MovingOrBlocked2Element(x, y);
10417   struct ElementInfo *ei = &element_info[element];
10418   struct ElementChangeInfo *change = &ei->change_page[page];
10419   boolean handle_action_before_change = FALSE;
10420
10421 #ifdef DEBUG
10422   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10423       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10424   {
10425     printf("\n\n");
10426     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10427            x, y, element, element_info[element].token_name);
10428     printf("HandleElementChange(): This should never happen!\n");
10429     printf("\n\n");
10430   }
10431 #endif
10432
10433   /* this can happen with classic bombs on walkable, changing elements */
10434   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10435   {
10436     return;
10437   }
10438
10439   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10440   {
10441     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10442
10443     if (change->can_change)
10444     {
10445       /* !!! not clear why graphic animation should be reset at all here !!! */
10446       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10447       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10448
10449       /*
10450         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10451
10452         When using an animation frame delay of 1 (this only happens with
10453         "sp_zonk.moving.left/right" in the classic graphics), the default
10454         (non-moving) animation shows wrong animation frames (while the
10455         moving animation, like "sp_zonk.moving.left/right", is correct,
10456         so this graphical bug never shows up with the classic graphics).
10457         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10458         be drawn instead of the correct frames 0,1,2,3. This is caused by
10459         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10460         an element change: First when the change delay ("ChangeDelay[][]")
10461         counter has reached zero after decrementing, then a second time in
10462         the next frame (after "GfxFrame[][]" was already incremented) when
10463         "ChangeDelay[][]" is reset to the initial delay value again.
10464
10465         This causes frame 0 to be drawn twice, while the last frame won't
10466         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10467
10468         As some animations may already be cleverly designed around this bug
10469         (at least the "Snake Bite" snake tail animation does this), it cannot
10470         simply be fixed here without breaking such existing animations.
10471         Unfortunately, it cannot easily be detected if a graphics set was
10472         designed "before" or "after" the bug was fixed. As a workaround,
10473         a new graphics set option "game.graphics_engine_version" was added
10474         to be able to specify the game's major release version for which the
10475         graphics set was designed, which can then be used to decide if the
10476         bugfix should be used (version 4 and above) or not (version 3 or
10477         below, or if no version was specified at all, as with old sets).
10478
10479         (The wrong/fixed animation frames can be tested with the test level set
10480         "test_gfxframe" and level "000", which contains a specially prepared
10481         custom element at level position (x/y) == (11/9) which uses the zonk
10482         animation mentioned above. Using "game.graphics_engine_version: 4"
10483         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10484         This can also be seen from the debug output for this test element.)
10485       */
10486
10487       /* when a custom element is about to change (for example by change delay),
10488          do not reset graphic animation when the custom element is moving */
10489       if (game.graphics_engine_version < 4 &&
10490           !IS_MOVING(x, y))
10491       {
10492         ResetGfxAnimation(x, y);
10493         ResetRandomAnimationValue(x, y);
10494       }
10495
10496       if (change->pre_change_function)
10497         change->pre_change_function(x, y);
10498     }
10499   }
10500
10501   ChangeDelay[x][y]--;
10502
10503   if (ChangeDelay[x][y] != 0)           /* continue element change */
10504   {
10505     if (change->can_change)
10506     {
10507       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10508
10509       if (IS_ANIMATED(graphic))
10510         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10511
10512       if (change->change_function)
10513         change->change_function(x, y);
10514     }
10515   }
10516   else                                  /* finish element change */
10517   {
10518     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10519     {
10520       page = ChangePage[x][y];
10521       ChangePage[x][y] = -1;
10522
10523       change = &ei->change_page[page];
10524     }
10525
10526     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10527     {
10528       ChangeDelay[x][y] = 1;            /* try change after next move step */
10529       ChangePage[x][y] = page;          /* remember page to use for change */
10530
10531       return;
10532     }
10533
10534     /* special case: set new level random seed before changing element */
10535     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10536       handle_action_before_change = TRUE;
10537
10538     if (change->has_action && handle_action_before_change)
10539       ExecuteCustomElementAction(x, y, element, page);
10540
10541     if (change->can_change)
10542     {
10543       if (ChangeElement(x, y, element, page))
10544       {
10545         if (change->post_change_function)
10546           change->post_change_function(x, y);
10547       }
10548     }
10549
10550     if (change->has_action && !handle_action_before_change)
10551       ExecuteCustomElementAction(x, y, element, page);
10552   }
10553 }
10554
10555 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10556                                               int trigger_element,
10557                                               int trigger_event,
10558                                               int trigger_player,
10559                                               int trigger_side,
10560                                               int trigger_page)
10561 {
10562   boolean change_done_any = FALSE;
10563   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10564   int i;
10565
10566   if (!(trigger_events[trigger_element][trigger_event]))
10567     return FALSE;
10568
10569   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10570
10571   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10572   {
10573     int element = EL_CUSTOM_START + i;
10574     boolean change_done = FALSE;
10575     int p;
10576
10577     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10578         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10579       continue;
10580
10581     for (p = 0; p < element_info[element].num_change_pages; p++)
10582     {
10583       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10584
10585       if (change->can_change_or_has_action &&
10586           change->has_event[trigger_event] &&
10587           change->trigger_side & trigger_side &&
10588           change->trigger_player & trigger_player &&
10589           change->trigger_page & trigger_page_bits &&
10590           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10591       {
10592         change->actual_trigger_element = trigger_element;
10593         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10594         change->actual_trigger_player_bits = trigger_player;
10595         change->actual_trigger_side = trigger_side;
10596         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10597         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10598
10599         if ((change->can_change && !change_done) || change->has_action)
10600         {
10601           int x, y;
10602
10603           SCAN_PLAYFIELD(x, y)
10604           {
10605             if (Feld[x][y] == element)
10606             {
10607               if (change->can_change && !change_done)
10608               {
10609                 /* if element already changed in this frame, not only prevent
10610                    another element change (checked in ChangeElement()), but
10611                    also prevent additional element actions for this element */
10612
10613                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10614                     !level.use_action_after_change_bug)
10615                   continue;
10616
10617                 ChangeDelay[x][y] = 1;
10618                 ChangeEvent[x][y] = trigger_event;
10619
10620                 HandleElementChange(x, y, p);
10621               }
10622               else if (change->has_action)
10623               {
10624                 /* if element already changed in this frame, not only prevent
10625                    another element change (checked in ChangeElement()), but
10626                    also prevent additional element actions for this element */
10627
10628                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10629                     !level.use_action_after_change_bug)
10630                   continue;
10631
10632                 ExecuteCustomElementAction(x, y, element, p);
10633                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10634               }
10635             }
10636           }
10637
10638           if (change->can_change)
10639           {
10640             change_done = TRUE;
10641             change_done_any = TRUE;
10642           }
10643         }
10644       }
10645     }
10646   }
10647
10648   RECURSION_LOOP_DETECTION_END();
10649
10650   return change_done_any;
10651 }
10652
10653 static boolean CheckElementChangeExt(int x, int y,
10654                                      int element,
10655                                      int trigger_element,
10656                                      int trigger_event,
10657                                      int trigger_player,
10658                                      int trigger_side)
10659 {
10660   boolean change_done = FALSE;
10661   int p;
10662
10663   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10664       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10665     return FALSE;
10666
10667   if (Feld[x][y] == EL_BLOCKED)
10668   {
10669     Blocked2Moving(x, y, &x, &y);
10670     element = Feld[x][y];
10671   }
10672
10673   /* check if element has already changed or is about to change after moving */
10674   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10675        Feld[x][y] != element) ||
10676
10677       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10678        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10679         ChangePage[x][y] != -1)))
10680     return FALSE;
10681
10682   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10683
10684   for (p = 0; p < element_info[element].num_change_pages; p++)
10685   {
10686     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10687
10688     /* check trigger element for all events where the element that is checked
10689        for changing interacts with a directly adjacent element -- this is
10690        different to element changes that affect other elements to change on the
10691        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10692     boolean check_trigger_element =
10693       (trigger_event == CE_TOUCHING_X ||
10694        trigger_event == CE_HITTING_X ||
10695        trigger_event == CE_HIT_BY_X ||
10696        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10697
10698     if (change->can_change_or_has_action &&
10699         change->has_event[trigger_event] &&
10700         change->trigger_side & trigger_side &&
10701         change->trigger_player & trigger_player &&
10702         (!check_trigger_element ||
10703          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10704     {
10705       change->actual_trigger_element = trigger_element;
10706       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10707       change->actual_trigger_player_bits = trigger_player;
10708       change->actual_trigger_side = trigger_side;
10709       change->actual_trigger_ce_value = CustomValue[x][y];
10710       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10711
10712       /* special case: trigger element not at (x,y) position for some events */
10713       if (check_trigger_element)
10714       {
10715         static struct
10716         {
10717           int dx, dy;
10718         } move_xy[] =
10719           {
10720             {  0,  0 },
10721             { -1,  0 },
10722             { +1,  0 },
10723             {  0,  0 },
10724             {  0, -1 },
10725             {  0,  0 }, { 0, 0 }, { 0, 0 },
10726             {  0, +1 }
10727           };
10728
10729         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10730         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10731
10732         change->actual_trigger_ce_value = CustomValue[xx][yy];
10733         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10734       }
10735
10736       if (change->can_change && !change_done)
10737       {
10738         ChangeDelay[x][y] = 1;
10739         ChangeEvent[x][y] = trigger_event;
10740
10741         HandleElementChange(x, y, p);
10742
10743         change_done = TRUE;
10744       }
10745       else if (change->has_action)
10746       {
10747         ExecuteCustomElementAction(x, y, element, p);
10748         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10749       }
10750     }
10751   }
10752
10753   RECURSION_LOOP_DETECTION_END();
10754
10755   return change_done;
10756 }
10757
10758 static void PlayPlayerSound(struct PlayerInfo *player)
10759 {
10760   int jx = player->jx, jy = player->jy;
10761   int sound_element = player->artwork_element;
10762   int last_action = player->last_action_waiting;
10763   int action = player->action_waiting;
10764
10765   if (player->is_waiting)
10766   {
10767     if (action != last_action)
10768       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10769     else
10770       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10771   }
10772   else
10773   {
10774     if (action != last_action)
10775       StopSound(element_info[sound_element].sound[last_action]);
10776
10777     if (last_action == ACTION_SLEEPING)
10778       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10779   }
10780 }
10781
10782 static void PlayAllPlayersSound()
10783 {
10784   int i;
10785
10786   for (i = 0; i < MAX_PLAYERS; i++)
10787     if (stored_player[i].active)
10788       PlayPlayerSound(&stored_player[i]);
10789 }
10790
10791 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10792 {
10793   boolean last_waiting = player->is_waiting;
10794   int move_dir = player->MovDir;
10795
10796   player->dir_waiting = move_dir;
10797   player->last_action_waiting = player->action_waiting;
10798
10799   if (is_waiting)
10800   {
10801     if (!last_waiting)          /* not waiting -> waiting */
10802     {
10803       player->is_waiting = TRUE;
10804
10805       player->frame_counter_bored =
10806         FrameCounter +
10807         game.player_boring_delay_fixed +
10808         GetSimpleRandom(game.player_boring_delay_random);
10809       player->frame_counter_sleeping =
10810         FrameCounter +
10811         game.player_sleeping_delay_fixed +
10812         GetSimpleRandom(game.player_sleeping_delay_random);
10813
10814       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10815     }
10816
10817     if (game.player_sleeping_delay_fixed +
10818         game.player_sleeping_delay_random > 0 &&
10819         player->anim_delay_counter == 0 &&
10820         player->post_delay_counter == 0 &&
10821         FrameCounter >= player->frame_counter_sleeping)
10822       player->is_sleeping = TRUE;
10823     else if (game.player_boring_delay_fixed +
10824              game.player_boring_delay_random > 0 &&
10825              FrameCounter >= player->frame_counter_bored)
10826       player->is_bored = TRUE;
10827
10828     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10829                               player->is_bored ? ACTION_BORING :
10830                               ACTION_WAITING);
10831
10832     if (player->is_sleeping && player->use_murphy)
10833     {
10834       /* special case for sleeping Murphy when leaning against non-free tile */
10835
10836       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10837           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10838            !IS_MOVING(player->jx - 1, player->jy)))
10839         move_dir = MV_LEFT;
10840       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10841                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10842                 !IS_MOVING(player->jx + 1, player->jy)))
10843         move_dir = MV_RIGHT;
10844       else
10845         player->is_sleeping = FALSE;
10846
10847       player->dir_waiting = move_dir;
10848     }
10849
10850     if (player->is_sleeping)
10851     {
10852       if (player->num_special_action_sleeping > 0)
10853       {
10854         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10855         {
10856           int last_special_action = player->special_action_sleeping;
10857           int num_special_action = player->num_special_action_sleeping;
10858           int special_action =
10859             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10860              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10861              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10862              last_special_action + 1 : ACTION_SLEEPING);
10863           int special_graphic =
10864             el_act_dir2img(player->artwork_element, special_action, move_dir);
10865
10866           player->anim_delay_counter =
10867             graphic_info[special_graphic].anim_delay_fixed +
10868             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10869           player->post_delay_counter =
10870             graphic_info[special_graphic].post_delay_fixed +
10871             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10872
10873           player->special_action_sleeping = special_action;
10874         }
10875
10876         if (player->anim_delay_counter > 0)
10877         {
10878           player->action_waiting = player->special_action_sleeping;
10879           player->anim_delay_counter--;
10880         }
10881         else if (player->post_delay_counter > 0)
10882         {
10883           player->post_delay_counter--;
10884         }
10885       }
10886     }
10887     else if (player->is_bored)
10888     {
10889       if (player->num_special_action_bored > 0)
10890       {
10891         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10892         {
10893           int special_action =
10894             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10895           int special_graphic =
10896             el_act_dir2img(player->artwork_element, special_action, move_dir);
10897
10898           player->anim_delay_counter =
10899             graphic_info[special_graphic].anim_delay_fixed +
10900             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10901           player->post_delay_counter =
10902             graphic_info[special_graphic].post_delay_fixed +
10903             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10904
10905           player->special_action_bored = special_action;
10906         }
10907
10908         if (player->anim_delay_counter > 0)
10909         {
10910           player->action_waiting = player->special_action_bored;
10911           player->anim_delay_counter--;
10912         }
10913         else if (player->post_delay_counter > 0)
10914         {
10915           player->post_delay_counter--;
10916         }
10917       }
10918     }
10919   }
10920   else if (last_waiting)        /* waiting -> not waiting */
10921   {
10922     player->is_waiting = FALSE;
10923     player->is_bored = FALSE;
10924     player->is_sleeping = FALSE;
10925
10926     player->frame_counter_bored = -1;
10927     player->frame_counter_sleeping = -1;
10928
10929     player->anim_delay_counter = 0;
10930     player->post_delay_counter = 0;
10931
10932     player->dir_waiting = player->MovDir;
10933     player->action_waiting = ACTION_DEFAULT;
10934
10935     player->special_action_bored = ACTION_DEFAULT;
10936     player->special_action_sleeping = ACTION_DEFAULT;
10937   }
10938 }
10939
10940 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10941 {
10942   if ((!player->is_moving  && player->was_moving) ||
10943       (player->MovPos == 0 && player->was_moving) ||
10944       (player->is_snapping && !player->was_snapping) ||
10945       (player->is_dropping && !player->was_dropping))
10946   {
10947     if (!CheckSaveEngineSnapshotToList())
10948       return;
10949
10950     player->was_moving = FALSE;
10951     player->was_snapping = TRUE;
10952     player->was_dropping = TRUE;
10953   }
10954   else
10955   {
10956     if (player->is_moving)
10957       player->was_moving = TRUE;
10958
10959     if (!player->is_snapping)
10960       player->was_snapping = FALSE;
10961
10962     if (!player->is_dropping)
10963       player->was_dropping = FALSE;
10964   }
10965 }
10966
10967 static void CheckSingleStepMode(struct PlayerInfo *player)
10968 {
10969   if (tape.single_step && tape.recording && !tape.pausing)
10970   {
10971     /* as it is called "single step mode", just return to pause mode when the
10972        player stopped moving after one tile (or never starts moving at all) */
10973     if (!player->is_moving &&
10974         !player->is_pushing &&
10975         !player->is_dropping_pressed)
10976     {
10977       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10978       SnapField(player, 0, 0);                  /* stop snapping */
10979     }
10980   }
10981
10982   CheckSaveEngineSnapshot(player);
10983 }
10984
10985 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10986 {
10987   int left      = player_action & JOY_LEFT;
10988   int right     = player_action & JOY_RIGHT;
10989   int up        = player_action & JOY_UP;
10990   int down      = player_action & JOY_DOWN;
10991   int button1   = player_action & JOY_BUTTON_1;
10992   int button2   = player_action & JOY_BUTTON_2;
10993   int dx        = (left ? -1 : right ? 1 : 0);
10994   int dy        = (up   ? -1 : down  ? 1 : 0);
10995
10996   if (!player->active || tape.pausing)
10997     return 0;
10998
10999   if (player_action)
11000   {
11001     if (button1)
11002       SnapField(player, dx, dy);
11003     else
11004     {
11005       if (button2)
11006         DropElement(player);
11007
11008       MovePlayer(player, dx, dy);
11009     }
11010
11011     CheckSingleStepMode(player);
11012
11013     SetPlayerWaiting(player, FALSE);
11014
11015     return player_action;
11016   }
11017   else
11018   {
11019     /* no actions for this player (no input at player's configured device) */
11020
11021     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11022     SnapField(player, 0, 0);
11023     CheckGravityMovementWhenNotMoving(player);
11024
11025     if (player->MovPos == 0)
11026       SetPlayerWaiting(player, TRUE);
11027
11028     if (player->MovPos == 0)    /* needed for tape.playing */
11029       player->is_moving = FALSE;
11030
11031     player->is_dropping = FALSE;
11032     player->is_dropping_pressed = FALSE;
11033     player->drop_pressed_delay = 0;
11034
11035     CheckSingleStepMode(player);
11036
11037     return 0;
11038   }
11039 }
11040
11041 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11042                                          byte *tape_action)
11043 {
11044   if (!tape.use_mouse)
11045     return;
11046
11047   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11048   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11049   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11050 }
11051
11052 static void SetTapeActionFromMouseAction(byte *tape_action,
11053                                          struct MouseActionInfo *mouse_action)
11054 {
11055   if (!tape.use_mouse)
11056     return;
11057
11058   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11059   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11060   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11061 }
11062
11063 static void CheckLevelTime()
11064 {
11065   int i;
11066
11067   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11068   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11069   {
11070     if (level.native_em_level->lev->home == 0)  /* all players at home */
11071     {
11072       PlayerWins(local_player);
11073
11074       AllPlayersGone = TRUE;
11075
11076       level.native_em_level->lev->home = -1;
11077     }
11078
11079     if (level.native_em_level->ply[0]->alive == 0 &&
11080         level.native_em_level->ply[1]->alive == 0 &&
11081         level.native_em_level->ply[2]->alive == 0 &&
11082         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11083       AllPlayersGone = TRUE;
11084   }
11085   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11086   {
11087     if (game_sp.LevelSolved &&
11088         !game_sp.GameOver)                              /* game won */
11089     {
11090       PlayerWins(local_player);
11091
11092       game_sp.GameOver = TRUE;
11093
11094       AllPlayersGone = TRUE;
11095     }
11096
11097     if (game_sp.GameOver)                               /* game lost */
11098       AllPlayersGone = TRUE;
11099   }
11100   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11101   {
11102     if (game_mm.level_solved &&
11103         !game_mm.game_over)                             /* game won */
11104     {
11105       PlayerWins(local_player);
11106
11107       game_mm.game_over = TRUE;
11108
11109       AllPlayersGone = TRUE;
11110     }
11111
11112     if (game_mm.game_over)                              /* game lost */
11113       AllPlayersGone = TRUE;
11114   }
11115
11116   if (TimeFrames >= FRAMES_PER_SECOND)
11117   {
11118     TimeFrames = 0;
11119     TapeTime++;
11120
11121     for (i = 0; i < MAX_PLAYERS; i++)
11122     {
11123       struct PlayerInfo *player = &stored_player[i];
11124
11125       if (SHIELD_ON(player))
11126       {
11127         player->shield_normal_time_left--;
11128
11129         if (player->shield_deadly_time_left > 0)
11130           player->shield_deadly_time_left--;
11131       }
11132     }
11133
11134     if (!local_player->LevelSolved && !level.use_step_counter)
11135     {
11136       TimePlayed++;
11137
11138       if (TimeLeft > 0)
11139       {
11140         TimeLeft--;
11141
11142         if (TimeLeft <= 10 && setup.time_limit)
11143           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11144
11145         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11146            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11147
11148         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11149
11150         if (!TimeLeft && setup.time_limit)
11151         {
11152           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11153             level.native_em_level->lev->killed_out_of_time = TRUE;
11154           else
11155             for (i = 0; i < MAX_PLAYERS; i++)
11156               KillPlayer(&stored_player[i]);
11157         }
11158       }
11159       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11160       {
11161         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11162       }
11163
11164       level.native_em_level->lev->time =
11165         (game.no_time_limit ? TimePlayed : TimeLeft);
11166     }
11167
11168     if (tape.recording || tape.playing)
11169       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11170   }
11171
11172   if (tape.recording || tape.playing)
11173     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11174
11175   UpdateAndDisplayGameControlValues();
11176 }
11177
11178 void AdvanceFrameAndPlayerCounters(int player_nr)
11179 {
11180   int i;
11181
11182   /* advance frame counters (global frame counter and time frame counter) */
11183   FrameCounter++;
11184   TimeFrames++;
11185
11186   /* advance player counters (counters for move delay, move animation etc.) */
11187   for (i = 0; i < MAX_PLAYERS; i++)
11188   {
11189     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11190     int move_delay_value = stored_player[i].move_delay_value;
11191     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11192
11193     if (!advance_player_counters)       /* not all players may be affected */
11194       continue;
11195
11196     if (move_frames == 0)       /* less than one move per game frame */
11197     {
11198       int stepsize = TILEX / move_delay_value;
11199       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11200       int count = (stored_player[i].is_moving ?
11201                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11202
11203       if (count % delay == 0)
11204         move_frames = 1;
11205     }
11206
11207     stored_player[i].Frame += move_frames;
11208
11209     if (stored_player[i].MovPos != 0)
11210       stored_player[i].StepFrame += move_frames;
11211
11212     if (stored_player[i].move_delay > 0)
11213       stored_player[i].move_delay--;
11214
11215     /* due to bugs in previous versions, counter must count up, not down */
11216     if (stored_player[i].push_delay != -1)
11217       stored_player[i].push_delay++;
11218
11219     if (stored_player[i].drop_delay > 0)
11220       stored_player[i].drop_delay--;
11221
11222     if (stored_player[i].is_dropping_pressed)
11223       stored_player[i].drop_pressed_delay++;
11224   }
11225 }
11226
11227 void StartGameActions(boolean init_network_game, boolean record_tape,
11228                       int random_seed)
11229 {
11230   unsigned int new_random_seed = InitRND(random_seed);
11231
11232   if (record_tape)
11233     TapeStartRecording(new_random_seed);
11234
11235 #if defined(NETWORK_AVALIABLE)
11236   if (init_network_game)
11237   {
11238     SendToServer_StartPlaying();
11239
11240     return;
11241   }
11242 #endif
11243
11244   InitGame();
11245 }
11246
11247 void GameActionsExt()
11248 {
11249 #if 0
11250   static unsigned int game_frame_delay = 0;
11251 #endif
11252   unsigned int game_frame_delay_value;
11253   byte *recorded_player_action;
11254   byte summarized_player_action = 0;
11255   byte tape_action[MAX_PLAYERS];
11256   int i;
11257
11258   /* detect endless loops, caused by custom element programming */
11259   if (recursion_loop_detected && recursion_loop_depth == 0)
11260   {
11261     char *message = getStringCat3("Internal Error! Element ",
11262                                   EL_NAME(recursion_loop_element),
11263                                   " caused endless loop! Quit the game?");
11264
11265     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11266           EL_NAME(recursion_loop_element));
11267
11268     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11269
11270     recursion_loop_detected = FALSE;    /* if game should be continued */
11271
11272     free(message);
11273
11274     return;
11275   }
11276
11277   if (game.restart_level)
11278     StartGameActions(options.network, setup.autorecord, level.random_seed);
11279
11280   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11281   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11282   {
11283     if (level.native_em_level->lev->home == 0)  /* all players at home */
11284     {
11285       PlayerWins(local_player);
11286
11287       AllPlayersGone = TRUE;
11288
11289       level.native_em_level->lev->home = -1;
11290     }
11291
11292     if (level.native_em_level->ply[0]->alive == 0 &&
11293         level.native_em_level->ply[1]->alive == 0 &&
11294         level.native_em_level->ply[2]->alive == 0 &&
11295         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11296       AllPlayersGone = TRUE;
11297   }
11298   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11299   {
11300     if (game_sp.LevelSolved &&
11301         !game_sp.GameOver)                              /* game won */
11302     {
11303       PlayerWins(local_player);
11304
11305       game_sp.GameOver = TRUE;
11306
11307       AllPlayersGone = TRUE;
11308     }
11309
11310     if (game_sp.GameOver)                               /* game lost */
11311       AllPlayersGone = TRUE;
11312   }
11313   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11314   {
11315     if (game_mm.level_solved &&
11316         !game_mm.game_over)                             /* game won */
11317     {
11318       PlayerWins(local_player);
11319
11320       game_mm.game_over = TRUE;
11321
11322       AllPlayersGone = TRUE;
11323     }
11324
11325     if (game_mm.game_over)                              /* game lost */
11326       AllPlayersGone = TRUE;
11327   }
11328
11329   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11330     GameWon();
11331
11332   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11333     TapeStop();
11334
11335   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11336     return;
11337
11338   game_frame_delay_value =
11339     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11340
11341   if (tape.playing && tape.warp_forward && !tape.pausing)
11342     game_frame_delay_value = 0;
11343
11344   SetVideoFrameDelay(game_frame_delay_value);
11345
11346 #if 0
11347 #if 0
11348   /* ---------- main game synchronization point ---------- */
11349
11350   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11351
11352   printf("::: skip == %d\n", skip);
11353
11354 #else
11355   /* ---------- main game synchronization point ---------- */
11356
11357   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11358 #endif
11359 #endif
11360
11361   if (network_playing && !network_player_action_received)
11362   {
11363     /* try to get network player actions in time */
11364
11365 #if defined(NETWORK_AVALIABLE)
11366     /* last chance to get network player actions without main loop delay */
11367     HandleNetworking();
11368 #endif
11369
11370     /* game was quit by network peer */
11371     if (game_status != GAME_MODE_PLAYING)
11372       return;
11373
11374     if (!network_player_action_received)
11375       return;           /* failed to get network player actions in time */
11376
11377     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11378   }
11379
11380   if (tape.pausing)
11381     return;
11382
11383   /* at this point we know that we really continue executing the game */
11384
11385   network_player_action_received = FALSE;
11386
11387   /* when playing tape, read previously recorded player input from tape data */
11388   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11389
11390   local_player->effective_mouse_action = local_player->mouse_action;
11391
11392   if (recorded_player_action != NULL)
11393     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11394                                  recorded_player_action);
11395
11396   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11397   if (tape.pausing)
11398     return;
11399
11400   if (tape.set_centered_player)
11401   {
11402     game.centered_player_nr_next = tape.centered_player_nr_next;
11403     game.set_centered_player = TRUE;
11404   }
11405
11406   for (i = 0; i < MAX_PLAYERS; i++)
11407   {
11408     summarized_player_action |= stored_player[i].action;
11409
11410     if (!network_playing && (game.team_mode || tape.playing))
11411       stored_player[i].effective_action = stored_player[i].action;
11412   }
11413
11414 #if defined(NETWORK_AVALIABLE)
11415   if (network_playing)
11416     SendToServer_MovePlayer(summarized_player_action);
11417 #endif
11418
11419   // summarize all actions at local players mapped input device position
11420   // (this allows using different input devices in single player mode)
11421   if (!options.network && !game.team_mode)
11422     stored_player[map_player_action[local_player->index_nr]].effective_action =
11423       summarized_player_action;
11424
11425   if (tape.recording &&
11426       setup.team_mode &&
11427       setup.input_on_focus &&
11428       game.centered_player_nr != -1)
11429   {
11430     for (i = 0; i < MAX_PLAYERS; i++)
11431       stored_player[i].effective_action =
11432         (i == game.centered_player_nr ? summarized_player_action : 0);
11433   }
11434
11435   if (recorded_player_action != NULL)
11436     for (i = 0; i < MAX_PLAYERS; i++)
11437       stored_player[i].effective_action = recorded_player_action[i];
11438
11439   for (i = 0; i < MAX_PLAYERS; i++)
11440   {
11441     tape_action[i] = stored_player[i].effective_action;
11442
11443     /* (this may happen in the RND game engine if a player was not present on
11444        the playfield on level start, but appeared later from a custom element */
11445     if (setup.team_mode &&
11446         tape.recording &&
11447         tape_action[i] &&
11448         !tape.player_participates[i])
11449       tape.player_participates[i] = TRUE;
11450   }
11451
11452   SetTapeActionFromMouseAction(tape_action,
11453                                &local_player->effective_mouse_action);
11454
11455   /* only record actions from input devices, but not programmed actions */
11456   if (tape.recording)
11457     TapeRecordAction(tape_action);
11458
11459 #if USE_NEW_PLAYER_ASSIGNMENTS
11460   // !!! also map player actions in single player mode !!!
11461   // if (game.team_mode)
11462   if (1)
11463   {
11464     byte mapped_action[MAX_PLAYERS];
11465
11466 #if DEBUG_PLAYER_ACTIONS
11467     printf(":::");
11468     for (i = 0; i < MAX_PLAYERS; i++)
11469       printf(" %d, ", stored_player[i].effective_action);
11470 #endif
11471
11472     for (i = 0; i < MAX_PLAYERS; i++)
11473       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11474
11475     for (i = 0; i < MAX_PLAYERS; i++)
11476       stored_player[i].effective_action = mapped_action[i];
11477
11478 #if DEBUG_PLAYER_ACTIONS
11479     printf(" =>");
11480     for (i = 0; i < MAX_PLAYERS; i++)
11481       printf(" %d, ", stored_player[i].effective_action);
11482     printf("\n");
11483 #endif
11484   }
11485 #if DEBUG_PLAYER_ACTIONS
11486   else
11487   {
11488     printf(":::");
11489     for (i = 0; i < MAX_PLAYERS; i++)
11490       printf(" %d, ", stored_player[i].effective_action);
11491     printf("\n");
11492   }
11493 #endif
11494 #endif
11495
11496   for (i = 0; i < MAX_PLAYERS; i++)
11497   {
11498     // allow engine snapshot in case of changed movement attempt
11499     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11500         (stored_player[i].effective_action & KEY_MOTION))
11501       game.snapshot.changed_action = TRUE;
11502
11503     // allow engine snapshot in case of snapping/dropping attempt
11504     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11505         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11506       game.snapshot.changed_action = TRUE;
11507
11508     game.snapshot.last_action[i] = stored_player[i].effective_action;
11509   }
11510
11511   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11512   {
11513     GameActions_EM_Main();
11514   }
11515   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11516   {
11517     GameActions_SP_Main();
11518   }
11519   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11520   {
11521     GameActions_MM_Main();
11522   }
11523   else
11524   {
11525     GameActions_RND_Main();
11526   }
11527
11528   BlitScreenToBitmap(backbuffer);
11529
11530   CheckLevelTime();
11531
11532   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11533
11534   if (global.show_frames_per_second)
11535   {
11536     static unsigned int fps_counter = 0;
11537     static int fps_frames = 0;
11538     unsigned int fps_delay_ms = Counter() - fps_counter;
11539
11540     fps_frames++;
11541
11542     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11543     {
11544       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11545
11546       fps_frames = 0;
11547       fps_counter = Counter();
11548
11549       /* always draw FPS to screen after FPS value was updated */
11550       redraw_mask |= REDRAW_FPS;
11551     }
11552
11553     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11554     if (GetDrawDeactivationMask() == REDRAW_NONE)
11555       redraw_mask |= REDRAW_FPS;
11556   }
11557 }
11558
11559 static void GameActions_CheckSaveEngineSnapshot()
11560 {
11561   if (!game.snapshot.save_snapshot)
11562     return;
11563
11564   // clear flag for saving snapshot _before_ saving snapshot
11565   game.snapshot.save_snapshot = FALSE;
11566
11567   SaveEngineSnapshotToList();
11568 }
11569
11570 void GameActions()
11571 {
11572   GameActionsExt();
11573
11574   GameActions_CheckSaveEngineSnapshot();
11575 }
11576
11577 void GameActions_EM_Main()
11578 {
11579   byte effective_action[MAX_PLAYERS];
11580   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11581   int i;
11582
11583   for (i = 0; i < MAX_PLAYERS; i++)
11584     effective_action[i] = stored_player[i].effective_action;
11585
11586   GameActions_EM(effective_action, warp_mode);
11587 }
11588
11589 void GameActions_SP_Main()
11590 {
11591   byte effective_action[MAX_PLAYERS];
11592   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11593   int i;
11594
11595   for (i = 0; i < MAX_PLAYERS; i++)
11596     effective_action[i] = stored_player[i].effective_action;
11597
11598   GameActions_SP(effective_action, warp_mode);
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601   {
11602     if (stored_player[i].force_dropping)
11603       stored_player[i].action |= KEY_BUTTON_DROP;
11604
11605     stored_player[i].force_dropping = FALSE;
11606   }
11607 }
11608
11609 void GameActions_MM_Main()
11610 {
11611   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11612
11613   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11614 }
11615
11616 void GameActions_RND_Main()
11617 {
11618   GameActions_RND();
11619 }
11620
11621 void GameActions_RND()
11622 {
11623   int magic_wall_x = 0, magic_wall_y = 0;
11624   int i, x, y, element, graphic, last_gfx_frame;
11625
11626   InitPlayfieldScanModeVars();
11627
11628   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11629   {
11630     SCAN_PLAYFIELD(x, y)
11631     {
11632       ChangeCount[x][y] = 0;
11633       ChangeEvent[x][y] = -1;
11634     }
11635   }
11636
11637   if (game.set_centered_player)
11638   {
11639     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11640
11641     /* switching to "all players" only possible if all players fit to screen */
11642     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11643     {
11644       game.centered_player_nr_next = game.centered_player_nr;
11645       game.set_centered_player = FALSE;
11646     }
11647
11648     /* do not switch focus to non-existing (or non-active) player */
11649     if (game.centered_player_nr_next >= 0 &&
11650         !stored_player[game.centered_player_nr_next].active)
11651     {
11652       game.centered_player_nr_next = game.centered_player_nr;
11653       game.set_centered_player = FALSE;
11654     }
11655   }
11656
11657   if (game.set_centered_player &&
11658       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11659   {
11660     int sx, sy;
11661
11662     if (game.centered_player_nr_next == -1)
11663     {
11664       setScreenCenteredToAllPlayers(&sx, &sy);
11665     }
11666     else
11667     {
11668       sx = stored_player[game.centered_player_nr_next].jx;
11669       sy = stored_player[game.centered_player_nr_next].jy;
11670     }
11671
11672     game.centered_player_nr = game.centered_player_nr_next;
11673     game.set_centered_player = FALSE;
11674
11675     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11676     DrawGameDoorValues();
11677   }
11678
11679   for (i = 0; i < MAX_PLAYERS; i++)
11680   {
11681     int actual_player_action = stored_player[i].effective_action;
11682
11683 #if 1
11684     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11685        - rnd_equinox_tetrachloride 048
11686        - rnd_equinox_tetrachloride_ii 096
11687        - rnd_emanuel_schmieg 002
11688        - doctor_sloan_ww 001, 020
11689     */
11690     if (stored_player[i].MovPos == 0)
11691       CheckGravityMovement(&stored_player[i]);
11692 #endif
11693
11694     /* overwrite programmed action with tape action */
11695     if (stored_player[i].programmed_action)
11696       actual_player_action = stored_player[i].programmed_action;
11697
11698     PlayerActions(&stored_player[i], actual_player_action);
11699
11700     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11701   }
11702
11703   ScrollScreen(NULL, SCROLL_GO_ON);
11704
11705   /* for backwards compatibility, the following code emulates a fixed bug that
11706      occured when pushing elements (causing elements that just made their last
11707      pushing step to already (if possible) make their first falling step in the
11708      same game frame, which is bad); this code is also needed to use the famous
11709      "spring push bug" which is used in older levels and might be wanted to be
11710      used also in newer levels, but in this case the buggy pushing code is only
11711      affecting the "spring" element and no other elements */
11712
11713   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11714   {
11715     for (i = 0; i < MAX_PLAYERS; i++)
11716     {
11717       struct PlayerInfo *player = &stored_player[i];
11718       int x = player->jx;
11719       int y = player->jy;
11720
11721       if (player->active && player->is_pushing && player->is_moving &&
11722           IS_MOVING(x, y) &&
11723           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11724            Feld[x][y] == EL_SPRING))
11725       {
11726         ContinueMoving(x, y);
11727
11728         /* continue moving after pushing (this is actually a bug) */
11729         if (!IS_MOVING(x, y))
11730           Stop[x][y] = FALSE;
11731       }
11732     }
11733   }
11734
11735   SCAN_PLAYFIELD(x, y)
11736   {
11737     ChangeCount[x][y] = 0;
11738     ChangeEvent[x][y] = -1;
11739
11740     /* this must be handled before main playfield loop */
11741     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11742     {
11743       MovDelay[x][y]--;
11744       if (MovDelay[x][y] <= 0)
11745         RemoveField(x, y);
11746     }
11747
11748     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11749     {
11750       MovDelay[x][y]--;
11751       if (MovDelay[x][y] <= 0)
11752       {
11753         RemoveField(x, y);
11754         TEST_DrawLevelField(x, y);
11755
11756         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11757       }
11758     }
11759
11760 #if DEBUG
11761     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11762     {
11763       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11764       printf("GameActions(): This should never happen!\n");
11765
11766       ChangePage[x][y] = -1;
11767     }
11768 #endif
11769
11770     Stop[x][y] = FALSE;
11771     if (WasJustMoving[x][y] > 0)
11772       WasJustMoving[x][y]--;
11773     if (WasJustFalling[x][y] > 0)
11774       WasJustFalling[x][y]--;
11775     if (CheckCollision[x][y] > 0)
11776       CheckCollision[x][y]--;
11777     if (CheckImpact[x][y] > 0)
11778       CheckImpact[x][y]--;
11779
11780     GfxFrame[x][y]++;
11781
11782     /* reset finished pushing action (not done in ContinueMoving() to allow
11783        continuous pushing animation for elements with zero push delay) */
11784     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11785     {
11786       ResetGfxAnimation(x, y);
11787       TEST_DrawLevelField(x, y);
11788     }
11789
11790 #if DEBUG
11791     if (IS_BLOCKED(x, y))
11792     {
11793       int oldx, oldy;
11794
11795       Blocked2Moving(x, y, &oldx, &oldy);
11796       if (!IS_MOVING(oldx, oldy))
11797       {
11798         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11799         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11800         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11801         printf("GameActions(): This should never happen!\n");
11802       }
11803     }
11804 #endif
11805   }
11806
11807   SCAN_PLAYFIELD(x, y)
11808   {
11809     element = Feld[x][y];
11810     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11811     last_gfx_frame = GfxFrame[x][y];
11812
11813     ResetGfxFrame(x, y);
11814
11815     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11816       DrawLevelGraphicAnimation(x, y, graphic);
11817
11818     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11819         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11820       ResetRandomAnimationValue(x, y);
11821
11822     SetRandomAnimationValue(x, y);
11823
11824     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11825
11826     if (IS_INACTIVE(element))
11827     {
11828       if (IS_ANIMATED(graphic))
11829         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11830
11831       continue;
11832     }
11833
11834     /* this may take place after moving, so 'element' may have changed */
11835     if (IS_CHANGING(x, y) &&
11836         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11837     {
11838       int page = element_info[element].event_page_nr[CE_DELAY];
11839
11840       HandleElementChange(x, y, page);
11841
11842       element = Feld[x][y];
11843       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11844     }
11845
11846     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11847     {
11848       StartMoving(x, y);
11849
11850       element = Feld[x][y];
11851       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11852
11853       if (IS_ANIMATED(graphic) &&
11854           !IS_MOVING(x, y) &&
11855           !Stop[x][y])
11856         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11857
11858       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11859         TEST_DrawTwinkleOnField(x, y);
11860     }
11861     else if (element == EL_ACID)
11862     {
11863       if (!Stop[x][y])
11864         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11865     }
11866     else if ((element == EL_EXIT_OPEN ||
11867               element == EL_EM_EXIT_OPEN ||
11868               element == EL_SP_EXIT_OPEN ||
11869               element == EL_STEEL_EXIT_OPEN ||
11870               element == EL_EM_STEEL_EXIT_OPEN ||
11871               element == EL_SP_TERMINAL ||
11872               element == EL_SP_TERMINAL_ACTIVE ||
11873               element == EL_EXTRA_TIME ||
11874               element == EL_SHIELD_NORMAL ||
11875               element == EL_SHIELD_DEADLY) &&
11876              IS_ANIMATED(graphic))
11877       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11878     else if (IS_MOVING(x, y))
11879       ContinueMoving(x, y);
11880     else if (IS_ACTIVE_BOMB(element))
11881       CheckDynamite(x, y);
11882     else if (element == EL_AMOEBA_GROWING)
11883       AmoebeWaechst(x, y);
11884     else if (element == EL_AMOEBA_SHRINKING)
11885       AmoebaDisappearing(x, y);
11886
11887 #if !USE_NEW_AMOEBA_CODE
11888     else if (IS_AMOEBALIVE(element))
11889       AmoebeAbleger(x, y);
11890 #endif
11891
11892     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11893       Life(x, y);
11894     else if (element == EL_EXIT_CLOSED)
11895       CheckExit(x, y);
11896     else if (element == EL_EM_EXIT_CLOSED)
11897       CheckExitEM(x, y);
11898     else if (element == EL_STEEL_EXIT_CLOSED)
11899       CheckExitSteel(x, y);
11900     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11901       CheckExitSteelEM(x, y);
11902     else if (element == EL_SP_EXIT_CLOSED)
11903       CheckExitSP(x, y);
11904     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11905              element == EL_EXPANDABLE_STEELWALL_GROWING)
11906       MauerWaechst(x, y);
11907     else if (element == EL_EXPANDABLE_WALL ||
11908              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11909              element == EL_EXPANDABLE_WALL_VERTICAL ||
11910              element == EL_EXPANDABLE_WALL_ANY ||
11911              element == EL_BD_EXPANDABLE_WALL)
11912       MauerAbleger(x, y);
11913     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11914              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11915              element == EL_EXPANDABLE_STEELWALL_ANY)
11916       MauerAblegerStahl(x, y);
11917     else if (element == EL_FLAMES)
11918       CheckForDragon(x, y);
11919     else if (element == EL_EXPLOSION)
11920       ; /* drawing of correct explosion animation is handled separately */
11921     else if (element == EL_ELEMENT_SNAPPING ||
11922              element == EL_DIAGONAL_SHRINKING ||
11923              element == EL_DIAGONAL_GROWING)
11924     {
11925       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11926
11927       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11928     }
11929     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11930       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11931
11932     if (IS_BELT_ACTIVE(element))
11933       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11934
11935     if (game.magic_wall_active)
11936     {
11937       int jx = local_player->jx, jy = local_player->jy;
11938
11939       /* play the element sound at the position nearest to the player */
11940       if ((element == EL_MAGIC_WALL_FULL ||
11941            element == EL_MAGIC_WALL_ACTIVE ||
11942            element == EL_MAGIC_WALL_EMPTYING ||
11943            element == EL_BD_MAGIC_WALL_FULL ||
11944            element == EL_BD_MAGIC_WALL_ACTIVE ||
11945            element == EL_BD_MAGIC_WALL_EMPTYING ||
11946            element == EL_DC_MAGIC_WALL_FULL ||
11947            element == EL_DC_MAGIC_WALL_ACTIVE ||
11948            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11949           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11950       {
11951         magic_wall_x = x;
11952         magic_wall_y = y;
11953       }
11954     }
11955   }
11956
11957 #if USE_NEW_AMOEBA_CODE
11958   /* new experimental amoeba growth stuff */
11959   if (!(FrameCounter % 8))
11960   {
11961     static unsigned int random = 1684108901;
11962
11963     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11964     {
11965       x = RND(lev_fieldx);
11966       y = RND(lev_fieldy);
11967       element = Feld[x][y];
11968
11969       if (!IS_PLAYER(x,y) &&
11970           (element == EL_EMPTY ||
11971            CAN_GROW_INTO(element) ||
11972            element == EL_QUICKSAND_EMPTY ||
11973            element == EL_QUICKSAND_FAST_EMPTY ||
11974            element == EL_ACID_SPLASH_LEFT ||
11975            element == EL_ACID_SPLASH_RIGHT))
11976       {
11977         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11978             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11979             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11980             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11981           Feld[x][y] = EL_AMOEBA_DROP;
11982       }
11983
11984       random = random * 129 + 1;
11985     }
11986   }
11987 #endif
11988
11989   game.explosions_delayed = FALSE;
11990
11991   SCAN_PLAYFIELD(x, y)
11992   {
11993     element = Feld[x][y];
11994
11995     if (ExplodeField[x][y])
11996       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11997     else if (element == EL_EXPLOSION)
11998       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11999
12000     ExplodeField[x][y] = EX_TYPE_NONE;
12001   }
12002
12003   game.explosions_delayed = TRUE;
12004
12005   if (game.magic_wall_active)
12006   {
12007     if (!(game.magic_wall_time_left % 4))
12008     {
12009       int element = Feld[magic_wall_x][magic_wall_y];
12010
12011       if (element == EL_BD_MAGIC_WALL_FULL ||
12012           element == EL_BD_MAGIC_WALL_ACTIVE ||
12013           element == EL_BD_MAGIC_WALL_EMPTYING)
12014         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12015       else if (element == EL_DC_MAGIC_WALL_FULL ||
12016                element == EL_DC_MAGIC_WALL_ACTIVE ||
12017                element == EL_DC_MAGIC_WALL_EMPTYING)
12018         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12019       else
12020         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12021     }
12022
12023     if (game.magic_wall_time_left > 0)
12024     {
12025       game.magic_wall_time_left--;
12026
12027       if (!game.magic_wall_time_left)
12028       {
12029         SCAN_PLAYFIELD(x, y)
12030         {
12031           element = Feld[x][y];
12032
12033           if (element == EL_MAGIC_WALL_ACTIVE ||
12034               element == EL_MAGIC_WALL_FULL)
12035           {
12036             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12037             TEST_DrawLevelField(x, y);
12038           }
12039           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12040                    element == EL_BD_MAGIC_WALL_FULL)
12041           {
12042             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12043             TEST_DrawLevelField(x, y);
12044           }
12045           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12046                    element == EL_DC_MAGIC_WALL_FULL)
12047           {
12048             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12049             TEST_DrawLevelField(x, y);
12050           }
12051         }
12052
12053         game.magic_wall_active = FALSE;
12054       }
12055     }
12056   }
12057
12058   if (game.light_time_left > 0)
12059   {
12060     game.light_time_left--;
12061
12062     if (game.light_time_left == 0)
12063       RedrawAllLightSwitchesAndInvisibleElements();
12064   }
12065
12066   if (game.timegate_time_left > 0)
12067   {
12068     game.timegate_time_left--;
12069
12070     if (game.timegate_time_left == 0)
12071       CloseAllOpenTimegates();
12072   }
12073
12074   if (game.lenses_time_left > 0)
12075   {
12076     game.lenses_time_left--;
12077
12078     if (game.lenses_time_left == 0)
12079       RedrawAllInvisibleElementsForLenses();
12080   }
12081
12082   if (game.magnify_time_left > 0)
12083   {
12084     game.magnify_time_left--;
12085
12086     if (game.magnify_time_left == 0)
12087       RedrawAllInvisibleElementsForMagnifier();
12088   }
12089
12090   for (i = 0; i < MAX_PLAYERS; i++)
12091   {
12092     struct PlayerInfo *player = &stored_player[i];
12093
12094     if (SHIELD_ON(player))
12095     {
12096       if (player->shield_deadly_time_left)
12097         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12098       else if (player->shield_normal_time_left)
12099         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12100     }
12101   }
12102
12103 #if USE_DELAYED_GFX_REDRAW
12104   SCAN_PLAYFIELD(x, y)
12105   {
12106     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12107     {
12108       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12109          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12110
12111       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12112         DrawLevelField(x, y);
12113
12114       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12115         DrawLevelFieldCrumbled(x, y);
12116
12117       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12118         DrawLevelFieldCrumbledNeighbours(x, y);
12119
12120       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12121         DrawTwinkleOnField(x, y);
12122     }
12123
12124     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12125   }
12126 #endif
12127
12128   DrawAllPlayers();
12129   PlayAllPlayersSound();
12130
12131   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12132   {
12133     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12134
12135     local_player->show_envelope = 0;
12136   }
12137
12138   /* use random number generator in every frame to make it less predictable */
12139   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12140     RND(1);
12141 }
12142
12143 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12144 {
12145   int min_x = x, min_y = y, max_x = x, max_y = y;
12146   int i;
12147
12148   for (i = 0; i < MAX_PLAYERS; i++)
12149   {
12150     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12151
12152     if (!stored_player[i].active || &stored_player[i] == player)
12153       continue;
12154
12155     min_x = MIN(min_x, jx);
12156     min_y = MIN(min_y, jy);
12157     max_x = MAX(max_x, jx);
12158     max_y = MAX(max_y, jy);
12159   }
12160
12161   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12162 }
12163
12164 static boolean AllPlayersInVisibleScreen()
12165 {
12166   int i;
12167
12168   for (i = 0; i < MAX_PLAYERS; i++)
12169   {
12170     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12171
12172     if (!stored_player[i].active)
12173       continue;
12174
12175     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12176       return FALSE;
12177   }
12178
12179   return TRUE;
12180 }
12181
12182 void ScrollLevel(int dx, int dy)
12183 {
12184   int scroll_offset = 2 * TILEX_VAR;
12185   int x, y;
12186
12187   BlitBitmap(drawto_field, drawto_field,
12188              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12189              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12190              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12191              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12192              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12193              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12194
12195   if (dx != 0)
12196   {
12197     x = (dx == 1 ? BX1 : BX2);
12198     for (y = BY1; y <= BY2; y++)
12199       DrawScreenField(x, y);
12200   }
12201
12202   if (dy != 0)
12203   {
12204     y = (dy == 1 ? BY1 : BY2);
12205     for (x = BX1; x <= BX2; x++)
12206       DrawScreenField(x, y);
12207   }
12208
12209   redraw_mask |= REDRAW_FIELD;
12210 }
12211
12212 static boolean canFallDown(struct PlayerInfo *player)
12213 {
12214   int jx = player->jx, jy = player->jy;
12215
12216   return (IN_LEV_FIELD(jx, jy + 1) &&
12217           (IS_FREE(jx, jy + 1) ||
12218            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12219           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12220           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12221 }
12222
12223 static boolean canPassField(int x, int y, int move_dir)
12224 {
12225   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12226   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12227   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12228   int nextx = x + dx;
12229   int nexty = y + dy;
12230   int element = Feld[x][y];
12231
12232   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12233           !CAN_MOVE(element) &&
12234           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12235           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12236           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12237 }
12238
12239 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12240 {
12241   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12242   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12243   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12244   int newx = x + dx;
12245   int newy = y + dy;
12246
12247   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12248           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12249           (IS_DIGGABLE(Feld[newx][newy]) ||
12250            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12251            canPassField(newx, newy, move_dir)));
12252 }
12253
12254 static void CheckGravityMovement(struct PlayerInfo *player)
12255 {
12256   if (player->gravity && !player->programmed_action)
12257   {
12258     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12259     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12260     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12261     int jx = player->jx, jy = player->jy;
12262     boolean player_is_moving_to_valid_field =
12263       (!player_is_snapping &&
12264        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12265         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12266     boolean player_can_fall_down = canFallDown(player);
12267
12268     if (player_can_fall_down &&
12269         !player_is_moving_to_valid_field)
12270       player->programmed_action = MV_DOWN;
12271   }
12272 }
12273
12274 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12275 {
12276   return CheckGravityMovement(player);
12277
12278   if (player->gravity && !player->programmed_action)
12279   {
12280     int jx = player->jx, jy = player->jy;
12281     boolean field_under_player_is_free =
12282       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12283     boolean player_is_standing_on_valid_field =
12284       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12285        (IS_WALKABLE(Feld[jx][jy]) &&
12286         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12287
12288     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12289       player->programmed_action = MV_DOWN;
12290   }
12291 }
12292
12293 /*
12294   MovePlayerOneStep()
12295   -----------------------------------------------------------------------------
12296   dx, dy:               direction (non-diagonal) to try to move the player to
12297   real_dx, real_dy:     direction as read from input device (can be diagonal)
12298 */
12299
12300 boolean MovePlayerOneStep(struct PlayerInfo *player,
12301                           int dx, int dy, int real_dx, int real_dy)
12302 {
12303   int jx = player->jx, jy = player->jy;
12304   int new_jx = jx + dx, new_jy = jy + dy;
12305   int can_move;
12306   boolean player_can_move = !player->cannot_move;
12307
12308   if (!player->active || (!dx && !dy))
12309     return MP_NO_ACTION;
12310
12311   player->MovDir = (dx < 0 ? MV_LEFT :
12312                     dx > 0 ? MV_RIGHT :
12313                     dy < 0 ? MV_UP :
12314                     dy > 0 ? MV_DOWN :  MV_NONE);
12315
12316   if (!IN_LEV_FIELD(new_jx, new_jy))
12317     return MP_NO_ACTION;
12318
12319   if (!player_can_move)
12320   {
12321     if (player->MovPos == 0)
12322     {
12323       player->is_moving = FALSE;
12324       player->is_digging = FALSE;
12325       player->is_collecting = FALSE;
12326       player->is_snapping = FALSE;
12327       player->is_pushing = FALSE;
12328     }
12329   }
12330
12331   if (!options.network && game.centered_player_nr == -1 &&
12332       !AllPlayersInSight(player, new_jx, new_jy))
12333     return MP_NO_ACTION;
12334
12335   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12336   if (can_move != MP_MOVING)
12337     return can_move;
12338
12339   /* check if DigField() has caused relocation of the player */
12340   if (player->jx != jx || player->jy != jy)
12341     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12342
12343   StorePlayer[jx][jy] = 0;
12344   player->last_jx = jx;
12345   player->last_jy = jy;
12346   player->jx = new_jx;
12347   player->jy = new_jy;
12348   StorePlayer[new_jx][new_jy] = player->element_nr;
12349
12350   if (player->move_delay_value_next != -1)
12351   {
12352     player->move_delay_value = player->move_delay_value_next;
12353     player->move_delay_value_next = -1;
12354   }
12355
12356   player->MovPos =
12357     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12358
12359   player->step_counter++;
12360
12361   PlayerVisit[jx][jy] = FrameCounter;
12362
12363   player->is_moving = TRUE;
12364
12365 #if 1
12366   /* should better be called in MovePlayer(), but this breaks some tapes */
12367   ScrollPlayer(player, SCROLL_INIT);
12368 #endif
12369
12370   return MP_MOVING;
12371 }
12372
12373 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12374 {
12375   int jx = player->jx, jy = player->jy;
12376   int old_jx = jx, old_jy = jy;
12377   int moved = MP_NO_ACTION;
12378
12379   if (!player->active)
12380     return FALSE;
12381
12382   if (!dx && !dy)
12383   {
12384     if (player->MovPos == 0)
12385     {
12386       player->is_moving = FALSE;
12387       player->is_digging = FALSE;
12388       player->is_collecting = FALSE;
12389       player->is_snapping = FALSE;
12390       player->is_pushing = FALSE;
12391     }
12392
12393     return FALSE;
12394   }
12395
12396   if (player->move_delay > 0)
12397     return FALSE;
12398
12399   player->move_delay = -1;              /* set to "uninitialized" value */
12400
12401   /* store if player is automatically moved to next field */
12402   player->is_auto_moving = (player->programmed_action != MV_NONE);
12403
12404   /* remove the last programmed player action */
12405   player->programmed_action = 0;
12406
12407   if (player->MovPos)
12408   {
12409     /* should only happen if pre-1.2 tape recordings are played */
12410     /* this is only for backward compatibility */
12411
12412     int original_move_delay_value = player->move_delay_value;
12413
12414 #if DEBUG
12415     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12416            tape.counter);
12417 #endif
12418
12419     /* scroll remaining steps with finest movement resolution */
12420     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12421
12422     while (player->MovPos)
12423     {
12424       ScrollPlayer(player, SCROLL_GO_ON);
12425       ScrollScreen(NULL, SCROLL_GO_ON);
12426
12427       AdvanceFrameAndPlayerCounters(player->index_nr);
12428
12429       DrawAllPlayers();
12430       BackToFront_WithFrameDelay(0);
12431     }
12432
12433     player->move_delay_value = original_move_delay_value;
12434   }
12435
12436   player->is_active = FALSE;
12437
12438   if (player->last_move_dir & MV_HORIZONTAL)
12439   {
12440     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12441       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12442   }
12443   else
12444   {
12445     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12446       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12447   }
12448
12449   if (!moved && !player->is_active)
12450   {
12451     player->is_moving = FALSE;
12452     player->is_digging = FALSE;
12453     player->is_collecting = FALSE;
12454     player->is_snapping = FALSE;
12455     player->is_pushing = FALSE;
12456   }
12457
12458   jx = player->jx;
12459   jy = player->jy;
12460
12461   if (moved & MP_MOVING && !ScreenMovPos &&
12462       (player->index_nr == game.centered_player_nr ||
12463        game.centered_player_nr == -1))
12464   {
12465     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12466     int offset = game.scroll_delay_value;
12467
12468     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12469     {
12470       /* actual player has left the screen -- scroll in that direction */
12471       if (jx != old_jx)         /* player has moved horizontally */
12472         scroll_x += (jx - old_jx);
12473       else                      /* player has moved vertically */
12474         scroll_y += (jy - old_jy);
12475     }
12476     else
12477     {
12478       if (jx != old_jx)         /* player has moved horizontally */
12479       {
12480         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12481             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12482           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12483
12484         /* don't scroll over playfield boundaries */
12485         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12486           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12487
12488         /* don't scroll more than one field at a time */
12489         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12490
12491         /* don't scroll against the player's moving direction */
12492         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12493             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12494           scroll_x = old_scroll_x;
12495       }
12496       else                      /* player has moved vertically */
12497       {
12498         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12499             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12500           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12501
12502         /* don't scroll over playfield boundaries */
12503         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12504           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12505
12506         /* don't scroll more than one field at a time */
12507         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12508
12509         /* don't scroll against the player's moving direction */
12510         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12511             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12512           scroll_y = old_scroll_y;
12513       }
12514     }
12515
12516     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12517     {
12518       if (!options.network && game.centered_player_nr == -1 &&
12519           !AllPlayersInVisibleScreen())
12520       {
12521         scroll_x = old_scroll_x;
12522         scroll_y = old_scroll_y;
12523       }
12524       else
12525       {
12526         ScrollScreen(player, SCROLL_INIT);
12527         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12528       }
12529     }
12530   }
12531
12532   player->StepFrame = 0;
12533
12534   if (moved & MP_MOVING)
12535   {
12536     if (old_jx != jx && old_jy == jy)
12537       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12538     else if (old_jx == jx && old_jy != jy)
12539       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12540
12541     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12542
12543     player->last_move_dir = player->MovDir;
12544     player->is_moving = TRUE;
12545     player->is_snapping = FALSE;
12546     player->is_switching = FALSE;
12547     player->is_dropping = FALSE;
12548     player->is_dropping_pressed = FALSE;
12549     player->drop_pressed_delay = 0;
12550
12551 #if 0
12552     /* should better be called here than above, but this breaks some tapes */
12553     ScrollPlayer(player, SCROLL_INIT);
12554 #endif
12555   }
12556   else
12557   {
12558     CheckGravityMovementWhenNotMoving(player);
12559
12560     player->is_moving = FALSE;
12561
12562     /* at this point, the player is allowed to move, but cannot move right now
12563        (e.g. because of something blocking the way) -- ensure that the player
12564        is also allowed to move in the next frame (in old versions before 3.1.1,
12565        the player was forced to wait again for eight frames before next try) */
12566
12567     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12568       player->move_delay = 0;   /* allow direct movement in the next frame */
12569   }
12570
12571   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12572     player->move_delay = player->move_delay_value;
12573
12574   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12575   {
12576     TestIfPlayerTouchesBadThing(jx, jy);
12577     TestIfPlayerTouchesCustomElement(jx, jy);
12578   }
12579
12580   if (!player->active)
12581     RemovePlayer(player);
12582
12583   return moved;
12584 }
12585
12586 void ScrollPlayer(struct PlayerInfo *player, int mode)
12587 {
12588   int jx = player->jx, jy = player->jy;
12589   int last_jx = player->last_jx, last_jy = player->last_jy;
12590   int move_stepsize = TILEX / player->move_delay_value;
12591
12592   if (!player->active)
12593     return;
12594
12595   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12596     return;
12597
12598   if (mode == SCROLL_INIT)
12599   {
12600     player->actual_frame_counter = FrameCounter;
12601     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12602
12603     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12604         Feld[last_jx][last_jy] == EL_EMPTY)
12605     {
12606       int last_field_block_delay = 0;   /* start with no blocking at all */
12607       int block_delay_adjustment = player->block_delay_adjustment;
12608
12609       /* if player blocks last field, add delay for exactly one move */
12610       if (player->block_last_field)
12611       {
12612         last_field_block_delay += player->move_delay_value;
12613
12614         /* when blocking enabled, prevent moving up despite gravity */
12615         if (player->gravity && player->MovDir == MV_UP)
12616           block_delay_adjustment = -1;
12617       }
12618
12619       /* add block delay adjustment (also possible when not blocking) */
12620       last_field_block_delay += block_delay_adjustment;
12621
12622       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12623       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12624     }
12625
12626     if (player->MovPos != 0)    /* player has not yet reached destination */
12627       return;
12628   }
12629   else if (!FrameReached(&player->actual_frame_counter, 1))
12630     return;
12631
12632   if (player->MovPos != 0)
12633   {
12634     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12635     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12636
12637     /* before DrawPlayer() to draw correct player graphic for this case */
12638     if (player->MovPos == 0)
12639       CheckGravityMovement(player);
12640   }
12641
12642   if (player->MovPos == 0)      /* player reached destination field */
12643   {
12644     if (player->move_delay_reset_counter > 0)
12645     {
12646       player->move_delay_reset_counter--;
12647
12648       if (player->move_delay_reset_counter == 0)
12649       {
12650         /* continue with normal speed after quickly moving through gate */
12651         HALVE_PLAYER_SPEED(player);
12652
12653         /* be able to make the next move without delay */
12654         player->move_delay = 0;
12655       }
12656     }
12657
12658     player->last_jx = jx;
12659     player->last_jy = jy;
12660
12661     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12662         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12663         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12664         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12665         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12666         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12667         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12668         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12669     {
12670       DrawPlayer(player);       /* needed here only to cleanup last field */
12671       RemovePlayer(player);
12672
12673       if (local_player->friends_still_needed == 0 ||
12674           IS_SP_ELEMENT(Feld[jx][jy]))
12675         PlayerWins(player);
12676     }
12677
12678     /* this breaks one level: "machine", level 000 */
12679     {
12680       int move_direction = player->MovDir;
12681       int enter_side = MV_DIR_OPPOSITE(move_direction);
12682       int leave_side = move_direction;
12683       int old_jx = last_jx;
12684       int old_jy = last_jy;
12685       int old_element = Feld[old_jx][old_jy];
12686       int new_element = Feld[jx][jy];
12687
12688       if (IS_CUSTOM_ELEMENT(old_element))
12689         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12690                                    CE_LEFT_BY_PLAYER,
12691                                    player->index_bit, leave_side);
12692
12693       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12694                                           CE_PLAYER_LEAVES_X,
12695                                           player->index_bit, leave_side);
12696
12697       if (IS_CUSTOM_ELEMENT(new_element))
12698         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12699                                    player->index_bit, enter_side);
12700
12701       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12702                                           CE_PLAYER_ENTERS_X,
12703                                           player->index_bit, enter_side);
12704
12705       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12706                                         CE_MOVE_OF_X, move_direction);
12707     }
12708
12709     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12710     {
12711       TestIfPlayerTouchesBadThing(jx, jy);
12712       TestIfPlayerTouchesCustomElement(jx, jy);
12713
12714       /* needed because pushed element has not yet reached its destination,
12715          so it would trigger a change event at its previous field location */
12716       if (!player->is_pushing)
12717         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12718
12719       if (!player->active)
12720         RemovePlayer(player);
12721     }
12722
12723     if (!local_player->LevelSolved && level.use_step_counter)
12724     {
12725       int i;
12726
12727       TimePlayed++;
12728
12729       if (TimeLeft > 0)
12730       {
12731         TimeLeft--;
12732
12733         if (TimeLeft <= 10 && setup.time_limit)
12734           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12735
12736         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12737
12738         DisplayGameControlValues();
12739
12740         if (!TimeLeft && setup.time_limit)
12741           for (i = 0; i < MAX_PLAYERS; i++)
12742             KillPlayer(&stored_player[i]);
12743       }
12744       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12745       {
12746         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12747
12748         DisplayGameControlValues();
12749       }
12750     }
12751
12752     if (tape.single_step && tape.recording && !tape.pausing &&
12753         !player->programmed_action)
12754       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12755
12756     if (!player->programmed_action)
12757       CheckSaveEngineSnapshot(player);
12758   }
12759 }
12760
12761 void ScrollScreen(struct PlayerInfo *player, int mode)
12762 {
12763   static unsigned int screen_frame_counter = 0;
12764
12765   if (mode == SCROLL_INIT)
12766   {
12767     /* set scrolling step size according to actual player's moving speed */
12768     ScrollStepSize = TILEX / player->move_delay_value;
12769
12770     screen_frame_counter = FrameCounter;
12771     ScreenMovDir = player->MovDir;
12772     ScreenMovPos = player->MovPos;
12773     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12774     return;
12775   }
12776   else if (!FrameReached(&screen_frame_counter, 1))
12777     return;
12778
12779   if (ScreenMovPos)
12780   {
12781     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12782     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12783     redraw_mask |= REDRAW_FIELD;
12784   }
12785   else
12786     ScreenMovDir = MV_NONE;
12787 }
12788
12789 void TestIfPlayerTouchesCustomElement(int x, int y)
12790 {
12791   static int xy[4][2] =
12792   {
12793     { 0, -1 },
12794     { -1, 0 },
12795     { +1, 0 },
12796     { 0, +1 }
12797   };
12798   static int trigger_sides[4][2] =
12799   {
12800     /* center side       border side */
12801     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12802     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12803     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12804     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12805   };
12806   static int touch_dir[4] =
12807   {
12808     MV_LEFT | MV_RIGHT,
12809     MV_UP   | MV_DOWN,
12810     MV_UP   | MV_DOWN,
12811     MV_LEFT | MV_RIGHT
12812   };
12813   int center_element = Feld[x][y];      /* should always be non-moving! */
12814   int i;
12815
12816   for (i = 0; i < NUM_DIRECTIONS; i++)
12817   {
12818     int xx = x + xy[i][0];
12819     int yy = y + xy[i][1];
12820     int center_side = trigger_sides[i][0];
12821     int border_side = trigger_sides[i][1];
12822     int border_element;
12823
12824     if (!IN_LEV_FIELD(xx, yy))
12825       continue;
12826
12827     if (IS_PLAYER(x, y))                /* player found at center element */
12828     {
12829       struct PlayerInfo *player = PLAYERINFO(x, y);
12830
12831       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12832         border_element = Feld[xx][yy];          /* may be moving! */
12833       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12834         border_element = Feld[xx][yy];
12835       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12836         border_element = MovingOrBlocked2Element(xx, yy);
12837       else
12838         continue;               /* center and border element do not touch */
12839
12840       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12841                                  player->index_bit, border_side);
12842       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12843                                           CE_PLAYER_TOUCHES_X,
12844                                           player->index_bit, border_side);
12845
12846       {
12847         /* use player element that is initially defined in the level playfield,
12848            not the player element that corresponds to the runtime player number
12849            (example: a level that contains EL_PLAYER_3 as the only player would
12850            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12851         int player_element = PLAYERINFO(x, y)->initial_element;
12852
12853         CheckElementChangeBySide(xx, yy, border_element, player_element,
12854                                  CE_TOUCHING_X, border_side);
12855       }
12856     }
12857     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12858     {
12859       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12860
12861       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12862       {
12863         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12864           continue;             /* center and border element do not touch */
12865       }
12866
12867       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12868                                  player->index_bit, center_side);
12869       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12870                                           CE_PLAYER_TOUCHES_X,
12871                                           player->index_bit, center_side);
12872
12873       {
12874         /* use player element that is initially defined in the level playfield,
12875            not the player element that corresponds to the runtime player number
12876            (example: a level that contains EL_PLAYER_3 as the only player would
12877            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12878         int player_element = PLAYERINFO(xx, yy)->initial_element;
12879
12880         CheckElementChangeBySide(x, y, center_element, player_element,
12881                                  CE_TOUCHING_X, center_side);
12882       }
12883
12884       break;
12885     }
12886   }
12887 }
12888
12889 void TestIfElementTouchesCustomElement(int x, int y)
12890 {
12891   static int xy[4][2] =
12892   {
12893     { 0, -1 },
12894     { -1, 0 },
12895     { +1, 0 },
12896     { 0, +1 }
12897   };
12898   static int trigger_sides[4][2] =
12899   {
12900     /* center side      border side */
12901     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12902     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12903     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12904     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12905   };
12906   static int touch_dir[4] =
12907   {
12908     MV_LEFT | MV_RIGHT,
12909     MV_UP   | MV_DOWN,
12910     MV_UP   | MV_DOWN,
12911     MV_LEFT | MV_RIGHT
12912   };
12913   boolean change_center_element = FALSE;
12914   int center_element = Feld[x][y];      /* should always be non-moving! */
12915   int border_element_old[NUM_DIRECTIONS];
12916   int i;
12917
12918   for (i = 0; i < NUM_DIRECTIONS; i++)
12919   {
12920     int xx = x + xy[i][0];
12921     int yy = y + xy[i][1];
12922     int border_element;
12923
12924     border_element_old[i] = -1;
12925
12926     if (!IN_LEV_FIELD(xx, yy))
12927       continue;
12928
12929     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12930       border_element = Feld[xx][yy];    /* may be moving! */
12931     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12932       border_element = Feld[xx][yy];
12933     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12934       border_element = MovingOrBlocked2Element(xx, yy);
12935     else
12936       continue;                 /* center and border element do not touch */
12937
12938     border_element_old[i] = border_element;
12939   }
12940
12941   for (i = 0; i < NUM_DIRECTIONS; i++)
12942   {
12943     int xx = x + xy[i][0];
12944     int yy = y + xy[i][1];
12945     int center_side = trigger_sides[i][0];
12946     int border_element = border_element_old[i];
12947
12948     if (border_element == -1)
12949       continue;
12950
12951     /* check for change of border element */
12952     CheckElementChangeBySide(xx, yy, border_element, center_element,
12953                              CE_TOUCHING_X, center_side);
12954
12955     /* (center element cannot be player, so we dont have to check this here) */
12956   }
12957
12958   for (i = 0; i < NUM_DIRECTIONS; i++)
12959   {
12960     int xx = x + xy[i][0];
12961     int yy = y + xy[i][1];
12962     int border_side = trigger_sides[i][1];
12963     int border_element = border_element_old[i];
12964
12965     if (border_element == -1)
12966       continue;
12967
12968     /* check for change of center element (but change it only once) */
12969     if (!change_center_element)
12970       change_center_element =
12971         CheckElementChangeBySide(x, y, center_element, border_element,
12972                                  CE_TOUCHING_X, border_side);
12973
12974     if (IS_PLAYER(xx, yy))
12975     {
12976       /* use player element that is initially defined in the level playfield,
12977          not the player element that corresponds to the runtime player number
12978          (example: a level that contains EL_PLAYER_3 as the only player would
12979          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12980       int player_element = PLAYERINFO(xx, yy)->initial_element;
12981
12982       CheckElementChangeBySide(x, y, center_element, player_element,
12983                                CE_TOUCHING_X, border_side);
12984     }
12985   }
12986 }
12987
12988 void TestIfElementHitsCustomElement(int x, int y, int direction)
12989 {
12990   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12991   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12992   int hitx = x + dx, hity = y + dy;
12993   int hitting_element = Feld[x][y];
12994   int touched_element;
12995
12996   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12997     return;
12998
12999   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13000                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13001
13002   if (IN_LEV_FIELD(hitx, hity))
13003   {
13004     int opposite_direction = MV_DIR_OPPOSITE(direction);
13005     int hitting_side = direction;
13006     int touched_side = opposite_direction;
13007     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13008                           MovDir[hitx][hity] != direction ||
13009                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13010
13011     object_hit = TRUE;
13012
13013     if (object_hit)
13014     {
13015       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13016                                CE_HITTING_X, touched_side);
13017
13018       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13019                                CE_HIT_BY_X, hitting_side);
13020
13021       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13022                                CE_HIT_BY_SOMETHING, opposite_direction);
13023
13024       if (IS_PLAYER(hitx, hity))
13025       {
13026         /* use player element that is initially defined in the level playfield,
13027            not the player element that corresponds to the runtime player number
13028            (example: a level that contains EL_PLAYER_3 as the only player would
13029            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13030         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13031
13032         CheckElementChangeBySide(x, y, hitting_element, player_element,
13033                                  CE_HITTING_X, touched_side);
13034       }
13035     }
13036   }
13037
13038   /* "hitting something" is also true when hitting the playfield border */
13039   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13040                            CE_HITTING_SOMETHING, direction);
13041 }
13042
13043 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13044 {
13045   int i, kill_x = -1, kill_y = -1;
13046
13047   int bad_element = -1;
13048   static int test_xy[4][2] =
13049   {
13050     { 0, -1 },
13051     { -1, 0 },
13052     { +1, 0 },
13053     { 0, +1 }
13054   };
13055   static int test_dir[4] =
13056   {
13057     MV_UP,
13058     MV_LEFT,
13059     MV_RIGHT,
13060     MV_DOWN
13061   };
13062
13063   for (i = 0; i < NUM_DIRECTIONS; i++)
13064   {
13065     int test_x, test_y, test_move_dir, test_element;
13066
13067     test_x = good_x + test_xy[i][0];
13068     test_y = good_y + test_xy[i][1];
13069
13070     if (!IN_LEV_FIELD(test_x, test_y))
13071       continue;
13072
13073     test_move_dir =
13074       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13075
13076     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13077
13078     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13079        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13080     */
13081     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13082         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13083     {
13084       kill_x = test_x;
13085       kill_y = test_y;
13086       bad_element = test_element;
13087
13088       break;
13089     }
13090   }
13091
13092   if (kill_x != -1 || kill_y != -1)
13093   {
13094     if (IS_PLAYER(good_x, good_y))
13095     {
13096       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13097
13098       if (player->shield_deadly_time_left > 0 &&
13099           !IS_INDESTRUCTIBLE(bad_element))
13100         Bang(kill_x, kill_y);
13101       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13102         KillPlayer(player);
13103     }
13104     else
13105       Bang(good_x, good_y);
13106   }
13107 }
13108
13109 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13110 {
13111   int i, kill_x = -1, kill_y = -1;
13112   int bad_element = Feld[bad_x][bad_y];
13113   static int test_xy[4][2] =
13114   {
13115     { 0, -1 },
13116     { -1, 0 },
13117     { +1, 0 },
13118     { 0, +1 }
13119   };
13120   static int touch_dir[4] =
13121   {
13122     MV_LEFT | MV_RIGHT,
13123     MV_UP   | MV_DOWN,
13124     MV_UP   | MV_DOWN,
13125     MV_LEFT | MV_RIGHT
13126   };
13127   static int test_dir[4] =
13128   {
13129     MV_UP,
13130     MV_LEFT,
13131     MV_RIGHT,
13132     MV_DOWN
13133   };
13134
13135   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13136     return;
13137
13138   for (i = 0; i < NUM_DIRECTIONS; i++)
13139   {
13140     int test_x, test_y, test_move_dir, test_element;
13141
13142     test_x = bad_x + test_xy[i][0];
13143     test_y = bad_y + test_xy[i][1];
13144
13145     if (!IN_LEV_FIELD(test_x, test_y))
13146       continue;
13147
13148     test_move_dir =
13149       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13150
13151     test_element = Feld[test_x][test_y];
13152
13153     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13154        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13155     */
13156     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13157         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13158     {
13159       /* good thing is player or penguin that does not move away */
13160       if (IS_PLAYER(test_x, test_y))
13161       {
13162         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13163
13164         if (bad_element == EL_ROBOT && player->is_moving)
13165           continue;     /* robot does not kill player if he is moving */
13166
13167         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13168         {
13169           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13170             continue;           /* center and border element do not touch */
13171         }
13172
13173         kill_x = test_x;
13174         kill_y = test_y;
13175
13176         break;
13177       }
13178       else if (test_element == EL_PENGUIN)
13179       {
13180         kill_x = test_x;
13181         kill_y = test_y;
13182
13183         break;
13184       }
13185     }
13186   }
13187
13188   if (kill_x != -1 || kill_y != -1)
13189   {
13190     if (IS_PLAYER(kill_x, kill_y))
13191     {
13192       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13193
13194       if (player->shield_deadly_time_left > 0 &&
13195           !IS_INDESTRUCTIBLE(bad_element))
13196         Bang(bad_x, bad_y);
13197       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13198         KillPlayer(player);
13199     }
13200     else
13201       Bang(kill_x, kill_y);
13202   }
13203 }
13204
13205 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13206 {
13207   int bad_element = Feld[bad_x][bad_y];
13208   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13209   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13210   int test_x = bad_x + dx, test_y = bad_y + dy;
13211   int test_move_dir, test_element;
13212   int kill_x = -1, kill_y = -1;
13213
13214   if (!IN_LEV_FIELD(test_x, test_y))
13215     return;
13216
13217   test_move_dir =
13218     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13219
13220   test_element = Feld[test_x][test_y];
13221
13222   if (test_move_dir != bad_move_dir)
13223   {
13224     /* good thing can be player or penguin that does not move away */
13225     if (IS_PLAYER(test_x, test_y))
13226     {
13227       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13228
13229       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13230          player as being hit when he is moving towards the bad thing, because
13231          the "get hit by" condition would be lost after the player stops) */
13232       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13233         return;         /* player moves away from bad thing */
13234
13235       kill_x = test_x;
13236       kill_y = test_y;
13237     }
13238     else if (test_element == EL_PENGUIN)
13239     {
13240       kill_x = test_x;
13241       kill_y = test_y;
13242     }
13243   }
13244
13245   if (kill_x != -1 || kill_y != -1)
13246   {
13247     if (IS_PLAYER(kill_x, kill_y))
13248     {
13249       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13250
13251       if (player->shield_deadly_time_left > 0 &&
13252           !IS_INDESTRUCTIBLE(bad_element))
13253         Bang(bad_x, bad_y);
13254       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13255         KillPlayer(player);
13256     }
13257     else
13258       Bang(kill_x, kill_y);
13259   }
13260 }
13261
13262 void TestIfPlayerTouchesBadThing(int x, int y)
13263 {
13264   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13265 }
13266
13267 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13268 {
13269   TestIfGoodThingHitsBadThing(x, y, move_dir);
13270 }
13271
13272 void TestIfBadThingTouchesPlayer(int x, int y)
13273 {
13274   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13275 }
13276
13277 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13278 {
13279   TestIfBadThingHitsGoodThing(x, y, move_dir);
13280 }
13281
13282 void TestIfFriendTouchesBadThing(int x, int y)
13283 {
13284   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13285 }
13286
13287 void TestIfBadThingTouchesFriend(int x, int y)
13288 {
13289   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13290 }
13291
13292 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13293 {
13294   int i, kill_x = bad_x, kill_y = bad_y;
13295   static int xy[4][2] =
13296   {
13297     { 0, -1 },
13298     { -1, 0 },
13299     { +1, 0 },
13300     { 0, +1 }
13301   };
13302
13303   for (i = 0; i < NUM_DIRECTIONS; i++)
13304   {
13305     int x, y, element;
13306
13307     x = bad_x + xy[i][0];
13308     y = bad_y + xy[i][1];
13309     if (!IN_LEV_FIELD(x, y))
13310       continue;
13311
13312     element = Feld[x][y];
13313     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13314         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13315     {
13316       kill_x = x;
13317       kill_y = y;
13318       break;
13319     }
13320   }
13321
13322   if (kill_x != bad_x || kill_y != bad_y)
13323     Bang(bad_x, bad_y);
13324 }
13325
13326 void KillPlayer(struct PlayerInfo *player)
13327 {
13328   int jx = player->jx, jy = player->jy;
13329
13330   if (!player->active)
13331     return;
13332
13333 #if 0
13334   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13335          player->killed, player->active, player->reanimated);
13336 #endif
13337
13338   /* the following code was introduced to prevent an infinite loop when calling
13339      -> Bang()
13340      -> CheckTriggeredElementChangeExt()
13341      -> ExecuteCustomElementAction()
13342      -> KillPlayer()
13343      -> (infinitely repeating the above sequence of function calls)
13344      which occurs when killing the player while having a CE with the setting
13345      "kill player X when explosion of <player X>"; the solution using a new
13346      field "player->killed" was chosen for backwards compatibility, although
13347      clever use of the fields "player->active" etc. would probably also work */
13348 #if 1
13349   if (player->killed)
13350     return;
13351 #endif
13352
13353   player->killed = TRUE;
13354
13355   /* remove accessible field at the player's position */
13356   Feld[jx][jy] = EL_EMPTY;
13357
13358   /* deactivate shield (else Bang()/Explode() would not work right) */
13359   player->shield_normal_time_left = 0;
13360   player->shield_deadly_time_left = 0;
13361
13362 #if 0
13363   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13364          player->killed, player->active, player->reanimated);
13365 #endif
13366
13367   Bang(jx, jy);
13368
13369 #if 0
13370   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13371          player->killed, player->active, player->reanimated);
13372 #endif
13373
13374   if (player->reanimated)       /* killed player may have been reanimated */
13375     player->killed = player->reanimated = FALSE;
13376   else
13377     BuryPlayer(player);
13378 }
13379
13380 static void KillPlayerUnlessEnemyProtected(int x, int y)
13381 {
13382   if (!PLAYER_ENEMY_PROTECTED(x, y))
13383     KillPlayer(PLAYERINFO(x, y));
13384 }
13385
13386 static void KillPlayerUnlessExplosionProtected(int x, int y)
13387 {
13388   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13389     KillPlayer(PLAYERINFO(x, y));
13390 }
13391
13392 void BuryPlayer(struct PlayerInfo *player)
13393 {
13394   int jx = player->jx, jy = player->jy;
13395
13396   if (!player->active)
13397     return;
13398
13399   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13400   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13401
13402   player->GameOver = TRUE;
13403   RemovePlayer(player);
13404 }
13405
13406 void RemovePlayer(struct PlayerInfo *player)
13407 {
13408   int jx = player->jx, jy = player->jy;
13409   int i, found = FALSE;
13410
13411   player->present = FALSE;
13412   player->active = FALSE;
13413
13414   if (!ExplodeField[jx][jy])
13415     StorePlayer[jx][jy] = 0;
13416
13417   if (player->is_moving)
13418     TEST_DrawLevelField(player->last_jx, player->last_jy);
13419
13420   for (i = 0; i < MAX_PLAYERS; i++)
13421     if (stored_player[i].active)
13422       found = TRUE;
13423
13424   if (!found)
13425     AllPlayersGone = TRUE;
13426
13427   ExitX = ZX = jx;
13428   ExitY = ZY = jy;
13429 }
13430
13431 static void setFieldForSnapping(int x, int y, int element, int direction)
13432 {
13433   struct ElementInfo *ei = &element_info[element];
13434   int direction_bit = MV_DIR_TO_BIT(direction);
13435   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13436   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13437                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13438
13439   Feld[x][y] = EL_ELEMENT_SNAPPING;
13440   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13441
13442   ResetGfxAnimation(x, y);
13443
13444   GfxElement[x][y] = element;
13445   GfxAction[x][y] = action;
13446   GfxDir[x][y] = direction;
13447   GfxFrame[x][y] = -1;
13448 }
13449
13450 /*
13451   =============================================================================
13452   checkDiagonalPushing()
13453   -----------------------------------------------------------------------------
13454   check if diagonal input device direction results in pushing of object
13455   (by checking if the alternative direction is walkable, diggable, ...)
13456   =============================================================================
13457 */
13458
13459 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13460                                     int x, int y, int real_dx, int real_dy)
13461 {
13462   int jx, jy, dx, dy, xx, yy;
13463
13464   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13465     return TRUE;
13466
13467   /* diagonal direction: check alternative direction */
13468   jx = player->jx;
13469   jy = player->jy;
13470   dx = x - jx;
13471   dy = y - jy;
13472   xx = jx + (dx == 0 ? real_dx : 0);
13473   yy = jy + (dy == 0 ? real_dy : 0);
13474
13475   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13476 }
13477
13478 /*
13479   =============================================================================
13480   DigField()
13481   -----------------------------------------------------------------------------
13482   x, y:                 field next to player (non-diagonal) to try to dig to
13483   real_dx, real_dy:     direction as read from input device (can be diagonal)
13484   =============================================================================
13485 */
13486
13487 static int DigField(struct PlayerInfo *player,
13488                     int oldx, int oldy, int x, int y,
13489                     int real_dx, int real_dy, int mode)
13490 {
13491   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13492   boolean player_was_pushing = player->is_pushing;
13493   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13494   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13495   int jx = oldx, jy = oldy;
13496   int dx = x - jx, dy = y - jy;
13497   int nextx = x + dx, nexty = y + dy;
13498   int move_direction = (dx == -1 ? MV_LEFT  :
13499                         dx == +1 ? MV_RIGHT :
13500                         dy == -1 ? MV_UP    :
13501                         dy == +1 ? MV_DOWN  : MV_NONE);
13502   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13503   int dig_side = MV_DIR_OPPOSITE(move_direction);
13504   int old_element = Feld[jx][jy];
13505   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13506   int collect_count;
13507
13508   if (is_player)                /* function can also be called by EL_PENGUIN */
13509   {
13510     if (player->MovPos == 0)
13511     {
13512       player->is_digging = FALSE;
13513       player->is_collecting = FALSE;
13514     }
13515
13516     if (player->MovPos == 0)    /* last pushing move finished */
13517       player->is_pushing = FALSE;
13518
13519     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13520     {
13521       player->is_switching = FALSE;
13522       player->push_delay = -1;
13523
13524       return MP_NO_ACTION;
13525     }
13526   }
13527
13528   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13529     old_element = Back[jx][jy];
13530
13531   /* in case of element dropped at player position, check background */
13532   else if (Back[jx][jy] != EL_EMPTY &&
13533            game.engine_version >= VERSION_IDENT(2,2,0,0))
13534     old_element = Back[jx][jy];
13535
13536   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13537     return MP_NO_ACTION;        /* field has no opening in this direction */
13538
13539   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13540     return MP_NO_ACTION;        /* field has no opening in this direction */
13541
13542   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13543   {
13544     SplashAcid(x, y);
13545
13546     Feld[jx][jy] = player->artwork_element;
13547     InitMovingField(jx, jy, MV_DOWN);
13548     Store[jx][jy] = EL_ACID;
13549     ContinueMoving(jx, jy);
13550     BuryPlayer(player);
13551
13552     return MP_DONT_RUN_INTO;
13553   }
13554
13555   if (player_can_move && DONT_RUN_INTO(element))
13556   {
13557     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13558
13559     return MP_DONT_RUN_INTO;
13560   }
13561
13562   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13563     return MP_NO_ACTION;
13564
13565   collect_count = element_info[element].collect_count_initial;
13566
13567   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13568     return MP_NO_ACTION;
13569
13570   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13571     player_can_move = player_can_move_or_snap;
13572
13573   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13574       game.engine_version >= VERSION_IDENT(2,2,0,0))
13575   {
13576     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13577                                player->index_bit, dig_side);
13578     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13579                                         player->index_bit, dig_side);
13580
13581     if (element == EL_DC_LANDMINE)
13582       Bang(x, y);
13583
13584     if (Feld[x][y] != element)          /* field changed by snapping */
13585       return MP_ACTION;
13586
13587     return MP_NO_ACTION;
13588   }
13589
13590   if (player->gravity && is_player && !player->is_auto_moving &&
13591       canFallDown(player) && move_direction != MV_DOWN &&
13592       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13593     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13594
13595   if (player_can_move &&
13596       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13597   {
13598     int sound_element = SND_ELEMENT(element);
13599     int sound_action = ACTION_WALKING;
13600
13601     if (IS_RND_GATE(element))
13602     {
13603       if (!player->key[RND_GATE_NR(element)])
13604         return MP_NO_ACTION;
13605     }
13606     else if (IS_RND_GATE_GRAY(element))
13607     {
13608       if (!player->key[RND_GATE_GRAY_NR(element)])
13609         return MP_NO_ACTION;
13610     }
13611     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13612     {
13613       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13614         return MP_NO_ACTION;
13615     }
13616     else if (element == EL_EXIT_OPEN ||
13617              element == EL_EM_EXIT_OPEN ||
13618              element == EL_EM_EXIT_OPENING ||
13619              element == EL_STEEL_EXIT_OPEN ||
13620              element == EL_EM_STEEL_EXIT_OPEN ||
13621              element == EL_EM_STEEL_EXIT_OPENING ||
13622              element == EL_SP_EXIT_OPEN ||
13623              element == EL_SP_EXIT_OPENING)
13624     {
13625       sound_action = ACTION_PASSING;    /* player is passing exit */
13626     }
13627     else if (element == EL_EMPTY)
13628     {
13629       sound_action = ACTION_MOVING;             /* nothing to walk on */
13630     }
13631
13632     /* play sound from background or player, whatever is available */
13633     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13634       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13635     else
13636       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13637   }
13638   else if (player_can_move &&
13639            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13640   {
13641     if (!ACCESS_FROM(element, opposite_direction))
13642       return MP_NO_ACTION;      /* field not accessible from this direction */
13643
13644     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13645       return MP_NO_ACTION;
13646
13647     if (IS_EM_GATE(element))
13648     {
13649       if (!player->key[EM_GATE_NR(element)])
13650         return MP_NO_ACTION;
13651     }
13652     else if (IS_EM_GATE_GRAY(element))
13653     {
13654       if (!player->key[EM_GATE_GRAY_NR(element)])
13655         return MP_NO_ACTION;
13656     }
13657     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13658     {
13659       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13660         return MP_NO_ACTION;
13661     }
13662     else if (IS_EMC_GATE(element))
13663     {
13664       if (!player->key[EMC_GATE_NR(element)])
13665         return MP_NO_ACTION;
13666     }
13667     else if (IS_EMC_GATE_GRAY(element))
13668     {
13669       if (!player->key[EMC_GATE_GRAY_NR(element)])
13670         return MP_NO_ACTION;
13671     }
13672     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13673     {
13674       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13675         return MP_NO_ACTION;
13676     }
13677     else if (element == EL_DC_GATE_WHITE ||
13678              element == EL_DC_GATE_WHITE_GRAY ||
13679              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13680     {
13681       if (player->num_white_keys == 0)
13682         return MP_NO_ACTION;
13683
13684       player->num_white_keys--;
13685     }
13686     else if (IS_SP_PORT(element))
13687     {
13688       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13689           element == EL_SP_GRAVITY_PORT_RIGHT ||
13690           element == EL_SP_GRAVITY_PORT_UP ||
13691           element == EL_SP_GRAVITY_PORT_DOWN)
13692         player->gravity = !player->gravity;
13693       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13694                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13695                element == EL_SP_GRAVITY_ON_PORT_UP ||
13696                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13697         player->gravity = TRUE;
13698       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13699                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13700                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13701                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13702         player->gravity = FALSE;
13703     }
13704
13705     /* automatically move to the next field with double speed */
13706     player->programmed_action = move_direction;
13707
13708     if (player->move_delay_reset_counter == 0)
13709     {
13710       player->move_delay_reset_counter = 2;     /* two double speed steps */
13711
13712       DOUBLE_PLAYER_SPEED(player);
13713     }
13714
13715     PlayLevelSoundAction(x, y, ACTION_PASSING);
13716   }
13717   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13718   {
13719     RemoveField(x, y);
13720
13721     if (mode != DF_SNAP)
13722     {
13723       GfxElement[x][y] = GFX_ELEMENT(element);
13724       player->is_digging = TRUE;
13725     }
13726
13727     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13728
13729     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13730                                         player->index_bit, dig_side);
13731
13732     if (mode == DF_SNAP)
13733     {
13734       if (level.block_snap_field)
13735         setFieldForSnapping(x, y, element, move_direction);
13736       else
13737         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13738
13739       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13740                                           player->index_bit, dig_side);
13741     }
13742   }
13743   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13744   {
13745     RemoveField(x, y);
13746
13747     if (is_player && mode != DF_SNAP)
13748     {
13749       GfxElement[x][y] = element;
13750       player->is_collecting = TRUE;
13751     }
13752
13753     if (element == EL_SPEED_PILL)
13754     {
13755       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13756     }
13757     else if (element == EL_EXTRA_TIME && level.time > 0)
13758     {
13759       TimeLeft += level.extra_time;
13760
13761       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13762
13763       DisplayGameControlValues();
13764     }
13765     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13766     {
13767       player->shield_normal_time_left += level.shield_normal_time;
13768       if (element == EL_SHIELD_DEADLY)
13769         player->shield_deadly_time_left += level.shield_deadly_time;
13770     }
13771     else if (element == EL_DYNAMITE ||
13772              element == EL_EM_DYNAMITE ||
13773              element == EL_SP_DISK_RED)
13774     {
13775       if (player->inventory_size < MAX_INVENTORY_SIZE)
13776         player->inventory_element[player->inventory_size++] = element;
13777
13778       DrawGameDoorValues();
13779     }
13780     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13781     {
13782       player->dynabomb_count++;
13783       player->dynabombs_left++;
13784     }
13785     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13786     {
13787       player->dynabomb_size++;
13788     }
13789     else if (element == EL_DYNABOMB_INCREASE_POWER)
13790     {
13791       player->dynabomb_xl = TRUE;
13792     }
13793     else if (IS_KEY(element))
13794     {
13795       player->key[KEY_NR(element)] = TRUE;
13796
13797       DrawGameDoorValues();
13798     }
13799     else if (element == EL_DC_KEY_WHITE)
13800     {
13801       player->num_white_keys++;
13802
13803       /* display white keys? */
13804       /* DrawGameDoorValues(); */
13805     }
13806     else if (IS_ENVELOPE(element))
13807     {
13808       player->show_envelope = element;
13809     }
13810     else if (element == EL_EMC_LENSES)
13811     {
13812       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13813
13814       RedrawAllInvisibleElementsForLenses();
13815     }
13816     else if (element == EL_EMC_MAGNIFIER)
13817     {
13818       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13819
13820       RedrawAllInvisibleElementsForMagnifier();
13821     }
13822     else if (IS_DROPPABLE(element) ||
13823              IS_THROWABLE(element))     /* can be collected and dropped */
13824     {
13825       int i;
13826
13827       if (collect_count == 0)
13828         player->inventory_infinite_element = element;
13829       else
13830         for (i = 0; i < collect_count; i++)
13831           if (player->inventory_size < MAX_INVENTORY_SIZE)
13832             player->inventory_element[player->inventory_size++] = element;
13833
13834       DrawGameDoorValues();
13835     }
13836     else if (collect_count > 0)
13837     {
13838       local_player->gems_still_needed -= collect_count;
13839       if (local_player->gems_still_needed < 0)
13840         local_player->gems_still_needed = 0;
13841
13842       game.snapshot.collected_item = TRUE;
13843
13844       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13845
13846       DisplayGameControlValues();
13847     }
13848
13849     RaiseScoreElement(element);
13850     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13851
13852     if (is_player)
13853       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13854                                           player->index_bit, dig_side);
13855
13856     if (mode == DF_SNAP)
13857     {
13858       if (level.block_snap_field)
13859         setFieldForSnapping(x, y, element, move_direction);
13860       else
13861         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13862
13863       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13864                                           player->index_bit, dig_side);
13865     }
13866   }
13867   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13868   {
13869     if (mode == DF_SNAP && element != EL_BD_ROCK)
13870       return MP_NO_ACTION;
13871
13872     if (CAN_FALL(element) && dy)
13873       return MP_NO_ACTION;
13874
13875     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13876         !(element == EL_SPRING && level.use_spring_bug))
13877       return MP_NO_ACTION;
13878
13879     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13880         ((move_direction & MV_VERTICAL &&
13881           ((element_info[element].move_pattern & MV_LEFT &&
13882             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13883            (element_info[element].move_pattern & MV_RIGHT &&
13884             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13885          (move_direction & MV_HORIZONTAL &&
13886           ((element_info[element].move_pattern & MV_UP &&
13887             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13888            (element_info[element].move_pattern & MV_DOWN &&
13889             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13890       return MP_NO_ACTION;
13891
13892     /* do not push elements already moving away faster than player */
13893     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13894         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13895       return MP_NO_ACTION;
13896
13897     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13898     {
13899       if (player->push_delay_value == -1 || !player_was_pushing)
13900         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13901     }
13902     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13903     {
13904       if (player->push_delay_value == -1)
13905         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13906     }
13907     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13908     {
13909       if (!player->is_pushing)
13910         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13911     }
13912
13913     player->is_pushing = TRUE;
13914     player->is_active = TRUE;
13915
13916     if (!(IN_LEV_FIELD(nextx, nexty) &&
13917           (IS_FREE(nextx, nexty) ||
13918            (IS_SB_ELEMENT(element) &&
13919             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13920            (IS_CUSTOM_ELEMENT(element) &&
13921             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13922       return MP_NO_ACTION;
13923
13924     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13925       return MP_NO_ACTION;
13926
13927     if (player->push_delay == -1)       /* new pushing; restart delay */
13928       player->push_delay = 0;
13929
13930     if (player->push_delay < player->push_delay_value &&
13931         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13932         element != EL_SPRING && element != EL_BALLOON)
13933     {
13934       /* make sure that there is no move delay before next try to push */
13935       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13936         player->move_delay = 0;
13937
13938       return MP_NO_ACTION;
13939     }
13940
13941     if (IS_CUSTOM_ELEMENT(element) &&
13942         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13943     {
13944       if (!DigFieldByCE(nextx, nexty, element))
13945         return MP_NO_ACTION;
13946     }
13947
13948     if (IS_SB_ELEMENT(element))
13949     {
13950       if (element == EL_SOKOBAN_FIELD_FULL)
13951       {
13952         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13953         local_player->sokobanfields_still_needed++;
13954       }
13955
13956       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13957       {
13958         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13959         local_player->sokobanfields_still_needed--;
13960       }
13961
13962       Feld[x][y] = EL_SOKOBAN_OBJECT;
13963
13964       if (Back[x][y] == Back[nextx][nexty])
13965         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13966       else if (Back[x][y] != 0)
13967         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13968                                     ACTION_EMPTYING);
13969       else
13970         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13971                                     ACTION_FILLING);
13972
13973       if (local_player->sokobanfields_still_needed == 0 &&
13974           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13975       {
13976         PlayerWins(player);
13977
13978         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13979       }
13980     }
13981     else
13982       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13983
13984     InitMovingField(x, y, move_direction);
13985     GfxAction[x][y] = ACTION_PUSHING;
13986
13987     if (mode == DF_SNAP)
13988       ContinueMoving(x, y);
13989     else
13990       MovPos[x][y] = (dx != 0 ? dx : dy);
13991
13992     Pushed[x][y] = TRUE;
13993     Pushed[nextx][nexty] = TRUE;
13994
13995     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13996       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13997     else
13998       player->push_delay_value = -1;    /* get new value later */
13999
14000     /* check for element change _after_ element has been pushed */
14001     if (game.use_change_when_pushing_bug)
14002     {
14003       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14004                                  player->index_bit, dig_side);
14005       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14006                                           player->index_bit, dig_side);
14007     }
14008   }
14009   else if (IS_SWITCHABLE(element))
14010   {
14011     if (PLAYER_SWITCHING(player, x, y))
14012     {
14013       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14014                                           player->index_bit, dig_side);
14015
14016       return MP_ACTION;
14017     }
14018
14019     player->is_switching = TRUE;
14020     player->switch_x = x;
14021     player->switch_y = y;
14022
14023     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14024
14025     if (element == EL_ROBOT_WHEEL)
14026     {
14027       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14028       ZX = x;
14029       ZY = y;
14030
14031       game.robot_wheel_active = TRUE;
14032
14033       TEST_DrawLevelField(x, y);
14034     }
14035     else if (element == EL_SP_TERMINAL)
14036     {
14037       int xx, yy;
14038
14039       SCAN_PLAYFIELD(xx, yy)
14040       {
14041         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14042         {
14043           Bang(xx, yy);
14044         }
14045         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14046         {
14047           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14048
14049           ResetGfxAnimation(xx, yy);
14050           TEST_DrawLevelField(xx, yy);
14051         }
14052       }
14053     }
14054     else if (IS_BELT_SWITCH(element))
14055     {
14056       ToggleBeltSwitch(x, y);
14057     }
14058     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14059              element == EL_SWITCHGATE_SWITCH_DOWN ||
14060              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14061              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14062     {
14063       ToggleSwitchgateSwitch(x, y);
14064     }
14065     else if (element == EL_LIGHT_SWITCH ||
14066              element == EL_LIGHT_SWITCH_ACTIVE)
14067     {
14068       ToggleLightSwitch(x, y);
14069     }
14070     else if (element == EL_TIMEGATE_SWITCH ||
14071              element == EL_DC_TIMEGATE_SWITCH)
14072     {
14073       ActivateTimegateSwitch(x, y);
14074     }
14075     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14076              element == EL_BALLOON_SWITCH_RIGHT ||
14077              element == EL_BALLOON_SWITCH_UP    ||
14078              element == EL_BALLOON_SWITCH_DOWN  ||
14079              element == EL_BALLOON_SWITCH_NONE  ||
14080              element == EL_BALLOON_SWITCH_ANY)
14081     {
14082       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14083                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14084                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14085                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14086                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14087                              move_direction);
14088     }
14089     else if (element == EL_LAMP)
14090     {
14091       Feld[x][y] = EL_LAMP_ACTIVE;
14092       local_player->lights_still_needed--;
14093
14094       ResetGfxAnimation(x, y);
14095       TEST_DrawLevelField(x, y);
14096     }
14097     else if (element == EL_TIME_ORB_FULL)
14098     {
14099       Feld[x][y] = EL_TIME_ORB_EMPTY;
14100
14101       if (level.time > 0 || level.use_time_orb_bug)
14102       {
14103         TimeLeft += level.time_orb_time;
14104         game.no_time_limit = FALSE;
14105
14106         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14107
14108         DisplayGameControlValues();
14109       }
14110
14111       ResetGfxAnimation(x, y);
14112       TEST_DrawLevelField(x, y);
14113     }
14114     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14115              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14116     {
14117       int xx, yy;
14118
14119       game.ball_state = !game.ball_state;
14120
14121       SCAN_PLAYFIELD(xx, yy)
14122       {
14123         int e = Feld[xx][yy];
14124
14125         if (game.ball_state)
14126         {
14127           if (e == EL_EMC_MAGIC_BALL)
14128             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14129           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14130             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14131         }
14132         else
14133         {
14134           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14135             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14136           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14137             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14138         }
14139       }
14140     }
14141
14142     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14143                                         player->index_bit, dig_side);
14144
14145     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14146                                         player->index_bit, dig_side);
14147
14148     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14149                                         player->index_bit, dig_side);
14150
14151     return MP_ACTION;
14152   }
14153   else
14154   {
14155     if (!PLAYER_SWITCHING(player, x, y))
14156     {
14157       player->is_switching = TRUE;
14158       player->switch_x = x;
14159       player->switch_y = y;
14160
14161       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14162                                  player->index_bit, dig_side);
14163       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14164                                           player->index_bit, dig_side);
14165
14166       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14167                                  player->index_bit, dig_side);
14168       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14169                                           player->index_bit, dig_side);
14170     }
14171
14172     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14173                                player->index_bit, dig_side);
14174     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14175                                         player->index_bit, dig_side);
14176
14177     return MP_NO_ACTION;
14178   }
14179
14180   player->push_delay = -1;
14181
14182   if (is_player)                /* function can also be called by EL_PENGUIN */
14183   {
14184     if (Feld[x][y] != element)          /* really digged/collected something */
14185     {
14186       player->is_collecting = !player->is_digging;
14187       player->is_active = TRUE;
14188     }
14189   }
14190
14191   return MP_MOVING;
14192 }
14193
14194 static boolean DigFieldByCE(int x, int y, int digging_element)
14195 {
14196   int element = Feld[x][y];
14197
14198   if (!IS_FREE(x, y))
14199   {
14200     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14201                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14202                   ACTION_BREAKING);
14203
14204     /* no element can dig solid indestructible elements */
14205     if (IS_INDESTRUCTIBLE(element) &&
14206         !IS_DIGGABLE(element) &&
14207         !IS_COLLECTIBLE(element))
14208       return FALSE;
14209
14210     if (AmoebaNr[x][y] &&
14211         (element == EL_AMOEBA_FULL ||
14212          element == EL_BD_AMOEBA ||
14213          element == EL_AMOEBA_GROWING))
14214     {
14215       AmoebaCnt[AmoebaNr[x][y]]--;
14216       AmoebaCnt2[AmoebaNr[x][y]]--;
14217     }
14218
14219     if (IS_MOVING(x, y))
14220       RemoveMovingField(x, y);
14221     else
14222     {
14223       RemoveField(x, y);
14224       TEST_DrawLevelField(x, y);
14225     }
14226
14227     /* if digged element was about to explode, prevent the explosion */
14228     ExplodeField[x][y] = EX_TYPE_NONE;
14229
14230     PlayLevelSoundAction(x, y, action);
14231   }
14232
14233   Store[x][y] = EL_EMPTY;
14234
14235   /* this makes it possible to leave the removed element again */
14236   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14237     Store[x][y] = element;
14238
14239   return TRUE;
14240 }
14241
14242 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14243 {
14244   int jx = player->jx, jy = player->jy;
14245   int x = jx + dx, y = jy + dy;
14246   int snap_direction = (dx == -1 ? MV_LEFT  :
14247                         dx == +1 ? MV_RIGHT :
14248                         dy == -1 ? MV_UP    :
14249                         dy == +1 ? MV_DOWN  : MV_NONE);
14250   boolean can_continue_snapping = (level.continuous_snapping &&
14251                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14252
14253   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14254     return FALSE;
14255
14256   if (!player->active || !IN_LEV_FIELD(x, y))
14257     return FALSE;
14258
14259   if (dx && dy)
14260     return FALSE;
14261
14262   if (!dx && !dy)
14263   {
14264     if (player->MovPos == 0)
14265       player->is_pushing = FALSE;
14266
14267     player->is_snapping = FALSE;
14268
14269     if (player->MovPos == 0)
14270     {
14271       player->is_moving = FALSE;
14272       player->is_digging = FALSE;
14273       player->is_collecting = FALSE;
14274     }
14275
14276     return FALSE;
14277   }
14278
14279   /* prevent snapping with already pressed snap key when not allowed */
14280   if (player->is_snapping && !can_continue_snapping)
14281     return FALSE;
14282
14283   player->MovDir = snap_direction;
14284
14285   if (player->MovPos == 0)
14286   {
14287     player->is_moving = FALSE;
14288     player->is_digging = FALSE;
14289     player->is_collecting = FALSE;
14290   }
14291
14292   player->is_dropping = FALSE;
14293   player->is_dropping_pressed = FALSE;
14294   player->drop_pressed_delay = 0;
14295
14296   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14297     return FALSE;
14298
14299   player->is_snapping = TRUE;
14300   player->is_active = TRUE;
14301
14302   if (player->MovPos == 0)
14303   {
14304     player->is_moving = FALSE;
14305     player->is_digging = FALSE;
14306     player->is_collecting = FALSE;
14307   }
14308
14309   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14310     TEST_DrawLevelField(player->last_jx, player->last_jy);
14311
14312   TEST_DrawLevelField(x, y);
14313
14314   return TRUE;
14315 }
14316
14317 static boolean DropElement(struct PlayerInfo *player)
14318 {
14319   int old_element, new_element;
14320   int dropx = player->jx, dropy = player->jy;
14321   int drop_direction = player->MovDir;
14322   int drop_side = drop_direction;
14323   int drop_element = get_next_dropped_element(player);
14324
14325   /* do not drop an element on top of another element; when holding drop key
14326      pressed without moving, dropped element must move away before the next
14327      element can be dropped (this is especially important if the next element
14328      is dynamite, which can be placed on background for historical reasons) */
14329   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14330     return MP_ACTION;
14331
14332   if (IS_THROWABLE(drop_element))
14333   {
14334     dropx += GET_DX_FROM_DIR(drop_direction);
14335     dropy += GET_DY_FROM_DIR(drop_direction);
14336
14337     if (!IN_LEV_FIELD(dropx, dropy))
14338       return FALSE;
14339   }
14340
14341   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14342   new_element = drop_element;           /* default: no change when dropping */
14343
14344   /* check if player is active, not moving and ready to drop */
14345   if (!player->active || player->MovPos || player->drop_delay > 0)
14346     return FALSE;
14347
14348   /* check if player has anything that can be dropped */
14349   if (new_element == EL_UNDEFINED)
14350     return FALSE;
14351
14352   /* only set if player has anything that can be dropped */
14353   player->is_dropping_pressed = TRUE;
14354
14355   /* check if drop key was pressed long enough for EM style dynamite */
14356   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14357     return FALSE;
14358
14359   /* check if anything can be dropped at the current position */
14360   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14361     return FALSE;
14362
14363   /* collected custom elements can only be dropped on empty fields */
14364   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14365     return FALSE;
14366
14367   if (old_element != EL_EMPTY)
14368     Back[dropx][dropy] = old_element;   /* store old element on this field */
14369
14370   ResetGfxAnimation(dropx, dropy);
14371   ResetRandomAnimationValue(dropx, dropy);
14372
14373   if (player->inventory_size > 0 ||
14374       player->inventory_infinite_element != EL_UNDEFINED)
14375   {
14376     if (player->inventory_size > 0)
14377     {
14378       player->inventory_size--;
14379
14380       DrawGameDoorValues();
14381
14382       if (new_element == EL_DYNAMITE)
14383         new_element = EL_DYNAMITE_ACTIVE;
14384       else if (new_element == EL_EM_DYNAMITE)
14385         new_element = EL_EM_DYNAMITE_ACTIVE;
14386       else if (new_element == EL_SP_DISK_RED)
14387         new_element = EL_SP_DISK_RED_ACTIVE;
14388     }
14389
14390     Feld[dropx][dropy] = new_element;
14391
14392     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14393       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14394                           el2img(Feld[dropx][dropy]), 0);
14395
14396     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14397
14398     /* needed if previous element just changed to "empty" in the last frame */
14399     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14400
14401     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14402                                player->index_bit, drop_side);
14403     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14404                                         CE_PLAYER_DROPS_X,
14405                                         player->index_bit, drop_side);
14406
14407     TestIfElementTouchesCustomElement(dropx, dropy);
14408   }
14409   else          /* player is dropping a dyna bomb */
14410   {
14411     player->dynabombs_left--;
14412
14413     Feld[dropx][dropy] = new_element;
14414
14415     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14416       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14417                           el2img(Feld[dropx][dropy]), 0);
14418
14419     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14420   }
14421
14422   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14423     InitField_WithBug1(dropx, dropy, FALSE);
14424
14425   new_element = Feld[dropx][dropy];     /* element might have changed */
14426
14427   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14428       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14429   {
14430     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14431       MovDir[dropx][dropy] = drop_direction;
14432
14433     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14434
14435     /* do not cause impact style collision by dropping elements that can fall */
14436     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14437   }
14438
14439   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14440   player->is_dropping = TRUE;
14441
14442   player->drop_pressed_delay = 0;
14443   player->is_dropping_pressed = FALSE;
14444
14445   player->drop_x = dropx;
14446   player->drop_y = dropy;
14447
14448   return TRUE;
14449 }
14450
14451 /* ------------------------------------------------------------------------- */
14452 /* game sound playing functions                                              */
14453 /* ------------------------------------------------------------------------- */
14454
14455 static int *loop_sound_frame = NULL;
14456 static int *loop_sound_volume = NULL;
14457
14458 void InitPlayLevelSound()
14459 {
14460   int num_sounds = getSoundListSize();
14461
14462   checked_free(loop_sound_frame);
14463   checked_free(loop_sound_volume);
14464
14465   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14466   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14467 }
14468
14469 static void PlayLevelSound(int x, int y, int nr)
14470 {
14471   int sx = SCREENX(x), sy = SCREENY(y);
14472   int volume, stereo_position;
14473   int max_distance = 8;
14474   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14475
14476   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14477       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14478     return;
14479
14480   if (!IN_LEV_FIELD(x, y) ||
14481       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14482       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14483     return;
14484
14485   volume = SOUND_MAX_VOLUME;
14486
14487   if (!IN_SCR_FIELD(sx, sy))
14488   {
14489     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14490     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14491
14492     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14493   }
14494
14495   stereo_position = (SOUND_MAX_LEFT +
14496                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14497                      (SCR_FIELDX + 2 * max_distance));
14498
14499   if (IS_LOOP_SOUND(nr))
14500   {
14501     /* This assures that quieter loop sounds do not overwrite louder ones,
14502        while restarting sound volume comparison with each new game frame. */
14503
14504     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14505       return;
14506
14507     loop_sound_volume[nr] = volume;
14508     loop_sound_frame[nr] = FrameCounter;
14509   }
14510
14511   PlaySoundExt(nr, volume, stereo_position, type);
14512 }
14513
14514 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14515 {
14516   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14517                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14518                  y < LEVELY(BY1) ? LEVELY(BY1) :
14519                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14520                  sound_action);
14521 }
14522
14523 static void PlayLevelSoundAction(int x, int y, int action)
14524 {
14525   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14526 }
14527
14528 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14529 {
14530   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14531
14532   if (sound_effect != SND_UNDEFINED)
14533     PlayLevelSound(x, y, sound_effect);
14534 }
14535
14536 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14537                                               int action)
14538 {
14539   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14540
14541   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14542     PlayLevelSound(x, y, sound_effect);
14543 }
14544
14545 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14546 {
14547   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14548
14549   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14550     PlayLevelSound(x, y, sound_effect);
14551 }
14552
14553 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14554 {
14555   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14556
14557   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14558     StopSound(sound_effect);
14559 }
14560
14561 static int getLevelMusicNr()
14562 {
14563   if (levelset.music[level_nr] != MUS_UNDEFINED)
14564     return levelset.music[level_nr];            /* from config file */
14565   else
14566     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14567 }
14568
14569 static void FadeLevelSounds()
14570 {
14571   FadeSounds();
14572 }
14573
14574 static void FadeLevelMusic()
14575 {
14576   int music_nr = getLevelMusicNr();
14577   char *curr_music = getCurrentlyPlayingMusicFilename();
14578   char *next_music = getMusicInfoEntryFilename(music_nr);
14579
14580   if (!strEqual(curr_music, next_music))
14581     FadeMusic();
14582 }
14583
14584 void FadeLevelSoundsAndMusic()
14585 {
14586   FadeLevelSounds();
14587   FadeLevelMusic();
14588 }
14589
14590 static void PlayLevelMusic()
14591 {
14592   int music_nr = getLevelMusicNr();
14593   char *curr_music = getCurrentlyPlayingMusicFilename();
14594   char *next_music = getMusicInfoEntryFilename(music_nr);
14595
14596   if (!strEqual(curr_music, next_music))
14597     PlayMusic(music_nr);
14598 }
14599
14600 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14601 {
14602   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14603   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14604   int x = xx - 1 - offset;
14605   int y = yy - 1 - offset;
14606
14607   switch (sample)
14608   {
14609     case SAMPLE_blank:
14610       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14611       break;
14612
14613     case SAMPLE_roll:
14614       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14615       break;
14616
14617     case SAMPLE_stone:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14619       break;
14620
14621     case SAMPLE_nut:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14623       break;
14624
14625     case SAMPLE_crack:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14627       break;
14628
14629     case SAMPLE_bug:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14631       break;
14632
14633     case SAMPLE_tank:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14635       break;
14636
14637     case SAMPLE_android_clone:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14639       break;
14640
14641     case SAMPLE_android_move:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14643       break;
14644
14645     case SAMPLE_spring:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14647       break;
14648
14649     case SAMPLE_slurp:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14651       break;
14652
14653     case SAMPLE_eater:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14655       break;
14656
14657     case SAMPLE_eater_eat:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14659       break;
14660
14661     case SAMPLE_alien:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14663       break;
14664
14665     case SAMPLE_collect:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14667       break;
14668
14669     case SAMPLE_diamond:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14671       break;
14672
14673     case SAMPLE_squash:
14674       /* !!! CHECK THIS !!! */
14675 #if 1
14676       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14677 #else
14678       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14679 #endif
14680       break;
14681
14682     case SAMPLE_wonderfall:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14684       break;
14685
14686     case SAMPLE_drip:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14688       break;
14689
14690     case SAMPLE_push:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14692       break;
14693
14694     case SAMPLE_dirt:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14696       break;
14697
14698     case SAMPLE_acid:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14700       break;
14701
14702     case SAMPLE_ball:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14704       break;
14705
14706     case SAMPLE_grow:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14708       break;
14709
14710     case SAMPLE_wonder:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14712       break;
14713
14714     case SAMPLE_door:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14716       break;
14717
14718     case SAMPLE_exit_open:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14720       break;
14721
14722     case SAMPLE_exit_leave:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14724       break;
14725
14726     case SAMPLE_dynamite:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14728       break;
14729
14730     case SAMPLE_tick:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14732       break;
14733
14734     case SAMPLE_press:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14736       break;
14737
14738     case SAMPLE_wheel:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14740       break;
14741
14742     case SAMPLE_boom:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14744       break;
14745
14746     case SAMPLE_die:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14748       break;
14749
14750     case SAMPLE_time:
14751       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14752       break;
14753
14754     default:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14756       break;
14757   }
14758 }
14759
14760 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14761 {
14762   int element = map_element_SP_to_RND(element_sp);
14763   int action = map_action_SP_to_RND(action_sp);
14764   int offset = (setup.sp_show_border_elements ? 0 : 1);
14765   int x = xx - offset;
14766   int y = yy - offset;
14767
14768   PlayLevelSoundElementAction(x, y, element, action);
14769 }
14770
14771 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14772 {
14773   int element = map_element_MM_to_RND(element_mm);
14774   int action = map_action_MM_to_RND(action_mm);
14775   int offset = 0;
14776   int x = xx - offset;
14777   int y = yy - offset;
14778
14779   if (!IS_MM_ELEMENT(element))
14780     element = EL_MM_DEFAULT;
14781
14782   PlayLevelSoundElementAction(x, y, element, action);
14783 }
14784
14785 void PlaySound_MM(int sound_mm)
14786 {
14787   int sound = map_sound_MM_to_RND(sound_mm);
14788
14789   if (sound == SND_UNDEFINED)
14790     return;
14791
14792   PlaySound(sound);
14793 }
14794
14795 void PlaySoundLoop_MM(int sound_mm)
14796 {
14797   int sound = map_sound_MM_to_RND(sound_mm);
14798
14799   if (sound == SND_UNDEFINED)
14800     return;
14801
14802   PlaySoundLoop(sound);
14803 }
14804
14805 void StopSound_MM(int sound_mm)
14806 {
14807   int sound = map_sound_MM_to_RND(sound_mm);
14808
14809   if (sound == SND_UNDEFINED)
14810     return;
14811
14812   StopSound(sound);
14813 }
14814
14815 void RaiseScore(int value)
14816 {
14817   local_player->score += value;
14818
14819   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14820
14821   DisplayGameControlValues();
14822 }
14823
14824 void RaiseScoreElement(int element)
14825 {
14826   switch (element)
14827   {
14828     case EL_EMERALD:
14829     case EL_BD_DIAMOND:
14830     case EL_EMERALD_YELLOW:
14831     case EL_EMERALD_RED:
14832     case EL_EMERALD_PURPLE:
14833     case EL_SP_INFOTRON:
14834       RaiseScore(level.score[SC_EMERALD]);
14835       break;
14836     case EL_DIAMOND:
14837       RaiseScore(level.score[SC_DIAMOND]);
14838       break;
14839     case EL_CRYSTAL:
14840       RaiseScore(level.score[SC_CRYSTAL]);
14841       break;
14842     case EL_PEARL:
14843       RaiseScore(level.score[SC_PEARL]);
14844       break;
14845     case EL_BUG:
14846     case EL_BD_BUTTERFLY:
14847     case EL_SP_ELECTRON:
14848       RaiseScore(level.score[SC_BUG]);
14849       break;
14850     case EL_SPACESHIP:
14851     case EL_BD_FIREFLY:
14852     case EL_SP_SNIKSNAK:
14853       RaiseScore(level.score[SC_SPACESHIP]);
14854       break;
14855     case EL_YAMYAM:
14856     case EL_DARK_YAMYAM:
14857       RaiseScore(level.score[SC_YAMYAM]);
14858       break;
14859     case EL_ROBOT:
14860       RaiseScore(level.score[SC_ROBOT]);
14861       break;
14862     case EL_PACMAN:
14863       RaiseScore(level.score[SC_PACMAN]);
14864       break;
14865     case EL_NUT:
14866       RaiseScore(level.score[SC_NUT]);
14867       break;
14868     case EL_DYNAMITE:
14869     case EL_EM_DYNAMITE:
14870     case EL_SP_DISK_RED:
14871     case EL_DYNABOMB_INCREASE_NUMBER:
14872     case EL_DYNABOMB_INCREASE_SIZE:
14873     case EL_DYNABOMB_INCREASE_POWER:
14874       RaiseScore(level.score[SC_DYNAMITE]);
14875       break;
14876     case EL_SHIELD_NORMAL:
14877     case EL_SHIELD_DEADLY:
14878       RaiseScore(level.score[SC_SHIELD]);
14879       break;
14880     case EL_EXTRA_TIME:
14881       RaiseScore(level.extra_time_score);
14882       break;
14883     case EL_KEY_1:
14884     case EL_KEY_2:
14885     case EL_KEY_3:
14886     case EL_KEY_4:
14887     case EL_EM_KEY_1:
14888     case EL_EM_KEY_2:
14889     case EL_EM_KEY_3:
14890     case EL_EM_KEY_4:
14891     case EL_EMC_KEY_5:
14892     case EL_EMC_KEY_6:
14893     case EL_EMC_KEY_7:
14894     case EL_EMC_KEY_8:
14895     case EL_DC_KEY_WHITE:
14896       RaiseScore(level.score[SC_KEY]);
14897       break;
14898     default:
14899       RaiseScore(element_info[element].collect_score);
14900       break;
14901   }
14902 }
14903
14904 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14905 {
14906   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14907   {
14908     /* closing door required in case of envelope style request dialogs */
14909     if (!skip_request)
14910       CloseDoor(DOOR_CLOSE_1);
14911
14912 #if defined(NETWORK_AVALIABLE)
14913     if (options.network)
14914       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14915     else
14916 #endif
14917     {
14918       if (quick_quit)
14919         FadeSkipNextFadeIn();
14920
14921       SetGameStatus(GAME_MODE_MAIN);
14922
14923       DrawMainMenu();
14924     }
14925   }
14926   else          /* continue playing the game */
14927   {
14928     if (tape.playing && tape.deactivate_display)
14929       TapeDeactivateDisplayOff(TRUE);
14930
14931     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14932
14933     if (tape.playing && tape.deactivate_display)
14934       TapeDeactivateDisplayOn();
14935   }
14936 }
14937
14938 void RequestQuitGame(boolean ask_if_really_quit)
14939 {
14940   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14941   boolean skip_request = AllPlayersGone || quick_quit;
14942
14943   RequestQuitGameExt(skip_request, quick_quit,
14944                      "Do you really want to quit the game?");
14945 }
14946
14947
14948 /* ------------------------------------------------------------------------- */
14949 /* random generator functions                                                */
14950 /* ------------------------------------------------------------------------- */
14951
14952 unsigned int InitEngineRandom_RND(int seed)
14953 {
14954   game.num_random_calls = 0;
14955
14956   return InitEngineRandom(seed);
14957 }
14958
14959 unsigned int RND(int max)
14960 {
14961   if (max > 0)
14962   {
14963     game.num_random_calls++;
14964
14965     return GetEngineRandom(max);
14966   }
14967
14968   return 0;
14969 }
14970
14971
14972 /* ------------------------------------------------------------------------- */
14973 /* game engine snapshot handling functions                                   */
14974 /* ------------------------------------------------------------------------- */
14975
14976 struct EngineSnapshotInfo
14977 {
14978   /* runtime values for custom element collect score */
14979   int collect_score[NUM_CUSTOM_ELEMENTS];
14980
14981   /* runtime values for group element choice position */
14982   int choice_pos[NUM_GROUP_ELEMENTS];
14983
14984   /* runtime values for belt position animations */
14985   int belt_graphic[4][NUM_BELT_PARTS];
14986   int belt_anim_mode[4][NUM_BELT_PARTS];
14987 };
14988
14989 static struct EngineSnapshotInfo engine_snapshot_rnd;
14990 static char *snapshot_level_identifier = NULL;
14991 static int snapshot_level_nr = -1;
14992
14993 static void SaveEngineSnapshotValues_RND()
14994 {
14995   static int belt_base_active_element[4] =
14996   {
14997     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14998     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14999     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15000     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15001   };
15002   int i, j;
15003
15004   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15005   {
15006     int element = EL_CUSTOM_START + i;
15007
15008     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15009   }
15010
15011   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15012   {
15013     int element = EL_GROUP_START + i;
15014
15015     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15016   }
15017
15018   for (i = 0; i < 4; i++)
15019   {
15020     for (j = 0; j < NUM_BELT_PARTS; j++)
15021     {
15022       int element = belt_base_active_element[i] + j;
15023       int graphic = el2img(element);
15024       int anim_mode = graphic_info[graphic].anim_mode;
15025
15026       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15027       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15028     }
15029   }
15030 }
15031
15032 static void LoadEngineSnapshotValues_RND()
15033 {
15034   unsigned int num_random_calls = game.num_random_calls;
15035   int i, j;
15036
15037   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15038   {
15039     int element = EL_CUSTOM_START + i;
15040
15041     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15042   }
15043
15044   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15045   {
15046     int element = EL_GROUP_START + i;
15047
15048     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15049   }
15050
15051   for (i = 0; i < 4; i++)
15052   {
15053     for (j = 0; j < NUM_BELT_PARTS; j++)
15054     {
15055       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15056       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15057
15058       graphic_info[graphic].anim_mode = anim_mode;
15059     }
15060   }
15061
15062   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15063   {
15064     InitRND(tape.random_seed);
15065     for (i = 0; i < num_random_calls; i++)
15066       RND(1);
15067   }
15068
15069   if (game.num_random_calls != num_random_calls)
15070   {
15071     Error(ERR_INFO, "number of random calls out of sync");
15072     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15073     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15074     Error(ERR_EXIT, "this should not happen -- please debug");
15075   }
15076 }
15077
15078 void FreeEngineSnapshotSingle()
15079 {
15080   FreeSnapshotSingle();
15081
15082   setString(&snapshot_level_identifier, NULL);
15083   snapshot_level_nr = -1;
15084 }
15085
15086 void FreeEngineSnapshotList()
15087 {
15088   FreeSnapshotList();
15089 }
15090
15091 ListNode *SaveEngineSnapshotBuffers()
15092 {
15093   ListNode *buffers = NULL;
15094
15095   /* copy some special values to a structure better suited for the snapshot */
15096
15097   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15098     SaveEngineSnapshotValues_RND();
15099   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15100     SaveEngineSnapshotValues_EM();
15101   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15102     SaveEngineSnapshotValues_SP(&buffers);
15103   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15104     SaveEngineSnapshotValues_MM(&buffers);
15105
15106   /* save values stored in special snapshot structure */
15107
15108   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15109     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15110   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15111     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15112   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15113     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15114   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15115     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15116
15117   /* save further RND engine values */
15118
15119   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15120   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15121   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15122
15123   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15124   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15125   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15126   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15127
15128   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15129   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15131   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15132   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15133
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15137
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15139
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15141
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15144
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15163
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15166
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15170
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15173
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15179
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15182
15183 #if 0
15184   ListNode *node = engine_snapshot_list_rnd;
15185   int num_bytes = 0;
15186
15187   while (node != NULL)
15188   {
15189     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15190
15191     node = node->next;
15192   }
15193
15194   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15195 #endif
15196
15197   return buffers;
15198 }
15199
15200 void SaveEngineSnapshotSingle()
15201 {
15202   ListNode *buffers = SaveEngineSnapshotBuffers();
15203
15204   /* finally save all snapshot buffers to single snapshot */
15205   SaveSnapshotSingle(buffers);
15206
15207   /* save level identification information */
15208   setString(&snapshot_level_identifier, leveldir_current->identifier);
15209   snapshot_level_nr = level_nr;
15210 }
15211
15212 boolean CheckSaveEngineSnapshotToList()
15213 {
15214   boolean save_snapshot =
15215     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15216      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15217       game.snapshot.changed_action) ||
15218      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15219       game.snapshot.collected_item));
15220
15221   game.snapshot.changed_action = FALSE;
15222   game.snapshot.collected_item = FALSE;
15223   game.snapshot.save_snapshot = save_snapshot;
15224
15225   return save_snapshot;
15226 }
15227
15228 void SaveEngineSnapshotToList()
15229 {
15230   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15231       tape.quick_resume)
15232     return;
15233
15234   ListNode *buffers = SaveEngineSnapshotBuffers();
15235
15236   /* finally save all snapshot buffers to snapshot list */
15237   SaveSnapshotToList(buffers);
15238 }
15239
15240 void SaveEngineSnapshotToListInitial()
15241 {
15242   FreeEngineSnapshotList();
15243
15244   SaveEngineSnapshotToList();
15245 }
15246
15247 void LoadEngineSnapshotValues()
15248 {
15249   /* restore special values from snapshot structure */
15250
15251   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15252     LoadEngineSnapshotValues_RND();
15253   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15254     LoadEngineSnapshotValues_EM();
15255   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15256     LoadEngineSnapshotValues_SP();
15257   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15258     LoadEngineSnapshotValues_MM();
15259 }
15260
15261 void LoadEngineSnapshotSingle()
15262 {
15263   LoadSnapshotSingle();
15264
15265   LoadEngineSnapshotValues();
15266 }
15267
15268 void LoadEngineSnapshot_Undo(int steps)
15269 {
15270   LoadSnapshotFromList_Older(steps);
15271
15272   LoadEngineSnapshotValues();
15273 }
15274
15275 void LoadEngineSnapshot_Redo(int steps)
15276 {
15277   LoadSnapshotFromList_Newer(steps);
15278
15279   LoadEngineSnapshotValues();
15280 }
15281
15282 boolean CheckEngineSnapshotSingle()
15283 {
15284   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15285           snapshot_level_nr == level_nr);
15286 }
15287
15288 boolean CheckEngineSnapshotList()
15289 {
15290   return CheckSnapshotList();
15291 }
15292
15293
15294 /* ---------- new game button stuff ---------------------------------------- */
15295
15296 static struct
15297 {
15298   int graphic;
15299   struct XY *pos;
15300   int gadget_id;
15301   char *infotext;
15302 } gamebutton_info[NUM_GAME_BUTTONS] =
15303 {
15304   {
15305     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
15306     GAME_CTRL_ID_STOP,                  "stop game"
15307   },
15308   {
15309     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
15310     GAME_CTRL_ID_PAUSE,                 "pause game"
15311   },
15312   {
15313     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
15314     GAME_CTRL_ID_PLAY,                  "play game"
15315   },
15316   {
15317     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
15318     GAME_CTRL_ID_UNDO,                  "undo step"
15319   },
15320   {
15321     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
15322     GAME_CTRL_ID_REDO,                  "redo step"
15323   },
15324   {
15325     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
15326     GAME_CTRL_ID_SAVE,                  "save game"
15327   },
15328   {
15329     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
15330     GAME_CTRL_ID_PAUSE2,                "pause game"
15331   },
15332   {
15333     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
15334     GAME_CTRL_ID_LOAD,                  "load game"
15335   },
15336   {
15337     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
15338     SOUND_CTRL_ID_MUSIC,                "background music on/off"
15339   },
15340   {
15341     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
15342     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
15343   },
15344   {
15345     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
15346     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
15347   }
15348 };
15349
15350 void CreateGameButtons()
15351 {
15352   int i;
15353
15354   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15355   {
15356     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15357     struct XY *pos = gamebutton_info[i].pos;
15358     struct GadgetInfo *gi;
15359     int button_type;
15360     boolean checked;
15361     unsigned int event_mask;
15362     int base_x = (tape.show_game_buttons ? VX : DX);
15363     int base_y = (tape.show_game_buttons ? VY : DY);
15364     int gd_x   = gfx->src_x;
15365     int gd_y   = gfx->src_y;
15366     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15367     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15368     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15369     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15370     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15371     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15372     int id = i;
15373
15374     if (gfx->bitmap == NULL)
15375     {
15376       game_gadget[id] = NULL;
15377
15378       continue;
15379     }
15380
15381     if (id == GAME_CTRL_ID_STOP ||
15382         id == GAME_CTRL_ID_PLAY ||
15383         id == GAME_CTRL_ID_SAVE ||
15384         id == GAME_CTRL_ID_LOAD)
15385     {
15386       button_type = GD_TYPE_NORMAL_BUTTON;
15387       checked = FALSE;
15388       event_mask = GD_EVENT_RELEASED;
15389     }
15390     else if (id == GAME_CTRL_ID_UNDO ||
15391              id == GAME_CTRL_ID_REDO)
15392     {
15393       button_type = GD_TYPE_NORMAL_BUTTON;
15394       checked = FALSE;
15395       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15396     }
15397     else
15398     {
15399       button_type = GD_TYPE_CHECK_BUTTON;
15400       checked =
15401         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15402          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15403          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15404       event_mask = GD_EVENT_PRESSED;
15405     }
15406
15407     gi = CreateGadget(GDI_CUSTOM_ID, id,
15408                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15409                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15410                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15411                       GDI_WIDTH, gfx->width,
15412                       GDI_HEIGHT, gfx->height,
15413                       GDI_TYPE, button_type,
15414                       GDI_STATE, GD_BUTTON_UNPRESSED,
15415                       GDI_CHECKED, checked,
15416                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15417                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15418                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15419                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15420                       GDI_DIRECT_DRAW, FALSE,
15421                       GDI_EVENT_MASK, event_mask,
15422                       GDI_CALLBACK_ACTION, HandleGameButtons,
15423                       GDI_END);
15424
15425     if (gi == NULL)
15426       Error(ERR_EXIT, "cannot create gadget");
15427
15428     game_gadget[id] = gi;
15429   }
15430 }
15431
15432 void FreeGameButtons()
15433 {
15434   int i;
15435
15436   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15437     FreeGadget(game_gadget[i]);
15438 }
15439
15440 static void UnmapGameButtonsAtSamePosition(int id)
15441 {
15442   int i;
15443
15444   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15445     if (i != id &&
15446         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15447         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15448       UnmapGadget(game_gadget[i]);
15449 }
15450
15451 static void UnmapGameButtonsAtSamePosition_All()
15452 {
15453   if (setup.show_snapshot_buttons)
15454   {
15455     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15456     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15457     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15458   }
15459   else
15460   {
15461     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15462     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15463     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15464   }
15465 }
15466
15467 static void MapGameButtonsAtSamePosition(int id)
15468 {
15469   int i;
15470
15471   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15472     if (i != id &&
15473         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15474         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15475       MapGadget(game_gadget[i]);
15476
15477   UnmapGameButtonsAtSamePosition_All();
15478 }
15479
15480 void MapUndoRedoButtons()
15481 {
15482   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15483   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15484
15485   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15486   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15487
15488   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15489 }
15490
15491 void UnmapUndoRedoButtons()
15492 {
15493   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15494   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15495
15496   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15497   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15498
15499   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15500 }
15501
15502 void MapGameButtons()
15503 {
15504   int i;
15505
15506   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15507     if (i != GAME_CTRL_ID_UNDO &&
15508         i != GAME_CTRL_ID_REDO)
15509       MapGadget(game_gadget[i]);
15510
15511   UnmapGameButtonsAtSamePosition_All();
15512
15513   RedrawGameButtons();
15514 }
15515
15516 void UnmapGameButtons()
15517 {
15518   int i;
15519
15520   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15521     UnmapGadget(game_gadget[i]);
15522 }
15523
15524 void RedrawGameButtons()
15525 {
15526   int i;
15527
15528   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15529     RedrawGadget(game_gadget[i]);
15530
15531   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15532   redraw_mask &= ~REDRAW_ALL;
15533 }
15534
15535 void GameUndoRedoExt()
15536 {
15537   ClearPlayerAction();
15538
15539   tape.pausing = TRUE;
15540
15541   RedrawPlayfield();
15542   UpdateAndDisplayGameControlValues();
15543
15544   DrawCompleteVideoDisplay();
15545   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15546   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15547   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15548
15549   BackToFront();
15550 }
15551
15552 void GameUndo(int steps)
15553 {
15554   if (!CheckEngineSnapshotList())
15555     return;
15556
15557   LoadEngineSnapshot_Undo(steps);
15558
15559   GameUndoRedoExt();
15560 }
15561
15562 void GameRedo(int steps)
15563 {
15564   if (!CheckEngineSnapshotList())
15565     return;
15566
15567   LoadEngineSnapshot_Redo(steps);
15568
15569   GameUndoRedoExt();
15570 }
15571
15572 static void HandleGameButtonsExt(int id, int button)
15573 {
15574   static boolean game_undo_executed = FALSE;
15575   int steps = BUTTON_STEPSIZE(button);
15576   boolean handle_game_buttons =
15577     (game_status == GAME_MODE_PLAYING ||
15578      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15579
15580   if (!handle_game_buttons)
15581     return;
15582
15583   switch (id)
15584   {
15585     case GAME_CTRL_ID_STOP:
15586       if (game_status == GAME_MODE_MAIN)
15587         break;
15588
15589       if (tape.playing)
15590         TapeStop();
15591       else
15592         RequestQuitGame(TRUE);
15593
15594       break;
15595
15596     case GAME_CTRL_ID_PAUSE:
15597     case GAME_CTRL_ID_PAUSE2:
15598       if (options.network && game_status == GAME_MODE_PLAYING)
15599       {
15600 #if defined(NETWORK_AVALIABLE)
15601         if (tape.pausing)
15602           SendToServer_ContinuePlaying();
15603         else
15604           SendToServer_PausePlaying();
15605 #endif
15606       }
15607       else
15608         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15609
15610       game_undo_executed = FALSE;
15611
15612       break;
15613
15614     case GAME_CTRL_ID_PLAY:
15615       if (game_status == GAME_MODE_MAIN)
15616       {
15617         StartGameActions(options.network, setup.autorecord, level.random_seed);
15618       }
15619       else if (tape.pausing)
15620       {
15621 #if defined(NETWORK_AVALIABLE)
15622         if (options.network)
15623           SendToServer_ContinuePlaying();
15624         else
15625 #endif
15626           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15627       }
15628       break;
15629
15630     case GAME_CTRL_ID_UNDO:
15631       // Important: When using "save snapshot when collecting an item" mode,
15632       // load last (current) snapshot for first "undo" after pressing "pause"
15633       // (else the last-but-one snapshot would be loaded, because the snapshot
15634       // pointer already points to the last snapshot when pressing "pause",
15635       // which is fine for "every step/move" mode, but not for "every collect")
15636       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15637           !game_undo_executed)
15638         steps--;
15639
15640       game_undo_executed = TRUE;
15641
15642       GameUndo(steps);
15643       break;
15644
15645     case GAME_CTRL_ID_REDO:
15646       GameRedo(steps);
15647       break;
15648
15649     case GAME_CTRL_ID_SAVE:
15650       TapeQuickSave();
15651       break;
15652
15653     case GAME_CTRL_ID_LOAD:
15654       TapeQuickLoad();
15655       break;
15656
15657     case SOUND_CTRL_ID_MUSIC:
15658       if (setup.sound_music)
15659       { 
15660         setup.sound_music = FALSE;
15661
15662         FadeMusic();
15663       }
15664       else if (audio.music_available)
15665       { 
15666         setup.sound = setup.sound_music = TRUE;
15667
15668         SetAudioMode(setup.sound);
15669
15670         PlayLevelMusic();
15671       }
15672       break;
15673
15674     case SOUND_CTRL_ID_LOOPS:
15675       if (setup.sound_loops)
15676         setup.sound_loops = FALSE;
15677       else if (audio.loops_available)
15678       {
15679         setup.sound = setup.sound_loops = TRUE;
15680
15681         SetAudioMode(setup.sound);
15682       }
15683       break;
15684
15685     case SOUND_CTRL_ID_SIMPLE:
15686       if (setup.sound_simple)
15687         setup.sound_simple = FALSE;
15688       else if (audio.sound_available)
15689       {
15690         setup.sound = setup.sound_simple = TRUE;
15691
15692         SetAudioMode(setup.sound);
15693       }
15694       break;
15695
15696     default:
15697       break;
15698   }
15699 }
15700
15701 static void HandleGameButtons(struct GadgetInfo *gi)
15702 {
15703   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15704 }
15705
15706 void HandleSoundButtonKeys(Key key)
15707 {
15708
15709   if (key == setup.shortcut.sound_simple)
15710     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15711   else if (key == setup.shortcut.sound_loops)
15712     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15713   else if (key == setup.shortcut.sound_music)
15714     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15715 }