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