fixed nasty bug with uninitialized game engine value
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 void InitGameControlValues()
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 void UpdatePlayfieldElementCount()
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 void UpdateGameControlValues()
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   /* used instead of "level_nr" (for network games) */
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       /* only one player in Supaplex game engine */
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   /* update game panel control frames */
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 void DisplayGameControlValues()
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   /* copy default game door content to main double buffer */
2533
2534   /* !!! CHECK AGAIN !!! */
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   /* redraw game control buttons */
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           /* use dynamic number of digits */
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          /* (used for normal state) */
2728         int font2 = pos->font_alt;      /* (used for active state) */
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           /* don't truncate output if "chars" is zero or less */
2740           size = strlen(s);
2741
2742           /* dynamically correct text alignment */
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues()
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 void UpdateGameDoorValues()
2771 {
2772   UpdateGameControlValues();
2773 }
2774
2775 void DrawGameDoorValues()
2776 {
2777   DisplayGameControlValues();
2778 }
2779
2780
2781 /*
2782   =============================================================================
2783   InitGameEngine()
2784   -----------------------------------------------------------------------------
2785   initialize game engine due to level / tape version number
2786   =============================================================================
2787 */
2788
2789 static void InitGameEngine()
2790 {
2791   int i, j, k, l, x, y;
2792
2793   /* set game engine from tape file when re-playing, else from level file */
2794   game.engine_version = (tape.playing ? tape.engine_version :
2795                          level.game_version);
2796
2797   /* set single or multi-player game mode (needed for re-playing tapes) */
2798   game.team_mode = setup.team_mode;
2799
2800   if (tape.playing)
2801   {
2802     int num_players = 0;
2803
2804     for (i = 0; i < MAX_PLAYERS; i++)
2805       if (tape.player_participates[i])
2806         num_players++;
2807
2808     /* multi-player tapes contain input data for more than one player */
2809     game.team_mode = (num_players > 1);
2810   }
2811
2812   /* ---------------------------------------------------------------------- */
2813   /* set flags for bugs and changes according to active game engine version */
2814   /* ---------------------------------------------------------------------- */
2815
2816   /*
2817     Summary of bugfix/change:
2818     Fixed handling for custom elements that change when pushed by the player.
2819
2820     Fixed/changed in version:
2821     3.1.0
2822
2823     Description:
2824     Before 3.1.0, custom elements that "change when pushing" changed directly
2825     after the player started pushing them (until then handled in "DigField()").
2826     Since 3.1.0, these custom elements are not changed until the "pushing"
2827     move of the element is finished (now handled in "ContinueMoving()").
2828
2829     Affected levels/tapes:
2830     The first condition is generally needed for all levels/tapes before version
2831     3.1.0, which might use the old behaviour before it was changed; known tapes
2832     that are affected are some tapes from the level set "Walpurgis Gardens" by
2833     Jamie Cullen.
2834     The second condition is an exception from the above case and is needed for
2835     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2836     above (including some development versions of 3.1.0), but before it was
2837     known that this change would break tapes like the above and was fixed in
2838     3.1.1, so that the changed behaviour was active although the engine version
2839     while recording maybe was before 3.1.0. There is at least one tape that is
2840     affected by this exception, which is the tape for the one-level set "Bug
2841     Machine" by Juergen Bonhagen.
2842   */
2843
2844   game.use_change_when_pushing_bug =
2845     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2846      !(tape.playing &&
2847        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2848        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2849
2850   /*
2851     Summary of bugfix/change:
2852     Fixed handling for blocking the field the player leaves when moving.
2853
2854     Fixed/changed in version:
2855     3.1.1
2856
2857     Description:
2858     Before 3.1.1, when "block last field when moving" was enabled, the field
2859     the player is leaving when moving was blocked for the time of the move,
2860     and was directly unblocked afterwards. This resulted in the last field
2861     being blocked for exactly one less than the number of frames of one player
2862     move. Additionally, even when blocking was disabled, the last field was
2863     blocked for exactly one frame.
2864     Since 3.1.1, due to changes in player movement handling, the last field
2865     is not blocked at all when blocking is disabled. When blocking is enabled,
2866     the last field is blocked for exactly the number of frames of one player
2867     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2868     last field is blocked for exactly one more than the number of frames of
2869     one player move.
2870
2871     Affected levels/tapes:
2872     (!!! yet to be determined -- probably many !!!)
2873   */
2874
2875   game.use_block_last_field_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,1,0));
2877
2878   game_em.use_single_button =
2879     (game.engine_version > VERSION_IDENT(4,0,0,2));
2880
2881   game_em.use_snap_key_bug =
2882     (game.engine_version < VERSION_IDENT(4,0,1,0));
2883
2884   /* ---------------------------------------------------------------------- */
2885
2886   /* set maximal allowed number of custom element changes per game frame */
2887   game.max_num_changes_per_frame = 1;
2888
2889   /* default scan direction: scan playfield from top/left to bottom/right */
2890   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2891
2892   /* dynamically adjust element properties according to game engine version */
2893   InitElementPropertiesEngine(game.engine_version);
2894
2895 #if 0
2896   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2897   printf("          tape version == %06d [%s] [file: %06d]\n",
2898          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2899          tape.file_version);
2900   printf("       => game.engine_version == %06d\n", game.engine_version);
2901 #endif
2902
2903   /* ---------- initialize player's initial move delay --------------------- */
2904
2905   /* dynamically adjust player properties according to level information */
2906   for (i = 0; i < MAX_PLAYERS; i++)
2907     game.initial_move_delay_value[i] =
2908       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2909
2910   /* dynamically adjust player properties according to game engine version */
2911   for (i = 0; i < MAX_PLAYERS; i++)
2912     game.initial_move_delay[i] =
2913       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2914        game.initial_move_delay_value[i] : 0);
2915
2916   /* ---------- initialize player's initial push delay --------------------- */
2917
2918   /* dynamically adjust player properties according to game engine version */
2919   game.initial_push_delay_value =
2920     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2921
2922   /* ---------- initialize changing elements ------------------------------- */
2923
2924   /* initialize changing elements information */
2925   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2926   {
2927     struct ElementInfo *ei = &element_info[i];
2928
2929     /* this pointer might have been changed in the level editor */
2930     ei->change = &ei->change_page[0];
2931
2932     if (!IS_CUSTOM_ELEMENT(i))
2933     {
2934       ei->change->target_element = EL_EMPTY_SPACE;
2935       ei->change->delay_fixed = 0;
2936       ei->change->delay_random = 0;
2937       ei->change->delay_frames = 1;
2938     }
2939
2940     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2941     {
2942       ei->has_change_event[j] = FALSE;
2943
2944       ei->event_page_nr[j] = 0;
2945       ei->event_page[j] = &ei->change_page[0];
2946     }
2947   }
2948
2949   /* add changing elements from pre-defined list */
2950   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2951   {
2952     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2953     struct ElementInfo *ei = &element_info[ch_delay->element];
2954
2955     ei->change->target_element       = ch_delay->target_element;
2956     ei->change->delay_fixed          = ch_delay->change_delay;
2957
2958     ei->change->pre_change_function  = ch_delay->pre_change_function;
2959     ei->change->change_function      = ch_delay->change_function;
2960     ei->change->post_change_function = ch_delay->post_change_function;
2961
2962     ei->change->can_change = TRUE;
2963     ei->change->can_change_or_has_action = TRUE;
2964
2965     ei->has_change_event[CE_DELAY] = TRUE;
2966
2967     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2968     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969   }
2970
2971   /* ---------- initialize internal run-time variables --------------------- */
2972
2973   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974   {
2975     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976
2977     for (j = 0; j < ei->num_change_pages; j++)
2978     {
2979       ei->change_page[j].can_change_or_has_action =
2980         (ei->change_page[j].can_change |
2981          ei->change_page[j].has_action);
2982     }
2983   }
2984
2985   /* add change events from custom element configuration */
2986   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2987   {
2988     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2989
2990     for (j = 0; j < ei->num_change_pages; j++)
2991     {
2992       if (!ei->change_page[j].can_change_or_has_action)
2993         continue;
2994
2995       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2996       {
2997         /* only add event page for the first page found with this event */
2998         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2999         {
3000           ei->has_change_event[k] = TRUE;
3001
3002           ei->event_page_nr[k] = j;
3003           ei->event_page[k] = &ei->change_page[j];
3004         }
3005       }
3006     }
3007   }
3008
3009   /* ---------- initialize reference elements in change conditions --------- */
3010
3011   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3012   {
3013     int element = EL_CUSTOM_START + i;
3014     struct ElementInfo *ei = &element_info[element];
3015
3016     for (j = 0; j < ei->num_change_pages; j++)
3017     {
3018       int trigger_element = ei->change_page[j].initial_trigger_element;
3019
3020       if (trigger_element >= EL_PREV_CE_8 &&
3021           trigger_element <= EL_NEXT_CE_8)
3022         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3023
3024       ei->change_page[j].trigger_element = trigger_element;
3025     }
3026   }
3027
3028   /* ---------- initialize run-time trigger player and element ------------- */
3029
3030   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3031   {
3032     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3033
3034     for (j = 0; j < ei->num_change_pages; j++)
3035     {
3036       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3037       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3038       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3039       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3040       ei->change_page[j].actual_trigger_ce_value = 0;
3041       ei->change_page[j].actual_trigger_ce_score = 0;
3042     }
3043   }
3044
3045   /* ---------- initialize trigger events ---------------------------------- */
3046
3047   /* initialize trigger events information */
3048   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3049     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3050       trigger_events[i][j] = FALSE;
3051
3052   /* add trigger events from element change event properties */
3053   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054   {
3055     struct ElementInfo *ei = &element_info[i];
3056
3057     for (j = 0; j < ei->num_change_pages; j++)
3058     {
3059       if (!ei->change_page[j].can_change_or_has_action)
3060         continue;
3061
3062       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3063       {
3064         int trigger_element = ei->change_page[j].trigger_element;
3065
3066         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3067         {
3068           if (ei->change_page[j].has_event[k])
3069           {
3070             if (IS_GROUP_ELEMENT(trigger_element))
3071             {
3072               struct ElementGroupInfo *group =
3073                 element_info[trigger_element].group;
3074
3075               for (l = 0; l < group->num_elements_resolved; l++)
3076                 trigger_events[group->element_resolved[l]][k] = TRUE;
3077             }
3078             else if (trigger_element == EL_ANY_ELEMENT)
3079               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3080                 trigger_events[l][k] = TRUE;
3081             else
3082               trigger_events[trigger_element][k] = TRUE;
3083           }
3084         }
3085       }
3086     }
3087   }
3088
3089   /* ---------- initialize push delay -------------------------------------- */
3090
3091   /* initialize push delay values to default */
3092   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3093   {
3094     if (!IS_CUSTOM_ELEMENT(i))
3095     {
3096       /* set default push delay values (corrected since version 3.0.7-1) */
3097       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3098       {
3099         element_info[i].push_delay_fixed = 2;
3100         element_info[i].push_delay_random = 8;
3101       }
3102       else
3103       {
3104         element_info[i].push_delay_fixed = 8;
3105         element_info[i].push_delay_random = 8;
3106       }
3107     }
3108   }
3109
3110   /* set push delay value for certain elements from pre-defined list */
3111   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3112   {
3113     int e = push_delay_list[i].element;
3114
3115     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3116     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117   }
3118
3119   /* set push delay value for Supaplex elements for newer engine versions */
3120   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3121   {
3122     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123     {
3124       if (IS_SP_ELEMENT(i))
3125       {
3126         /* set SP push delay to just enough to push under a falling zonk */
3127         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3128
3129         element_info[i].push_delay_fixed  = delay;
3130         element_info[i].push_delay_random = 0;
3131       }
3132     }
3133   }
3134
3135   /* ---------- initialize move stepsize ----------------------------------- */
3136
3137   /* initialize move stepsize values to default */
3138   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3139     if (!IS_CUSTOM_ELEMENT(i))
3140       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3141
3142   /* set move stepsize value for certain elements from pre-defined list */
3143   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3144   {
3145     int e = move_stepsize_list[i].element;
3146
3147     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148   }
3149
3150   /* ---------- initialize collect score ----------------------------------- */
3151
3152   /* initialize collect score values for custom elements from initial value */
3153   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3154     if (IS_CUSTOM_ELEMENT(i))
3155       element_info[i].collect_score = element_info[i].collect_score_initial;
3156
3157   /* ---------- initialize collect count ----------------------------------- */
3158
3159   /* initialize collect count values for non-custom elements */
3160   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3161     if (!IS_CUSTOM_ELEMENT(i))
3162       element_info[i].collect_count_initial = 0;
3163
3164   /* add collect count values for all elements from pre-defined list */
3165   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3166     element_info[collect_count_list[i].element].collect_count_initial =
3167       collect_count_list[i].count;
3168
3169   /* ---------- initialize access direction -------------------------------- */
3170
3171   /* initialize access direction values to default (access from every side) */
3172   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3173     if (!IS_CUSTOM_ELEMENT(i))
3174       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3175
3176   /* set access direction value for certain elements from pre-defined list */
3177   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3178     element_info[access_direction_list[i].element].access_direction =
3179       access_direction_list[i].direction;
3180
3181   /* ---------- initialize explosion content ------------------------------- */
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183   {
3184     if (IS_CUSTOM_ELEMENT(i))
3185       continue;
3186
3187     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3188     {
3189       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3190
3191       element_info[i].content.e[x][y] =
3192         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3193          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3194          i == EL_PLAYER_3 ? EL_EMERALD :
3195          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3196          i == EL_MOLE ? EL_EMERALD_RED :
3197          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3198          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3199          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3200          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3201          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3202          i == EL_WALL_EMERALD ? EL_EMERALD :
3203          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3204          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3205          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3206          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3207          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3208          i == EL_WALL_PEARL ? EL_PEARL :
3209          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3210          EL_EMPTY);
3211     }
3212   }
3213
3214   /* ---------- initialize recursion detection ------------------------------ */
3215   recursion_loop_depth = 0;
3216   recursion_loop_detected = FALSE;
3217   recursion_loop_element = EL_UNDEFINED;
3218
3219   /* ---------- initialize graphics engine ---------------------------------- */
3220   game.scroll_delay_value =
3221     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3222      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3223   game.scroll_delay_value =
3224     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3225
3226   /* ---------- initialize game engine snapshots ---------------------------- */
3227   for (i = 0; i < MAX_PLAYERS; i++)
3228     game.snapshot.last_action[i] = 0;
3229   game.snapshot.changed_action = FALSE;
3230   game.snapshot.collected_item = FALSE;
3231   game.snapshot.mode =
3232     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3233      SNAPSHOT_MODE_EVERY_STEP :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3235      SNAPSHOT_MODE_EVERY_MOVE :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3237      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3238   game.snapshot.save_snapshot = FALSE;
3239
3240   /* ---------- initialize level time for Supaplex engine ------------------- */
3241   /* Supaplex levels with time limit currently unsupported -- should be added */
3242   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3243     level.time = 0;
3244 }
3245
3246 int get_num_special_action(int element, int action_first, int action_last)
3247 {
3248   int num_special_action = 0;
3249   int i, j;
3250
3251   for (i = action_first; i <= action_last; i++)
3252   {
3253     boolean found = FALSE;
3254
3255     for (j = 0; j < NUM_DIRECTIONS; j++)
3256       if (el_act_dir2img(element, i, j) !=
3257           el_act_dir2img(element, ACTION_DEFAULT, j))
3258         found = TRUE;
3259
3260     if (found)
3261       num_special_action++;
3262     else
3263       break;
3264   }
3265
3266   return num_special_action;
3267 }
3268
3269
3270 /*
3271   =============================================================================
3272   InitGame()
3273   -----------------------------------------------------------------------------
3274   initialize and start new game
3275   =============================================================================
3276 */
3277
3278 #if DEBUG_INIT_PLAYER
3279 static void DebugPrintPlayerStatus(char *message)
3280 {
3281   int i;
3282
3283   if (!options.debug)
3284     return;
3285
3286   printf("%s:\n", message);
3287
3288   for (i = 0; i < MAX_PLAYERS; i++)
3289   {
3290     struct PlayerInfo *player = &stored_player[i];
3291
3292     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3293            i + 1,
3294            player->present,
3295            player->connected,
3296            player->connected_locally,
3297            player->connected_network,
3298            player->active);
3299
3300     if (local_player == player)
3301       printf(" (local player)");
3302
3303     printf("\n");
3304   }
3305 }
3306 #endif
3307
3308 void InitGame()
3309 {
3310   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3312   int fade_mask = REDRAW_FIELD;
3313
3314   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3315   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3316   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3317   int initial_move_dir = MV_DOWN;
3318   int i, j, x, y;
3319
3320   // required here to update video display before fading (FIX THIS)
3321   DrawMaskedBorder(REDRAW_DOOR_2);
3322
3323   if (!game.restart_level)
3324     CloseDoor(DOOR_CLOSE_1);
3325
3326   SetGameStatus(GAME_MODE_PLAYING);
3327
3328   if (level_editor_test_game)
3329     FadeSkipNextFadeIn();
3330   else
3331     FadeSetEnterScreen();
3332
3333   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3334     fade_mask = REDRAW_ALL;
3335
3336   FadeLevelSoundsAndMusic();
3337
3338   ExpireSoundLoops(TRUE);
3339
3340   FadeOut(fade_mask);
3341
3342   /* needed if different viewport properties defined for playing */
3343   ChangeViewportPropertiesIfNeeded();
3344
3345   ClearField();
3346
3347   DrawCompleteVideoDisplay();
3348
3349   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3350
3351   InitGameEngine();
3352   InitGameControlValues();
3353
3354   /* don't play tapes over network */
3355   network_playing = (network.enabled && !tape.playing);
3356
3357   for (i = 0; i < MAX_PLAYERS; i++)
3358   {
3359     struct PlayerInfo *player = &stored_player[i];
3360
3361     player->index_nr = i;
3362     player->index_bit = (1 << i);
3363     player->element_nr = EL_PLAYER_1 + i;
3364
3365     player->present = FALSE;
3366     player->active = FALSE;
3367     player->mapped = FALSE;
3368
3369     player->killed = FALSE;
3370     player->reanimated = FALSE;
3371
3372     player->action = 0;
3373     player->effective_action = 0;
3374     player->programmed_action = 0;
3375
3376     player->mouse_action.lx = 0;
3377     player->mouse_action.ly = 0;
3378     player->mouse_action.button = 0;
3379     player->mouse_action.button_hint = 0;
3380
3381     player->effective_mouse_action.lx = 0;
3382     player->effective_mouse_action.ly = 0;
3383     player->effective_mouse_action.button = 0;
3384     player->effective_mouse_action.button_hint = 0;
3385
3386     player->score = 0;
3387     player->score_final = 0;
3388
3389     player->health = MAX_HEALTH;
3390     player->health_final = MAX_HEALTH;
3391
3392     player->gems_still_needed = level.gems_needed;
3393     player->sokobanfields_still_needed = 0;
3394     player->lights_still_needed = 0;
3395     player->players_still_needed = 0;
3396     player->friends_still_needed = 0;
3397
3398     for (j = 0; j < MAX_NUM_KEYS; j++)
3399       player->key[j] = FALSE;
3400
3401     player->num_white_keys = 0;
3402
3403     player->dynabomb_count = 0;
3404     player->dynabomb_size = 1;
3405     player->dynabombs_left = 0;
3406     player->dynabomb_xl = FALSE;
3407
3408     player->MovDir = initial_move_dir;
3409     player->MovPos = 0;
3410     player->GfxPos = 0;
3411     player->GfxDir = initial_move_dir;
3412     player->GfxAction = ACTION_DEFAULT;
3413     player->Frame = 0;
3414     player->StepFrame = 0;
3415
3416     player->initial_element = player->element_nr;
3417     player->artwork_element =
3418       (level.use_artwork_element[i] ? level.artwork_element[i] :
3419        player->element_nr);
3420     player->use_murphy = FALSE;
3421
3422     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3423     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3424
3425     player->gravity = level.initial_player_gravity[i];
3426
3427     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3428
3429     player->actual_frame_counter = 0;
3430
3431     player->step_counter = 0;
3432
3433     player->last_move_dir = initial_move_dir;
3434
3435     player->is_active = FALSE;
3436
3437     player->is_waiting = FALSE;
3438     player->is_moving = FALSE;
3439     player->is_auto_moving = FALSE;
3440     player->is_digging = FALSE;
3441     player->is_snapping = FALSE;
3442     player->is_collecting = FALSE;
3443     player->is_pushing = FALSE;
3444     player->is_switching = FALSE;
3445     player->is_dropping = FALSE;
3446     player->is_dropping_pressed = FALSE;
3447
3448     player->is_bored = FALSE;
3449     player->is_sleeping = FALSE;
3450
3451     player->was_waiting = TRUE;
3452     player->was_moving = FALSE;
3453     player->was_snapping = FALSE;
3454     player->was_dropping = FALSE;
3455
3456     player->force_dropping = FALSE;
3457
3458     player->frame_counter_bored = -1;
3459     player->frame_counter_sleeping = -1;
3460
3461     player->anim_delay_counter = 0;
3462     player->post_delay_counter = 0;
3463
3464     player->dir_waiting = initial_move_dir;
3465     player->action_waiting = ACTION_DEFAULT;
3466     player->last_action_waiting = ACTION_DEFAULT;
3467     player->special_action_bored = ACTION_DEFAULT;
3468     player->special_action_sleeping = ACTION_DEFAULT;
3469
3470     player->switch_x = -1;
3471     player->switch_y = -1;
3472
3473     player->drop_x = -1;
3474     player->drop_y = -1;
3475
3476     player->show_envelope = 0;
3477
3478     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3479
3480     player->push_delay       = -1;      /* initialized when pushing starts */
3481     player->push_delay_value = game.initial_push_delay_value;
3482
3483     player->drop_delay = 0;
3484     player->drop_pressed_delay = 0;
3485
3486     player->last_jx = -1;
3487     player->last_jy = -1;
3488     player->jx = -1;
3489     player->jy = -1;
3490
3491     player->shield_normal_time_left = 0;
3492     player->shield_deadly_time_left = 0;
3493
3494     player->inventory_infinite_element = EL_UNDEFINED;
3495     player->inventory_size = 0;
3496
3497     if (level.use_initial_inventory[i])
3498     {
3499       for (j = 0; j < level.initial_inventory_size[i]; j++)
3500       {
3501         int element = level.initial_inventory_content[i][j];
3502         int collect_count = element_info[element].collect_count_initial;
3503         int k;
3504
3505         if (!IS_CUSTOM_ELEMENT(element))
3506           collect_count = 1;
3507
3508         if (collect_count == 0)
3509           player->inventory_infinite_element = element;
3510         else
3511           for (k = 0; k < collect_count; k++)
3512             if (player->inventory_size < MAX_INVENTORY_SIZE)
3513               player->inventory_element[player->inventory_size++] = element;
3514       }
3515     }
3516
3517     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3518     SnapField(player, 0, 0);
3519
3520     player->LevelSolved = FALSE;
3521     player->GameOver = FALSE;
3522
3523     player->LevelSolved_GameWon = FALSE;
3524     player->LevelSolved_GameEnd = FALSE;
3525     player->LevelSolved_PanelOff = FALSE;
3526     player->LevelSolved_SaveTape = FALSE;
3527     player->LevelSolved_SaveScore = FALSE;
3528
3529     player->LevelSolved_CountingTime = 0;
3530     player->LevelSolved_CountingScore = 0;
3531     player->LevelSolved_CountingHealth = 0;
3532
3533     map_player_action[i] = i;
3534   }
3535
3536   network_player_action_received = FALSE;
3537
3538   /* initial null action */
3539   if (network_playing)
3540     SendToServer_MovePlayer(MV_NONE);
3541
3542   ZX = ZY = -1;
3543   ExitX = ExitY = -1;
3544
3545   FrameCounter = 0;
3546   TimeFrames = 0;
3547   TimePlayed = 0;
3548   TimeLeft = level.time;
3549   TapeTime = 0;
3550
3551   ScreenMovDir = MV_NONE;
3552   ScreenMovPos = 0;
3553   ScreenGfxPos = 0;
3554
3555   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3556
3557   AllPlayersGone = FALSE;
3558
3559   game.no_time_limit = (level.time == 0);
3560
3561   game.yamyam_content_nr = 0;
3562   game.robot_wheel_active = FALSE;
3563   game.magic_wall_active = FALSE;
3564   game.magic_wall_time_left = 0;
3565   game.light_time_left = 0;
3566   game.timegate_time_left = 0;
3567   game.switchgate_pos = 0;
3568   game.wind_direction = level.wind_direction_initial;
3569
3570   game.lenses_time_left = 0;
3571   game.magnify_time_left = 0;
3572
3573   game.ball_state = level.ball_state_initial;
3574   game.ball_content_nr = 0;
3575
3576   game.explosions_delayed = TRUE;
3577
3578   game.envelope_active = FALSE;
3579
3580   for (i = 0; i < NUM_BELTS; i++)
3581   {
3582     game.belt_dir[i] = MV_NONE;
3583     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3584   }
3585
3586   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3587     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3588
3589 #if DEBUG_INIT_PLAYER
3590   DebugPrintPlayerStatus("Player status at level initialization");
3591 #endif
3592
3593   SCAN_PLAYFIELD(x, y)
3594   {
3595     Feld[x][y] = level.field[x][y];
3596     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3597     ChangeDelay[x][y] = 0;
3598     ChangePage[x][y] = -1;
3599     CustomValue[x][y] = 0;              /* initialized in InitField() */
3600     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3601     AmoebaNr[x][y] = 0;
3602     WasJustMoving[x][y] = 0;
3603     WasJustFalling[x][y] = 0;
3604     CheckCollision[x][y] = 0;
3605     CheckImpact[x][y] = 0;
3606     Stop[x][y] = FALSE;
3607     Pushed[x][y] = FALSE;
3608
3609     ChangeCount[x][y] = 0;
3610     ChangeEvent[x][y] = -1;
3611
3612     ExplodePhase[x][y] = 0;
3613     ExplodeDelay[x][y] = 0;
3614     ExplodeField[x][y] = EX_TYPE_NONE;
3615
3616     RunnerVisit[x][y] = 0;
3617     PlayerVisit[x][y] = 0;
3618
3619     GfxFrame[x][y] = 0;
3620     GfxRandom[x][y] = INIT_GFX_RANDOM();
3621     GfxElement[x][y] = EL_UNDEFINED;
3622     GfxAction[x][y] = ACTION_DEFAULT;
3623     GfxDir[x][y] = MV_NONE;
3624     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3625   }
3626
3627   SCAN_PLAYFIELD(x, y)
3628   {
3629     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3630       emulate_bd = FALSE;
3631     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3632       emulate_sb = FALSE;
3633     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3634       emulate_sp = FALSE;
3635
3636     InitField(x, y, TRUE);
3637
3638     ResetGfxAnimation(x, y);
3639   }
3640
3641   InitBeltMovement();
3642
3643   for (i = 0; i < MAX_PLAYERS; i++)
3644   {
3645     struct PlayerInfo *player = &stored_player[i];
3646
3647     /* set number of special actions for bored and sleeping animation */
3648     player->num_special_action_bored =
3649       get_num_special_action(player->artwork_element,
3650                              ACTION_BORING_1, ACTION_BORING_LAST);
3651     player->num_special_action_sleeping =
3652       get_num_special_action(player->artwork_element,
3653                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3654   }
3655
3656   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3657                     emulate_sb ? EMU_SOKOBAN :
3658                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3659
3660   /* initialize type of slippery elements */
3661   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3662   {
3663     if (!IS_CUSTOM_ELEMENT(i))
3664     {
3665       /* default: elements slip down either to the left or right randomly */
3666       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3667
3668       /* SP style elements prefer to slip down on the left side */
3669       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3670         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3671
3672       /* BD style elements prefer to slip down on the left side */
3673       if (game.emulation == EMU_BOULDERDASH)
3674         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3675     }
3676   }
3677
3678   /* initialize explosion and ignition delay */
3679   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3680   {
3681     if (!IS_CUSTOM_ELEMENT(i))
3682     {
3683       int num_phase = 8;
3684       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3685                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3686                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3687       int last_phase = (num_phase + 1) * delay;
3688       int half_phase = (num_phase / 2) * delay;
3689
3690       element_info[i].explosion_delay = last_phase - 1;
3691       element_info[i].ignition_delay = half_phase;
3692
3693       if (i == EL_BLACK_ORB)
3694         element_info[i].ignition_delay = 1;
3695     }
3696   }
3697
3698   /* correct non-moving belts to start moving left */
3699   for (i = 0; i < NUM_BELTS; i++)
3700     if (game.belt_dir[i] == MV_NONE)
3701       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3702
3703 #if USE_NEW_PLAYER_ASSIGNMENTS
3704   for (i = 0; i < MAX_PLAYERS; i++)
3705   {
3706     stored_player[i].connected = FALSE;
3707
3708     /* in network game mode, the local player might not be the first player */
3709     if (stored_player[i].connected_locally)
3710       local_player = &stored_player[i];
3711   }
3712
3713   if (!network.enabled)
3714     local_player->connected = TRUE;
3715
3716   if (tape.playing)
3717   {
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719       stored_player[i].connected = tape.player_participates[i];
3720   }
3721   else if (network.enabled)
3722   {
3723     /* add team mode players connected over the network (needed for correct
3724        assignment of player figures from level to locally playing players) */
3725
3726     for (i = 0; i < MAX_PLAYERS; i++)
3727       if (stored_player[i].connected_network)
3728         stored_player[i].connected = TRUE;
3729   }
3730   else if (game.team_mode)
3731   {
3732     /* try to guess locally connected team mode players (needed for correct
3733        assignment of player figures from level to locally playing players) */
3734
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736       if (setup.input[i].use_joystick ||
3737           setup.input[i].key.left != KSYM_UNDEFINED)
3738         stored_player[i].connected = TRUE;
3739   }
3740
3741 #if DEBUG_INIT_PLAYER
3742   DebugPrintPlayerStatus("Player status after level initialization");
3743 #endif
3744
3745 #if DEBUG_INIT_PLAYER
3746   if (options.debug)
3747     printf("Reassigning players ...\n");
3748 #endif
3749
3750   /* check if any connected player was not found in playfield */
3751   for (i = 0; i < MAX_PLAYERS; i++)
3752   {
3753     struct PlayerInfo *player = &stored_player[i];
3754
3755     if (player->connected && !player->present)
3756     {
3757       struct PlayerInfo *field_player = NULL;
3758
3759 #if DEBUG_INIT_PLAYER
3760       if (options.debug)
3761         printf("- looking for field player for player %d ...\n", i + 1);
3762 #endif
3763
3764       /* assign first free player found that is present in the playfield */
3765
3766       /* first try: look for unmapped playfield player that is not connected */
3767       for (j = 0; j < MAX_PLAYERS; j++)
3768         if (field_player == NULL &&
3769             stored_player[j].present &&
3770             !stored_player[j].mapped &&
3771             !stored_player[j].connected)
3772           field_player = &stored_player[j];
3773
3774       /* second try: look for *any* unmapped playfield player */
3775       for (j = 0; j < MAX_PLAYERS; j++)
3776         if (field_player == NULL &&
3777             stored_player[j].present &&
3778             !stored_player[j].mapped)
3779           field_player = &stored_player[j];
3780
3781       if (field_player != NULL)
3782       {
3783         int jx = field_player->jx, jy = field_player->jy;
3784
3785 #if DEBUG_INIT_PLAYER
3786         if (options.debug)
3787           printf("- found player %d\n", field_player->index_nr + 1);
3788 #endif
3789
3790         player->present = FALSE;
3791         player->active = FALSE;
3792
3793         field_player->present = TRUE;
3794         field_player->active = TRUE;
3795
3796         /*
3797         player->initial_element = field_player->initial_element;
3798         player->artwork_element = field_player->artwork_element;
3799
3800         player->block_last_field       = field_player->block_last_field;
3801         player->block_delay_adjustment = field_player->block_delay_adjustment;
3802         */
3803
3804         StorePlayer[jx][jy] = field_player->element_nr;
3805
3806         field_player->jx = field_player->last_jx = jx;
3807         field_player->jy = field_player->last_jy = jy;
3808
3809         if (local_player == player)
3810           local_player = field_player;
3811
3812         map_player_action[field_player->index_nr] = i;
3813
3814         field_player->mapped = TRUE;
3815
3816 #if DEBUG_INIT_PLAYER
3817         if (options.debug)
3818           printf("- map_player_action[%d] == %d\n",
3819                  field_player->index_nr + 1, i + 1);
3820 #endif
3821       }
3822     }
3823
3824     if (player->connected && player->present)
3825       player->mapped = TRUE;
3826   }
3827
3828 #if DEBUG_INIT_PLAYER
3829   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3830 #endif
3831
3832 #else
3833
3834   /* check if any connected player was not found in playfield */
3835   for (i = 0; i < MAX_PLAYERS; i++)
3836   {
3837     struct PlayerInfo *player = &stored_player[i];
3838
3839     if (player->connected && !player->present)
3840     {
3841       for (j = 0; j < MAX_PLAYERS; j++)
3842       {
3843         struct PlayerInfo *field_player = &stored_player[j];
3844         int jx = field_player->jx, jy = field_player->jy;
3845
3846         /* assign first free player found that is present in the playfield */
3847         if (field_player->present && !field_player->connected)
3848         {
3849           player->present = TRUE;
3850           player->active = TRUE;
3851
3852           field_player->present = FALSE;
3853           field_player->active = FALSE;
3854
3855           player->initial_element = field_player->initial_element;
3856           player->artwork_element = field_player->artwork_element;
3857
3858           player->block_last_field       = field_player->block_last_field;
3859           player->block_delay_adjustment = field_player->block_delay_adjustment;
3860
3861           StorePlayer[jx][jy] = player->element_nr;
3862
3863           player->jx = player->last_jx = jx;
3864           player->jy = player->last_jy = jy;
3865
3866           break;
3867         }
3868       }
3869     }
3870   }
3871 #endif
3872
3873 #if 0
3874   printf("::: local_player->present == %d\n", local_player->present);
3875 #endif
3876
3877   /* set focus to local player for network games, else to all players */
3878   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3879   game.centered_player_nr_next = game.centered_player_nr;
3880   game.set_centered_player = FALSE;
3881
3882   if (network_playing && tape.recording)
3883   {
3884     /* store client dependent player focus when recording network games */
3885     tape.centered_player_nr_next = game.centered_player_nr_next;
3886     tape.set_centered_player = TRUE;
3887   }
3888
3889   if (tape.playing)
3890   {
3891     /* when playing a tape, eliminate all players who do not participate */
3892
3893 #if USE_NEW_PLAYER_ASSIGNMENTS
3894
3895     if (!game.team_mode)
3896     {
3897       for (i = 0; i < MAX_PLAYERS; i++)
3898       {
3899         if (stored_player[i].active &&
3900             !tape.player_participates[map_player_action[i]])
3901         {
3902           struct PlayerInfo *player = &stored_player[i];
3903           int jx = player->jx, jy = player->jy;
3904
3905 #if DEBUG_INIT_PLAYER
3906           if (options.debug)
3907             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3908 #endif
3909
3910           player->active = FALSE;
3911           StorePlayer[jx][jy] = 0;
3912           Feld[jx][jy] = EL_EMPTY;
3913         }
3914       }
3915     }
3916
3917 #else
3918
3919     for (i = 0; i < MAX_PLAYERS; i++)
3920     {
3921       if (stored_player[i].active &&
3922           !tape.player_participates[i])
3923       {
3924         struct PlayerInfo *player = &stored_player[i];
3925         int jx = player->jx, jy = player->jy;
3926
3927         player->active = FALSE;
3928         StorePlayer[jx][jy] = 0;
3929         Feld[jx][jy] = EL_EMPTY;
3930       }
3931     }
3932 #endif
3933   }
3934   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3935   {
3936     /* when in single player mode, eliminate all but the local player */
3937
3938     for (i = 0; i < MAX_PLAYERS; i++)
3939     {
3940       struct PlayerInfo *player = &stored_player[i];
3941
3942       if (player->active && player != local_player)
3943       {
3944         int jx = player->jx, jy = player->jy;
3945
3946         player->active = FALSE;
3947         player->present = FALSE;
3948
3949         StorePlayer[jx][jy] = 0;
3950         Feld[jx][jy] = EL_EMPTY;
3951       }
3952     }
3953   }
3954
3955   for (i = 0; i < MAX_PLAYERS; i++)
3956     if (stored_player[i].active)
3957       local_player->players_still_needed++;
3958
3959   /* when recording the game, store which players take part in the game */
3960   if (tape.recording)
3961   {
3962 #if USE_NEW_PLAYER_ASSIGNMENTS
3963     for (i = 0; i < MAX_PLAYERS; i++)
3964       if (stored_player[i].connected)
3965         tape.player_participates[i] = TRUE;
3966 #else
3967     for (i = 0; i < MAX_PLAYERS; i++)
3968       if (stored_player[i].active)
3969         tape.player_participates[i] = TRUE;
3970 #endif
3971   }
3972
3973 #if DEBUG_INIT_PLAYER
3974   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3975 #endif
3976
3977   if (BorderElement == EL_EMPTY)
3978   {
3979     SBX_Left = 0;
3980     SBX_Right = lev_fieldx - SCR_FIELDX;
3981     SBY_Upper = 0;
3982     SBY_Lower = lev_fieldy - SCR_FIELDY;
3983   }
3984   else
3985   {
3986     SBX_Left = -1;
3987     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3988     SBY_Upper = -1;
3989     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3990   }
3991
3992   if (full_lev_fieldx <= SCR_FIELDX)
3993     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3994   if (full_lev_fieldy <= SCR_FIELDY)
3995     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3996
3997   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3998     SBX_Left--;
3999   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4000     SBY_Upper--;
4001
4002   /* if local player not found, look for custom element that might create
4003      the player (make some assumptions about the right custom element) */
4004   if (!local_player->present)
4005   {
4006     int start_x = 0, start_y = 0;
4007     int found_rating = 0;
4008     int found_element = EL_UNDEFINED;
4009     int player_nr = local_player->index_nr;
4010
4011     SCAN_PLAYFIELD(x, y)
4012     {
4013       int element = Feld[x][y];
4014       int content;
4015       int xx, yy;
4016       boolean is_player;
4017
4018       if (level.use_start_element[player_nr] &&
4019           level.start_element[player_nr] == element &&
4020           found_rating < 4)
4021       {
4022         start_x = x;
4023         start_y = y;
4024
4025         found_rating = 4;
4026         found_element = element;
4027       }
4028
4029       if (!IS_CUSTOM_ELEMENT(element))
4030         continue;
4031
4032       if (CAN_CHANGE(element))
4033       {
4034         for (i = 0; i < element_info[element].num_change_pages; i++)
4035         {
4036           /* check for player created from custom element as single target */
4037           content = element_info[element].change_page[i].target_element;
4038           is_player = ELEM_IS_PLAYER(content);
4039
4040           if (is_player && (found_rating < 3 ||
4041                             (found_rating == 3 && element < found_element)))
4042           {
4043             start_x = x;
4044             start_y = y;
4045
4046             found_rating = 3;
4047             found_element = element;
4048           }
4049         }
4050       }
4051
4052       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4053       {
4054         /* check for player created from custom element as explosion content */
4055         content = element_info[element].content.e[xx][yy];
4056         is_player = ELEM_IS_PLAYER(content);
4057
4058         if (is_player && (found_rating < 2 ||
4059                           (found_rating == 2 && element < found_element)))
4060         {
4061           start_x = x + xx - 1;
4062           start_y = y + yy - 1;
4063
4064           found_rating = 2;
4065           found_element = element;
4066         }
4067
4068         if (!CAN_CHANGE(element))
4069           continue;
4070
4071         for (i = 0; i < element_info[element].num_change_pages; i++)
4072         {
4073           /* check for player created from custom element as extended target */
4074           content =
4075             element_info[element].change_page[i].target_content.e[xx][yy];
4076
4077           is_player = ELEM_IS_PLAYER(content);
4078
4079           if (is_player && (found_rating < 1 ||
4080                             (found_rating == 1 && element < found_element)))
4081           {
4082             start_x = x + xx - 1;
4083             start_y = y + yy - 1;
4084
4085             found_rating = 1;
4086             found_element = element;
4087           }
4088         }
4089       }
4090     }
4091
4092     scroll_x = SCROLL_POSITION_X(start_x);
4093     scroll_y = SCROLL_POSITION_Y(start_y);
4094   }
4095   else
4096   {
4097     scroll_x = SCROLL_POSITION_X(local_player->jx);
4098     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4099   }
4100
4101   /* !!! FIX THIS (START) !!! */
4102   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4103   {
4104     InitGameEngine_EM();
4105   }
4106   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4107   {
4108     InitGameEngine_SP();
4109   }
4110   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4111   {
4112     InitGameEngine_MM();
4113   }
4114   else
4115   {
4116     DrawLevel(REDRAW_FIELD);
4117     DrawAllPlayers();
4118
4119     /* after drawing the level, correct some elements */
4120     if (game.timegate_time_left == 0)
4121       CloseAllOpenTimegates();
4122   }
4123
4124   /* blit playfield from scroll buffer to normal back buffer for fading in */
4125   BlitScreenToBitmap(backbuffer);
4126   /* !!! FIX THIS (END) !!! */
4127
4128   DrawMaskedBorder(fade_mask);
4129
4130   FadeIn(fade_mask);
4131
4132 #if 1
4133   // full screen redraw is required at this point in the following cases:
4134   // - special editor door undrawn when game was started from level editor
4135   // - drawing area (playfield) was changed and has to be removed completely
4136   redraw_mask = REDRAW_ALL;
4137   BackToFront();
4138 #endif
4139
4140   if (!game.restart_level)
4141   {
4142     /* copy default game door content to main double buffer */
4143
4144     /* !!! CHECK AGAIN !!! */
4145     SetPanelBackground();
4146     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4147     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4148   }
4149
4150   SetPanelBackground();
4151   SetDrawBackgroundMask(REDRAW_DOOR_1);
4152
4153   UpdateAndDisplayGameControlValues();
4154
4155   if (!game.restart_level)
4156   {
4157     UnmapGameButtons();
4158     UnmapTapeButtons();
4159
4160     FreeGameButtons();
4161     CreateGameButtons();
4162
4163     MapGameButtons();
4164     MapTapeButtons();
4165
4166     /* copy actual game door content to door double buffer for OpenDoor() */
4167     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4168
4169     OpenDoor(DOOR_OPEN_ALL);
4170
4171     KeyboardAutoRepeatOffUnlessAutoplay();
4172
4173 #if DEBUG_INIT_PLAYER
4174     DebugPrintPlayerStatus("Player status (final)");
4175 #endif
4176   }
4177
4178   UnmapAllGadgets();
4179
4180   MapGameButtons();
4181   MapTapeButtons();
4182
4183   if (!game.restart_level && !tape.playing)
4184   {
4185     LevelStats_incPlayed(level_nr);
4186
4187     SaveLevelSetup_SeriesInfo();
4188   }
4189
4190   game.restart_level = FALSE;
4191   game.restart_game_message = NULL;
4192
4193   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4194     InitGameActions_MM();
4195
4196   SaveEngineSnapshotToListInitial();
4197
4198   if (!game.restart_level)
4199   {
4200     PlaySound(SND_GAME_STARTING);
4201
4202     if (setup.sound_music)
4203       PlayLevelMusic();
4204   }
4205 }
4206
4207 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4208                         int actual_player_x, int actual_player_y)
4209 {
4210   /* this is used for non-R'n'D game engines to update certain engine values */
4211
4212   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4213   {
4214     actual_player_x = correctLevelPosX_EM(actual_player_x);
4215     actual_player_y = correctLevelPosY_EM(actual_player_y);
4216   }
4217
4218   /* needed to determine if sounds are played within the visible screen area */
4219   scroll_x = actual_scroll_x;
4220   scroll_y = actual_scroll_y;
4221
4222   /* needed to get player position for "follow finger" playing input method */
4223   local_player->jx = actual_player_x;
4224   local_player->jy = actual_player_y;
4225 }
4226
4227 void InitMovDir(int x, int y)
4228 {
4229   int i, element = Feld[x][y];
4230   static int xy[4][2] =
4231   {
4232     {  0, +1 },
4233     { +1,  0 },
4234     {  0, -1 },
4235     { -1,  0 }
4236   };
4237   static int direction[3][4] =
4238   {
4239     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4240     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4241     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4242   };
4243
4244   switch (element)
4245   {
4246     case EL_BUG_RIGHT:
4247     case EL_BUG_UP:
4248     case EL_BUG_LEFT:
4249     case EL_BUG_DOWN:
4250       Feld[x][y] = EL_BUG;
4251       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4252       break;
4253
4254     case EL_SPACESHIP_RIGHT:
4255     case EL_SPACESHIP_UP:
4256     case EL_SPACESHIP_LEFT:
4257     case EL_SPACESHIP_DOWN:
4258       Feld[x][y] = EL_SPACESHIP;
4259       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4260       break;
4261
4262     case EL_BD_BUTTERFLY_RIGHT:
4263     case EL_BD_BUTTERFLY_UP:
4264     case EL_BD_BUTTERFLY_LEFT:
4265     case EL_BD_BUTTERFLY_DOWN:
4266       Feld[x][y] = EL_BD_BUTTERFLY;
4267       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4268       break;
4269
4270     case EL_BD_FIREFLY_RIGHT:
4271     case EL_BD_FIREFLY_UP:
4272     case EL_BD_FIREFLY_LEFT:
4273     case EL_BD_FIREFLY_DOWN:
4274       Feld[x][y] = EL_BD_FIREFLY;
4275       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4276       break;
4277
4278     case EL_PACMAN_RIGHT:
4279     case EL_PACMAN_UP:
4280     case EL_PACMAN_LEFT:
4281     case EL_PACMAN_DOWN:
4282       Feld[x][y] = EL_PACMAN;
4283       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4284       break;
4285
4286     case EL_YAMYAM_LEFT:
4287     case EL_YAMYAM_RIGHT:
4288     case EL_YAMYAM_UP:
4289     case EL_YAMYAM_DOWN:
4290       Feld[x][y] = EL_YAMYAM;
4291       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4292       break;
4293
4294     case EL_SP_SNIKSNAK:
4295       MovDir[x][y] = MV_UP;
4296       break;
4297
4298     case EL_SP_ELECTRON:
4299       MovDir[x][y] = MV_LEFT;
4300       break;
4301
4302     case EL_MOLE_LEFT:
4303     case EL_MOLE_RIGHT:
4304     case EL_MOLE_UP:
4305     case EL_MOLE_DOWN:
4306       Feld[x][y] = EL_MOLE;
4307       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4308       break;
4309
4310     default:
4311       if (IS_CUSTOM_ELEMENT(element))
4312       {
4313         struct ElementInfo *ei = &element_info[element];
4314         int move_direction_initial = ei->move_direction_initial;
4315         int move_pattern = ei->move_pattern;
4316
4317         if (move_direction_initial == MV_START_PREVIOUS)
4318         {
4319           if (MovDir[x][y] != MV_NONE)
4320             return;
4321
4322           move_direction_initial = MV_START_AUTOMATIC;
4323         }
4324
4325         if (move_direction_initial == MV_START_RANDOM)
4326           MovDir[x][y] = 1 << RND(4);
4327         else if (move_direction_initial & MV_ANY_DIRECTION)
4328           MovDir[x][y] = move_direction_initial;
4329         else if (move_pattern == MV_ALL_DIRECTIONS ||
4330                  move_pattern == MV_TURNING_LEFT ||
4331                  move_pattern == MV_TURNING_RIGHT ||
4332                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4333                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4334                  move_pattern == MV_TURNING_RANDOM)
4335           MovDir[x][y] = 1 << RND(4);
4336         else if (move_pattern == MV_HORIZONTAL)
4337           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4338         else if (move_pattern == MV_VERTICAL)
4339           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4340         else if (move_pattern & MV_ANY_DIRECTION)
4341           MovDir[x][y] = element_info[element].move_pattern;
4342         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4343                  move_pattern == MV_ALONG_RIGHT_SIDE)
4344         {
4345           /* use random direction as default start direction */
4346           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4347             MovDir[x][y] = 1 << RND(4);
4348
4349           for (i = 0; i < NUM_DIRECTIONS; i++)
4350           {
4351             int x1 = x + xy[i][0];
4352             int y1 = y + xy[i][1];
4353
4354             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4355             {
4356               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4357                 MovDir[x][y] = direction[0][i];
4358               else
4359                 MovDir[x][y] = direction[1][i];
4360
4361               break;
4362             }
4363           }
4364         }                
4365       }
4366       else
4367       {
4368         MovDir[x][y] = 1 << RND(4);
4369
4370         if (element != EL_BUG &&
4371             element != EL_SPACESHIP &&
4372             element != EL_BD_BUTTERFLY &&
4373             element != EL_BD_FIREFLY)
4374           break;
4375
4376         for (i = 0; i < NUM_DIRECTIONS; i++)
4377         {
4378           int x1 = x + xy[i][0];
4379           int y1 = y + xy[i][1];
4380
4381           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4382           {
4383             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4384             {
4385               MovDir[x][y] = direction[0][i];
4386               break;
4387             }
4388             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4389                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4390             {
4391               MovDir[x][y] = direction[1][i];
4392               break;
4393             }
4394           }
4395         }
4396       }
4397       break;
4398   }
4399
4400   GfxDir[x][y] = MovDir[x][y];
4401 }
4402
4403 void InitAmoebaNr(int x, int y)
4404 {
4405   int i;
4406   int group_nr = AmoebeNachbarNr(x, y);
4407
4408   if (group_nr == 0)
4409   {
4410     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4411     {
4412       if (AmoebaCnt[i] == 0)
4413       {
4414         group_nr = i;
4415         break;
4416       }
4417     }
4418   }
4419
4420   AmoebaNr[x][y] = group_nr;
4421   AmoebaCnt[group_nr]++;
4422   AmoebaCnt2[group_nr]++;
4423 }
4424
4425 static void PlayerWins(struct PlayerInfo *player)
4426 {
4427   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4428       local_player->players_still_needed > 0)
4429     return;
4430
4431   player->LevelSolved = TRUE;
4432   player->GameOver = TRUE;
4433
4434   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4435                          level.native_em_level->lev->score :
4436                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4437                          game_mm.score :
4438                          player->score);
4439   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4440                           MM_HEALTH(game_mm.laser_overload_value) :
4441                           player->health);
4442
4443   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4444                                       TimeLeft);
4445   player->LevelSolved_CountingScore = player->score_final;
4446   player->LevelSolved_CountingHealth = player->health_final;
4447 }
4448
4449 void GameWon()
4450 {
4451   static int time_count_steps;
4452   static int time, time_final;
4453   static int score, score_final;
4454   static int health, health_final;
4455   static int game_over_delay_1 = 0;
4456   static int game_over_delay_2 = 0;
4457   static int game_over_delay_3 = 0;
4458   int game_over_delay_value_1 = 50;
4459   int game_over_delay_value_2 = 25;
4460   int game_over_delay_value_3 = 50;
4461
4462   if (!local_player->LevelSolved_GameWon)
4463   {
4464     int i;
4465
4466     /* do not start end game actions before the player stops moving (to exit) */
4467     if (local_player->MovPos)
4468       return;
4469
4470     local_player->LevelSolved_GameWon = TRUE;
4471     local_player->LevelSolved_SaveTape = tape.recording;
4472     local_player->LevelSolved_SaveScore = !tape.playing;
4473
4474     if (!tape.playing)
4475     {
4476       LevelStats_incSolved(level_nr);
4477
4478       SaveLevelSetup_SeriesInfo();
4479     }
4480
4481     if (tape.auto_play)         /* tape might already be stopped here */
4482       tape.auto_play_level_solved = TRUE;
4483
4484     TapeStop();
4485
4486     game_over_delay_1 = 0;
4487     game_over_delay_2 = 0;
4488     game_over_delay_3 = game_over_delay_value_3;
4489
4490     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4491     score = score_final = local_player->score_final;
4492     health = health_final = local_player->health_final;
4493
4494     if (level.score[SC_TIME_BONUS] > 0)
4495     {
4496       if (TimeLeft > 0)
4497       {
4498         time_final = 0;
4499         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4500       }
4501       else if (game.no_time_limit && TimePlayed < 999)
4502       {
4503         time_final = 999;
4504         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4505       }
4506
4507       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4508
4509       game_over_delay_1 = game_over_delay_value_1;
4510
4511       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4512       {
4513         health_final = 0;
4514         score_final += health * level.score[SC_TIME_BONUS];
4515
4516         game_over_delay_2 = game_over_delay_value_2;
4517       }
4518
4519       local_player->score_final = score_final;
4520       local_player->health_final = health_final;
4521     }
4522
4523     if (level_editor_test_game)
4524     {
4525       time = time_final;
4526       score = score_final;
4527
4528       local_player->LevelSolved_CountingTime = time;
4529       local_player->LevelSolved_CountingScore = score;
4530
4531       game_panel_controls[GAME_PANEL_TIME].value = time;
4532       game_panel_controls[GAME_PANEL_SCORE].value = score;
4533
4534       DisplayGameControlValues();
4535     }
4536
4537     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4538     {
4539       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4540       {
4541         /* close exit door after last player */
4542         if ((AllPlayersGone &&
4543              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4544               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4545               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4546             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4547             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4548         {
4549           int element = Feld[ExitX][ExitY];
4550
4551           Feld[ExitX][ExitY] =
4552             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4553              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4554              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4555              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4556              EL_EM_STEEL_EXIT_CLOSING);
4557
4558           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4559         }
4560
4561         /* player disappears */
4562         DrawLevelField(ExitX, ExitY);
4563       }
4564
4565       for (i = 0; i < MAX_PLAYERS; i++)
4566       {
4567         struct PlayerInfo *player = &stored_player[i];
4568
4569         if (player->present)
4570         {
4571           RemovePlayer(player);
4572
4573           /* player disappears */
4574           DrawLevelField(player->jx, player->jy);
4575         }
4576       }
4577     }
4578
4579     PlaySound(SND_GAME_WINNING);
4580   }
4581
4582   if (game_over_delay_1 > 0)
4583   {
4584     game_over_delay_1--;
4585
4586     return;
4587   }
4588
4589   if (time != time_final)
4590   {
4591     int time_to_go = ABS(time_final - time);
4592     int time_count_dir = (time < time_final ? +1 : -1);
4593
4594     if (time_to_go < time_count_steps)
4595       time_count_steps = 1;
4596
4597     time  += time_count_steps * time_count_dir;
4598     score += time_count_steps * level.score[SC_TIME_BONUS];
4599
4600     local_player->LevelSolved_CountingTime = time;
4601     local_player->LevelSolved_CountingScore = score;
4602
4603     game_panel_controls[GAME_PANEL_TIME].value = time;
4604     game_panel_controls[GAME_PANEL_SCORE].value = score;
4605
4606     DisplayGameControlValues();
4607
4608     if (time == time_final)
4609       StopSound(SND_GAME_LEVELTIME_BONUS);
4610     else if (setup.sound_loops)
4611       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4612     else
4613       PlaySound(SND_GAME_LEVELTIME_BONUS);
4614
4615     return;
4616   }
4617
4618   if (game_over_delay_2 > 0)
4619   {
4620     game_over_delay_2--;
4621
4622     return;
4623   }
4624
4625   if (health != health_final)
4626   {
4627     int health_count_dir = (health < health_final ? +1 : -1);
4628
4629     health += health_count_dir;
4630     score  += level.score[SC_TIME_BONUS];
4631
4632     local_player->LevelSolved_CountingHealth = health;
4633     local_player->LevelSolved_CountingScore = score;
4634
4635     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4636     game_panel_controls[GAME_PANEL_SCORE].value = score;
4637
4638     DisplayGameControlValues();
4639
4640     if (health == health_final)
4641       StopSound(SND_GAME_LEVELTIME_BONUS);
4642     else if (setup.sound_loops)
4643       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4644     else
4645       PlaySound(SND_GAME_LEVELTIME_BONUS);
4646
4647     return;
4648   }
4649
4650   local_player->LevelSolved_PanelOff = TRUE;
4651
4652   if (game_over_delay_3 > 0)
4653   {
4654     game_over_delay_3--;
4655
4656     return;
4657   }
4658
4659   GameEnd();
4660 }
4661
4662 void GameEnd()
4663 {
4664   int hi_pos;
4665
4666   local_player->LevelSolved_GameEnd = TRUE;
4667
4668   if (local_player->LevelSolved_SaveTape)
4669   {
4670     /* make sure that request dialog to save tape does not open door again */
4671     if (!global.use_envelope_request)
4672       CloseDoor(DOOR_CLOSE_1);
4673
4674     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4675   }
4676
4677   /* if no tape is to be saved, close both doors simultaneously */
4678   CloseDoor(DOOR_CLOSE_ALL);
4679
4680   if (level_editor_test_game)
4681   {
4682     SetGameStatus(GAME_MODE_MAIN);
4683
4684     DrawMainMenu();
4685
4686     return;
4687   }
4688
4689   if (!local_player->LevelSolved_SaveScore)
4690   {
4691     SetGameStatus(GAME_MODE_MAIN);
4692
4693     DrawMainMenu();
4694
4695     return;
4696   }
4697
4698   if (level_nr == leveldir_current->handicap_level)
4699   {
4700     leveldir_current->handicap_level++;
4701
4702     SaveLevelSetup_SeriesInfo();
4703   }
4704
4705   if (setup.increment_levels &&
4706       level_nr < leveldir_current->last_level &&
4707       !network_playing)
4708   {
4709     level_nr++;         /* advance to next level */
4710     TapeErase();        /* start with empty tape */
4711
4712     if (setup.auto_play_next_level)
4713     {
4714       LoadLevel(level_nr);
4715
4716       SaveLevelSetup_SeriesInfo();
4717     }
4718   }
4719
4720   /* used instead of last "level_nr" (for network games) */
4721   hi_pos = NewHiScore(levelset.level_nr);
4722
4723   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4724   {
4725     SetGameStatus(GAME_MODE_SCORES);
4726
4727     DrawHallOfFame(levelset.level_nr, hi_pos);
4728   }
4729   else if (setup.auto_play_next_level && setup.increment_levels &&
4730            !network_playing)
4731   {
4732     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4733   }
4734   else
4735   {
4736     SetGameStatus(GAME_MODE_MAIN);
4737
4738     DrawMainMenu();
4739   }
4740 }
4741
4742 int NewHiScore(int level_nr)
4743 {
4744   int k, l;
4745   int position = -1;
4746   boolean one_score_entry_per_name = !program.many_scores_per_name;
4747
4748   LoadScore(level_nr);
4749
4750   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4751       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4752     return -1;
4753
4754   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4755   {
4756     if (local_player->score_final > highscore[k].Score)
4757     {
4758       /* player has made it to the hall of fame */
4759
4760       if (k < MAX_SCORE_ENTRIES - 1)
4761       {
4762         int m = MAX_SCORE_ENTRIES - 1;
4763
4764         if (one_score_entry_per_name)
4765         {
4766           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4767             if (strEqual(setup.player_name, highscore[l].Name))
4768               m = l;
4769
4770           if (m == k)   /* player's new highscore overwrites his old one */
4771             goto put_into_list;
4772         }
4773
4774         for (l = m; l > k; l--)
4775         {
4776           strcpy(highscore[l].Name, highscore[l - 1].Name);
4777           highscore[l].Score = highscore[l - 1].Score;
4778         }
4779       }
4780
4781       put_into_list:
4782
4783       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4784       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4785       highscore[k].Score = local_player->score_final; 
4786       position = k;
4787
4788       break;
4789     }
4790     else if (one_score_entry_per_name &&
4791              !strncmp(setup.player_name, highscore[k].Name,
4792                       MAX_PLAYER_NAME_LEN))
4793       break;    /* player already there with a higher score */
4794   }
4795
4796   if (position >= 0) 
4797     SaveScore(level_nr);
4798
4799   return position;
4800 }
4801
4802 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4803 {
4804   int element = Feld[x][y];
4805   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4806   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4807   int horiz_move = (dx != 0);
4808   int sign = (horiz_move ? dx : dy);
4809   int step = sign * element_info[element].move_stepsize;
4810
4811   /* special values for move stepsize for spring and things on conveyor belt */
4812   if (horiz_move)
4813   {
4814     if (CAN_FALL(element) &&
4815         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4816       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4817     else if (element == EL_SPRING)
4818       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4819   }
4820
4821   return step;
4822 }
4823
4824 inline static int getElementMoveStepsize(int x, int y)
4825 {
4826   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4827 }
4828
4829 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4830 {
4831   if (player->GfxAction != action || player->GfxDir != dir)
4832   {
4833     player->GfxAction = action;
4834     player->GfxDir = dir;
4835     player->Frame = 0;
4836     player->StepFrame = 0;
4837   }
4838 }
4839
4840 static void ResetGfxFrame(int x, int y)
4841 {
4842   // profiling showed that "autotest" spends 10~20% of its time in this function
4843   if (DrawingDeactivatedField())
4844     return;
4845
4846   int element = Feld[x][y];
4847   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4848
4849   if (graphic_info[graphic].anim_global_sync)
4850     GfxFrame[x][y] = FrameCounter;
4851   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4852     GfxFrame[x][y] = CustomValue[x][y];
4853   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4854     GfxFrame[x][y] = element_info[element].collect_score;
4855   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4856     GfxFrame[x][y] = ChangeDelay[x][y];
4857 }
4858
4859 static void ResetGfxAnimation(int x, int y)
4860 {
4861   GfxAction[x][y] = ACTION_DEFAULT;
4862   GfxDir[x][y] = MovDir[x][y];
4863   GfxFrame[x][y] = 0;
4864
4865   ResetGfxFrame(x, y);
4866 }
4867
4868 static void ResetRandomAnimationValue(int x, int y)
4869 {
4870   GfxRandom[x][y] = INIT_GFX_RANDOM();
4871 }
4872
4873 void InitMovingField(int x, int y, int direction)
4874 {
4875   int element = Feld[x][y];
4876   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4877   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4878   int newx = x + dx;
4879   int newy = y + dy;
4880   boolean is_moving_before, is_moving_after;
4881
4882   /* check if element was/is moving or being moved before/after mode change */
4883   is_moving_before = (WasJustMoving[x][y] != 0);
4884   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4885
4886   /* reset animation only for moving elements which change direction of moving
4887      or which just started or stopped moving
4888      (else CEs with property "can move" / "not moving" are reset each frame) */
4889   if (is_moving_before != is_moving_after ||
4890       direction != MovDir[x][y])
4891     ResetGfxAnimation(x, y);
4892
4893   MovDir[x][y] = direction;
4894   GfxDir[x][y] = direction;
4895
4896   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4897                      direction == MV_DOWN && CAN_FALL(element) ?
4898                      ACTION_FALLING : ACTION_MOVING);
4899
4900   /* this is needed for CEs with property "can move" / "not moving" */
4901
4902   if (is_moving_after)
4903   {
4904     if (Feld[newx][newy] == EL_EMPTY)
4905       Feld[newx][newy] = EL_BLOCKED;
4906
4907     MovDir[newx][newy] = MovDir[x][y];
4908
4909     CustomValue[newx][newy] = CustomValue[x][y];
4910
4911     GfxFrame[newx][newy] = GfxFrame[x][y];
4912     GfxRandom[newx][newy] = GfxRandom[x][y];
4913     GfxAction[newx][newy] = GfxAction[x][y];
4914     GfxDir[newx][newy] = GfxDir[x][y];
4915   }
4916 }
4917
4918 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4919 {
4920   int direction = MovDir[x][y];
4921   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4922   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4923
4924   *goes_to_x = newx;
4925   *goes_to_y = newy;
4926 }
4927
4928 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4929 {
4930   int oldx = x, oldy = y;
4931   int direction = MovDir[x][y];
4932
4933   if (direction == MV_LEFT)
4934     oldx++;
4935   else if (direction == MV_RIGHT)
4936     oldx--;
4937   else if (direction == MV_UP)
4938     oldy++;
4939   else if (direction == MV_DOWN)
4940     oldy--;
4941
4942   *comes_from_x = oldx;
4943   *comes_from_y = oldy;
4944 }
4945
4946 int MovingOrBlocked2Element(int x, int y)
4947 {
4948   int element = Feld[x][y];
4949
4950   if (element == EL_BLOCKED)
4951   {
4952     int oldx, oldy;
4953
4954     Blocked2Moving(x, y, &oldx, &oldy);
4955     return Feld[oldx][oldy];
4956   }
4957   else
4958     return element;
4959 }
4960
4961 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4962 {
4963   /* like MovingOrBlocked2Element(), but if element is moving
4964      and (x,y) is the field the moving element is just leaving,
4965      return EL_BLOCKED instead of the element value */
4966   int element = Feld[x][y];
4967
4968   if (IS_MOVING(x, y))
4969   {
4970     if (element == EL_BLOCKED)
4971     {
4972       int oldx, oldy;
4973
4974       Blocked2Moving(x, y, &oldx, &oldy);
4975       return Feld[oldx][oldy];
4976     }
4977     else
4978       return EL_BLOCKED;
4979   }
4980   else
4981     return element;
4982 }
4983
4984 static void RemoveField(int x, int y)
4985 {
4986   Feld[x][y] = EL_EMPTY;
4987
4988   MovPos[x][y] = 0;
4989   MovDir[x][y] = 0;
4990   MovDelay[x][y] = 0;
4991
4992   CustomValue[x][y] = 0;
4993
4994   AmoebaNr[x][y] = 0;
4995   ChangeDelay[x][y] = 0;
4996   ChangePage[x][y] = -1;
4997   Pushed[x][y] = FALSE;
4998
4999   GfxElement[x][y] = EL_UNDEFINED;
5000   GfxAction[x][y] = ACTION_DEFAULT;
5001   GfxDir[x][y] = MV_NONE;
5002 }
5003
5004 void RemoveMovingField(int x, int y)
5005 {
5006   int oldx = x, oldy = y, newx = x, newy = y;
5007   int element = Feld[x][y];
5008   int next_element = EL_UNDEFINED;
5009
5010   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5011     return;
5012
5013   if (IS_MOVING(x, y))
5014   {
5015     Moving2Blocked(x, y, &newx, &newy);
5016
5017     if (Feld[newx][newy] != EL_BLOCKED)
5018     {
5019       /* element is moving, but target field is not free (blocked), but
5020          already occupied by something different (example: acid pool);
5021          in this case, only remove the moving field, but not the target */
5022
5023       RemoveField(oldx, oldy);
5024
5025       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5026
5027       TEST_DrawLevelField(oldx, oldy);
5028
5029       return;
5030     }
5031   }
5032   else if (element == EL_BLOCKED)
5033   {
5034     Blocked2Moving(x, y, &oldx, &oldy);
5035     if (!IS_MOVING(oldx, oldy))
5036       return;
5037   }
5038
5039   if (element == EL_BLOCKED &&
5040       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5041        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5042        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5043        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5044        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5045        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5046     next_element = get_next_element(Feld[oldx][oldy]);
5047
5048   RemoveField(oldx, oldy);
5049   RemoveField(newx, newy);
5050
5051   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5052
5053   if (next_element != EL_UNDEFINED)
5054     Feld[oldx][oldy] = next_element;
5055
5056   TEST_DrawLevelField(oldx, oldy);
5057   TEST_DrawLevelField(newx, newy);
5058 }
5059
5060 void DrawDynamite(int x, int y)
5061 {
5062   int sx = SCREENX(x), sy = SCREENY(y);
5063   int graphic = el2img(Feld[x][y]);
5064   int frame;
5065
5066   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5067     return;
5068
5069   if (IS_WALKABLE_INSIDE(Back[x][y]))
5070     return;
5071
5072   if (Back[x][y])
5073     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5074   else if (Store[x][y])
5075     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5076
5077   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5078
5079   if (Back[x][y] || Store[x][y])
5080     DrawGraphicThruMask(sx, sy, graphic, frame);
5081   else
5082     DrawGraphic(sx, sy, graphic, frame);
5083 }
5084
5085 void CheckDynamite(int x, int y)
5086 {
5087   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5088   {
5089     MovDelay[x][y]--;
5090
5091     if (MovDelay[x][y] != 0)
5092     {
5093       DrawDynamite(x, y);
5094       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5095
5096       return;
5097     }
5098   }
5099
5100   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5101
5102   Bang(x, y);
5103 }
5104
5105 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5106 {
5107   boolean num_checked_players = 0;
5108   int i;
5109
5110   for (i = 0; i < MAX_PLAYERS; i++)
5111   {
5112     if (stored_player[i].active)
5113     {
5114       int sx = stored_player[i].jx;
5115       int sy = stored_player[i].jy;
5116
5117       if (num_checked_players == 0)
5118       {
5119         *sx1 = *sx2 = sx;
5120         *sy1 = *sy2 = sy;
5121       }
5122       else
5123       {
5124         *sx1 = MIN(*sx1, sx);
5125         *sy1 = MIN(*sy1, sy);
5126         *sx2 = MAX(*sx2, sx);
5127         *sy2 = MAX(*sy2, sy);
5128       }
5129
5130       num_checked_players++;
5131     }
5132   }
5133 }
5134
5135 static boolean checkIfAllPlayersFitToScreen_RND()
5136 {
5137   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5138
5139   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5140
5141   return (sx2 - sx1 < SCR_FIELDX &&
5142           sy2 - sy1 < SCR_FIELDY);
5143 }
5144
5145 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5146 {
5147   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5148
5149   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5150
5151   *sx = (sx1 + sx2) / 2;
5152   *sy = (sy1 + sy2) / 2;
5153 }
5154
5155 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5156                         boolean center_screen, boolean quick_relocation)
5157 {
5158   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5159   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5160   boolean no_delay = (tape.warp_forward);
5161   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5162   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5163   int new_scroll_x, new_scroll_y;
5164
5165   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5166   {
5167     /* case 1: quick relocation inside visible screen (without scrolling) */
5168
5169     RedrawPlayfield();
5170
5171     return;
5172   }
5173
5174   if (!level.shifted_relocation || center_screen)
5175   {
5176     /* relocation _with_ centering of screen */
5177
5178     new_scroll_x = SCROLL_POSITION_X(x);
5179     new_scroll_y = SCROLL_POSITION_Y(y);
5180   }
5181   else
5182   {
5183     /* relocation _without_ centering of screen */
5184
5185     int center_scroll_x = SCROLL_POSITION_X(old_x);
5186     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5187     int offset_x = x + (scroll_x - center_scroll_x);
5188     int offset_y = y + (scroll_y - center_scroll_y);
5189
5190     /* for new screen position, apply previous offset to center position */
5191     new_scroll_x = SCROLL_POSITION_X(offset_x);
5192     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5193   }
5194
5195   if (quick_relocation)
5196   {
5197     /* case 2: quick relocation (redraw without visible scrolling) */
5198
5199     scroll_x = new_scroll_x;
5200     scroll_y = new_scroll_y;
5201
5202     RedrawPlayfield();
5203
5204     return;
5205   }
5206
5207   /* case 3: visible relocation (with scrolling to new position) */
5208
5209   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5210
5211   SetVideoFrameDelay(wait_delay_value);
5212
5213   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5214   {
5215     int dx = 0, dy = 0;
5216     int fx = FX, fy = FY;
5217
5218     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5219     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5220
5221     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5222       break;
5223
5224     scroll_x -= dx;
5225     scroll_y -= dy;
5226
5227     fx += dx * TILEX / 2;
5228     fy += dy * TILEY / 2;
5229
5230     ScrollLevel(dx, dy);
5231     DrawAllPlayers();
5232
5233     /* scroll in two steps of half tile size to make things smoother */
5234     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5235
5236     /* scroll second step to align at full tile size */
5237     BlitScreenToBitmap(window);
5238   }
5239
5240   DrawAllPlayers();
5241   BackToFront();
5242
5243   SetVideoFrameDelay(frame_delay_value_old);
5244 }
5245
5246 void RelocatePlayer(int jx, int jy, int el_player_raw)
5247 {
5248   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5249   int player_nr = GET_PLAYER_NR(el_player);
5250   struct PlayerInfo *player = &stored_player[player_nr];
5251   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5252   boolean no_delay = (tape.warp_forward);
5253   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5254   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5255   int old_jx = player->jx;
5256   int old_jy = player->jy;
5257   int old_element = Feld[old_jx][old_jy];
5258   int element = Feld[jx][jy];
5259   boolean player_relocated = (old_jx != jx || old_jy != jy);
5260
5261   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5262   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5263   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5264   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5265   int leave_side_horiz = move_dir_horiz;
5266   int leave_side_vert  = move_dir_vert;
5267   int enter_side = enter_side_horiz | enter_side_vert;
5268   int leave_side = leave_side_horiz | leave_side_vert;
5269
5270   if (player->GameOver)         /* do not reanimate dead player */
5271     return;
5272
5273   if (!player_relocated)        /* no need to relocate the player */
5274     return;
5275
5276   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5277   {
5278     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5279     DrawLevelField(jx, jy);
5280   }
5281
5282   if (player->present)
5283   {
5284     while (player->MovPos)
5285     {
5286       ScrollPlayer(player, SCROLL_GO_ON);
5287       ScrollScreen(NULL, SCROLL_GO_ON);
5288
5289       AdvanceFrameAndPlayerCounters(player->index_nr);
5290
5291       DrawPlayer(player);
5292
5293       BackToFront_WithFrameDelay(wait_delay_value);
5294     }
5295
5296     DrawPlayer(player);         /* needed here only to cleanup last field */
5297     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5298
5299     player->is_moving = FALSE;
5300   }
5301
5302   if (IS_CUSTOM_ELEMENT(old_element))
5303     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5304                                CE_LEFT_BY_PLAYER,
5305                                player->index_bit, leave_side);
5306
5307   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5308                                       CE_PLAYER_LEAVES_X,
5309                                       player->index_bit, leave_side);
5310
5311   Feld[jx][jy] = el_player;
5312   InitPlayerField(jx, jy, el_player, TRUE);
5313
5314   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5315      possible that the relocation target field did not contain a player element,
5316      but a walkable element, to which the new player was relocated -- in this
5317      case, restore that (already initialized!) element on the player field */
5318   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5319   {
5320     Feld[jx][jy] = element;     /* restore previously existing element */
5321   }
5322
5323   /* only visually relocate centered player */
5324   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5325                      FALSE, level.instant_relocation);
5326
5327   TestIfPlayerTouchesBadThing(jx, jy);
5328   TestIfPlayerTouchesCustomElement(jx, jy);
5329
5330   if (IS_CUSTOM_ELEMENT(element))
5331     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5332                                player->index_bit, enter_side);
5333
5334   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5335                                       player->index_bit, enter_side);
5336
5337   if (player->is_switching)
5338   {
5339     /* ensure that relocation while still switching an element does not cause
5340        a new element to be treated as also switched directly after relocation
5341        (this is important for teleporter switches that teleport the player to
5342        a place where another teleporter switch is in the same direction, which
5343        would then incorrectly be treated as immediately switched before the
5344        direction key that caused the switch was released) */
5345
5346     player->switch_x += jx - old_jx;
5347     player->switch_y += jy - old_jy;
5348   }
5349 }
5350
5351 void Explode(int ex, int ey, int phase, int mode)
5352 {
5353   int x, y;
5354   int last_phase;
5355   int border_element;
5356
5357   /* !!! eliminate this variable !!! */
5358   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5359
5360   if (game.explosions_delayed)
5361   {
5362     ExplodeField[ex][ey] = mode;
5363     return;
5364   }
5365
5366   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5367   {
5368     int center_element = Feld[ex][ey];
5369     int artwork_element, explosion_element;     /* set these values later */
5370
5371     /* remove things displayed in background while burning dynamite */
5372     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5373       Back[ex][ey] = 0;
5374
5375     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5376     {
5377       /* put moving element to center field (and let it explode there) */
5378       center_element = MovingOrBlocked2Element(ex, ey);
5379       RemoveMovingField(ex, ey);
5380       Feld[ex][ey] = center_element;
5381     }
5382
5383     /* now "center_element" is finally determined -- set related values now */
5384     artwork_element = center_element;           /* for custom player artwork */
5385     explosion_element = center_element;         /* for custom player artwork */
5386
5387     if (IS_PLAYER(ex, ey))
5388     {
5389       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5390
5391       artwork_element = stored_player[player_nr].artwork_element;
5392
5393       if (level.use_explosion_element[player_nr])
5394       {
5395         explosion_element = level.explosion_element[player_nr];
5396         artwork_element = explosion_element;
5397       }
5398     }
5399
5400     if (mode == EX_TYPE_NORMAL ||
5401         mode == EX_TYPE_CENTER ||
5402         mode == EX_TYPE_CROSS)
5403       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5404
5405     last_phase = element_info[explosion_element].explosion_delay + 1;
5406
5407     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5408     {
5409       int xx = x - ex + 1;
5410       int yy = y - ey + 1;
5411       int element;
5412
5413       if (!IN_LEV_FIELD(x, y) ||
5414           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5415           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5416         continue;
5417
5418       element = Feld[x][y];
5419
5420       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5421       {
5422         element = MovingOrBlocked2Element(x, y);
5423
5424         if (!IS_EXPLOSION_PROOF(element))
5425           RemoveMovingField(x, y);
5426       }
5427
5428       /* indestructible elements can only explode in center (but not flames) */
5429       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5430                                            mode == EX_TYPE_BORDER)) ||
5431           element == EL_FLAMES)
5432         continue;
5433
5434       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5435          behaviour, for example when touching a yamyam that explodes to rocks
5436          with active deadly shield, a rock is created under the player !!! */
5437       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5438 #if 0
5439       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5440           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5441            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5442 #else
5443       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5444 #endif
5445       {
5446         if (IS_ACTIVE_BOMB(element))
5447         {
5448           /* re-activate things under the bomb like gate or penguin */
5449           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5450           Back[x][y] = 0;
5451         }
5452
5453         continue;
5454       }
5455
5456       /* save walkable background elements while explosion on same tile */
5457       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5458           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5459         Back[x][y] = element;
5460
5461       /* ignite explodable elements reached by other explosion */
5462       if (element == EL_EXPLOSION)
5463         element = Store2[x][y];
5464
5465       if (AmoebaNr[x][y] &&
5466           (element == EL_AMOEBA_FULL ||
5467            element == EL_BD_AMOEBA ||
5468            element == EL_AMOEBA_GROWING))
5469       {
5470         AmoebaCnt[AmoebaNr[x][y]]--;
5471         AmoebaCnt2[AmoebaNr[x][y]]--;
5472       }
5473
5474       RemoveField(x, y);
5475
5476       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5477       {
5478         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5479
5480         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5481
5482         if (PLAYERINFO(ex, ey)->use_murphy)
5483           Store[x][y] = EL_EMPTY;
5484       }
5485
5486       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5487          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5488       else if (ELEM_IS_PLAYER(center_element))
5489         Store[x][y] = EL_EMPTY;
5490       else if (center_element == EL_YAMYAM)
5491         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5492       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5493         Store[x][y] = element_info[center_element].content.e[xx][yy];
5494 #if 1
5495       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5496          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5497          otherwise) -- FIX THIS !!! */
5498       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5499         Store[x][y] = element_info[element].content.e[1][1];
5500 #else
5501       else if (!CAN_EXPLODE(element))
5502         Store[x][y] = element_info[element].content.e[1][1];
5503 #endif
5504       else
5505         Store[x][y] = EL_EMPTY;
5506
5507       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5508           center_element == EL_AMOEBA_TO_DIAMOND)
5509         Store2[x][y] = element;
5510
5511       Feld[x][y] = EL_EXPLOSION;
5512       GfxElement[x][y] = artwork_element;
5513
5514       ExplodePhase[x][y] = 1;
5515       ExplodeDelay[x][y] = last_phase;
5516
5517       Stop[x][y] = TRUE;
5518     }
5519
5520     if (center_element == EL_YAMYAM)
5521       game.yamyam_content_nr =
5522         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5523
5524     return;
5525   }
5526
5527   if (Stop[ex][ey])
5528     return;
5529
5530   x = ex;
5531   y = ey;
5532
5533   if (phase == 1)
5534     GfxFrame[x][y] = 0;         /* restart explosion animation */
5535
5536   last_phase = ExplodeDelay[x][y];
5537
5538   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5539
5540   /* this can happen if the player leaves an explosion just in time */
5541   if (GfxElement[x][y] == EL_UNDEFINED)
5542     GfxElement[x][y] = EL_EMPTY;
5543
5544   border_element = Store2[x][y];
5545   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5546     border_element = StorePlayer[x][y];
5547
5548   if (phase == element_info[border_element].ignition_delay ||
5549       phase == last_phase)
5550   {
5551     boolean border_explosion = FALSE;
5552
5553     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5554         !PLAYER_EXPLOSION_PROTECTED(x, y))
5555     {
5556       KillPlayerUnlessExplosionProtected(x, y);
5557       border_explosion = TRUE;
5558     }
5559     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5560     {
5561       Feld[x][y] = Store2[x][y];
5562       Store2[x][y] = 0;
5563       Bang(x, y);
5564       border_explosion = TRUE;
5565     }
5566     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5567     {
5568       AmoebeUmwandeln(x, y);
5569       Store2[x][y] = 0;
5570       border_explosion = TRUE;
5571     }
5572
5573     /* if an element just explodes due to another explosion (chain-reaction),
5574        do not immediately end the new explosion when it was the last frame of
5575        the explosion (as it would be done in the following "if"-statement!) */
5576     if (border_explosion && phase == last_phase)
5577       return;
5578   }
5579
5580   if (phase == last_phase)
5581   {
5582     int element;
5583
5584     element = Feld[x][y] = Store[x][y];
5585     Store[x][y] = Store2[x][y] = 0;
5586     GfxElement[x][y] = EL_UNDEFINED;
5587
5588     /* player can escape from explosions and might therefore be still alive */
5589     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5590         element <= EL_PLAYER_IS_EXPLODING_4)
5591     {
5592       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5593       int explosion_element = EL_PLAYER_1 + player_nr;
5594       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5595       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5596
5597       if (level.use_explosion_element[player_nr])
5598         explosion_element = level.explosion_element[player_nr];
5599
5600       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5601                     element_info[explosion_element].content.e[xx][yy]);
5602     }
5603
5604     /* restore probably existing indestructible background element */
5605     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5606       element = Feld[x][y] = Back[x][y];
5607     Back[x][y] = 0;
5608
5609     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5610     GfxDir[x][y] = MV_NONE;
5611     ChangeDelay[x][y] = 0;
5612     ChangePage[x][y] = -1;
5613
5614     CustomValue[x][y] = 0;
5615
5616     InitField_WithBug2(x, y, FALSE);
5617
5618     TEST_DrawLevelField(x, y);
5619
5620     TestIfElementTouchesCustomElement(x, y);
5621
5622     if (GFX_CRUMBLED(element))
5623       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5624
5625     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5626       StorePlayer[x][y] = 0;
5627
5628     if (ELEM_IS_PLAYER(element))
5629       RelocatePlayer(x, y, element);
5630   }
5631   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5632   {
5633     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5634     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5635
5636     if (phase == delay)
5637       TEST_DrawLevelFieldCrumbled(x, y);
5638
5639     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5640     {
5641       DrawLevelElement(x, y, Back[x][y]);
5642       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5643     }
5644     else if (IS_WALKABLE_UNDER(Back[x][y]))
5645     {
5646       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5647       DrawLevelElementThruMask(x, y, Back[x][y]);
5648     }
5649     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5650       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5651   }
5652 }
5653
5654 void DynaExplode(int ex, int ey)
5655 {
5656   int i, j;
5657   int dynabomb_element = Feld[ex][ey];
5658   int dynabomb_size = 1;
5659   boolean dynabomb_xl = FALSE;
5660   struct PlayerInfo *player;
5661   static int xy[4][2] =
5662   {
5663     { 0, -1 },
5664     { -1, 0 },
5665     { +1, 0 },
5666     { 0, +1 }
5667   };
5668
5669   if (IS_ACTIVE_BOMB(dynabomb_element))
5670   {
5671     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5672     dynabomb_size = player->dynabomb_size;
5673     dynabomb_xl = player->dynabomb_xl;
5674     player->dynabombs_left++;
5675   }
5676
5677   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5678
5679   for (i = 0; i < NUM_DIRECTIONS; i++)
5680   {
5681     for (j = 1; j <= dynabomb_size; j++)
5682     {
5683       int x = ex + j * xy[i][0];
5684       int y = ey + j * xy[i][1];
5685       int element;
5686
5687       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5688         break;
5689
5690       element = Feld[x][y];
5691
5692       /* do not restart explosions of fields with active bombs */
5693       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5694         continue;
5695
5696       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5697
5698       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5699           !IS_DIGGABLE(element) && !dynabomb_xl)
5700         break;
5701     }
5702   }
5703 }
5704
5705 void Bang(int x, int y)
5706 {
5707   int element = MovingOrBlocked2Element(x, y);
5708   int explosion_type = EX_TYPE_NORMAL;
5709
5710   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5711   {
5712     struct PlayerInfo *player = PLAYERINFO(x, y);
5713
5714     element = Feld[x][y] = player->initial_element;
5715
5716     if (level.use_explosion_element[player->index_nr])
5717     {
5718       int explosion_element = level.explosion_element[player->index_nr];
5719
5720       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5721         explosion_type = EX_TYPE_CROSS;
5722       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5723         explosion_type = EX_TYPE_CENTER;
5724     }
5725   }
5726
5727   switch (element)
5728   {
5729     case EL_BUG:
5730     case EL_SPACESHIP:
5731     case EL_BD_BUTTERFLY:
5732     case EL_BD_FIREFLY:
5733     case EL_YAMYAM:
5734     case EL_DARK_YAMYAM:
5735     case EL_ROBOT:
5736     case EL_PACMAN:
5737     case EL_MOLE:
5738       RaiseScoreElement(element);
5739       break;
5740
5741     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5742     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5743     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5744     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5745     case EL_DYNABOMB_INCREASE_NUMBER:
5746     case EL_DYNABOMB_INCREASE_SIZE:
5747     case EL_DYNABOMB_INCREASE_POWER:
5748       explosion_type = EX_TYPE_DYNA;
5749       break;
5750
5751     case EL_DC_LANDMINE:
5752       explosion_type = EX_TYPE_CENTER;
5753       break;
5754
5755     case EL_PENGUIN:
5756     case EL_LAMP:
5757     case EL_LAMP_ACTIVE:
5758     case EL_AMOEBA_TO_DIAMOND:
5759       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5760         explosion_type = EX_TYPE_CENTER;
5761       break;
5762
5763     default:
5764       if (element_info[element].explosion_type == EXPLODES_CROSS)
5765         explosion_type = EX_TYPE_CROSS;
5766       else if (element_info[element].explosion_type == EXPLODES_1X1)
5767         explosion_type = EX_TYPE_CENTER;
5768       break;
5769   }
5770
5771   if (explosion_type == EX_TYPE_DYNA)
5772     DynaExplode(x, y);
5773   else
5774     Explode(x, y, EX_PHASE_START, explosion_type);
5775
5776   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5777 }
5778
5779 void SplashAcid(int x, int y)
5780 {
5781   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5782       (!IN_LEV_FIELD(x - 1, y - 2) ||
5783        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5784     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5785
5786   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5787       (!IN_LEV_FIELD(x + 1, y - 2) ||
5788        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5789     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5790
5791   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5792 }
5793
5794 static void InitBeltMovement()
5795 {
5796   static int belt_base_element[4] =
5797   {
5798     EL_CONVEYOR_BELT_1_LEFT,
5799     EL_CONVEYOR_BELT_2_LEFT,
5800     EL_CONVEYOR_BELT_3_LEFT,
5801     EL_CONVEYOR_BELT_4_LEFT
5802   };
5803   static int belt_base_active_element[4] =
5804   {
5805     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5806     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5807     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5808     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5809   };
5810
5811   int x, y, i, j;
5812
5813   /* set frame order for belt animation graphic according to belt direction */
5814   for (i = 0; i < NUM_BELTS; i++)
5815   {
5816     int belt_nr = i;
5817
5818     for (j = 0; j < NUM_BELT_PARTS; j++)
5819     {
5820       int element = belt_base_active_element[belt_nr] + j;
5821       int graphic_1 = el2img(element);
5822       int graphic_2 = el2panelimg(element);
5823
5824       if (game.belt_dir[i] == MV_LEFT)
5825       {
5826         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5827         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5828       }
5829       else
5830       {
5831         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5832         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5833       }
5834     }
5835   }
5836
5837   SCAN_PLAYFIELD(x, y)
5838   {
5839     int element = Feld[x][y];
5840
5841     for (i = 0; i < NUM_BELTS; i++)
5842     {
5843       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5844       {
5845         int e_belt_nr = getBeltNrFromBeltElement(element);
5846         int belt_nr = i;
5847
5848         if (e_belt_nr == belt_nr)
5849         {
5850           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5851
5852           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5853         }
5854       }
5855     }
5856   }
5857 }
5858
5859 static void ToggleBeltSwitch(int x, int y)
5860 {
5861   static int belt_base_element[4] =
5862   {
5863     EL_CONVEYOR_BELT_1_LEFT,
5864     EL_CONVEYOR_BELT_2_LEFT,
5865     EL_CONVEYOR_BELT_3_LEFT,
5866     EL_CONVEYOR_BELT_4_LEFT
5867   };
5868   static int belt_base_active_element[4] =
5869   {
5870     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5871     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5872     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5873     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5874   };
5875   static int belt_base_switch_element[4] =
5876   {
5877     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5878     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5879     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5880     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5881   };
5882   static int belt_move_dir[4] =
5883   {
5884     MV_LEFT,
5885     MV_NONE,
5886     MV_RIGHT,
5887     MV_NONE,
5888   };
5889
5890   int element = Feld[x][y];
5891   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5892   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5893   int belt_dir = belt_move_dir[belt_dir_nr];
5894   int xx, yy, i;
5895
5896   if (!IS_BELT_SWITCH(element))
5897     return;
5898
5899   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5900   game.belt_dir[belt_nr] = belt_dir;
5901
5902   if (belt_dir_nr == 3)
5903     belt_dir_nr = 1;
5904
5905   /* set frame order for belt animation graphic according to belt direction */
5906   for (i = 0; i < NUM_BELT_PARTS; i++)
5907   {
5908     int element = belt_base_active_element[belt_nr] + i;
5909     int graphic_1 = el2img(element);
5910     int graphic_2 = el2panelimg(element);
5911
5912     if (belt_dir == MV_LEFT)
5913     {
5914       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5915       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5916     }
5917     else
5918     {
5919       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5920       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5921     }
5922   }
5923
5924   SCAN_PLAYFIELD(xx, yy)
5925   {
5926     int element = Feld[xx][yy];
5927
5928     if (IS_BELT_SWITCH(element))
5929     {
5930       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5931
5932       if (e_belt_nr == belt_nr)
5933       {
5934         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5935         TEST_DrawLevelField(xx, yy);
5936       }
5937     }
5938     else if (IS_BELT(element) && belt_dir != MV_NONE)
5939     {
5940       int e_belt_nr = getBeltNrFromBeltElement(element);
5941
5942       if (e_belt_nr == belt_nr)
5943       {
5944         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5945
5946         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5947         TEST_DrawLevelField(xx, yy);
5948       }
5949     }
5950     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5951     {
5952       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5953
5954       if (e_belt_nr == belt_nr)
5955       {
5956         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5957
5958         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5959         TEST_DrawLevelField(xx, yy);
5960       }
5961     }
5962   }
5963 }
5964
5965 static void ToggleSwitchgateSwitch(int x, int y)
5966 {
5967   int xx, yy;
5968
5969   game.switchgate_pos = !game.switchgate_pos;
5970
5971   SCAN_PLAYFIELD(xx, yy)
5972   {
5973     int element = Feld[xx][yy];
5974
5975     if (element == EL_SWITCHGATE_SWITCH_UP)
5976     {
5977       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5978       TEST_DrawLevelField(xx, yy);
5979     }
5980     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5981     {
5982       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5983       TEST_DrawLevelField(xx, yy);
5984     }
5985     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5986     {
5987       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5988       TEST_DrawLevelField(xx, yy);
5989     }
5990     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5991     {
5992       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5993       TEST_DrawLevelField(xx, yy);
5994     }
5995     else if (element == EL_SWITCHGATE_OPEN ||
5996              element == EL_SWITCHGATE_OPENING)
5997     {
5998       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5999
6000       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6001     }
6002     else if (element == EL_SWITCHGATE_CLOSED ||
6003              element == EL_SWITCHGATE_CLOSING)
6004     {
6005       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6006
6007       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6008     }
6009   }
6010 }
6011
6012 static int getInvisibleActiveFromInvisibleElement(int element)
6013 {
6014   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6015           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6016           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6017           element);
6018 }
6019
6020 static int getInvisibleFromInvisibleActiveElement(int element)
6021 {
6022   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6023           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6024           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6025           element);
6026 }
6027
6028 static void RedrawAllLightSwitchesAndInvisibleElements()
6029 {
6030   int x, y;
6031
6032   SCAN_PLAYFIELD(x, y)
6033   {
6034     int element = Feld[x][y];
6035
6036     if (element == EL_LIGHT_SWITCH &&
6037         game.light_time_left > 0)
6038     {
6039       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6040       TEST_DrawLevelField(x, y);
6041     }
6042     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6043              game.light_time_left == 0)
6044     {
6045       Feld[x][y] = EL_LIGHT_SWITCH;
6046       TEST_DrawLevelField(x, y);
6047     }
6048     else if (element == EL_EMC_DRIPPER &&
6049              game.light_time_left > 0)
6050     {
6051       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6052       TEST_DrawLevelField(x, y);
6053     }
6054     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6055              game.light_time_left == 0)
6056     {
6057       Feld[x][y] = EL_EMC_DRIPPER;
6058       TEST_DrawLevelField(x, y);
6059     }
6060     else if (element == EL_INVISIBLE_STEELWALL ||
6061              element == EL_INVISIBLE_WALL ||
6062              element == EL_INVISIBLE_SAND)
6063     {
6064       if (game.light_time_left > 0)
6065         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6066
6067       TEST_DrawLevelField(x, y);
6068
6069       /* uncrumble neighbour fields, if needed */
6070       if (element == EL_INVISIBLE_SAND)
6071         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6072     }
6073     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6074              element == EL_INVISIBLE_WALL_ACTIVE ||
6075              element == EL_INVISIBLE_SAND_ACTIVE)
6076     {
6077       if (game.light_time_left == 0)
6078         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6079
6080       TEST_DrawLevelField(x, y);
6081
6082       /* re-crumble neighbour fields, if needed */
6083       if (element == EL_INVISIBLE_SAND)
6084         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6085     }
6086   }
6087 }
6088
6089 static void RedrawAllInvisibleElementsForLenses()
6090 {
6091   int x, y;
6092
6093   SCAN_PLAYFIELD(x, y)
6094   {
6095     int element = Feld[x][y];
6096
6097     if (element == EL_EMC_DRIPPER &&
6098         game.lenses_time_left > 0)
6099     {
6100       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6101       TEST_DrawLevelField(x, y);
6102     }
6103     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6104              game.lenses_time_left == 0)
6105     {
6106       Feld[x][y] = EL_EMC_DRIPPER;
6107       TEST_DrawLevelField(x, y);
6108     }
6109     else if (element == EL_INVISIBLE_STEELWALL ||
6110              element == EL_INVISIBLE_WALL ||
6111              element == EL_INVISIBLE_SAND)
6112     {
6113       if (game.lenses_time_left > 0)
6114         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6115
6116       TEST_DrawLevelField(x, y);
6117
6118       /* uncrumble neighbour fields, if needed */
6119       if (element == EL_INVISIBLE_SAND)
6120         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6121     }
6122     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6123              element == EL_INVISIBLE_WALL_ACTIVE ||
6124              element == EL_INVISIBLE_SAND_ACTIVE)
6125     {
6126       if (game.lenses_time_left == 0)
6127         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6128
6129       TEST_DrawLevelField(x, y);
6130
6131       /* re-crumble neighbour fields, if needed */
6132       if (element == EL_INVISIBLE_SAND)
6133         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6134     }
6135   }
6136 }
6137
6138 static void RedrawAllInvisibleElementsForMagnifier()
6139 {
6140   int x, y;
6141
6142   SCAN_PLAYFIELD(x, y)
6143   {
6144     int element = Feld[x][y];
6145
6146     if (element == EL_EMC_FAKE_GRASS &&
6147         game.magnify_time_left > 0)
6148     {
6149       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6150       TEST_DrawLevelField(x, y);
6151     }
6152     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6153              game.magnify_time_left == 0)
6154     {
6155       Feld[x][y] = EL_EMC_FAKE_GRASS;
6156       TEST_DrawLevelField(x, y);
6157     }
6158     else if (IS_GATE_GRAY(element) &&
6159              game.magnify_time_left > 0)
6160     {
6161       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6162                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6163                     IS_EM_GATE_GRAY(element) ?
6164                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6165                     IS_EMC_GATE_GRAY(element) ?
6166                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6167                     IS_DC_GATE_GRAY(element) ?
6168                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6169                     element);
6170       TEST_DrawLevelField(x, y);
6171     }
6172     else if (IS_GATE_GRAY_ACTIVE(element) &&
6173              game.magnify_time_left == 0)
6174     {
6175       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6176                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6177                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6178                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6179                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6180                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6181                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6182                     EL_DC_GATE_WHITE_GRAY :
6183                     element);
6184       TEST_DrawLevelField(x, y);
6185     }
6186   }
6187 }
6188
6189 static void ToggleLightSwitch(int x, int y)
6190 {
6191   int element = Feld[x][y];
6192
6193   game.light_time_left =
6194     (element == EL_LIGHT_SWITCH ?
6195      level.time_light * FRAMES_PER_SECOND : 0);
6196
6197   RedrawAllLightSwitchesAndInvisibleElements();
6198 }
6199
6200 static void ActivateTimegateSwitch(int x, int y)
6201 {
6202   int xx, yy;
6203
6204   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6205
6206   SCAN_PLAYFIELD(xx, yy)
6207   {
6208     int element = Feld[xx][yy];
6209
6210     if (element == EL_TIMEGATE_CLOSED ||
6211         element == EL_TIMEGATE_CLOSING)
6212     {
6213       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6214       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6215     }
6216
6217     /*
6218     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6219     {
6220       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6221       TEST_DrawLevelField(xx, yy);
6222     }
6223     */
6224
6225   }
6226
6227   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6228                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6229 }
6230
6231 void Impact(int x, int y)
6232 {
6233   boolean last_line = (y == lev_fieldy - 1);
6234   boolean object_hit = FALSE;
6235   boolean impact = (last_line || object_hit);
6236   int element = Feld[x][y];
6237   int smashed = EL_STEELWALL;
6238
6239   if (!last_line)       /* check if element below was hit */
6240   {
6241     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6242       return;
6243
6244     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6245                                          MovDir[x][y + 1] != MV_DOWN ||
6246                                          MovPos[x][y + 1] <= TILEY / 2));
6247
6248     /* do not smash moving elements that left the smashed field in time */
6249     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6250         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6251       object_hit = FALSE;
6252
6253 #if USE_QUICKSAND_IMPACT_BUGFIX
6254     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6255     {
6256       RemoveMovingField(x, y + 1);
6257       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6258       Feld[x][y + 2] = EL_ROCK;
6259       TEST_DrawLevelField(x, y + 2);
6260
6261       object_hit = TRUE;
6262     }
6263
6264     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6265     {
6266       RemoveMovingField(x, y + 1);
6267       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6268       Feld[x][y + 2] = EL_ROCK;
6269       TEST_DrawLevelField(x, y + 2);
6270
6271       object_hit = TRUE;
6272     }
6273 #endif
6274
6275     if (object_hit)
6276       smashed = MovingOrBlocked2Element(x, y + 1);
6277
6278     impact = (last_line || object_hit);
6279   }
6280
6281   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6282   {
6283     SplashAcid(x, y + 1);
6284     return;
6285   }
6286
6287   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6288   /* only reset graphic animation if graphic really changes after impact */
6289   if (impact &&
6290       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6291   {
6292     ResetGfxAnimation(x, y);
6293     TEST_DrawLevelField(x, y);
6294   }
6295
6296   if (impact && CAN_EXPLODE_IMPACT(element))
6297   {
6298     Bang(x, y);
6299     return;
6300   }
6301   else if (impact && element == EL_PEARL &&
6302            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6303   {
6304     ResetGfxAnimation(x, y);
6305
6306     Feld[x][y] = EL_PEARL_BREAKING;
6307     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6308     return;
6309   }
6310   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6311   {
6312     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6313
6314     return;
6315   }
6316
6317   if (impact && element == EL_AMOEBA_DROP)
6318   {
6319     if (object_hit && IS_PLAYER(x, y + 1))
6320       KillPlayerUnlessEnemyProtected(x, y + 1);
6321     else if (object_hit && smashed == EL_PENGUIN)
6322       Bang(x, y + 1);
6323     else
6324     {
6325       Feld[x][y] = EL_AMOEBA_GROWING;
6326       Store[x][y] = EL_AMOEBA_WET;
6327
6328       ResetRandomAnimationValue(x, y);
6329     }
6330     return;
6331   }
6332
6333   if (object_hit)               /* check which object was hit */
6334   {
6335     if ((CAN_PASS_MAGIC_WALL(element) && 
6336          (smashed == EL_MAGIC_WALL ||
6337           smashed == EL_BD_MAGIC_WALL)) ||
6338         (CAN_PASS_DC_MAGIC_WALL(element) &&
6339          smashed == EL_DC_MAGIC_WALL))
6340     {
6341       int xx, yy;
6342       int activated_magic_wall =
6343         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6344          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6345          EL_DC_MAGIC_WALL_ACTIVE);
6346
6347       /* activate magic wall / mill */
6348       SCAN_PLAYFIELD(xx, yy)
6349       {
6350         if (Feld[xx][yy] == smashed)
6351           Feld[xx][yy] = activated_magic_wall;
6352       }
6353
6354       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6355       game.magic_wall_active = TRUE;
6356
6357       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6358                             SND_MAGIC_WALL_ACTIVATING :
6359                             smashed == EL_BD_MAGIC_WALL ?
6360                             SND_BD_MAGIC_WALL_ACTIVATING :
6361                             SND_DC_MAGIC_WALL_ACTIVATING));
6362     }
6363
6364     if (IS_PLAYER(x, y + 1))
6365     {
6366       if (CAN_SMASH_PLAYER(element))
6367       {
6368         KillPlayerUnlessEnemyProtected(x, y + 1);
6369         return;
6370       }
6371     }
6372     else if (smashed == EL_PENGUIN)
6373     {
6374       if (CAN_SMASH_PLAYER(element))
6375       {
6376         Bang(x, y + 1);
6377         return;
6378       }
6379     }
6380     else if (element == EL_BD_DIAMOND)
6381     {
6382       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6383       {
6384         Bang(x, y + 1);
6385         return;
6386       }
6387     }
6388     else if (((element == EL_SP_INFOTRON ||
6389                element == EL_SP_ZONK) &&
6390               (smashed == EL_SP_SNIKSNAK ||
6391                smashed == EL_SP_ELECTRON ||
6392                smashed == EL_SP_DISK_ORANGE)) ||
6393              (element == EL_SP_INFOTRON &&
6394               smashed == EL_SP_DISK_YELLOW))
6395     {
6396       Bang(x, y + 1);
6397       return;
6398     }
6399     else if (CAN_SMASH_EVERYTHING(element))
6400     {
6401       if (IS_CLASSIC_ENEMY(smashed) ||
6402           CAN_EXPLODE_SMASHED(smashed))
6403       {
6404         Bang(x, y + 1);
6405         return;
6406       }
6407       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6408       {
6409         if (smashed == EL_LAMP ||
6410             smashed == EL_LAMP_ACTIVE)
6411         {
6412           Bang(x, y + 1);
6413           return;
6414         }
6415         else if (smashed == EL_NUT)
6416         {
6417           Feld[x][y + 1] = EL_NUT_BREAKING;
6418           PlayLevelSound(x, y, SND_NUT_BREAKING);
6419           RaiseScoreElement(EL_NUT);
6420           return;
6421         }
6422         else if (smashed == EL_PEARL)
6423         {
6424           ResetGfxAnimation(x, y);
6425
6426           Feld[x][y + 1] = EL_PEARL_BREAKING;
6427           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6428           return;
6429         }
6430         else if (smashed == EL_DIAMOND)
6431         {
6432           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6433           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6434           return;
6435         }
6436         else if (IS_BELT_SWITCH(smashed))
6437         {
6438           ToggleBeltSwitch(x, y + 1);
6439         }
6440         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6441                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6442                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6443                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6444         {
6445           ToggleSwitchgateSwitch(x, y + 1);
6446         }
6447         else if (smashed == EL_LIGHT_SWITCH ||
6448                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6449         {
6450           ToggleLightSwitch(x, y + 1);
6451         }
6452         else
6453         {
6454           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6455
6456           CheckElementChangeBySide(x, y + 1, smashed, element,
6457                                    CE_SWITCHED, CH_SIDE_TOP);
6458           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6459                                             CH_SIDE_TOP);
6460         }
6461       }
6462       else
6463       {
6464         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6465       }
6466     }
6467   }
6468
6469   /* play sound of magic wall / mill */
6470   if (!last_line &&
6471       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6472        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6473        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6474   {
6475     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6476       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6477     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6478       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6479     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6480       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6481
6482     return;
6483   }
6484
6485   /* play sound of object that hits the ground */
6486   if (last_line || object_hit)
6487     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6488 }
6489
6490 inline static void TurnRoundExt(int x, int y)
6491 {
6492   static struct
6493   {
6494     int dx, dy;
6495   } move_xy[] =
6496   {
6497     {  0,  0 },
6498     { -1,  0 },
6499     { +1,  0 },
6500     {  0,  0 },
6501     {  0, -1 },
6502     {  0,  0 }, { 0, 0 }, { 0, 0 },
6503     {  0, +1 }
6504   };
6505   static struct
6506   {
6507     int left, right, back;
6508   } turn[] =
6509   {
6510     { 0,        0,              0        },
6511     { MV_DOWN,  MV_UP,          MV_RIGHT },
6512     { MV_UP,    MV_DOWN,        MV_LEFT  },
6513     { 0,        0,              0        },
6514     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6515     { 0,        0,              0        },
6516     { 0,        0,              0        },
6517     { 0,        0,              0        },
6518     { MV_RIGHT, MV_LEFT,        MV_UP    }
6519   };
6520
6521   int element = Feld[x][y];
6522   int move_pattern = element_info[element].move_pattern;
6523
6524   int old_move_dir = MovDir[x][y];
6525   int left_dir  = turn[old_move_dir].left;
6526   int right_dir = turn[old_move_dir].right;
6527   int back_dir  = turn[old_move_dir].back;
6528
6529   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6530   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6531   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6532   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6533
6534   int left_x  = x + left_dx,  left_y  = y + left_dy;
6535   int right_x = x + right_dx, right_y = y + right_dy;
6536   int move_x  = x + move_dx,  move_y  = y + move_dy;
6537
6538   int xx, yy;
6539
6540   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6541   {
6542     TestIfBadThingTouchesOtherBadThing(x, y);
6543
6544     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6545       MovDir[x][y] = right_dir;
6546     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6547       MovDir[x][y] = left_dir;
6548
6549     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6550       MovDelay[x][y] = 9;
6551     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6552       MovDelay[x][y] = 1;
6553   }
6554   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6555   {
6556     TestIfBadThingTouchesOtherBadThing(x, y);
6557
6558     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6559       MovDir[x][y] = left_dir;
6560     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6561       MovDir[x][y] = right_dir;
6562
6563     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6564       MovDelay[x][y] = 9;
6565     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6566       MovDelay[x][y] = 1;
6567   }
6568   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6569   {
6570     TestIfBadThingTouchesOtherBadThing(x, y);
6571
6572     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6573       MovDir[x][y] = left_dir;
6574     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6575       MovDir[x][y] = right_dir;
6576
6577     if (MovDir[x][y] != old_move_dir)
6578       MovDelay[x][y] = 9;
6579   }
6580   else if (element == EL_YAMYAM)
6581   {
6582     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6583     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6584
6585     if (can_turn_left && can_turn_right)
6586       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6587     else if (can_turn_left)
6588       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6589     else if (can_turn_right)
6590       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6591     else
6592       MovDir[x][y] = back_dir;
6593
6594     MovDelay[x][y] = 16 + 16 * RND(3);
6595   }
6596   else if (element == EL_DARK_YAMYAM)
6597   {
6598     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6599                                                          left_x, left_y);
6600     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6601                                                          right_x, right_y);
6602
6603     if (can_turn_left && can_turn_right)
6604       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6605     else if (can_turn_left)
6606       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6607     else if (can_turn_right)
6608       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6609     else
6610       MovDir[x][y] = back_dir;
6611
6612     MovDelay[x][y] = 16 + 16 * RND(3);
6613   }
6614   else if (element == EL_PACMAN)
6615   {
6616     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6617     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6618
6619     if (can_turn_left && can_turn_right)
6620       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6621     else if (can_turn_left)
6622       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6623     else if (can_turn_right)
6624       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6625     else
6626       MovDir[x][y] = back_dir;
6627
6628     MovDelay[x][y] = 6 + RND(40);
6629   }
6630   else if (element == EL_PIG)
6631   {
6632     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6633     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6634     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6635     boolean should_turn_left, should_turn_right, should_move_on;
6636     int rnd_value = 24;
6637     int rnd = RND(rnd_value);
6638
6639     should_turn_left = (can_turn_left &&
6640                         (!can_move_on ||
6641                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6642                                                    y + back_dy + left_dy)));
6643     should_turn_right = (can_turn_right &&
6644                          (!can_move_on ||
6645                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6646                                                     y + back_dy + right_dy)));
6647     should_move_on = (can_move_on &&
6648                       (!can_turn_left ||
6649                        !can_turn_right ||
6650                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6651                                                  y + move_dy + left_dy) ||
6652                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6653                                                  y + move_dy + right_dy)));
6654
6655     if (should_turn_left || should_turn_right || should_move_on)
6656     {
6657       if (should_turn_left && should_turn_right && should_move_on)
6658         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6659                         rnd < 2 * rnd_value / 3 ? right_dir :
6660                         old_move_dir);
6661       else if (should_turn_left && should_turn_right)
6662         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6663       else if (should_turn_left && should_move_on)
6664         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6665       else if (should_turn_right && should_move_on)
6666         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6667       else if (should_turn_left)
6668         MovDir[x][y] = left_dir;
6669       else if (should_turn_right)
6670         MovDir[x][y] = right_dir;
6671       else if (should_move_on)
6672         MovDir[x][y] = old_move_dir;
6673     }
6674     else if (can_move_on && rnd > rnd_value / 8)
6675       MovDir[x][y] = old_move_dir;
6676     else if (can_turn_left && can_turn_right)
6677       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6678     else if (can_turn_left && rnd > rnd_value / 8)
6679       MovDir[x][y] = left_dir;
6680     else if (can_turn_right && rnd > rnd_value/8)
6681       MovDir[x][y] = right_dir;
6682     else
6683       MovDir[x][y] = back_dir;
6684
6685     xx = x + move_xy[MovDir[x][y]].dx;
6686     yy = y + move_xy[MovDir[x][y]].dy;
6687
6688     if (!IN_LEV_FIELD(xx, yy) ||
6689         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6690       MovDir[x][y] = old_move_dir;
6691
6692     MovDelay[x][y] = 0;
6693   }
6694   else if (element == EL_DRAGON)
6695   {
6696     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6697     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6698     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6699     int rnd_value = 24;
6700     int rnd = RND(rnd_value);
6701
6702     if (can_move_on && rnd > rnd_value / 8)
6703       MovDir[x][y] = old_move_dir;
6704     else if (can_turn_left && can_turn_right)
6705       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6706     else if (can_turn_left && rnd > rnd_value / 8)
6707       MovDir[x][y] = left_dir;
6708     else if (can_turn_right && rnd > rnd_value / 8)
6709       MovDir[x][y] = right_dir;
6710     else
6711       MovDir[x][y] = back_dir;
6712
6713     xx = x + move_xy[MovDir[x][y]].dx;
6714     yy = y + move_xy[MovDir[x][y]].dy;
6715
6716     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6717       MovDir[x][y] = old_move_dir;
6718
6719     MovDelay[x][y] = 0;
6720   }
6721   else if (element == EL_MOLE)
6722   {
6723     boolean can_move_on =
6724       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6725                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6726                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6727     if (!can_move_on)
6728     {
6729       boolean can_turn_left =
6730         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6731                               IS_AMOEBOID(Feld[left_x][left_y])));
6732
6733       boolean can_turn_right =
6734         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6735                               IS_AMOEBOID(Feld[right_x][right_y])));
6736
6737       if (can_turn_left && can_turn_right)
6738         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6739       else if (can_turn_left)
6740         MovDir[x][y] = left_dir;
6741       else
6742         MovDir[x][y] = right_dir;
6743     }
6744
6745     if (MovDir[x][y] != old_move_dir)
6746       MovDelay[x][y] = 9;
6747   }
6748   else if (element == EL_BALLOON)
6749   {
6750     MovDir[x][y] = game.wind_direction;
6751     MovDelay[x][y] = 0;
6752   }
6753   else if (element == EL_SPRING)
6754   {
6755     if (MovDir[x][y] & MV_HORIZONTAL)
6756     {
6757       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6758           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6759       {
6760         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6761         ResetGfxAnimation(move_x, move_y);
6762         TEST_DrawLevelField(move_x, move_y);
6763
6764         MovDir[x][y] = back_dir;
6765       }
6766       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6767                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6768         MovDir[x][y] = MV_NONE;
6769     }
6770
6771     MovDelay[x][y] = 0;
6772   }
6773   else if (element == EL_ROBOT ||
6774            element == EL_SATELLITE ||
6775            element == EL_PENGUIN ||
6776            element == EL_EMC_ANDROID)
6777   {
6778     int attr_x = -1, attr_y = -1;
6779
6780     if (AllPlayersGone)
6781     {
6782       attr_x = ExitX;
6783       attr_y = ExitY;
6784     }
6785     else
6786     {
6787       int i;
6788
6789       for (i = 0; i < MAX_PLAYERS; i++)
6790       {
6791         struct PlayerInfo *player = &stored_player[i];
6792         int jx = player->jx, jy = player->jy;
6793
6794         if (!player->active)
6795           continue;
6796
6797         if (attr_x == -1 ||
6798             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6799         {
6800           attr_x = jx;
6801           attr_y = jy;
6802         }
6803       }
6804     }
6805
6806     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6807         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6808          game.engine_version < VERSION_IDENT(3,1,0,0)))
6809     {
6810       attr_x = ZX;
6811       attr_y = ZY;
6812     }
6813
6814     if (element == EL_PENGUIN)
6815     {
6816       int i;
6817       static int xy[4][2] =
6818       {
6819         { 0, -1 },
6820         { -1, 0 },
6821         { +1, 0 },
6822         { 0, +1 }
6823       };
6824
6825       for (i = 0; i < NUM_DIRECTIONS; i++)
6826       {
6827         int ex = x + xy[i][0];
6828         int ey = y + xy[i][1];
6829
6830         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6831                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6832                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6833                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6834         {
6835           attr_x = ex;
6836           attr_y = ey;
6837           break;
6838         }
6839       }
6840     }
6841
6842     MovDir[x][y] = MV_NONE;
6843     if (attr_x < x)
6844       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6845     else if (attr_x > x)
6846       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6847     if (attr_y < y)
6848       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6849     else if (attr_y > y)
6850       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6851
6852     if (element == EL_ROBOT)
6853     {
6854       int newx, newy;
6855
6856       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6857         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6858       Moving2Blocked(x, y, &newx, &newy);
6859
6860       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6861         MovDelay[x][y] = 8 + 8 * !RND(3);
6862       else
6863         MovDelay[x][y] = 16;
6864     }
6865     else if (element == EL_PENGUIN)
6866     {
6867       int newx, newy;
6868
6869       MovDelay[x][y] = 1;
6870
6871       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6872       {
6873         boolean first_horiz = RND(2);
6874         int new_move_dir = MovDir[x][y];
6875
6876         MovDir[x][y] =
6877           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6878         Moving2Blocked(x, y, &newx, &newy);
6879
6880         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6881           return;
6882
6883         MovDir[x][y] =
6884           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6885         Moving2Blocked(x, y, &newx, &newy);
6886
6887         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6888           return;
6889
6890         MovDir[x][y] = old_move_dir;
6891         return;
6892       }
6893     }
6894     else if (element == EL_SATELLITE)
6895     {
6896       int newx, newy;
6897
6898       MovDelay[x][y] = 1;
6899
6900       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6901       {
6902         boolean first_horiz = RND(2);
6903         int new_move_dir = MovDir[x][y];
6904
6905         MovDir[x][y] =
6906           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6907         Moving2Blocked(x, y, &newx, &newy);
6908
6909         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6910           return;
6911
6912         MovDir[x][y] =
6913           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6914         Moving2Blocked(x, y, &newx, &newy);
6915
6916         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6917           return;
6918
6919         MovDir[x][y] = old_move_dir;
6920         return;
6921       }
6922     }
6923     else if (element == EL_EMC_ANDROID)
6924     {
6925       static int check_pos[16] =
6926       {
6927         -1,             /*  0 => (invalid)          */
6928         7,              /*  1 => MV_LEFT            */
6929         3,              /*  2 => MV_RIGHT           */
6930         -1,             /*  3 => (invalid)          */
6931         1,              /*  4 =>            MV_UP   */
6932         0,              /*  5 => MV_LEFT  | MV_UP   */
6933         2,              /*  6 => MV_RIGHT | MV_UP   */
6934         -1,             /*  7 => (invalid)          */
6935         5,              /*  8 =>            MV_DOWN */
6936         6,              /*  9 => MV_LEFT  | MV_DOWN */
6937         4,              /* 10 => MV_RIGHT | MV_DOWN */
6938         -1,             /* 11 => (invalid)          */
6939         -1,             /* 12 => (invalid)          */
6940         -1,             /* 13 => (invalid)          */
6941         -1,             /* 14 => (invalid)          */
6942         -1,             /* 15 => (invalid)          */
6943       };
6944       static struct
6945       {
6946         int dx, dy;
6947         int dir;
6948       } check_xy[8] =
6949       {
6950         { -1, -1,       MV_LEFT  | MV_UP   },
6951         {  0, -1,                  MV_UP   },
6952         { +1, -1,       MV_RIGHT | MV_UP   },
6953         { +1,  0,       MV_RIGHT           },
6954         { +1, +1,       MV_RIGHT | MV_DOWN },
6955         {  0, +1,                  MV_DOWN },
6956         { -1, +1,       MV_LEFT  | MV_DOWN },
6957         { -1,  0,       MV_LEFT            },
6958       };
6959       int start_pos, check_order;
6960       boolean can_clone = FALSE;
6961       int i;
6962
6963       /* check if there is any free field around current position */
6964       for (i = 0; i < 8; i++)
6965       {
6966         int newx = x + check_xy[i].dx;
6967         int newy = y + check_xy[i].dy;
6968
6969         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6970         {
6971           can_clone = TRUE;
6972
6973           break;
6974         }
6975       }
6976
6977       if (can_clone)            /* randomly find an element to clone */
6978       {
6979         can_clone = FALSE;
6980
6981         start_pos = check_pos[RND(8)];
6982         check_order = (RND(2) ? -1 : +1);
6983
6984         for (i = 0; i < 8; i++)
6985         {
6986           int pos_raw = start_pos + i * check_order;
6987           int pos = (pos_raw + 8) % 8;
6988           int newx = x + check_xy[pos].dx;
6989           int newy = y + check_xy[pos].dy;
6990
6991           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6992           {
6993             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6994             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6995
6996             Store[x][y] = Feld[newx][newy];
6997
6998             can_clone = TRUE;
6999
7000             break;
7001           }
7002         }
7003       }
7004
7005       if (can_clone)            /* randomly find a direction to move */
7006       {
7007         can_clone = FALSE;
7008
7009         start_pos = check_pos[RND(8)];
7010         check_order = (RND(2) ? -1 : +1);
7011
7012         for (i = 0; i < 8; i++)
7013         {
7014           int pos_raw = start_pos + i * check_order;
7015           int pos = (pos_raw + 8) % 8;
7016           int newx = x + check_xy[pos].dx;
7017           int newy = y + check_xy[pos].dy;
7018           int new_move_dir = check_xy[pos].dir;
7019
7020           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7021           {
7022             MovDir[x][y] = new_move_dir;
7023             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7024
7025             can_clone = TRUE;
7026
7027             break;
7028           }
7029         }
7030       }
7031
7032       if (can_clone)            /* cloning and moving successful */
7033         return;
7034
7035       /* cannot clone -- try to move towards player */
7036
7037       start_pos = check_pos[MovDir[x][y] & 0x0f];
7038       check_order = (RND(2) ? -1 : +1);
7039
7040       for (i = 0; i < 3; i++)
7041       {
7042         /* first check start_pos, then previous/next or (next/previous) pos */
7043         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7044         int pos = (pos_raw + 8) % 8;
7045         int newx = x + check_xy[pos].dx;
7046         int newy = y + check_xy[pos].dy;
7047         int new_move_dir = check_xy[pos].dir;
7048
7049         if (IS_PLAYER(newx, newy))
7050           break;
7051
7052         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7053         {
7054           MovDir[x][y] = new_move_dir;
7055           MovDelay[x][y] = level.android_move_time * 8 + 1;
7056
7057           break;
7058         }
7059       }
7060     }
7061   }
7062   else if (move_pattern == MV_TURNING_LEFT ||
7063            move_pattern == MV_TURNING_RIGHT ||
7064            move_pattern == MV_TURNING_LEFT_RIGHT ||
7065            move_pattern == MV_TURNING_RIGHT_LEFT ||
7066            move_pattern == MV_TURNING_RANDOM ||
7067            move_pattern == MV_ALL_DIRECTIONS)
7068   {
7069     boolean can_turn_left =
7070       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7071     boolean can_turn_right =
7072       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7073
7074     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7075       return;
7076
7077     if (move_pattern == MV_TURNING_LEFT)
7078       MovDir[x][y] = left_dir;
7079     else if (move_pattern == MV_TURNING_RIGHT)
7080       MovDir[x][y] = right_dir;
7081     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7082       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7083     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7084       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7085     else if (move_pattern == MV_TURNING_RANDOM)
7086       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7087                       can_turn_right && !can_turn_left ? right_dir :
7088                       RND(2) ? left_dir : right_dir);
7089     else if (can_turn_left && can_turn_right)
7090       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7091     else if (can_turn_left)
7092       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7093     else if (can_turn_right)
7094       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7095     else
7096       MovDir[x][y] = back_dir;
7097
7098     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7099   }
7100   else if (move_pattern == MV_HORIZONTAL ||
7101            move_pattern == MV_VERTICAL)
7102   {
7103     if (move_pattern & old_move_dir)
7104       MovDir[x][y] = back_dir;
7105     else if (move_pattern == MV_HORIZONTAL)
7106       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7107     else if (move_pattern == MV_VERTICAL)
7108       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7109
7110     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7111   }
7112   else if (move_pattern & MV_ANY_DIRECTION)
7113   {
7114     MovDir[x][y] = move_pattern;
7115     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7116   }
7117   else if (move_pattern & MV_WIND_DIRECTION)
7118   {
7119     MovDir[x][y] = game.wind_direction;
7120     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7121   }
7122   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7123   {
7124     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7125       MovDir[x][y] = left_dir;
7126     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7127       MovDir[x][y] = right_dir;
7128
7129     if (MovDir[x][y] != old_move_dir)
7130       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7131   }
7132   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7133   {
7134     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7135       MovDir[x][y] = right_dir;
7136     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7137       MovDir[x][y] = left_dir;
7138
7139     if (MovDir[x][y] != old_move_dir)
7140       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7141   }
7142   else if (move_pattern == MV_TOWARDS_PLAYER ||
7143            move_pattern == MV_AWAY_FROM_PLAYER)
7144   {
7145     int attr_x = -1, attr_y = -1;
7146     int newx, newy;
7147     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7148
7149     if (AllPlayersGone)
7150     {
7151       attr_x = ExitX;
7152       attr_y = ExitY;
7153     }
7154     else
7155     {
7156       int i;
7157
7158       for (i = 0; i < MAX_PLAYERS; i++)
7159       {
7160         struct PlayerInfo *player = &stored_player[i];
7161         int jx = player->jx, jy = player->jy;
7162
7163         if (!player->active)
7164           continue;
7165
7166         if (attr_x == -1 ||
7167             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7168         {
7169           attr_x = jx;
7170           attr_y = jy;
7171         }
7172       }
7173     }
7174
7175     MovDir[x][y] = MV_NONE;
7176     if (attr_x < x)
7177       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7178     else if (attr_x > x)
7179       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7180     if (attr_y < y)
7181       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7182     else if (attr_y > y)
7183       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7184
7185     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7186
7187     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7188     {
7189       boolean first_horiz = RND(2);
7190       int new_move_dir = MovDir[x][y];
7191
7192       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7193       {
7194         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7195         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7196
7197         return;
7198       }
7199
7200       MovDir[x][y] =
7201         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7202       Moving2Blocked(x, y, &newx, &newy);
7203
7204       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7205         return;
7206
7207       MovDir[x][y] =
7208         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7209       Moving2Blocked(x, y, &newx, &newy);
7210
7211       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7212         return;
7213
7214       MovDir[x][y] = old_move_dir;
7215     }
7216   }
7217   else if (move_pattern == MV_WHEN_PUSHED ||
7218            move_pattern == MV_WHEN_DROPPED)
7219   {
7220     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7221       MovDir[x][y] = MV_NONE;
7222
7223     MovDelay[x][y] = 0;
7224   }
7225   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7226   {
7227     static int test_xy[7][2] =
7228     {
7229       { 0, -1 },
7230       { -1, 0 },
7231       { +1, 0 },
7232       { 0, +1 },
7233       { 0, -1 },
7234       { -1, 0 },
7235       { +1, 0 },
7236     };
7237     static int test_dir[7] =
7238     {
7239       MV_UP,
7240       MV_LEFT,
7241       MV_RIGHT,
7242       MV_DOWN,
7243       MV_UP,
7244       MV_LEFT,
7245       MV_RIGHT,
7246     };
7247     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7248     int move_preference = -1000000;     /* start with very low preference */
7249     int new_move_dir = MV_NONE;
7250     int start_test = RND(4);
7251     int i;
7252
7253     for (i = 0; i < NUM_DIRECTIONS; i++)
7254     {
7255       int move_dir = test_dir[start_test + i];
7256       int move_dir_preference;
7257
7258       xx = x + test_xy[start_test + i][0];
7259       yy = y + test_xy[start_test + i][1];
7260
7261       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7262           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7263       {
7264         new_move_dir = move_dir;
7265
7266         break;
7267       }
7268
7269       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7270         continue;
7271
7272       move_dir_preference = -1 * RunnerVisit[xx][yy];
7273       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7274         move_dir_preference = PlayerVisit[xx][yy];
7275
7276       if (move_dir_preference > move_preference)
7277       {
7278         /* prefer field that has not been visited for the longest time */
7279         move_preference = move_dir_preference;
7280         new_move_dir = move_dir;
7281       }
7282       else if (move_dir_preference == move_preference &&
7283                move_dir == old_move_dir)
7284       {
7285         /* prefer last direction when all directions are preferred equally */
7286         move_preference = move_dir_preference;
7287         new_move_dir = move_dir;
7288       }
7289     }
7290
7291     MovDir[x][y] = new_move_dir;
7292     if (old_move_dir != new_move_dir)
7293       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7294   }
7295 }
7296
7297 static void TurnRound(int x, int y)
7298 {
7299   int direction = MovDir[x][y];
7300
7301   TurnRoundExt(x, y);
7302
7303   GfxDir[x][y] = MovDir[x][y];
7304
7305   if (direction != MovDir[x][y])
7306     GfxFrame[x][y] = 0;
7307
7308   if (MovDelay[x][y])
7309     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7310
7311   ResetGfxFrame(x, y);
7312 }
7313
7314 static boolean JustBeingPushed(int x, int y)
7315 {
7316   int i;
7317
7318   for (i = 0; i < MAX_PLAYERS; i++)
7319   {
7320     struct PlayerInfo *player = &stored_player[i];
7321
7322     if (player->active && player->is_pushing && player->MovPos)
7323     {
7324       int next_jx = player->jx + (player->jx - player->last_jx);
7325       int next_jy = player->jy + (player->jy - player->last_jy);
7326
7327       if (x == next_jx && y == next_jy)
7328         return TRUE;
7329     }
7330   }
7331
7332   return FALSE;
7333 }
7334
7335 void StartMoving(int x, int y)
7336 {
7337   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7338   int element = Feld[x][y];
7339
7340   if (Stop[x][y])
7341     return;
7342
7343   if (MovDelay[x][y] == 0)
7344     GfxAction[x][y] = ACTION_DEFAULT;
7345
7346   if (CAN_FALL(element) && y < lev_fieldy - 1)
7347   {
7348     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7349         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7350       if (JustBeingPushed(x, y))
7351         return;
7352
7353     if (element == EL_QUICKSAND_FULL)
7354     {
7355       if (IS_FREE(x, y + 1))
7356       {
7357         InitMovingField(x, y, MV_DOWN);
7358         started_moving = TRUE;
7359
7360         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7361 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7362         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7363           Store[x][y] = EL_ROCK;
7364 #else
7365         Store[x][y] = EL_ROCK;
7366 #endif
7367
7368         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7369       }
7370       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7371       {
7372         if (!MovDelay[x][y])
7373         {
7374           MovDelay[x][y] = TILEY + 1;
7375
7376           ResetGfxAnimation(x, y);
7377           ResetGfxAnimation(x, y + 1);
7378         }
7379
7380         if (MovDelay[x][y])
7381         {
7382           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7383           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7384
7385           MovDelay[x][y]--;
7386           if (MovDelay[x][y])
7387             return;
7388         }
7389
7390         Feld[x][y] = EL_QUICKSAND_EMPTY;
7391         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7392         Store[x][y + 1] = Store[x][y];
7393         Store[x][y] = 0;
7394
7395         PlayLevelSoundAction(x, y, ACTION_FILLING);
7396       }
7397       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7398       {
7399         if (!MovDelay[x][y])
7400         {
7401           MovDelay[x][y] = TILEY + 1;
7402
7403           ResetGfxAnimation(x, y);
7404           ResetGfxAnimation(x, y + 1);
7405         }
7406
7407         if (MovDelay[x][y])
7408         {
7409           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7410           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7411
7412           MovDelay[x][y]--;
7413           if (MovDelay[x][y])
7414             return;
7415         }
7416
7417         Feld[x][y] = EL_QUICKSAND_EMPTY;
7418         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7419         Store[x][y + 1] = Store[x][y];
7420         Store[x][y] = 0;
7421
7422         PlayLevelSoundAction(x, y, ACTION_FILLING);
7423       }
7424     }
7425     else if (element == EL_QUICKSAND_FAST_FULL)
7426     {
7427       if (IS_FREE(x, y + 1))
7428       {
7429         InitMovingField(x, y, MV_DOWN);
7430         started_moving = TRUE;
7431
7432         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7433 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7434         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7435           Store[x][y] = EL_ROCK;
7436 #else
7437         Store[x][y] = EL_ROCK;
7438 #endif
7439
7440         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7441       }
7442       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7443       {
7444         if (!MovDelay[x][y])
7445         {
7446           MovDelay[x][y] = TILEY + 1;
7447
7448           ResetGfxAnimation(x, y);
7449           ResetGfxAnimation(x, y + 1);
7450         }
7451
7452         if (MovDelay[x][y])
7453         {
7454           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7455           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7456
7457           MovDelay[x][y]--;
7458           if (MovDelay[x][y])
7459             return;
7460         }
7461
7462         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7463         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7464         Store[x][y + 1] = Store[x][y];
7465         Store[x][y] = 0;
7466
7467         PlayLevelSoundAction(x, y, ACTION_FILLING);
7468       }
7469       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7470       {
7471         if (!MovDelay[x][y])
7472         {
7473           MovDelay[x][y] = TILEY + 1;
7474
7475           ResetGfxAnimation(x, y);
7476           ResetGfxAnimation(x, y + 1);
7477         }
7478
7479         if (MovDelay[x][y])
7480         {
7481           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7482           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7483
7484           MovDelay[x][y]--;
7485           if (MovDelay[x][y])
7486             return;
7487         }
7488
7489         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7490         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7491         Store[x][y + 1] = Store[x][y];
7492         Store[x][y] = 0;
7493
7494         PlayLevelSoundAction(x, y, ACTION_FILLING);
7495       }
7496     }
7497     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7498              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7499     {
7500       InitMovingField(x, y, MV_DOWN);
7501       started_moving = TRUE;
7502
7503       Feld[x][y] = EL_QUICKSAND_FILLING;
7504       Store[x][y] = element;
7505
7506       PlayLevelSoundAction(x, y, ACTION_FILLING);
7507     }
7508     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7509              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7510     {
7511       InitMovingField(x, y, MV_DOWN);
7512       started_moving = TRUE;
7513
7514       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7515       Store[x][y] = element;
7516
7517       PlayLevelSoundAction(x, y, ACTION_FILLING);
7518     }
7519     else if (element == EL_MAGIC_WALL_FULL)
7520     {
7521       if (IS_FREE(x, y + 1))
7522       {
7523         InitMovingField(x, y, MV_DOWN);
7524         started_moving = TRUE;
7525
7526         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7527         Store[x][y] = EL_CHANGED(Store[x][y]);
7528       }
7529       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7530       {
7531         if (!MovDelay[x][y])
7532           MovDelay[x][y] = TILEY / 4 + 1;
7533
7534         if (MovDelay[x][y])
7535         {
7536           MovDelay[x][y]--;
7537           if (MovDelay[x][y])
7538             return;
7539         }
7540
7541         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7542         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7543         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7544         Store[x][y] = 0;
7545       }
7546     }
7547     else if (element == EL_BD_MAGIC_WALL_FULL)
7548     {
7549       if (IS_FREE(x, y + 1))
7550       {
7551         InitMovingField(x, y, MV_DOWN);
7552         started_moving = TRUE;
7553
7554         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7555         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7556       }
7557       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7558       {
7559         if (!MovDelay[x][y])
7560           MovDelay[x][y] = TILEY / 4 + 1;
7561
7562         if (MovDelay[x][y])
7563         {
7564           MovDelay[x][y]--;
7565           if (MovDelay[x][y])
7566             return;
7567         }
7568
7569         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7570         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7571         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7572         Store[x][y] = 0;
7573       }
7574     }
7575     else if (element == EL_DC_MAGIC_WALL_FULL)
7576     {
7577       if (IS_FREE(x, y + 1))
7578       {
7579         InitMovingField(x, y, MV_DOWN);
7580         started_moving = TRUE;
7581
7582         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7583         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7584       }
7585       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7586       {
7587         if (!MovDelay[x][y])
7588           MovDelay[x][y] = TILEY / 4 + 1;
7589
7590         if (MovDelay[x][y])
7591         {
7592           MovDelay[x][y]--;
7593           if (MovDelay[x][y])
7594             return;
7595         }
7596
7597         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7598         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7599         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7600         Store[x][y] = 0;
7601       }
7602     }
7603     else if ((CAN_PASS_MAGIC_WALL(element) &&
7604               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7605                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7606              (CAN_PASS_DC_MAGIC_WALL(element) &&
7607               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7608
7609     {
7610       InitMovingField(x, y, MV_DOWN);
7611       started_moving = TRUE;
7612
7613       Feld[x][y] =
7614         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7615          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7616          EL_DC_MAGIC_WALL_FILLING);
7617       Store[x][y] = element;
7618     }
7619     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7620     {
7621       SplashAcid(x, y + 1);
7622
7623       InitMovingField(x, y, MV_DOWN);
7624       started_moving = TRUE;
7625
7626       Store[x][y] = EL_ACID;
7627     }
7628     else if (
7629              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7630               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7631              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7632               CAN_FALL(element) && WasJustFalling[x][y] &&
7633               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7634
7635              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7636               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7637               (Feld[x][y + 1] == EL_BLOCKED)))
7638     {
7639       /* this is needed for a special case not covered by calling "Impact()"
7640          from "ContinueMoving()": if an element moves to a tile directly below
7641          another element which was just falling on that tile (which was empty
7642          in the previous frame), the falling element above would just stop
7643          instead of smashing the element below (in previous version, the above
7644          element was just checked for "moving" instead of "falling", resulting
7645          in incorrect smashes caused by horizontal movement of the above
7646          element; also, the case of the player being the element to smash was
7647          simply not covered here... :-/ ) */
7648
7649       CheckCollision[x][y] = 0;
7650       CheckImpact[x][y] = 0;
7651
7652       Impact(x, y);
7653     }
7654     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7655     {
7656       if (MovDir[x][y] == MV_NONE)
7657       {
7658         InitMovingField(x, y, MV_DOWN);
7659         started_moving = TRUE;
7660       }
7661     }
7662     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7663     {
7664       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7665         MovDir[x][y] = MV_DOWN;
7666
7667       InitMovingField(x, y, MV_DOWN);
7668       started_moving = TRUE;
7669     }
7670     else if (element == EL_AMOEBA_DROP)
7671     {
7672       Feld[x][y] = EL_AMOEBA_GROWING;
7673       Store[x][y] = EL_AMOEBA_WET;
7674     }
7675     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7676               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7677              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7678              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7679     {
7680       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7681                                 (IS_FREE(x - 1, y + 1) ||
7682                                  Feld[x - 1][y + 1] == EL_ACID));
7683       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7684                                 (IS_FREE(x + 1, y + 1) ||
7685                                  Feld[x + 1][y + 1] == EL_ACID));
7686       boolean can_fall_any  = (can_fall_left || can_fall_right);
7687       boolean can_fall_both = (can_fall_left && can_fall_right);
7688       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7689
7690       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7691       {
7692         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7693           can_fall_right = FALSE;
7694         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7695           can_fall_left = FALSE;
7696         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7697           can_fall_right = FALSE;
7698         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7699           can_fall_left = FALSE;
7700
7701         can_fall_any  = (can_fall_left || can_fall_right);
7702         can_fall_both = FALSE;
7703       }
7704
7705       if (can_fall_both)
7706       {
7707         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7708           can_fall_right = FALSE;       /* slip down on left side */
7709         else
7710           can_fall_left = !(can_fall_right = RND(2));
7711
7712         can_fall_both = FALSE;
7713       }
7714
7715       if (can_fall_any)
7716       {
7717         /* if not determined otherwise, prefer left side for slipping down */
7718         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7719         started_moving = TRUE;
7720       }
7721     }
7722     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7723     {
7724       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7725       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7726       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7727       int belt_dir = game.belt_dir[belt_nr];
7728
7729       if ((belt_dir == MV_LEFT  && left_is_free) ||
7730           (belt_dir == MV_RIGHT && right_is_free))
7731       {
7732         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7733
7734         InitMovingField(x, y, belt_dir);
7735         started_moving = TRUE;
7736
7737         Pushed[x][y] = TRUE;
7738         Pushed[nextx][y] = TRUE;
7739
7740         GfxAction[x][y] = ACTION_DEFAULT;
7741       }
7742       else
7743       {
7744         MovDir[x][y] = 0;       /* if element was moving, stop it */
7745       }
7746     }
7747   }
7748
7749   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7750   if (CAN_MOVE(element) && !started_moving)
7751   {
7752     int move_pattern = element_info[element].move_pattern;
7753     int newx, newy;
7754
7755     Moving2Blocked(x, y, &newx, &newy);
7756
7757     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7758       return;
7759
7760     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7761         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7762     {
7763       WasJustMoving[x][y] = 0;
7764       CheckCollision[x][y] = 0;
7765
7766       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7767
7768       if (Feld[x][y] != element)        /* element has changed */
7769         return;
7770     }
7771
7772     if (!MovDelay[x][y])        /* start new movement phase */
7773     {
7774       /* all objects that can change their move direction after each step
7775          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7776
7777       if (element != EL_YAMYAM &&
7778           element != EL_DARK_YAMYAM &&
7779           element != EL_PACMAN &&
7780           !(move_pattern & MV_ANY_DIRECTION) &&
7781           move_pattern != MV_TURNING_LEFT &&
7782           move_pattern != MV_TURNING_RIGHT &&
7783           move_pattern != MV_TURNING_LEFT_RIGHT &&
7784           move_pattern != MV_TURNING_RIGHT_LEFT &&
7785           move_pattern != MV_TURNING_RANDOM)
7786       {
7787         TurnRound(x, y);
7788
7789         if (MovDelay[x][y] && (element == EL_BUG ||
7790                                element == EL_SPACESHIP ||
7791                                element == EL_SP_SNIKSNAK ||
7792                                element == EL_SP_ELECTRON ||
7793                                element == EL_MOLE))
7794           TEST_DrawLevelField(x, y);
7795       }
7796     }
7797
7798     if (MovDelay[x][y])         /* wait some time before next movement */
7799     {
7800       MovDelay[x][y]--;
7801
7802       if (element == EL_ROBOT ||
7803           element == EL_YAMYAM ||
7804           element == EL_DARK_YAMYAM)
7805       {
7806         DrawLevelElementAnimationIfNeeded(x, y, element);
7807         PlayLevelSoundAction(x, y, ACTION_WAITING);
7808       }
7809       else if (element == EL_SP_ELECTRON)
7810         DrawLevelElementAnimationIfNeeded(x, y, element);
7811       else if (element == EL_DRAGON)
7812       {
7813         int i;
7814         int dir = MovDir[x][y];
7815         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7816         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7817         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7818                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7819                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7820                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7821         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7822
7823         GfxAction[x][y] = ACTION_ATTACKING;
7824
7825         if (IS_PLAYER(x, y))
7826           DrawPlayerField(x, y);
7827         else
7828           TEST_DrawLevelField(x, y);
7829
7830         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7831
7832         for (i = 1; i <= 3; i++)
7833         {
7834           int xx = x + i * dx;
7835           int yy = y + i * dy;
7836           int sx = SCREENX(xx);
7837           int sy = SCREENY(yy);
7838           int flame_graphic = graphic + (i - 1);
7839
7840           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7841             break;
7842
7843           if (MovDelay[x][y])
7844           {
7845             int flamed = MovingOrBlocked2Element(xx, yy);
7846
7847             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7848               Bang(xx, yy);
7849             else
7850               RemoveMovingField(xx, yy);
7851
7852             ChangeDelay[xx][yy] = 0;
7853
7854             Feld[xx][yy] = EL_FLAMES;
7855
7856             if (IN_SCR_FIELD(sx, sy))
7857             {
7858               TEST_DrawLevelFieldCrumbled(xx, yy);
7859               DrawGraphic(sx, sy, flame_graphic, frame);
7860             }
7861           }
7862           else
7863           {
7864             if (Feld[xx][yy] == EL_FLAMES)
7865               Feld[xx][yy] = EL_EMPTY;
7866             TEST_DrawLevelField(xx, yy);
7867           }
7868         }
7869       }
7870
7871       if (MovDelay[x][y])       /* element still has to wait some time */
7872       {
7873         PlayLevelSoundAction(x, y, ACTION_WAITING);
7874
7875         return;
7876       }
7877     }
7878
7879     /* now make next step */
7880
7881     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7882
7883     if (DONT_COLLIDE_WITH(element) &&
7884         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7885         !PLAYER_ENEMY_PROTECTED(newx, newy))
7886     {
7887       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7888
7889       return;
7890     }
7891
7892     else if (CAN_MOVE_INTO_ACID(element) &&
7893              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7894              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7895              (MovDir[x][y] == MV_DOWN ||
7896               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7897     {
7898       SplashAcid(newx, newy);
7899       Store[x][y] = EL_ACID;
7900     }
7901     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7902     {
7903       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7904           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7905           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7906           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7907       {
7908         RemoveField(x, y);
7909         TEST_DrawLevelField(x, y);
7910
7911         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7912         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7913           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7914
7915         local_player->friends_still_needed--;
7916         if (!local_player->friends_still_needed &&
7917             !local_player->GameOver && AllPlayersGone)
7918           PlayerWins(local_player);
7919
7920         return;
7921       }
7922       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7923       {
7924         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7925           TEST_DrawLevelField(newx, newy);
7926         else
7927           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7928       }
7929       else if (!IS_FREE(newx, newy))
7930       {
7931         GfxAction[x][y] = ACTION_WAITING;
7932
7933         if (IS_PLAYER(x, y))
7934           DrawPlayerField(x, y);
7935         else
7936           TEST_DrawLevelField(x, y);
7937
7938         return;
7939       }
7940     }
7941     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7942     {
7943       if (IS_FOOD_PIG(Feld[newx][newy]))
7944       {
7945         if (IS_MOVING(newx, newy))
7946           RemoveMovingField(newx, newy);
7947         else
7948         {
7949           Feld[newx][newy] = EL_EMPTY;
7950           TEST_DrawLevelField(newx, newy);
7951         }
7952
7953         PlayLevelSound(x, y, SND_PIG_DIGGING);
7954       }
7955       else if (!IS_FREE(newx, newy))
7956       {
7957         if (IS_PLAYER(x, y))
7958           DrawPlayerField(x, y);
7959         else
7960           TEST_DrawLevelField(x, y);
7961
7962         return;
7963       }
7964     }
7965     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7966     {
7967       if (Store[x][y] != EL_EMPTY)
7968       {
7969         boolean can_clone = FALSE;
7970         int xx, yy;
7971
7972         /* check if element to clone is still there */
7973         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7974         {
7975           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7976           {
7977             can_clone = TRUE;
7978
7979             break;
7980           }
7981         }
7982
7983         /* cannot clone or target field not free anymore -- do not clone */
7984         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7985           Store[x][y] = EL_EMPTY;
7986       }
7987
7988       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7989       {
7990         if (IS_MV_DIAGONAL(MovDir[x][y]))
7991         {
7992           int diagonal_move_dir = MovDir[x][y];
7993           int stored = Store[x][y];
7994           int change_delay = 8;
7995           int graphic;
7996
7997           /* android is moving diagonally */
7998
7999           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8000
8001           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8002           GfxElement[x][y] = EL_EMC_ANDROID;
8003           GfxAction[x][y] = ACTION_SHRINKING;
8004           GfxDir[x][y] = diagonal_move_dir;
8005           ChangeDelay[x][y] = change_delay;
8006
8007           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8008                                    GfxDir[x][y]);
8009
8010           DrawLevelGraphicAnimation(x, y, graphic);
8011           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8012
8013           if (Feld[newx][newy] == EL_ACID)
8014           {
8015             SplashAcid(newx, newy);
8016
8017             return;
8018           }
8019
8020           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8021
8022           Store[newx][newy] = EL_EMC_ANDROID;
8023           GfxElement[newx][newy] = EL_EMC_ANDROID;
8024           GfxAction[newx][newy] = ACTION_GROWING;
8025           GfxDir[newx][newy] = diagonal_move_dir;
8026           ChangeDelay[newx][newy] = change_delay;
8027
8028           graphic = el_act_dir2img(GfxElement[newx][newy],
8029                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8030
8031           DrawLevelGraphicAnimation(newx, newy, graphic);
8032           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8033
8034           return;
8035         }
8036         else
8037         {
8038           Feld[newx][newy] = EL_EMPTY;
8039           TEST_DrawLevelField(newx, newy);
8040
8041           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8042         }
8043       }
8044       else if (!IS_FREE(newx, newy))
8045       {
8046         return;
8047       }
8048     }
8049     else if (IS_CUSTOM_ELEMENT(element) &&
8050              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8051     {
8052       if (!DigFieldByCE(newx, newy, element))
8053         return;
8054
8055       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8056       {
8057         RunnerVisit[x][y] = FrameCounter;
8058         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8059       }
8060     }
8061     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8062     {
8063       if (!IS_FREE(newx, newy))
8064       {
8065         if (IS_PLAYER(x, y))
8066           DrawPlayerField(x, y);
8067         else
8068           TEST_DrawLevelField(x, y);
8069
8070         return;
8071       }
8072       else
8073       {
8074         boolean wanna_flame = !RND(10);
8075         int dx = newx - x, dy = newy - y;
8076         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8077         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8078         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8079                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8080         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8081                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8082
8083         if ((wanna_flame ||
8084              IS_CLASSIC_ENEMY(element1) ||
8085              IS_CLASSIC_ENEMY(element2)) &&
8086             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8087             element1 != EL_FLAMES && element2 != EL_FLAMES)
8088         {
8089           ResetGfxAnimation(x, y);
8090           GfxAction[x][y] = ACTION_ATTACKING;
8091
8092           if (IS_PLAYER(x, y))
8093             DrawPlayerField(x, y);
8094           else
8095             TEST_DrawLevelField(x, y);
8096
8097           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8098
8099           MovDelay[x][y] = 50;
8100
8101           Feld[newx][newy] = EL_FLAMES;
8102           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8103             Feld[newx1][newy1] = EL_FLAMES;
8104           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8105             Feld[newx2][newy2] = EL_FLAMES;
8106
8107           return;
8108         }
8109       }
8110     }
8111     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8112              Feld[newx][newy] == EL_DIAMOND)
8113     {
8114       if (IS_MOVING(newx, newy))
8115         RemoveMovingField(newx, newy);
8116       else
8117       {
8118         Feld[newx][newy] = EL_EMPTY;
8119         TEST_DrawLevelField(newx, newy);
8120       }
8121
8122       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8123     }
8124     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8125              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8126     {
8127       if (AmoebaNr[newx][newy])
8128       {
8129         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8130         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8131             Feld[newx][newy] == EL_BD_AMOEBA)
8132           AmoebaCnt[AmoebaNr[newx][newy]]--;
8133       }
8134
8135       if (IS_MOVING(newx, newy))
8136       {
8137         RemoveMovingField(newx, newy);
8138       }
8139       else
8140       {
8141         Feld[newx][newy] = EL_EMPTY;
8142         TEST_DrawLevelField(newx, newy);
8143       }
8144
8145       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8146     }
8147     else if ((element == EL_PACMAN || element == EL_MOLE)
8148              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8149     {
8150       if (AmoebaNr[newx][newy])
8151       {
8152         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8153         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8154             Feld[newx][newy] == EL_BD_AMOEBA)
8155           AmoebaCnt[AmoebaNr[newx][newy]]--;
8156       }
8157
8158       if (element == EL_MOLE)
8159       {
8160         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8161         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8162
8163         ResetGfxAnimation(x, y);
8164         GfxAction[x][y] = ACTION_DIGGING;
8165         TEST_DrawLevelField(x, y);
8166
8167         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8168
8169         return;                         /* wait for shrinking amoeba */
8170       }
8171       else      /* element == EL_PACMAN */
8172       {
8173         Feld[newx][newy] = EL_EMPTY;
8174         TEST_DrawLevelField(newx, newy);
8175         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8176       }
8177     }
8178     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8179              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8180               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8181     {
8182       /* wait for shrinking amoeba to completely disappear */
8183       return;
8184     }
8185     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8186     {
8187       /* object was running against a wall */
8188
8189       TurnRound(x, y);
8190
8191       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8192         DrawLevelElementAnimation(x, y, element);
8193
8194       if (DONT_TOUCH(element))
8195         TestIfBadThingTouchesPlayer(x, y);
8196
8197       return;
8198     }
8199
8200     InitMovingField(x, y, MovDir[x][y]);
8201
8202     PlayLevelSoundAction(x, y, ACTION_MOVING);
8203   }
8204
8205   if (MovDir[x][y])
8206     ContinueMoving(x, y);
8207 }
8208
8209 void ContinueMoving(int x, int y)
8210 {
8211   int element = Feld[x][y];
8212   struct ElementInfo *ei = &element_info[element];
8213   int direction = MovDir[x][y];
8214   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8215   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8216   int newx = x + dx, newy = y + dy;
8217   int stored = Store[x][y];
8218   int stored_new = Store[newx][newy];
8219   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8220   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8221   boolean last_line = (newy == lev_fieldy - 1);
8222
8223   MovPos[x][y] += getElementMoveStepsize(x, y);
8224
8225   if (pushed_by_player) /* special case: moving object pushed by player */
8226     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8227
8228   if (ABS(MovPos[x][y]) < TILEX)
8229   {
8230     TEST_DrawLevelField(x, y);
8231
8232     return;     /* element is still moving */
8233   }
8234
8235   /* element reached destination field */
8236
8237   Feld[x][y] = EL_EMPTY;
8238   Feld[newx][newy] = element;
8239   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8240
8241   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8242   {
8243     element = Feld[newx][newy] = EL_ACID;
8244   }
8245   else if (element == EL_MOLE)
8246   {
8247     Feld[x][y] = EL_SAND;
8248
8249     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8250   }
8251   else if (element == EL_QUICKSAND_FILLING)
8252   {
8253     element = Feld[newx][newy] = get_next_element(element);
8254     Store[newx][newy] = Store[x][y];
8255   }
8256   else if (element == EL_QUICKSAND_EMPTYING)
8257   {
8258     Feld[x][y] = get_next_element(element);
8259     element = Feld[newx][newy] = Store[x][y];
8260   }
8261   else if (element == EL_QUICKSAND_FAST_FILLING)
8262   {
8263     element = Feld[newx][newy] = get_next_element(element);
8264     Store[newx][newy] = Store[x][y];
8265   }
8266   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8267   {
8268     Feld[x][y] = get_next_element(element);
8269     element = Feld[newx][newy] = Store[x][y];
8270   }
8271   else if (element == EL_MAGIC_WALL_FILLING)
8272   {
8273     element = Feld[newx][newy] = get_next_element(element);
8274     if (!game.magic_wall_active)
8275       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8276     Store[newx][newy] = Store[x][y];
8277   }
8278   else if (element == EL_MAGIC_WALL_EMPTYING)
8279   {
8280     Feld[x][y] = get_next_element(element);
8281     if (!game.magic_wall_active)
8282       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8283     element = Feld[newx][newy] = Store[x][y];
8284
8285     InitField(newx, newy, FALSE);
8286   }
8287   else if (element == EL_BD_MAGIC_WALL_FILLING)
8288   {
8289     element = Feld[newx][newy] = get_next_element(element);
8290     if (!game.magic_wall_active)
8291       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8292     Store[newx][newy] = Store[x][y];
8293   }
8294   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8295   {
8296     Feld[x][y] = get_next_element(element);
8297     if (!game.magic_wall_active)
8298       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8299     element = Feld[newx][newy] = Store[x][y];
8300
8301     InitField(newx, newy, FALSE);
8302   }
8303   else if (element == EL_DC_MAGIC_WALL_FILLING)
8304   {
8305     element = Feld[newx][newy] = get_next_element(element);
8306     if (!game.magic_wall_active)
8307       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8308     Store[newx][newy] = Store[x][y];
8309   }
8310   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8311   {
8312     Feld[x][y] = get_next_element(element);
8313     if (!game.magic_wall_active)
8314       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8315     element = Feld[newx][newy] = Store[x][y];
8316
8317     InitField(newx, newy, FALSE);
8318   }
8319   else if (element == EL_AMOEBA_DROPPING)
8320   {
8321     Feld[x][y] = get_next_element(element);
8322     element = Feld[newx][newy] = Store[x][y];
8323   }
8324   else if (element == EL_SOKOBAN_OBJECT)
8325   {
8326     if (Back[x][y])
8327       Feld[x][y] = Back[x][y];
8328
8329     if (Back[newx][newy])
8330       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8331
8332     Back[x][y] = Back[newx][newy] = 0;
8333   }
8334
8335   Store[x][y] = EL_EMPTY;
8336   MovPos[x][y] = 0;
8337   MovDir[x][y] = 0;
8338   MovDelay[x][y] = 0;
8339
8340   MovDelay[newx][newy] = 0;
8341
8342   if (CAN_CHANGE_OR_HAS_ACTION(element))
8343   {
8344     /* copy element change control values to new field */
8345     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8346     ChangePage[newx][newy]  = ChangePage[x][y];
8347     ChangeCount[newx][newy] = ChangeCount[x][y];
8348     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8349   }
8350
8351   CustomValue[newx][newy] = CustomValue[x][y];
8352
8353   ChangeDelay[x][y] = 0;
8354   ChangePage[x][y] = -1;
8355   ChangeCount[x][y] = 0;
8356   ChangeEvent[x][y] = -1;
8357
8358   CustomValue[x][y] = 0;
8359
8360   /* copy animation control values to new field */
8361   GfxFrame[newx][newy]  = GfxFrame[x][y];
8362   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8363   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8364   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8365
8366   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8367
8368   /* some elements can leave other elements behind after moving */
8369   if (ei->move_leave_element != EL_EMPTY &&
8370       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8371       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8372   {
8373     int move_leave_element = ei->move_leave_element;
8374
8375     /* this makes it possible to leave the removed element again */
8376     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8377       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8378
8379     Feld[x][y] = move_leave_element;
8380
8381     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8382       MovDir[x][y] = direction;
8383
8384     InitField(x, y, FALSE);
8385
8386     if (GFX_CRUMBLED(Feld[x][y]))
8387       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8388
8389     if (ELEM_IS_PLAYER(move_leave_element))
8390       RelocatePlayer(x, y, move_leave_element);
8391   }
8392
8393   /* do this after checking for left-behind element */
8394   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8395
8396   if (!CAN_MOVE(element) ||
8397       (CAN_FALL(element) && direction == MV_DOWN &&
8398        (element == EL_SPRING ||
8399         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8400         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8401     GfxDir[x][y] = MovDir[newx][newy] = 0;
8402
8403   TEST_DrawLevelField(x, y);
8404   TEST_DrawLevelField(newx, newy);
8405
8406   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8407
8408   /* prevent pushed element from moving on in pushed direction */
8409   if (pushed_by_player && CAN_MOVE(element) &&
8410       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8411       !(element_info[element].move_pattern & direction))
8412     TurnRound(newx, newy);
8413
8414   /* prevent elements on conveyor belt from moving on in last direction */
8415   if (pushed_by_conveyor && CAN_FALL(element) &&
8416       direction & MV_HORIZONTAL)
8417     MovDir[newx][newy] = 0;
8418
8419   if (!pushed_by_player)
8420   {
8421     int nextx = newx + dx, nexty = newy + dy;
8422     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8423
8424     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8425
8426     if (CAN_FALL(element) && direction == MV_DOWN)
8427       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8428
8429     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8430       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8431
8432     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8433       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8434   }
8435
8436   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8437   {
8438     TestIfBadThingTouchesPlayer(newx, newy);
8439     TestIfBadThingTouchesFriend(newx, newy);
8440
8441     if (!IS_CUSTOM_ELEMENT(element))
8442       TestIfBadThingTouchesOtherBadThing(newx, newy);
8443   }
8444   else if (element == EL_PENGUIN)
8445     TestIfFriendTouchesBadThing(newx, newy);
8446
8447   if (DONT_GET_HIT_BY(element))
8448   {
8449     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8450   }
8451
8452   /* give the player one last chance (one more frame) to move away */
8453   if (CAN_FALL(element) && direction == MV_DOWN &&
8454       (last_line || (!IS_FREE(x, newy + 1) &&
8455                      (!IS_PLAYER(x, newy + 1) ||
8456                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8457     Impact(x, newy);
8458
8459   if (pushed_by_player && !game.use_change_when_pushing_bug)
8460   {
8461     int push_side = MV_DIR_OPPOSITE(direction);
8462     struct PlayerInfo *player = PLAYERINFO(x, y);
8463
8464     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8465                                player->index_bit, push_side);
8466     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8467                                         player->index_bit, push_side);
8468   }
8469
8470   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8471     MovDelay[newx][newy] = 1;
8472
8473   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8474
8475   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8476   TestIfElementHitsCustomElement(newx, newy, direction);
8477   TestIfPlayerTouchesCustomElement(newx, newy);
8478   TestIfElementTouchesCustomElement(newx, newy);
8479
8480   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8481       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8482     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8483                              MV_DIR_OPPOSITE(direction));
8484 }
8485
8486 int AmoebeNachbarNr(int ax, int ay)
8487 {
8488   int i;
8489   int element = Feld[ax][ay];
8490   int group_nr = 0;
8491   static int xy[4][2] =
8492   {
8493     { 0, -1 },
8494     { -1, 0 },
8495     { +1, 0 },
8496     { 0, +1 }
8497   };
8498
8499   for (i = 0; i < NUM_DIRECTIONS; i++)
8500   {
8501     int x = ax + xy[i][0];
8502     int y = ay + xy[i][1];
8503
8504     if (!IN_LEV_FIELD(x, y))
8505       continue;
8506
8507     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8508       group_nr = AmoebaNr[x][y];
8509   }
8510
8511   return group_nr;
8512 }
8513
8514 void AmoebenVereinigen(int ax, int ay)
8515 {
8516   int i, x, y, xx, yy;
8517   int new_group_nr = AmoebaNr[ax][ay];
8518   static int xy[4][2] =
8519   {
8520     { 0, -1 },
8521     { -1, 0 },
8522     { +1, 0 },
8523     { 0, +1 }
8524   };
8525
8526   if (new_group_nr == 0)
8527     return;
8528
8529   for (i = 0; i < NUM_DIRECTIONS; i++)
8530   {
8531     x = ax + xy[i][0];
8532     y = ay + xy[i][1];
8533
8534     if (!IN_LEV_FIELD(x, y))
8535       continue;
8536
8537     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8538          Feld[x][y] == EL_BD_AMOEBA ||
8539          Feld[x][y] == EL_AMOEBA_DEAD) &&
8540         AmoebaNr[x][y] != new_group_nr)
8541     {
8542       int old_group_nr = AmoebaNr[x][y];
8543
8544       if (old_group_nr == 0)
8545         return;
8546
8547       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8548       AmoebaCnt[old_group_nr] = 0;
8549       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8550       AmoebaCnt2[old_group_nr] = 0;
8551
8552       SCAN_PLAYFIELD(xx, yy)
8553       {
8554         if (AmoebaNr[xx][yy] == old_group_nr)
8555           AmoebaNr[xx][yy] = new_group_nr;
8556       }
8557     }
8558   }
8559 }
8560
8561 void AmoebeUmwandeln(int ax, int ay)
8562 {
8563   int i, x, y;
8564
8565   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8566   {
8567     int group_nr = AmoebaNr[ax][ay];
8568
8569 #ifdef DEBUG
8570     if (group_nr == 0)
8571     {
8572       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8573       printf("AmoebeUmwandeln(): This should never happen!\n");
8574       return;
8575     }
8576 #endif
8577
8578     SCAN_PLAYFIELD(x, y)
8579     {
8580       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8581       {
8582         AmoebaNr[x][y] = 0;
8583         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8584       }
8585     }
8586
8587     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8588                             SND_AMOEBA_TURNING_TO_GEM :
8589                             SND_AMOEBA_TURNING_TO_ROCK));
8590     Bang(ax, ay);
8591   }
8592   else
8593   {
8594     static int xy[4][2] =
8595     {
8596       { 0, -1 },
8597       { -1, 0 },
8598       { +1, 0 },
8599       { 0, +1 }
8600     };
8601
8602     for (i = 0; i < NUM_DIRECTIONS; i++)
8603     {
8604       x = ax + xy[i][0];
8605       y = ay + xy[i][1];
8606
8607       if (!IN_LEV_FIELD(x, y))
8608         continue;
8609
8610       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8611       {
8612         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8613                               SND_AMOEBA_TURNING_TO_GEM :
8614                               SND_AMOEBA_TURNING_TO_ROCK));
8615         Bang(x, y);
8616       }
8617     }
8618   }
8619 }
8620
8621 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8622 {
8623   int x, y;
8624   int group_nr = AmoebaNr[ax][ay];
8625   boolean done = FALSE;
8626
8627 #ifdef DEBUG
8628   if (group_nr == 0)
8629   {
8630     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8631     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8632     return;
8633   }
8634 #endif
8635
8636   SCAN_PLAYFIELD(x, y)
8637   {
8638     if (AmoebaNr[x][y] == group_nr &&
8639         (Feld[x][y] == EL_AMOEBA_DEAD ||
8640          Feld[x][y] == EL_BD_AMOEBA ||
8641          Feld[x][y] == EL_AMOEBA_GROWING))
8642     {
8643       AmoebaNr[x][y] = 0;
8644       Feld[x][y] = new_element;
8645       InitField(x, y, FALSE);
8646       TEST_DrawLevelField(x, y);
8647       done = TRUE;
8648     }
8649   }
8650
8651   if (done)
8652     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8653                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8654                             SND_BD_AMOEBA_TURNING_TO_GEM));
8655 }
8656
8657 void AmoebeWaechst(int x, int y)
8658 {
8659   static unsigned int sound_delay = 0;
8660   static unsigned int sound_delay_value = 0;
8661
8662   if (!MovDelay[x][y])          /* start new growing cycle */
8663   {
8664     MovDelay[x][y] = 7;
8665
8666     if (DelayReached(&sound_delay, sound_delay_value))
8667     {
8668       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8669       sound_delay_value = 30;
8670     }
8671   }
8672
8673   if (MovDelay[x][y])           /* wait some time before growing bigger */
8674   {
8675     MovDelay[x][y]--;
8676     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8677     {
8678       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8679                                            6 - MovDelay[x][y]);
8680
8681       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8682     }
8683
8684     if (!MovDelay[x][y])
8685     {
8686       Feld[x][y] = Store[x][y];
8687       Store[x][y] = 0;
8688       TEST_DrawLevelField(x, y);
8689     }
8690   }
8691 }
8692
8693 void AmoebaDisappearing(int x, int y)
8694 {
8695   static unsigned int sound_delay = 0;
8696   static unsigned int sound_delay_value = 0;
8697
8698   if (!MovDelay[x][y])          /* start new shrinking cycle */
8699   {
8700     MovDelay[x][y] = 7;
8701
8702     if (DelayReached(&sound_delay, sound_delay_value))
8703       sound_delay_value = 30;
8704   }
8705
8706   if (MovDelay[x][y])           /* wait some time before shrinking */
8707   {
8708     MovDelay[x][y]--;
8709     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8710     {
8711       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8712                                            6 - MovDelay[x][y]);
8713
8714       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8715     }
8716
8717     if (!MovDelay[x][y])
8718     {
8719       Feld[x][y] = EL_EMPTY;
8720       TEST_DrawLevelField(x, y);
8721
8722       /* don't let mole enter this field in this cycle;
8723          (give priority to objects falling to this field from above) */
8724       Stop[x][y] = TRUE;
8725     }
8726   }
8727 }
8728
8729 void AmoebeAbleger(int ax, int ay)
8730 {
8731   int i;
8732   int element = Feld[ax][ay];
8733   int graphic = el2img(element);
8734   int newax = ax, neway = ay;
8735   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8736   static int xy[4][2] =
8737   {
8738     { 0, -1 },
8739     { -1, 0 },
8740     { +1, 0 },
8741     { 0, +1 }
8742   };
8743
8744   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8745   {
8746     Feld[ax][ay] = EL_AMOEBA_DEAD;
8747     TEST_DrawLevelField(ax, ay);
8748     return;
8749   }
8750
8751   if (IS_ANIMATED(graphic))
8752     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8753
8754   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8755     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8756
8757   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8758   {
8759     MovDelay[ax][ay]--;
8760     if (MovDelay[ax][ay])
8761       return;
8762   }
8763
8764   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8765   {
8766     int start = RND(4);
8767     int x = ax + xy[start][0];
8768     int y = ay + xy[start][1];
8769
8770     if (!IN_LEV_FIELD(x, y))
8771       return;
8772
8773     if (IS_FREE(x, y) ||
8774         CAN_GROW_INTO(Feld[x][y]) ||
8775         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8776         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8777     {
8778       newax = x;
8779       neway = y;
8780     }
8781
8782     if (newax == ax && neway == ay)
8783       return;
8784   }
8785   else                          /* normal or "filled" (BD style) amoeba */
8786   {
8787     int start = RND(4);
8788     boolean waiting_for_player = FALSE;
8789
8790     for (i = 0; i < NUM_DIRECTIONS; i++)
8791     {
8792       int j = (start + i) % 4;
8793       int x = ax + xy[j][0];
8794       int y = ay + xy[j][1];
8795
8796       if (!IN_LEV_FIELD(x, y))
8797         continue;
8798
8799       if (IS_FREE(x, y) ||
8800           CAN_GROW_INTO(Feld[x][y]) ||
8801           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8802           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8803       {
8804         newax = x;
8805         neway = y;
8806         break;
8807       }
8808       else if (IS_PLAYER(x, y))
8809         waiting_for_player = TRUE;
8810     }
8811
8812     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8813     {
8814       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8815       {
8816         Feld[ax][ay] = EL_AMOEBA_DEAD;
8817         TEST_DrawLevelField(ax, ay);
8818         AmoebaCnt[AmoebaNr[ax][ay]]--;
8819
8820         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8821         {
8822           if (element == EL_AMOEBA_FULL)
8823             AmoebeUmwandeln(ax, ay);
8824           else if (element == EL_BD_AMOEBA)
8825             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8826         }
8827       }
8828       return;
8829     }
8830     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8831     {
8832       /* amoeba gets larger by growing in some direction */
8833
8834       int new_group_nr = AmoebaNr[ax][ay];
8835
8836 #ifdef DEBUG
8837   if (new_group_nr == 0)
8838   {
8839     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8840     printf("AmoebeAbleger(): This should never happen!\n");
8841     return;
8842   }
8843 #endif
8844
8845       AmoebaNr[newax][neway] = new_group_nr;
8846       AmoebaCnt[new_group_nr]++;
8847       AmoebaCnt2[new_group_nr]++;
8848
8849       /* if amoeba touches other amoeba(s) after growing, unify them */
8850       AmoebenVereinigen(newax, neway);
8851
8852       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8853       {
8854         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8855         return;
8856       }
8857     }
8858   }
8859
8860   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8861       (neway == lev_fieldy - 1 && newax != ax))
8862   {
8863     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8864     Store[newax][neway] = element;
8865   }
8866   else if (neway == ay || element == EL_EMC_DRIPPER)
8867   {
8868     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8869
8870     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8871   }
8872   else
8873   {
8874     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8875     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8876     Store[ax][ay] = EL_AMOEBA_DROP;
8877     ContinueMoving(ax, ay);
8878     return;
8879   }
8880
8881   TEST_DrawLevelField(newax, neway);
8882 }
8883
8884 void Life(int ax, int ay)
8885 {
8886   int x1, y1, x2, y2;
8887   int life_time = 40;
8888   int element = Feld[ax][ay];
8889   int graphic = el2img(element);
8890   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8891                          level.biomaze);
8892   boolean changed = FALSE;
8893
8894   if (IS_ANIMATED(graphic))
8895     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8896
8897   if (Stop[ax][ay])
8898     return;
8899
8900   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8901     MovDelay[ax][ay] = life_time;
8902
8903   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8904   {
8905     MovDelay[ax][ay]--;
8906     if (MovDelay[ax][ay])
8907       return;
8908   }
8909
8910   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8911   {
8912     int xx = ax+x1, yy = ay+y1;
8913     int nachbarn = 0;
8914
8915     if (!IN_LEV_FIELD(xx, yy))
8916       continue;
8917
8918     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8919     {
8920       int x = xx+x2, y = yy+y2;
8921
8922       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8923         continue;
8924
8925       if (((Feld[x][y] == element ||
8926             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8927            !Stop[x][y]) ||
8928           (IS_FREE(x, y) && Stop[x][y]))
8929         nachbarn++;
8930     }
8931
8932     if (xx == ax && yy == ay)           /* field in the middle */
8933     {
8934       if (nachbarn < life_parameter[0] ||
8935           nachbarn > life_parameter[1])
8936       {
8937         Feld[xx][yy] = EL_EMPTY;
8938         if (!Stop[xx][yy])
8939           TEST_DrawLevelField(xx, yy);
8940         Stop[xx][yy] = TRUE;
8941         changed = TRUE;
8942       }
8943     }
8944     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8945     {                                   /* free border field */
8946       if (nachbarn >= life_parameter[2] &&
8947           nachbarn <= life_parameter[3])
8948       {
8949         Feld[xx][yy] = element;
8950         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8951         if (!Stop[xx][yy])
8952           TEST_DrawLevelField(xx, yy);
8953         Stop[xx][yy] = TRUE;
8954         changed = TRUE;
8955       }
8956     }
8957   }
8958
8959   if (changed)
8960     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8961                    SND_GAME_OF_LIFE_GROWING);
8962 }
8963
8964 static void InitRobotWheel(int x, int y)
8965 {
8966   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8967 }
8968
8969 static void RunRobotWheel(int x, int y)
8970 {
8971   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8972 }
8973
8974 static void StopRobotWheel(int x, int y)
8975 {
8976   if (ZX == x && ZY == y)
8977   {
8978     ZX = ZY = -1;
8979
8980     game.robot_wheel_active = FALSE;
8981   }
8982 }
8983
8984 static void InitTimegateWheel(int x, int y)
8985 {
8986   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8987 }
8988
8989 static void RunTimegateWheel(int x, int y)
8990 {
8991   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8992 }
8993
8994 static void InitMagicBallDelay(int x, int y)
8995 {
8996   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8997 }
8998
8999 static void ActivateMagicBall(int bx, int by)
9000 {
9001   int x, y;
9002
9003   if (level.ball_random)
9004   {
9005     int pos_border = RND(8);    /* select one of the eight border elements */
9006     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9007     int xx = pos_content % 3;
9008     int yy = pos_content / 3;
9009
9010     x = bx - 1 + xx;
9011     y = by - 1 + yy;
9012
9013     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9014       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9015   }
9016   else
9017   {
9018     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9019     {
9020       int xx = x - bx + 1;
9021       int yy = y - by + 1;
9022
9023       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9024         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9025     }
9026   }
9027
9028   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9029 }
9030
9031 void CheckExit(int x, int y)
9032 {
9033   if (local_player->gems_still_needed > 0 ||
9034       local_player->sokobanfields_still_needed > 0 ||
9035       local_player->lights_still_needed > 0)
9036   {
9037     int element = Feld[x][y];
9038     int graphic = el2img(element);
9039
9040     if (IS_ANIMATED(graphic))
9041       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9042
9043     return;
9044   }
9045
9046   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9047     return;
9048
9049   Feld[x][y] = EL_EXIT_OPENING;
9050
9051   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9052 }
9053
9054 void CheckExitEM(int x, int y)
9055 {
9056   if (local_player->gems_still_needed > 0 ||
9057       local_player->sokobanfields_still_needed > 0 ||
9058       local_player->lights_still_needed > 0)
9059   {
9060     int element = Feld[x][y];
9061     int graphic = el2img(element);
9062
9063     if (IS_ANIMATED(graphic))
9064       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9065
9066     return;
9067   }
9068
9069   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9070     return;
9071
9072   Feld[x][y] = EL_EM_EXIT_OPENING;
9073
9074   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9075 }
9076
9077 void CheckExitSteel(int x, int y)
9078 {
9079   if (local_player->gems_still_needed > 0 ||
9080       local_player->sokobanfields_still_needed > 0 ||
9081       local_player->lights_still_needed > 0)
9082   {
9083     int element = Feld[x][y];
9084     int graphic = el2img(element);
9085
9086     if (IS_ANIMATED(graphic))
9087       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9088
9089     return;
9090   }
9091
9092   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9093     return;
9094
9095   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9096
9097   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9098 }
9099
9100 void CheckExitSteelEM(int x, int y)
9101 {
9102   if (local_player->gems_still_needed > 0 ||
9103       local_player->sokobanfields_still_needed > 0 ||
9104       local_player->lights_still_needed > 0)
9105   {
9106     int element = Feld[x][y];
9107     int graphic = el2img(element);
9108
9109     if (IS_ANIMATED(graphic))
9110       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9111
9112     return;
9113   }
9114
9115   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9116     return;
9117
9118   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9119
9120   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9121 }
9122
9123 void CheckExitSP(int x, int y)
9124 {
9125   if (local_player->gems_still_needed > 0)
9126   {
9127     int element = Feld[x][y];
9128     int graphic = el2img(element);
9129
9130     if (IS_ANIMATED(graphic))
9131       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9132
9133     return;
9134   }
9135
9136   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9137     return;
9138
9139   Feld[x][y] = EL_SP_EXIT_OPENING;
9140
9141   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9142 }
9143
9144 static void CloseAllOpenTimegates()
9145 {
9146   int x, y;
9147
9148   SCAN_PLAYFIELD(x, y)
9149   {
9150     int element = Feld[x][y];
9151
9152     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9153     {
9154       Feld[x][y] = EL_TIMEGATE_CLOSING;
9155
9156       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9157     }
9158   }
9159 }
9160
9161 void DrawTwinkleOnField(int x, int y)
9162 {
9163   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9164     return;
9165
9166   if (Feld[x][y] == EL_BD_DIAMOND)
9167     return;
9168
9169   if (MovDelay[x][y] == 0)      /* next animation frame */
9170     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9171
9172   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9173   {
9174     MovDelay[x][y]--;
9175
9176     DrawLevelElementAnimation(x, y, Feld[x][y]);
9177
9178     if (MovDelay[x][y] != 0)
9179     {
9180       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9181                                            10 - MovDelay[x][y]);
9182
9183       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9184     }
9185   }
9186 }
9187
9188 void MauerWaechst(int x, int y)
9189 {
9190   int delay = 6;
9191
9192   if (!MovDelay[x][y])          /* next animation frame */
9193     MovDelay[x][y] = 3 * delay;
9194
9195   if (MovDelay[x][y])           /* wait some time before next frame */
9196   {
9197     MovDelay[x][y]--;
9198
9199     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9200     {
9201       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9202       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9203
9204       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9205     }
9206
9207     if (!MovDelay[x][y])
9208     {
9209       if (MovDir[x][y] == MV_LEFT)
9210       {
9211         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9212           TEST_DrawLevelField(x - 1, y);
9213       }
9214       else if (MovDir[x][y] == MV_RIGHT)
9215       {
9216         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9217           TEST_DrawLevelField(x + 1, y);
9218       }
9219       else if (MovDir[x][y] == MV_UP)
9220       {
9221         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9222           TEST_DrawLevelField(x, y - 1);
9223       }
9224       else
9225       {
9226         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9227           TEST_DrawLevelField(x, y + 1);
9228       }
9229
9230       Feld[x][y] = Store[x][y];
9231       Store[x][y] = 0;
9232       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9233       TEST_DrawLevelField(x, y);
9234     }
9235   }
9236 }
9237
9238 void MauerAbleger(int ax, int ay)
9239 {
9240   int element = Feld[ax][ay];
9241   int graphic = el2img(element);
9242   boolean oben_frei = FALSE, unten_frei = FALSE;
9243   boolean links_frei = FALSE, rechts_frei = FALSE;
9244   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9245   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9246   boolean new_wall = FALSE;
9247
9248   if (IS_ANIMATED(graphic))
9249     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9250
9251   if (!MovDelay[ax][ay])        /* start building new wall */
9252     MovDelay[ax][ay] = 6;
9253
9254   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9255   {
9256     MovDelay[ax][ay]--;
9257     if (MovDelay[ax][ay])
9258       return;
9259   }
9260
9261   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9262     oben_frei = TRUE;
9263   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9264     unten_frei = TRUE;
9265   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9266     links_frei = TRUE;
9267   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9268     rechts_frei = TRUE;
9269
9270   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9271       element == EL_EXPANDABLE_WALL_ANY)
9272   {
9273     if (oben_frei)
9274     {
9275       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9276       Store[ax][ay-1] = element;
9277       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9278       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9279         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9280                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9281       new_wall = TRUE;
9282     }
9283     if (unten_frei)
9284     {
9285       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9286       Store[ax][ay+1] = element;
9287       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9288       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9289         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9290                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9291       new_wall = TRUE;
9292     }
9293   }
9294
9295   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9296       element == EL_EXPANDABLE_WALL_ANY ||
9297       element == EL_EXPANDABLE_WALL ||
9298       element == EL_BD_EXPANDABLE_WALL)
9299   {
9300     if (links_frei)
9301     {
9302       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9303       Store[ax-1][ay] = element;
9304       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9305       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9306         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9307                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9308       new_wall = TRUE;
9309     }
9310
9311     if (rechts_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_RIGHT;
9316       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9317         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9318                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9319       new_wall = TRUE;
9320     }
9321   }
9322
9323   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9324     TEST_DrawLevelField(ax, ay);
9325
9326   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9327     oben_massiv = TRUE;
9328   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9329     unten_massiv = TRUE;
9330   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9331     links_massiv = TRUE;
9332   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9333     rechts_massiv = TRUE;
9334
9335   if (((oben_massiv && unten_massiv) ||
9336        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9337        element == EL_EXPANDABLE_WALL) &&
9338       ((links_massiv && rechts_massiv) ||
9339        element == EL_EXPANDABLE_WALL_VERTICAL))
9340     Feld[ax][ay] = EL_WALL;
9341
9342   if (new_wall)
9343     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9344 }
9345
9346 void MauerAblegerStahl(int ax, int ay)
9347 {
9348   int element = Feld[ax][ay];
9349   int graphic = el2img(element);
9350   boolean oben_frei = FALSE, unten_frei = FALSE;
9351   boolean links_frei = FALSE, rechts_frei = FALSE;
9352   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9353   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9354   boolean new_wall = FALSE;
9355
9356   if (IS_ANIMATED(graphic))
9357     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9358
9359   if (!MovDelay[ax][ay])        /* start building new wall */
9360     MovDelay[ax][ay] = 6;
9361
9362   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9363   {
9364     MovDelay[ax][ay]--;
9365     if (MovDelay[ax][ay])
9366       return;
9367   }
9368
9369   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9370     oben_frei = TRUE;
9371   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9372     unten_frei = TRUE;
9373   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9374     links_frei = TRUE;
9375   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9376     rechts_frei = TRUE;
9377
9378   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9379       element == EL_EXPANDABLE_STEELWALL_ANY)
9380   {
9381     if (oben_frei)
9382     {
9383       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9384       Store[ax][ay-1] = element;
9385       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9386       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9387         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9388                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9389       new_wall = TRUE;
9390     }
9391     if (unten_frei)
9392     {
9393       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9394       Store[ax][ay+1] = element;
9395       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9396       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9397         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9398                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9399       new_wall = TRUE;
9400     }
9401   }
9402
9403   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9404       element == EL_EXPANDABLE_STEELWALL_ANY)
9405   {
9406     if (links_frei)
9407     {
9408       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9409       Store[ax-1][ay] = element;
9410       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9411       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9412         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9413                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9414       new_wall = TRUE;
9415     }
9416
9417     if (rechts_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_RIGHT;
9422       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9423         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9424                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9425       new_wall = TRUE;
9426     }
9427   }
9428
9429   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9430     oben_massiv = TRUE;
9431   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9432     unten_massiv = TRUE;
9433   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9434     links_massiv = TRUE;
9435   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9436     rechts_massiv = TRUE;
9437
9438   if (((oben_massiv && unten_massiv) ||
9439        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9440       ((links_massiv && rechts_massiv) ||
9441        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9442     Feld[ax][ay] = EL_STEELWALL;
9443
9444   if (new_wall)
9445     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9446 }
9447
9448 void CheckForDragon(int x, int y)
9449 {
9450   int i, j;
9451   boolean dragon_found = FALSE;
9452   static int xy[4][2] =
9453   {
9454     { 0, -1 },
9455     { -1, 0 },
9456     { +1, 0 },
9457     { 0, +1 }
9458   };
9459
9460   for (i = 0; i < NUM_DIRECTIONS; i++)
9461   {
9462     for (j = 0; j < 4; j++)
9463     {
9464       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9465
9466       if (IN_LEV_FIELD(xx, yy) &&
9467           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9468       {
9469         if (Feld[xx][yy] == EL_DRAGON)
9470           dragon_found = TRUE;
9471       }
9472       else
9473         break;
9474     }
9475   }
9476
9477   if (!dragon_found)
9478   {
9479     for (i = 0; i < NUM_DIRECTIONS; i++)
9480     {
9481       for (j = 0; j < 3; j++)
9482       {
9483         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9484   
9485         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9486         {
9487           Feld[xx][yy] = EL_EMPTY;
9488           TEST_DrawLevelField(xx, yy);
9489         }
9490         else
9491           break;
9492       }
9493     }
9494   }
9495 }
9496
9497 static void InitBuggyBase(int x, int y)
9498 {
9499   int element = Feld[x][y];
9500   int activating_delay = FRAMES_PER_SECOND / 4;
9501
9502   ChangeDelay[x][y] =
9503     (element == EL_SP_BUGGY_BASE ?
9504      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9505      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9506      activating_delay :
9507      element == EL_SP_BUGGY_BASE_ACTIVE ?
9508      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9509 }
9510
9511 static void WarnBuggyBase(int x, int y)
9512 {
9513   int i;
9514   static int xy[4][2] =
9515   {
9516     { 0, -1 },
9517     { -1, 0 },
9518     { +1, 0 },
9519     { 0, +1 }
9520   };
9521
9522   for (i = 0; i < NUM_DIRECTIONS; i++)
9523   {
9524     int xx = x + xy[i][0];
9525     int yy = y + xy[i][1];
9526
9527     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9528     {
9529       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9530
9531       break;
9532     }
9533   }
9534 }
9535
9536 static void InitTrap(int x, int y)
9537 {
9538   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9539 }
9540
9541 static void ActivateTrap(int x, int y)
9542 {
9543   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9544 }
9545
9546 static void ChangeActiveTrap(int x, int y)
9547 {
9548   int graphic = IMG_TRAP_ACTIVE;
9549
9550   /* if new animation frame was drawn, correct crumbled sand border */
9551   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9552     TEST_DrawLevelFieldCrumbled(x, y);
9553 }
9554
9555 static int getSpecialActionElement(int element, int number, int base_element)
9556 {
9557   return (element != EL_EMPTY ? element :
9558           number != -1 ? base_element + number - 1 :
9559           EL_EMPTY);
9560 }
9561
9562 static int getModifiedActionNumber(int value_old, int operator, int operand,
9563                                    int value_min, int value_max)
9564 {
9565   int value_new = (operator == CA_MODE_SET      ? operand :
9566                    operator == CA_MODE_ADD      ? value_old + operand :
9567                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9568                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9569                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9570                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9571                    value_old);
9572
9573   return (value_new < value_min ? value_min :
9574           value_new > value_max ? value_max :
9575           value_new);
9576 }
9577
9578 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9579 {
9580   struct ElementInfo *ei = &element_info[element];
9581   struct ElementChangeInfo *change = &ei->change_page[page];
9582   int target_element = change->target_element;
9583   int action_type = change->action_type;
9584   int action_mode = change->action_mode;
9585   int action_arg = change->action_arg;
9586   int action_element = change->action_element;
9587   int i;
9588
9589   if (!change->has_action)
9590     return;
9591
9592   /* ---------- determine action paramater values -------------------------- */
9593
9594   int level_time_value =
9595     (level.time > 0 ? TimeLeft :
9596      TimePlayed);
9597
9598   int action_arg_element_raw =
9599     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9600      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9601      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9602      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9603      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9604      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9605      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9606      EL_EMPTY);
9607   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9608
9609   int action_arg_direction =
9610     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9611      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9612      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9613      change->actual_trigger_side :
9614      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9615      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9616      MV_NONE);
9617
9618   int action_arg_number_min =
9619     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9620      CA_ARG_MIN);
9621
9622   int action_arg_number_max =
9623     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9624      action_type == CA_SET_LEVEL_GEMS ? 999 :
9625      action_type == CA_SET_LEVEL_TIME ? 9999 :
9626      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9627      action_type == CA_SET_CE_VALUE ? 9999 :
9628      action_type == CA_SET_CE_SCORE ? 9999 :
9629      CA_ARG_MAX);
9630
9631   int action_arg_number_reset =
9632     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9633      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9634      action_type == CA_SET_LEVEL_TIME ? level.time :
9635      action_type == CA_SET_LEVEL_SCORE ? 0 :
9636      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9637      action_type == CA_SET_CE_SCORE ? 0 :
9638      0);
9639
9640   int action_arg_number =
9641     (action_arg <= CA_ARG_MAX ? action_arg :
9642      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9643      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9644      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9645      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9646      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9647      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9648      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9649      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9650      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9651      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9652      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9653      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9654      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9655      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9656      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9657      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9658      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9659      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9660      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9661      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9662      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9663      -1);
9664
9665   int action_arg_number_old =
9666     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9667      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9668      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9669      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9670      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9671      0);
9672
9673   int action_arg_number_new =
9674     getModifiedActionNumber(action_arg_number_old,
9675                             action_mode, action_arg_number,
9676                             action_arg_number_min, action_arg_number_max);
9677
9678   int trigger_player_bits =
9679     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9680      change->actual_trigger_player_bits : change->trigger_player);
9681
9682   int action_arg_player_bits =
9683     (action_arg >= CA_ARG_PLAYER_1 &&
9684      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9685      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9686      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9687      PLAYER_BITS_ANY);
9688
9689   /* ---------- execute action  -------------------------------------------- */
9690
9691   switch (action_type)
9692   {
9693     case CA_NO_ACTION:
9694     {
9695       return;
9696     }
9697
9698     /* ---------- level actions  ------------------------------------------- */
9699
9700     case CA_RESTART_LEVEL:
9701     {
9702       game.restart_level = TRUE;
9703
9704       break;
9705     }
9706
9707     case CA_SHOW_ENVELOPE:
9708     {
9709       int element = getSpecialActionElement(action_arg_element,
9710                                             action_arg_number, EL_ENVELOPE_1);
9711
9712       if (IS_ENVELOPE(element))
9713         local_player->show_envelope = element;
9714
9715       break;
9716     }
9717
9718     case CA_SET_LEVEL_TIME:
9719     {
9720       if (level.time > 0)       /* only modify limited time value */
9721       {
9722         TimeLeft = action_arg_number_new;
9723
9724         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9725
9726         DisplayGameControlValues();
9727
9728         if (!TimeLeft && setup.time_limit)
9729           for (i = 0; i < MAX_PLAYERS; i++)
9730             KillPlayer(&stored_player[i]);
9731       }
9732
9733       break;
9734     }
9735
9736     case CA_SET_LEVEL_SCORE:
9737     {
9738       local_player->score = action_arg_number_new;
9739
9740       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9741
9742       DisplayGameControlValues();
9743
9744       break;
9745     }
9746
9747     case CA_SET_LEVEL_GEMS:
9748     {
9749       local_player->gems_still_needed = action_arg_number_new;
9750
9751       game.snapshot.collected_item = TRUE;
9752
9753       game_panel_controls[GAME_PANEL_GEMS].value =
9754         local_player->gems_still_needed;
9755
9756       DisplayGameControlValues();
9757
9758       break;
9759     }
9760
9761     case CA_SET_LEVEL_WIND:
9762     {
9763       game.wind_direction = action_arg_direction;
9764
9765       break;
9766     }
9767
9768     case CA_SET_LEVEL_RANDOM_SEED:
9769     {
9770       /* ensure that setting a new random seed while playing is predictable */
9771       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9772
9773       break;
9774     }
9775
9776     /* ---------- player actions  ------------------------------------------ */
9777
9778     case CA_MOVE_PLAYER:
9779     {
9780       /* automatically move to the next field in specified direction */
9781       for (i = 0; i < MAX_PLAYERS; i++)
9782         if (trigger_player_bits & (1 << i))
9783           stored_player[i].programmed_action = action_arg_direction;
9784
9785       break;
9786     }
9787
9788     case CA_EXIT_PLAYER:
9789     {
9790       for (i = 0; i < MAX_PLAYERS; i++)
9791         if (action_arg_player_bits & (1 << i))
9792           ExitPlayer(&stored_player[i]);
9793
9794       if (AllPlayersGone)
9795         PlayerWins(local_player);
9796
9797       break;
9798     }
9799
9800     case CA_KILL_PLAYER:
9801     {
9802       for (i = 0; i < MAX_PLAYERS; i++)
9803         if (action_arg_player_bits & (1 << i))
9804           KillPlayer(&stored_player[i]);
9805
9806       break;
9807     }
9808
9809     case CA_SET_PLAYER_KEYS:
9810     {
9811       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9812       int element = getSpecialActionElement(action_arg_element,
9813                                             action_arg_number, EL_KEY_1);
9814
9815       if (IS_KEY(element))
9816       {
9817         for (i = 0; i < MAX_PLAYERS; i++)
9818         {
9819           if (trigger_player_bits & (1 << i))
9820           {
9821             stored_player[i].key[KEY_NR(element)] = key_state;
9822
9823             DrawGameDoorValues();
9824           }
9825         }
9826       }
9827
9828       break;
9829     }
9830
9831     case CA_SET_PLAYER_SPEED:
9832     {
9833       for (i = 0; i < MAX_PLAYERS; i++)
9834       {
9835         if (trigger_player_bits & (1 << i))
9836         {
9837           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9838
9839           if (action_arg == CA_ARG_SPEED_FASTER &&
9840               stored_player[i].cannot_move)
9841           {
9842             action_arg_number = STEPSIZE_VERY_SLOW;
9843           }
9844           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9845                    action_arg == CA_ARG_SPEED_FASTER)
9846           {
9847             action_arg_number = 2;
9848             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9849                            CA_MODE_MULTIPLY);
9850           }
9851           else if (action_arg == CA_ARG_NUMBER_RESET)
9852           {
9853             action_arg_number = level.initial_player_stepsize[i];
9854           }
9855
9856           move_stepsize =
9857             getModifiedActionNumber(move_stepsize,
9858                                     action_mode,
9859                                     action_arg_number,
9860                                     action_arg_number_min,
9861                                     action_arg_number_max);
9862
9863           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9864         }
9865       }
9866
9867       break;
9868     }
9869
9870     case CA_SET_PLAYER_SHIELD:
9871     {
9872       for (i = 0; i < MAX_PLAYERS; i++)
9873       {
9874         if (trigger_player_bits & (1 << i))
9875         {
9876           if (action_arg == CA_ARG_SHIELD_OFF)
9877           {
9878             stored_player[i].shield_normal_time_left = 0;
9879             stored_player[i].shield_deadly_time_left = 0;
9880           }
9881           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9882           {
9883             stored_player[i].shield_normal_time_left = 999999;
9884           }
9885           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9886           {
9887             stored_player[i].shield_normal_time_left = 999999;
9888             stored_player[i].shield_deadly_time_left = 999999;
9889           }
9890         }
9891       }
9892
9893       break;
9894     }
9895
9896     case CA_SET_PLAYER_GRAVITY:
9897     {
9898       for (i = 0; i < MAX_PLAYERS; i++)
9899       {
9900         if (trigger_player_bits & (1 << i))
9901         {
9902           stored_player[i].gravity =
9903             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9904              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9905              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9906              stored_player[i].gravity);
9907         }
9908       }
9909
9910       break;
9911     }
9912
9913     case CA_SET_PLAYER_ARTWORK:
9914     {
9915       for (i = 0; i < MAX_PLAYERS; i++)
9916       {
9917         if (trigger_player_bits & (1 << i))
9918         {
9919           int artwork_element = action_arg_element;
9920
9921           if (action_arg == CA_ARG_ELEMENT_RESET)
9922             artwork_element =
9923               (level.use_artwork_element[i] ? level.artwork_element[i] :
9924                stored_player[i].element_nr);
9925
9926           if (stored_player[i].artwork_element != artwork_element)
9927             stored_player[i].Frame = 0;
9928
9929           stored_player[i].artwork_element = artwork_element;
9930
9931           SetPlayerWaiting(&stored_player[i], FALSE);
9932
9933           /* set number of special actions for bored and sleeping animation */
9934           stored_player[i].num_special_action_bored =
9935             get_num_special_action(artwork_element,
9936                                    ACTION_BORING_1, ACTION_BORING_LAST);
9937           stored_player[i].num_special_action_sleeping =
9938             get_num_special_action(artwork_element,
9939                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9940         }
9941       }
9942
9943       break;
9944     }
9945
9946     case CA_SET_PLAYER_INVENTORY:
9947     {
9948       for (i = 0; i < MAX_PLAYERS; i++)
9949       {
9950         struct PlayerInfo *player = &stored_player[i];
9951         int j, k;
9952
9953         if (trigger_player_bits & (1 << i))
9954         {
9955           int inventory_element = action_arg_element;
9956
9957           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9958               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9959               action_arg == CA_ARG_ELEMENT_ACTION)
9960           {
9961             int element = inventory_element;
9962             int collect_count = element_info[element].collect_count_initial;
9963
9964             if (!IS_CUSTOM_ELEMENT(element))
9965               collect_count = 1;
9966
9967             if (collect_count == 0)
9968               player->inventory_infinite_element = element;
9969             else
9970               for (k = 0; k < collect_count; k++)
9971                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9972                   player->inventory_element[player->inventory_size++] =
9973                     element;
9974           }
9975           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9976                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9977                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9978           {
9979             if (player->inventory_infinite_element != EL_UNDEFINED &&
9980                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9981                                      action_arg_element_raw))
9982               player->inventory_infinite_element = EL_UNDEFINED;
9983
9984             for (k = 0, j = 0; j < player->inventory_size; j++)
9985             {
9986               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9987                                         action_arg_element_raw))
9988                 player->inventory_element[k++] = player->inventory_element[j];
9989             }
9990
9991             player->inventory_size = k;
9992           }
9993           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9994           {
9995             if (player->inventory_size > 0)
9996             {
9997               for (j = 0; j < player->inventory_size - 1; j++)
9998                 player->inventory_element[j] = player->inventory_element[j + 1];
9999
10000               player->inventory_size--;
10001             }
10002           }
10003           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10004           {
10005             if (player->inventory_size > 0)
10006               player->inventory_size--;
10007           }
10008           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10009           {
10010             player->inventory_infinite_element = EL_UNDEFINED;
10011             player->inventory_size = 0;
10012           }
10013           else if (action_arg == CA_ARG_INVENTORY_RESET)
10014           {
10015             player->inventory_infinite_element = EL_UNDEFINED;
10016             player->inventory_size = 0;
10017
10018             if (level.use_initial_inventory[i])
10019             {
10020               for (j = 0; j < level.initial_inventory_size[i]; j++)
10021               {
10022                 int element = level.initial_inventory_content[i][j];
10023                 int collect_count = element_info[element].collect_count_initial;
10024
10025                 if (!IS_CUSTOM_ELEMENT(element))
10026                   collect_count = 1;
10027
10028                 if (collect_count == 0)
10029                   player->inventory_infinite_element = element;
10030                 else
10031                   for (k = 0; k < collect_count; k++)
10032                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10033                       player->inventory_element[player->inventory_size++] =
10034                         element;
10035               }
10036             }
10037           }
10038         }
10039       }
10040
10041       break;
10042     }
10043
10044     /* ---------- CE actions  ---------------------------------------------- */
10045
10046     case CA_SET_CE_VALUE:
10047     {
10048       int last_ce_value = CustomValue[x][y];
10049
10050       CustomValue[x][y] = action_arg_number_new;
10051
10052       if (CustomValue[x][y] != last_ce_value)
10053       {
10054         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10055         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10056
10057         if (CustomValue[x][y] == 0)
10058         {
10059           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10060           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10061         }
10062       }
10063
10064       break;
10065     }
10066
10067     case CA_SET_CE_SCORE:
10068     {
10069       int last_ce_score = ei->collect_score;
10070
10071       ei->collect_score = action_arg_number_new;
10072
10073       if (ei->collect_score != last_ce_score)
10074       {
10075         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10076         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10077
10078         if (ei->collect_score == 0)
10079         {
10080           int xx, yy;
10081
10082           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10083           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10084
10085           /*
10086             This is a very special case that seems to be a mixture between
10087             CheckElementChange() and CheckTriggeredElementChange(): while
10088             the first one only affects single elements that are triggered
10089             directly, the second one affects multiple elements in the playfield
10090             that are triggered indirectly by another element. This is a third
10091             case: Changing the CE score always affects multiple identical CEs,
10092             so every affected CE must be checked, not only the single CE for
10093             which the CE score was changed in the first place (as every instance
10094             of that CE shares the same CE score, and therefore also can change)!
10095           */
10096           SCAN_PLAYFIELD(xx, yy)
10097           {
10098             if (Feld[xx][yy] == element)
10099               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10100                                  CE_SCORE_GETS_ZERO);
10101           }
10102         }
10103       }
10104
10105       break;
10106     }
10107
10108     case CA_SET_CE_ARTWORK:
10109     {
10110       int artwork_element = action_arg_element;
10111       boolean reset_frame = FALSE;
10112       int xx, yy;
10113
10114       if (action_arg == CA_ARG_ELEMENT_RESET)
10115         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10116                            element);
10117
10118       if (ei->gfx_element != artwork_element)
10119         reset_frame = TRUE;
10120
10121       ei->gfx_element = artwork_element;
10122
10123       SCAN_PLAYFIELD(xx, yy)
10124       {
10125         if (Feld[xx][yy] == element)
10126         {
10127           if (reset_frame)
10128           {
10129             ResetGfxAnimation(xx, yy);
10130             ResetRandomAnimationValue(xx, yy);
10131           }
10132
10133           TEST_DrawLevelField(xx, yy);
10134         }
10135       }
10136
10137       break;
10138     }
10139
10140     /* ---------- engine actions  ------------------------------------------ */
10141
10142     case CA_SET_ENGINE_SCAN_MODE:
10143     {
10144       InitPlayfieldScanMode(action_arg);
10145
10146       break;
10147     }
10148
10149     default:
10150       break;
10151   }
10152 }
10153
10154 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10155 {
10156   int old_element = Feld[x][y];
10157   int new_element = GetElementFromGroupElement(element);
10158   int previous_move_direction = MovDir[x][y];
10159   int last_ce_value = CustomValue[x][y];
10160   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10161   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10162   boolean add_player_onto_element = (new_element_is_player &&
10163                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10164                                      IS_WALKABLE(old_element));
10165
10166   if (!add_player_onto_element)
10167   {
10168     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10169       RemoveMovingField(x, y);
10170     else
10171       RemoveField(x, y);
10172
10173     Feld[x][y] = new_element;
10174
10175     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10176       MovDir[x][y] = previous_move_direction;
10177
10178     if (element_info[new_element].use_last_ce_value)
10179       CustomValue[x][y] = last_ce_value;
10180
10181     InitField_WithBug1(x, y, FALSE);
10182
10183     new_element = Feld[x][y];   /* element may have changed */
10184
10185     ResetGfxAnimation(x, y);
10186     ResetRandomAnimationValue(x, y);
10187
10188     TEST_DrawLevelField(x, y);
10189
10190     if (GFX_CRUMBLED(new_element))
10191       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10192   }
10193
10194   /* check if element under the player changes from accessible to unaccessible
10195      (needed for special case of dropping element which then changes) */
10196   /* (must be checked after creating new element for walkable group elements) */
10197   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10198       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10199   {
10200     Bang(x, y);
10201
10202     return;
10203   }
10204
10205   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10206   if (new_element_is_player)
10207     RelocatePlayer(x, y, new_element);
10208
10209   if (is_change)
10210     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10211
10212   TestIfBadThingTouchesPlayer(x, y);
10213   TestIfPlayerTouchesCustomElement(x, y);
10214   TestIfElementTouchesCustomElement(x, y);
10215 }
10216
10217 static void CreateField(int x, int y, int element)
10218 {
10219   CreateFieldExt(x, y, element, FALSE);
10220 }
10221
10222 static void CreateElementFromChange(int x, int y, int element)
10223 {
10224   element = GET_VALID_RUNTIME_ELEMENT(element);
10225
10226   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10227   {
10228     int old_element = Feld[x][y];
10229
10230     /* prevent changed element from moving in same engine frame
10231        unless both old and new element can either fall or move */
10232     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10233         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10234       Stop[x][y] = TRUE;
10235   }
10236
10237   CreateFieldExt(x, y, element, TRUE);
10238 }
10239
10240 static boolean ChangeElement(int x, int y, int element, int page)
10241 {
10242   struct ElementInfo *ei = &element_info[element];
10243   struct ElementChangeInfo *change = &ei->change_page[page];
10244   int ce_value = CustomValue[x][y];
10245   int ce_score = ei->collect_score;
10246   int target_element;
10247   int old_element = Feld[x][y];
10248
10249   /* always use default change event to prevent running into a loop */
10250   if (ChangeEvent[x][y] == -1)
10251     ChangeEvent[x][y] = CE_DELAY;
10252
10253   if (ChangeEvent[x][y] == CE_DELAY)
10254   {
10255     /* reset actual trigger element, trigger player and action element */
10256     change->actual_trigger_element = EL_EMPTY;
10257     change->actual_trigger_player = EL_EMPTY;
10258     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10259     change->actual_trigger_side = CH_SIDE_NONE;
10260     change->actual_trigger_ce_value = 0;
10261     change->actual_trigger_ce_score = 0;
10262   }
10263
10264   /* do not change elements more than a specified maximum number of changes */
10265   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10266     return FALSE;
10267
10268   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10269
10270   if (change->explode)
10271   {
10272     Bang(x, y);
10273
10274     return TRUE;
10275   }
10276
10277   if (change->use_target_content)
10278   {
10279     boolean complete_replace = TRUE;
10280     boolean can_replace[3][3];
10281     int xx, yy;
10282
10283     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10284     {
10285       boolean is_empty;
10286       boolean is_walkable;
10287       boolean is_diggable;
10288       boolean is_collectible;
10289       boolean is_removable;
10290       boolean is_destructible;
10291       int ex = x + xx - 1;
10292       int ey = y + yy - 1;
10293       int content_element = change->target_content.e[xx][yy];
10294       int e;
10295
10296       can_replace[xx][yy] = TRUE;
10297
10298       if (ex == x && ey == y)   /* do not check changing element itself */
10299         continue;
10300
10301       if (content_element == EL_EMPTY_SPACE)
10302       {
10303         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10304
10305         continue;
10306       }
10307
10308       if (!IN_LEV_FIELD(ex, ey))
10309       {
10310         can_replace[xx][yy] = FALSE;
10311         complete_replace = FALSE;
10312
10313         continue;
10314       }
10315
10316       e = Feld[ex][ey];
10317
10318       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10319         e = MovingOrBlocked2Element(ex, ey);
10320
10321       is_empty = (IS_FREE(ex, ey) ||
10322                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10323
10324       is_walkable     = (is_empty || IS_WALKABLE(e));
10325       is_diggable     = (is_empty || IS_DIGGABLE(e));
10326       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10327       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10328       is_removable    = (is_diggable || is_collectible);
10329
10330       can_replace[xx][yy] =
10331         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10332           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10333           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10334           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10335           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10336           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10337          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10338
10339       if (!can_replace[xx][yy])
10340         complete_replace = FALSE;
10341     }
10342
10343     if (!change->only_if_complete || complete_replace)
10344     {
10345       boolean something_has_changed = FALSE;
10346
10347       if (change->only_if_complete && change->use_random_replace &&
10348           RND(100) < change->random_percentage)
10349         return FALSE;
10350
10351       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10352       {
10353         int ex = x + xx - 1;
10354         int ey = y + yy - 1;
10355         int content_element;
10356
10357         if (can_replace[xx][yy] && (!change->use_random_replace ||
10358                                     RND(100) < change->random_percentage))
10359         {
10360           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10361             RemoveMovingField(ex, ey);
10362
10363           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10364
10365           content_element = change->target_content.e[xx][yy];
10366           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10367                                               ce_value, ce_score);
10368
10369           CreateElementFromChange(ex, ey, target_element);
10370
10371           something_has_changed = TRUE;
10372
10373           /* for symmetry reasons, freeze newly created border elements */
10374           if (ex != x || ey != y)
10375             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10376         }
10377       }
10378
10379       if (something_has_changed)
10380       {
10381         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10382         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10383       }
10384     }
10385   }
10386   else
10387   {
10388     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10389                                         ce_value, ce_score);
10390
10391     if (element == EL_DIAGONAL_GROWING ||
10392         element == EL_DIAGONAL_SHRINKING)
10393     {
10394       target_element = Store[x][y];
10395
10396       Store[x][y] = EL_EMPTY;
10397     }
10398
10399     CreateElementFromChange(x, y, target_element);
10400
10401     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10402     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10403   }
10404
10405   /* this uses direct change before indirect change */
10406   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10407
10408   return TRUE;
10409 }
10410
10411 static void HandleElementChange(int x, int y, int page)
10412 {
10413   int element = MovingOrBlocked2Element(x, y);
10414   struct ElementInfo *ei = &element_info[element];
10415   struct ElementChangeInfo *change = &ei->change_page[page];
10416   boolean handle_action_before_change = FALSE;
10417
10418 #ifdef DEBUG
10419   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10420       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10421   {
10422     printf("\n\n");
10423     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10424            x, y, element, element_info[element].token_name);
10425     printf("HandleElementChange(): This should never happen!\n");
10426     printf("\n\n");
10427   }
10428 #endif
10429
10430   /* this can happen with classic bombs on walkable, changing elements */
10431   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10432   {
10433     return;
10434   }
10435
10436   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10437   {
10438     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10439
10440     if (change->can_change)
10441     {
10442       /* !!! not clear why graphic animation should be reset at all here !!! */
10443       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10444       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10445
10446       /*
10447         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10448
10449         When using an animation frame delay of 1 (this only happens with
10450         "sp_zonk.moving.left/right" in the classic graphics), the default
10451         (non-moving) animation shows wrong animation frames (while the
10452         moving animation, like "sp_zonk.moving.left/right", is correct,
10453         so this graphical bug never shows up with the classic graphics).
10454         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10455         be drawn instead of the correct frames 0,1,2,3. This is caused by
10456         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10457         an element change: First when the change delay ("ChangeDelay[][]")
10458         counter has reached zero after decrementing, then a second time in
10459         the next frame (after "GfxFrame[][]" was already incremented) when
10460         "ChangeDelay[][]" is reset to the initial delay value again.
10461
10462         This causes frame 0 to be drawn twice, while the last frame won't
10463         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10464
10465         As some animations may already be cleverly designed around this bug
10466         (at least the "Snake Bite" snake tail animation does this), it cannot
10467         simply be fixed here without breaking such existing animations.
10468         Unfortunately, it cannot easily be detected if a graphics set was
10469         designed "before" or "after" the bug was fixed. As a workaround,
10470         a new graphics set option "game.graphics_engine_version" was added
10471         to be able to specify the game's major release version for which the
10472         graphics set was designed, which can then be used to decide if the
10473         bugfix should be used (version 4 and above) or not (version 3 or
10474         below, or if no version was specified at all, as with old sets).
10475
10476         (The wrong/fixed animation frames can be tested with the test level set
10477         "test_gfxframe" and level "000", which contains a specially prepared
10478         custom element at level position (x/y) == (11/9) which uses the zonk
10479         animation mentioned above. Using "game.graphics_engine_version: 4"
10480         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10481         This can also be seen from the debug output for this test element.)
10482       */
10483
10484       /* when a custom element is about to change (for example by change delay),
10485          do not reset graphic animation when the custom element is moving */
10486       if (game.graphics_engine_version < 4 &&
10487           !IS_MOVING(x, y))
10488       {
10489         ResetGfxAnimation(x, y);
10490         ResetRandomAnimationValue(x, y);
10491       }
10492
10493       if (change->pre_change_function)
10494         change->pre_change_function(x, y);
10495     }
10496   }
10497
10498   ChangeDelay[x][y]--;
10499
10500   if (ChangeDelay[x][y] != 0)           /* continue element change */
10501   {
10502     if (change->can_change)
10503     {
10504       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10505
10506       if (IS_ANIMATED(graphic))
10507         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10508
10509       if (change->change_function)
10510         change->change_function(x, y);
10511     }
10512   }
10513   else                                  /* finish element change */
10514   {
10515     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10516     {
10517       page = ChangePage[x][y];
10518       ChangePage[x][y] = -1;
10519
10520       change = &ei->change_page[page];
10521     }
10522
10523     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10524     {
10525       ChangeDelay[x][y] = 1;            /* try change after next move step */
10526       ChangePage[x][y] = page;          /* remember page to use for change */
10527
10528       return;
10529     }
10530
10531     /* special case: set new level random seed before changing element */
10532     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10533       handle_action_before_change = TRUE;
10534
10535     if (change->has_action && handle_action_before_change)
10536       ExecuteCustomElementAction(x, y, element, page);
10537
10538     if (change->can_change)
10539     {
10540       if (ChangeElement(x, y, element, page))
10541       {
10542         if (change->post_change_function)
10543           change->post_change_function(x, y);
10544       }
10545     }
10546
10547     if (change->has_action && !handle_action_before_change)
10548       ExecuteCustomElementAction(x, y, element, page);
10549   }
10550 }
10551
10552 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10553                                               int trigger_element,
10554                                               int trigger_event,
10555                                               int trigger_player,
10556                                               int trigger_side,
10557                                               int trigger_page)
10558 {
10559   boolean change_done_any = FALSE;
10560   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10561   int i;
10562
10563   if (!(trigger_events[trigger_element][trigger_event]))
10564     return FALSE;
10565
10566   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10567
10568   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10569   {
10570     int element = EL_CUSTOM_START + i;
10571     boolean change_done = FALSE;
10572     int p;
10573
10574     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10575         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10576       continue;
10577
10578     for (p = 0; p < element_info[element].num_change_pages; p++)
10579     {
10580       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10581
10582       if (change->can_change_or_has_action &&
10583           change->has_event[trigger_event] &&
10584           change->trigger_side & trigger_side &&
10585           change->trigger_player & trigger_player &&
10586           change->trigger_page & trigger_page_bits &&
10587           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10588       {
10589         change->actual_trigger_element = trigger_element;
10590         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10591         change->actual_trigger_player_bits = trigger_player;
10592         change->actual_trigger_side = trigger_side;
10593         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10594         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10595
10596         if ((change->can_change && !change_done) || change->has_action)
10597         {
10598           int x, y;
10599
10600           SCAN_PLAYFIELD(x, y)
10601           {
10602             if (Feld[x][y] == element)
10603             {
10604               if (change->can_change && !change_done)
10605               {
10606                 /* if element already changed in this frame, not only prevent
10607                    another element change (checked in ChangeElement()), but
10608                    also prevent additional element actions for this element */
10609
10610                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10611                     !level.use_action_after_change_bug)
10612                   continue;
10613
10614                 ChangeDelay[x][y] = 1;
10615                 ChangeEvent[x][y] = trigger_event;
10616
10617                 HandleElementChange(x, y, p);
10618               }
10619               else if (change->has_action)
10620               {
10621                 /* if element already changed in this frame, not only prevent
10622                    another element change (checked in ChangeElement()), but
10623                    also prevent additional element actions for this element */
10624
10625                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10626                     !level.use_action_after_change_bug)
10627                   continue;
10628
10629                 ExecuteCustomElementAction(x, y, element, p);
10630                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10631               }
10632             }
10633           }
10634
10635           if (change->can_change)
10636           {
10637             change_done = TRUE;
10638             change_done_any = TRUE;
10639           }
10640         }
10641       }
10642     }
10643   }
10644
10645   RECURSION_LOOP_DETECTION_END();
10646
10647   return change_done_any;
10648 }
10649
10650 static boolean CheckElementChangeExt(int x, int y,
10651                                      int element,
10652                                      int trigger_element,
10653                                      int trigger_event,
10654                                      int trigger_player,
10655                                      int trigger_side)
10656 {
10657   boolean change_done = FALSE;
10658   int p;
10659
10660   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10661       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10662     return FALSE;
10663
10664   if (Feld[x][y] == EL_BLOCKED)
10665   {
10666     Blocked2Moving(x, y, &x, &y);
10667     element = Feld[x][y];
10668   }
10669
10670   /* check if element has already changed or is about to change after moving */
10671   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10672        Feld[x][y] != element) ||
10673
10674       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10675        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10676         ChangePage[x][y] != -1)))
10677     return FALSE;
10678
10679   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10680
10681   for (p = 0; p < element_info[element].num_change_pages; p++)
10682   {
10683     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10684
10685     /* check trigger element for all events where the element that is checked
10686        for changing interacts with a directly adjacent element -- this is
10687        different to element changes that affect other elements to change on the
10688        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10689     boolean check_trigger_element =
10690       (trigger_event == CE_TOUCHING_X ||
10691        trigger_event == CE_HITTING_X ||
10692        trigger_event == CE_HIT_BY_X ||
10693        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10694
10695     if (change->can_change_or_has_action &&
10696         change->has_event[trigger_event] &&
10697         change->trigger_side & trigger_side &&
10698         change->trigger_player & trigger_player &&
10699         (!check_trigger_element ||
10700          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10701     {
10702       change->actual_trigger_element = trigger_element;
10703       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10704       change->actual_trigger_player_bits = trigger_player;
10705       change->actual_trigger_side = trigger_side;
10706       change->actual_trigger_ce_value = CustomValue[x][y];
10707       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10708
10709       /* special case: trigger element not at (x,y) position for some events */
10710       if (check_trigger_element)
10711       {
10712         static struct
10713         {
10714           int dx, dy;
10715         } move_xy[] =
10716           {
10717             {  0,  0 },
10718             { -1,  0 },
10719             { +1,  0 },
10720             {  0,  0 },
10721             {  0, -1 },
10722             {  0,  0 }, { 0, 0 }, { 0, 0 },
10723             {  0, +1 }
10724           };
10725
10726         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10727         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10728
10729         change->actual_trigger_ce_value = CustomValue[xx][yy];
10730         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10731       }
10732
10733       if (change->can_change && !change_done)
10734       {
10735         ChangeDelay[x][y] = 1;
10736         ChangeEvent[x][y] = trigger_event;
10737
10738         HandleElementChange(x, y, p);
10739
10740         change_done = TRUE;
10741       }
10742       else if (change->has_action)
10743       {
10744         ExecuteCustomElementAction(x, y, element, p);
10745         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10746       }
10747     }
10748   }
10749
10750   RECURSION_LOOP_DETECTION_END();
10751
10752   return change_done;
10753 }
10754
10755 static void PlayPlayerSound(struct PlayerInfo *player)
10756 {
10757   int jx = player->jx, jy = player->jy;
10758   int sound_element = player->artwork_element;
10759   int last_action = player->last_action_waiting;
10760   int action = player->action_waiting;
10761
10762   if (player->is_waiting)
10763   {
10764     if (action != last_action)
10765       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10766     else
10767       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10768   }
10769   else
10770   {
10771     if (action != last_action)
10772       StopSound(element_info[sound_element].sound[last_action]);
10773
10774     if (last_action == ACTION_SLEEPING)
10775       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10776   }
10777 }
10778
10779 static void PlayAllPlayersSound()
10780 {
10781   int i;
10782
10783   for (i = 0; i < MAX_PLAYERS; i++)
10784     if (stored_player[i].active)
10785       PlayPlayerSound(&stored_player[i]);
10786 }
10787
10788 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10789 {
10790   boolean last_waiting = player->is_waiting;
10791   int move_dir = player->MovDir;
10792
10793   player->dir_waiting = move_dir;
10794   player->last_action_waiting = player->action_waiting;
10795
10796   if (is_waiting)
10797   {
10798     if (!last_waiting)          /* not waiting -> waiting */
10799     {
10800       player->is_waiting = TRUE;
10801
10802       player->frame_counter_bored =
10803         FrameCounter +
10804         game.player_boring_delay_fixed +
10805         GetSimpleRandom(game.player_boring_delay_random);
10806       player->frame_counter_sleeping =
10807         FrameCounter +
10808         game.player_sleeping_delay_fixed +
10809         GetSimpleRandom(game.player_sleeping_delay_random);
10810
10811       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10812     }
10813
10814     if (game.player_sleeping_delay_fixed +
10815         game.player_sleeping_delay_random > 0 &&
10816         player->anim_delay_counter == 0 &&
10817         player->post_delay_counter == 0 &&
10818         FrameCounter >= player->frame_counter_sleeping)
10819       player->is_sleeping = TRUE;
10820     else if (game.player_boring_delay_fixed +
10821              game.player_boring_delay_random > 0 &&
10822              FrameCounter >= player->frame_counter_bored)
10823       player->is_bored = TRUE;
10824
10825     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10826                               player->is_bored ? ACTION_BORING :
10827                               ACTION_WAITING);
10828
10829     if (player->is_sleeping && player->use_murphy)
10830     {
10831       /* special case for sleeping Murphy when leaning against non-free tile */
10832
10833       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10834           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10835            !IS_MOVING(player->jx - 1, player->jy)))
10836         move_dir = MV_LEFT;
10837       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10838                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10839                 !IS_MOVING(player->jx + 1, player->jy)))
10840         move_dir = MV_RIGHT;
10841       else
10842         player->is_sleeping = FALSE;
10843
10844       player->dir_waiting = move_dir;
10845     }
10846
10847     if (player->is_sleeping)
10848     {
10849       if (player->num_special_action_sleeping > 0)
10850       {
10851         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10852         {
10853           int last_special_action = player->special_action_sleeping;
10854           int num_special_action = player->num_special_action_sleeping;
10855           int special_action =
10856             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10857              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10858              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10859              last_special_action + 1 : ACTION_SLEEPING);
10860           int special_graphic =
10861             el_act_dir2img(player->artwork_element, special_action, move_dir);
10862
10863           player->anim_delay_counter =
10864             graphic_info[special_graphic].anim_delay_fixed +
10865             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10866           player->post_delay_counter =
10867             graphic_info[special_graphic].post_delay_fixed +
10868             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10869
10870           player->special_action_sleeping = special_action;
10871         }
10872
10873         if (player->anim_delay_counter > 0)
10874         {
10875           player->action_waiting = player->special_action_sleeping;
10876           player->anim_delay_counter--;
10877         }
10878         else if (player->post_delay_counter > 0)
10879         {
10880           player->post_delay_counter--;
10881         }
10882       }
10883     }
10884     else if (player->is_bored)
10885     {
10886       if (player->num_special_action_bored > 0)
10887       {
10888         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10889         {
10890           int special_action =
10891             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10892           int special_graphic =
10893             el_act_dir2img(player->artwork_element, special_action, move_dir);
10894
10895           player->anim_delay_counter =
10896             graphic_info[special_graphic].anim_delay_fixed +
10897             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10898           player->post_delay_counter =
10899             graphic_info[special_graphic].post_delay_fixed +
10900             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10901
10902           player->special_action_bored = special_action;
10903         }
10904
10905         if (player->anim_delay_counter > 0)
10906         {
10907           player->action_waiting = player->special_action_bored;
10908           player->anim_delay_counter--;
10909         }
10910         else if (player->post_delay_counter > 0)
10911         {
10912           player->post_delay_counter--;
10913         }
10914       }
10915     }
10916   }
10917   else if (last_waiting)        /* waiting -> not waiting */
10918   {
10919     player->is_waiting = FALSE;
10920     player->is_bored = FALSE;
10921     player->is_sleeping = FALSE;
10922
10923     player->frame_counter_bored = -1;
10924     player->frame_counter_sleeping = -1;
10925
10926     player->anim_delay_counter = 0;
10927     player->post_delay_counter = 0;
10928
10929     player->dir_waiting = player->MovDir;
10930     player->action_waiting = ACTION_DEFAULT;
10931
10932     player->special_action_bored = ACTION_DEFAULT;
10933     player->special_action_sleeping = ACTION_DEFAULT;
10934   }
10935 }
10936
10937 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10938 {
10939   if ((!player->is_moving  && player->was_moving) ||
10940       (player->MovPos == 0 && player->was_moving) ||
10941       (player->is_snapping && !player->was_snapping) ||
10942       (player->is_dropping && !player->was_dropping))
10943   {
10944     if (!CheckSaveEngineSnapshotToList())
10945       return;
10946
10947     player->was_moving = FALSE;
10948     player->was_snapping = TRUE;
10949     player->was_dropping = TRUE;
10950   }
10951   else
10952   {
10953     if (player->is_moving)
10954       player->was_moving = TRUE;
10955
10956     if (!player->is_snapping)
10957       player->was_snapping = FALSE;
10958
10959     if (!player->is_dropping)
10960       player->was_dropping = FALSE;
10961   }
10962 }
10963
10964 static void CheckSingleStepMode(struct PlayerInfo *player)
10965 {
10966   if (tape.single_step && tape.recording && !tape.pausing)
10967   {
10968     /* as it is called "single step mode", just return to pause mode when the
10969        player stopped moving after one tile (or never starts moving at all) */
10970     if (!player->is_moving &&
10971         !player->is_pushing &&
10972         !player->is_dropping_pressed)
10973     {
10974       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10975       SnapField(player, 0, 0);                  /* stop snapping */
10976     }
10977   }
10978
10979   CheckSaveEngineSnapshot(player);
10980 }
10981
10982 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10983 {
10984   int left      = player_action & JOY_LEFT;
10985   int right     = player_action & JOY_RIGHT;
10986   int up        = player_action & JOY_UP;
10987   int down      = player_action & JOY_DOWN;
10988   int button1   = player_action & JOY_BUTTON_1;
10989   int button2   = player_action & JOY_BUTTON_2;
10990   int dx        = (left ? -1 : right ? 1 : 0);
10991   int dy        = (up   ? -1 : down  ? 1 : 0);
10992
10993   if (!player->active || tape.pausing)
10994     return 0;
10995
10996   if (player_action)
10997   {
10998     if (button1)
10999       SnapField(player, dx, dy);
11000     else
11001     {
11002       if (button2)
11003         DropElement(player);
11004
11005       MovePlayer(player, dx, dy);
11006     }
11007
11008     CheckSingleStepMode(player);
11009
11010     SetPlayerWaiting(player, FALSE);
11011
11012     return player_action;
11013   }
11014   else
11015   {
11016     /* no actions for this player (no input at player's configured device) */
11017
11018     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11019     SnapField(player, 0, 0);
11020     CheckGravityMovementWhenNotMoving(player);
11021
11022     if (player->MovPos == 0)
11023       SetPlayerWaiting(player, TRUE);
11024
11025     if (player->MovPos == 0)    /* needed for tape.playing */
11026       player->is_moving = FALSE;
11027
11028     player->is_dropping = FALSE;
11029     player->is_dropping_pressed = FALSE;
11030     player->drop_pressed_delay = 0;
11031
11032     CheckSingleStepMode(player);
11033
11034     return 0;
11035   }
11036 }
11037
11038 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11039                                          byte *tape_action)
11040 {
11041   if (!tape.use_mouse)
11042     return;
11043
11044   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11045   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11046   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11047 }
11048
11049 static void SetTapeActionFromMouseAction(byte *tape_action,
11050                                          struct MouseActionInfo *mouse_action)
11051 {
11052   if (!tape.use_mouse)
11053     return;
11054
11055   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11056   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11057   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11058 }
11059
11060 static void CheckLevelTime()
11061 {
11062   int i;
11063
11064   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11065   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11066   {
11067     if (level.native_em_level->lev->home == 0)  /* all players at home */
11068     {
11069       PlayerWins(local_player);
11070
11071       AllPlayersGone = TRUE;
11072
11073       level.native_em_level->lev->home = -1;
11074     }
11075
11076     if (level.native_em_level->ply[0]->alive == 0 &&
11077         level.native_em_level->ply[1]->alive == 0 &&
11078         level.native_em_level->ply[2]->alive == 0 &&
11079         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11080       AllPlayersGone = TRUE;
11081   }
11082   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11083   {
11084     if (game_sp.LevelSolved &&
11085         !game_sp.GameOver)                              /* game won */
11086     {
11087       PlayerWins(local_player);
11088
11089       game_sp.GameOver = TRUE;
11090
11091       AllPlayersGone = TRUE;
11092     }
11093
11094     if (game_sp.GameOver)                               /* game lost */
11095       AllPlayersGone = TRUE;
11096   }
11097   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11098   {
11099     if (game_mm.level_solved &&
11100         !game_mm.game_over)                             /* game won */
11101     {
11102       PlayerWins(local_player);
11103
11104       game_mm.game_over = TRUE;
11105
11106       AllPlayersGone = TRUE;
11107     }
11108
11109     if (game_mm.game_over)                              /* game lost */
11110       AllPlayersGone = TRUE;
11111   }
11112
11113   if (TimeFrames >= FRAMES_PER_SECOND)
11114   {
11115     TimeFrames = 0;
11116     TapeTime++;
11117
11118     for (i = 0; i < MAX_PLAYERS; i++)
11119     {
11120       struct PlayerInfo *player = &stored_player[i];
11121
11122       if (SHIELD_ON(player))
11123       {
11124         player->shield_normal_time_left--;
11125
11126         if (player->shield_deadly_time_left > 0)
11127           player->shield_deadly_time_left--;
11128       }
11129     }
11130
11131     if (!local_player->LevelSolved && !level.use_step_counter)
11132     {
11133       TimePlayed++;
11134
11135       if (TimeLeft > 0)
11136       {
11137         TimeLeft--;
11138
11139         if (TimeLeft <= 10 && setup.time_limit)
11140           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11141
11142         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11143            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11144
11145         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11146
11147         if (!TimeLeft && setup.time_limit)
11148         {
11149           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11150             level.native_em_level->lev->killed_out_of_time = TRUE;
11151           else
11152             for (i = 0; i < MAX_PLAYERS; i++)
11153               KillPlayer(&stored_player[i]);
11154         }
11155       }
11156       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11157       {
11158         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11159       }
11160
11161       level.native_em_level->lev->time =
11162         (game.no_time_limit ? TimePlayed : TimeLeft);
11163     }
11164
11165     if (tape.recording || tape.playing)
11166       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11167   }
11168
11169   if (tape.recording || tape.playing)
11170     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11171
11172   UpdateAndDisplayGameControlValues();
11173 }
11174
11175 void AdvanceFrameAndPlayerCounters(int player_nr)
11176 {
11177   int i;
11178
11179   /* advance frame counters (global frame counter and time frame counter) */
11180   FrameCounter++;
11181   TimeFrames++;
11182
11183   /* advance player counters (counters for move delay, move animation etc.) */
11184   for (i = 0; i < MAX_PLAYERS; i++)
11185   {
11186     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11187     int move_delay_value = stored_player[i].move_delay_value;
11188     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11189
11190     if (!advance_player_counters)       /* not all players may be affected */
11191       continue;
11192
11193     if (move_frames == 0)       /* less than one move per game frame */
11194     {
11195       int stepsize = TILEX / move_delay_value;
11196       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11197       int count = (stored_player[i].is_moving ?
11198                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11199
11200       if (count % delay == 0)
11201         move_frames = 1;
11202     }
11203
11204     stored_player[i].Frame += move_frames;
11205
11206     if (stored_player[i].MovPos != 0)
11207       stored_player[i].StepFrame += move_frames;
11208
11209     if (stored_player[i].move_delay > 0)
11210       stored_player[i].move_delay--;
11211
11212     /* due to bugs in previous versions, counter must count up, not down */
11213     if (stored_player[i].push_delay != -1)
11214       stored_player[i].push_delay++;
11215
11216     if (stored_player[i].drop_delay > 0)
11217       stored_player[i].drop_delay--;
11218
11219     if (stored_player[i].is_dropping_pressed)
11220       stored_player[i].drop_pressed_delay++;
11221   }
11222 }
11223
11224 void StartGameActions(boolean init_network_game, boolean record_tape,
11225                       int random_seed)
11226 {
11227   unsigned int new_random_seed = InitRND(random_seed);
11228
11229   if (record_tape)
11230     TapeStartRecording(new_random_seed);
11231
11232   if (init_network_game)
11233   {
11234     SendToServer_LevelFile();
11235     SendToServer_StartPlaying();
11236
11237     return;
11238   }
11239
11240   InitGame();
11241 }
11242
11243 void GameActionsExt()
11244 {
11245 #if 0
11246   static unsigned int game_frame_delay = 0;
11247 #endif
11248   unsigned int game_frame_delay_value;
11249   byte *recorded_player_action;
11250   byte summarized_player_action = 0;
11251   byte tape_action[MAX_PLAYERS];
11252   int i;
11253
11254   /* detect endless loops, caused by custom element programming */
11255   if (recursion_loop_detected && recursion_loop_depth == 0)
11256   {
11257     char *message = getStringCat3("Internal Error! Element ",
11258                                   EL_NAME(recursion_loop_element),
11259                                   " caused endless loop! Quit the game?");
11260
11261     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11262           EL_NAME(recursion_loop_element));
11263
11264     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11265
11266     recursion_loop_detected = FALSE;    /* if game should be continued */
11267
11268     free(message);
11269
11270     return;
11271   }
11272
11273   if (game.restart_level)
11274     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11275
11276   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11277   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11278   {
11279     if (level.native_em_level->lev->home == 0)  /* all players at home */
11280     {
11281       PlayerWins(local_player);
11282
11283       AllPlayersGone = TRUE;
11284
11285       level.native_em_level->lev->home = -1;
11286     }
11287
11288     if (level.native_em_level->ply[0]->alive == 0 &&
11289         level.native_em_level->ply[1]->alive == 0 &&
11290         level.native_em_level->ply[2]->alive == 0 &&
11291         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11292       AllPlayersGone = TRUE;
11293   }
11294   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11295   {
11296     if (game_sp.LevelSolved &&
11297         !game_sp.GameOver)                              /* game won */
11298     {
11299       PlayerWins(local_player);
11300
11301       game_sp.GameOver = TRUE;
11302
11303       AllPlayersGone = TRUE;
11304     }
11305
11306     if (game_sp.GameOver)                               /* game lost */
11307       AllPlayersGone = TRUE;
11308   }
11309   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11310   {
11311     if (game_mm.level_solved &&
11312         !game_mm.game_over)                             /* game won */
11313     {
11314       PlayerWins(local_player);
11315
11316       game_mm.game_over = TRUE;
11317
11318       AllPlayersGone = TRUE;
11319     }
11320
11321     if (game_mm.game_over)                              /* game lost */
11322       AllPlayersGone = TRUE;
11323   }
11324
11325   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11326     GameWon();
11327
11328   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11329     TapeStop();
11330
11331   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11332     return;
11333
11334   game_frame_delay_value =
11335     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11336
11337   if (tape.playing && tape.warp_forward && !tape.pausing)
11338     game_frame_delay_value = 0;
11339
11340   SetVideoFrameDelay(game_frame_delay_value);
11341
11342 #if 0
11343 #if 0
11344   /* ---------- main game synchronization point ---------- */
11345
11346   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11347
11348   printf("::: skip == %d\n", skip);
11349
11350 #else
11351   /* ---------- main game synchronization point ---------- */
11352
11353   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11354 #endif
11355 #endif
11356
11357   if (network_playing && !network_player_action_received)
11358   {
11359     /* try to get network player actions in time */
11360
11361     /* last chance to get network player actions without main loop delay */
11362     HandleNetworking();
11363
11364     /* game was quit by network peer */
11365     if (game_status != GAME_MODE_PLAYING)
11366       return;
11367
11368     if (!network_player_action_received)
11369       return;           /* failed to get network player actions in time */
11370
11371     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11372   }
11373
11374   if (tape.pausing)
11375     return;
11376
11377   /* at this point we know that we really continue executing the game */
11378
11379   network_player_action_received = FALSE;
11380
11381   /* when playing tape, read previously recorded player input from tape data */
11382   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11383
11384   local_player->effective_mouse_action = local_player->mouse_action;
11385
11386   if (recorded_player_action != NULL)
11387     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11388                                  recorded_player_action);
11389
11390   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11391   if (tape.pausing)
11392     return;
11393
11394   if (tape.set_centered_player)
11395   {
11396     game.centered_player_nr_next = tape.centered_player_nr_next;
11397     game.set_centered_player = TRUE;
11398   }
11399
11400   for (i = 0; i < MAX_PLAYERS; i++)
11401   {
11402     summarized_player_action |= stored_player[i].action;
11403
11404     if (!network_playing && (game.team_mode || tape.playing))
11405       stored_player[i].effective_action = stored_player[i].action;
11406   }
11407
11408   if (network_playing)
11409     SendToServer_MovePlayer(summarized_player_action);
11410
11411   // summarize all actions at local players mapped input device position
11412   // (this allows using different input devices in single player mode)
11413   if (!network.enabled && !game.team_mode)
11414     stored_player[map_player_action[local_player->index_nr]].effective_action =
11415       summarized_player_action;
11416
11417   if (tape.recording &&
11418       setup.team_mode &&
11419       setup.input_on_focus &&
11420       game.centered_player_nr != -1)
11421   {
11422     for (i = 0; i < MAX_PLAYERS; i++)
11423       stored_player[i].effective_action =
11424         (i == game.centered_player_nr ? summarized_player_action : 0);
11425   }
11426
11427   if (recorded_player_action != NULL)
11428     for (i = 0; i < MAX_PLAYERS; i++)
11429       stored_player[i].effective_action = recorded_player_action[i];
11430
11431   for (i = 0; i < MAX_PLAYERS; i++)
11432   {
11433     tape_action[i] = stored_player[i].effective_action;
11434
11435     /* (this may happen in the RND game engine if a player was not present on
11436        the playfield on level start, but appeared later from a custom element */
11437     if (setup.team_mode &&
11438         tape.recording &&
11439         tape_action[i] &&
11440         !tape.player_participates[i])
11441       tape.player_participates[i] = TRUE;
11442   }
11443
11444   SetTapeActionFromMouseAction(tape_action,
11445                                &local_player->effective_mouse_action);
11446
11447   /* only record actions from input devices, but not programmed actions */
11448   if (tape.recording)
11449     TapeRecordAction(tape_action);
11450
11451 #if USE_NEW_PLAYER_ASSIGNMENTS
11452   // !!! also map player actions in single player mode !!!
11453   // if (game.team_mode)
11454   if (1)
11455   {
11456     byte mapped_action[MAX_PLAYERS];
11457
11458 #if DEBUG_PLAYER_ACTIONS
11459     printf(":::");
11460     for (i = 0; i < MAX_PLAYERS; i++)
11461       printf(" %d, ", stored_player[i].effective_action);
11462 #endif
11463
11464     for (i = 0; i < MAX_PLAYERS; i++)
11465       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11466
11467     for (i = 0; i < MAX_PLAYERS; i++)
11468       stored_player[i].effective_action = mapped_action[i];
11469
11470 #if DEBUG_PLAYER_ACTIONS
11471     printf(" =>");
11472     for (i = 0; i < MAX_PLAYERS; i++)
11473       printf(" %d, ", stored_player[i].effective_action);
11474     printf("\n");
11475 #endif
11476   }
11477 #if DEBUG_PLAYER_ACTIONS
11478   else
11479   {
11480     printf(":::");
11481     for (i = 0; i < MAX_PLAYERS; i++)
11482       printf(" %d, ", stored_player[i].effective_action);
11483     printf("\n");
11484   }
11485 #endif
11486 #endif
11487
11488   for (i = 0; i < MAX_PLAYERS; i++)
11489   {
11490     // allow engine snapshot in case of changed movement attempt
11491     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11492         (stored_player[i].effective_action & KEY_MOTION))
11493       game.snapshot.changed_action = TRUE;
11494
11495     // allow engine snapshot in case of snapping/dropping attempt
11496     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11497         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11498       game.snapshot.changed_action = TRUE;
11499
11500     game.snapshot.last_action[i] = stored_player[i].effective_action;
11501   }
11502
11503   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11504   {
11505     GameActions_EM_Main();
11506   }
11507   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11508   {
11509     GameActions_SP_Main();
11510   }
11511   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11512   {
11513     GameActions_MM_Main();
11514   }
11515   else
11516   {
11517     GameActions_RND_Main();
11518   }
11519
11520   BlitScreenToBitmap(backbuffer);
11521
11522   CheckLevelTime();
11523
11524   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11525
11526   if (global.show_frames_per_second)
11527   {
11528     static unsigned int fps_counter = 0;
11529     static int fps_frames = 0;
11530     unsigned int fps_delay_ms = Counter() - fps_counter;
11531
11532     fps_frames++;
11533
11534     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11535     {
11536       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11537
11538       fps_frames = 0;
11539       fps_counter = Counter();
11540
11541       /* always draw FPS to screen after FPS value was updated */
11542       redraw_mask |= REDRAW_FPS;
11543     }
11544
11545     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11546     if (GetDrawDeactivationMask() == REDRAW_NONE)
11547       redraw_mask |= REDRAW_FPS;
11548   }
11549 }
11550
11551 static void GameActions_CheckSaveEngineSnapshot()
11552 {
11553   if (!game.snapshot.save_snapshot)
11554     return;
11555
11556   // clear flag for saving snapshot _before_ saving snapshot
11557   game.snapshot.save_snapshot = FALSE;
11558
11559   SaveEngineSnapshotToList();
11560 }
11561
11562 void GameActions()
11563 {
11564   GameActionsExt();
11565
11566   GameActions_CheckSaveEngineSnapshot();
11567 }
11568
11569 void GameActions_EM_Main()
11570 {
11571   byte effective_action[MAX_PLAYERS];
11572   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11573   int i;
11574
11575   for (i = 0; i < MAX_PLAYERS; i++)
11576     effective_action[i] = stored_player[i].effective_action;
11577
11578   GameActions_EM(effective_action, warp_mode);
11579 }
11580
11581 void GameActions_SP_Main()
11582 {
11583   byte effective_action[MAX_PLAYERS];
11584   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11585   int i;
11586
11587   for (i = 0; i < MAX_PLAYERS; i++)
11588     effective_action[i] = stored_player[i].effective_action;
11589
11590   GameActions_SP(effective_action, warp_mode);
11591
11592   for (i = 0; i < MAX_PLAYERS; i++)
11593   {
11594     if (stored_player[i].force_dropping)
11595       stored_player[i].action |= KEY_BUTTON_DROP;
11596
11597     stored_player[i].force_dropping = FALSE;
11598   }
11599 }
11600
11601 void GameActions_MM_Main()
11602 {
11603   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11604
11605   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11606 }
11607
11608 void GameActions_RND_Main()
11609 {
11610   GameActions_RND();
11611 }
11612
11613 void GameActions_RND()
11614 {
11615   int magic_wall_x = 0, magic_wall_y = 0;
11616   int i, x, y, element, graphic, last_gfx_frame;
11617
11618   InitPlayfieldScanModeVars();
11619
11620   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11621   {
11622     SCAN_PLAYFIELD(x, y)
11623     {
11624       ChangeCount[x][y] = 0;
11625       ChangeEvent[x][y] = -1;
11626     }
11627   }
11628
11629   if (game.set_centered_player)
11630   {
11631     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11632
11633     /* switching to "all players" only possible if all players fit to screen */
11634     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11635     {
11636       game.centered_player_nr_next = game.centered_player_nr;
11637       game.set_centered_player = FALSE;
11638     }
11639
11640     /* do not switch focus to non-existing (or non-active) player */
11641     if (game.centered_player_nr_next >= 0 &&
11642         !stored_player[game.centered_player_nr_next].active)
11643     {
11644       game.centered_player_nr_next = game.centered_player_nr;
11645       game.set_centered_player = FALSE;
11646     }
11647   }
11648
11649   if (game.set_centered_player &&
11650       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11651   {
11652     int sx, sy;
11653
11654     if (game.centered_player_nr_next == -1)
11655     {
11656       setScreenCenteredToAllPlayers(&sx, &sy);
11657     }
11658     else
11659     {
11660       sx = stored_player[game.centered_player_nr_next].jx;
11661       sy = stored_player[game.centered_player_nr_next].jy;
11662     }
11663
11664     game.centered_player_nr = game.centered_player_nr_next;
11665     game.set_centered_player = FALSE;
11666
11667     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11668     DrawGameDoorValues();
11669   }
11670
11671   for (i = 0; i < MAX_PLAYERS; i++)
11672   {
11673     int actual_player_action = stored_player[i].effective_action;
11674
11675 #if 1
11676     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11677        - rnd_equinox_tetrachloride 048
11678        - rnd_equinox_tetrachloride_ii 096
11679        - rnd_emanuel_schmieg 002
11680        - doctor_sloan_ww 001, 020
11681     */
11682     if (stored_player[i].MovPos == 0)
11683       CheckGravityMovement(&stored_player[i]);
11684 #endif
11685
11686     /* overwrite programmed action with tape action */
11687     if (stored_player[i].programmed_action)
11688       actual_player_action = stored_player[i].programmed_action;
11689
11690     PlayerActions(&stored_player[i], actual_player_action);
11691
11692     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11693   }
11694
11695   ScrollScreen(NULL, SCROLL_GO_ON);
11696
11697   /* for backwards compatibility, the following code emulates a fixed bug that
11698      occured when pushing elements (causing elements that just made their last
11699      pushing step to already (if possible) make their first falling step in the
11700      same game frame, which is bad); this code is also needed to use the famous
11701      "spring push bug" which is used in older levels and might be wanted to be
11702      used also in newer levels, but in this case the buggy pushing code is only
11703      affecting the "spring" element and no other elements */
11704
11705   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11706   {
11707     for (i = 0; i < MAX_PLAYERS; i++)
11708     {
11709       struct PlayerInfo *player = &stored_player[i];
11710       int x = player->jx;
11711       int y = player->jy;
11712
11713       if (player->active && player->is_pushing && player->is_moving &&
11714           IS_MOVING(x, y) &&
11715           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11716            Feld[x][y] == EL_SPRING))
11717       {
11718         ContinueMoving(x, y);
11719
11720         /* continue moving after pushing (this is actually a bug) */
11721         if (!IS_MOVING(x, y))
11722           Stop[x][y] = FALSE;
11723       }
11724     }
11725   }
11726
11727   SCAN_PLAYFIELD(x, y)
11728   {
11729     ChangeCount[x][y] = 0;
11730     ChangeEvent[x][y] = -1;
11731
11732     /* this must be handled before main playfield loop */
11733     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11734     {
11735       MovDelay[x][y]--;
11736       if (MovDelay[x][y] <= 0)
11737         RemoveField(x, y);
11738     }
11739
11740     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11741     {
11742       MovDelay[x][y]--;
11743       if (MovDelay[x][y] <= 0)
11744       {
11745         RemoveField(x, y);
11746         TEST_DrawLevelField(x, y);
11747
11748         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11749       }
11750     }
11751
11752 #if DEBUG
11753     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11754     {
11755       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11756       printf("GameActions(): This should never happen!\n");
11757
11758       ChangePage[x][y] = -1;
11759     }
11760 #endif
11761
11762     Stop[x][y] = FALSE;
11763     if (WasJustMoving[x][y] > 0)
11764       WasJustMoving[x][y]--;
11765     if (WasJustFalling[x][y] > 0)
11766       WasJustFalling[x][y]--;
11767     if (CheckCollision[x][y] > 0)
11768       CheckCollision[x][y]--;
11769     if (CheckImpact[x][y] > 0)
11770       CheckImpact[x][y]--;
11771
11772     GfxFrame[x][y]++;
11773
11774     /* reset finished pushing action (not done in ContinueMoving() to allow
11775        continuous pushing animation for elements with zero push delay) */
11776     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11777     {
11778       ResetGfxAnimation(x, y);
11779       TEST_DrawLevelField(x, y);
11780     }
11781
11782 #if DEBUG
11783     if (IS_BLOCKED(x, y))
11784     {
11785       int oldx, oldy;
11786
11787       Blocked2Moving(x, y, &oldx, &oldy);
11788       if (!IS_MOVING(oldx, oldy))
11789       {
11790         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11791         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11792         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11793         printf("GameActions(): This should never happen!\n");
11794       }
11795     }
11796 #endif
11797   }
11798
11799   SCAN_PLAYFIELD(x, y)
11800   {
11801     element = Feld[x][y];
11802     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11803     last_gfx_frame = GfxFrame[x][y];
11804
11805     ResetGfxFrame(x, y);
11806
11807     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11808       DrawLevelGraphicAnimation(x, y, graphic);
11809
11810     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11811         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11812       ResetRandomAnimationValue(x, y);
11813
11814     SetRandomAnimationValue(x, y);
11815
11816     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11817
11818     if (IS_INACTIVE(element))
11819     {
11820       if (IS_ANIMATED(graphic))
11821         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11822
11823       continue;
11824     }
11825
11826     /* this may take place after moving, so 'element' may have changed */
11827     if (IS_CHANGING(x, y) &&
11828         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11829     {
11830       int page = element_info[element].event_page_nr[CE_DELAY];
11831
11832       HandleElementChange(x, y, page);
11833
11834       element = Feld[x][y];
11835       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11836     }
11837
11838     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11839     {
11840       StartMoving(x, y);
11841
11842       element = Feld[x][y];
11843       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11844
11845       if (IS_ANIMATED(graphic) &&
11846           !IS_MOVING(x, y) &&
11847           !Stop[x][y])
11848         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11849
11850       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11851         TEST_DrawTwinkleOnField(x, y);
11852     }
11853     else if (element == EL_ACID)
11854     {
11855       if (!Stop[x][y])
11856         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11857     }
11858     else if ((element == EL_EXIT_OPEN ||
11859               element == EL_EM_EXIT_OPEN ||
11860               element == EL_SP_EXIT_OPEN ||
11861               element == EL_STEEL_EXIT_OPEN ||
11862               element == EL_EM_STEEL_EXIT_OPEN ||
11863               element == EL_SP_TERMINAL ||
11864               element == EL_SP_TERMINAL_ACTIVE ||
11865               element == EL_EXTRA_TIME ||
11866               element == EL_SHIELD_NORMAL ||
11867               element == EL_SHIELD_DEADLY) &&
11868              IS_ANIMATED(graphic))
11869       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11870     else if (IS_MOVING(x, y))
11871       ContinueMoving(x, y);
11872     else if (IS_ACTIVE_BOMB(element))
11873       CheckDynamite(x, y);
11874     else if (element == EL_AMOEBA_GROWING)
11875       AmoebeWaechst(x, y);
11876     else if (element == EL_AMOEBA_SHRINKING)
11877       AmoebaDisappearing(x, y);
11878
11879 #if !USE_NEW_AMOEBA_CODE
11880     else if (IS_AMOEBALIVE(element))
11881       AmoebeAbleger(x, y);
11882 #endif
11883
11884     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11885       Life(x, y);
11886     else if (element == EL_EXIT_CLOSED)
11887       CheckExit(x, y);
11888     else if (element == EL_EM_EXIT_CLOSED)
11889       CheckExitEM(x, y);
11890     else if (element == EL_STEEL_EXIT_CLOSED)
11891       CheckExitSteel(x, y);
11892     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11893       CheckExitSteelEM(x, y);
11894     else if (element == EL_SP_EXIT_CLOSED)
11895       CheckExitSP(x, y);
11896     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11897              element == EL_EXPANDABLE_STEELWALL_GROWING)
11898       MauerWaechst(x, y);
11899     else if (element == EL_EXPANDABLE_WALL ||
11900              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11901              element == EL_EXPANDABLE_WALL_VERTICAL ||
11902              element == EL_EXPANDABLE_WALL_ANY ||
11903              element == EL_BD_EXPANDABLE_WALL)
11904       MauerAbleger(x, y);
11905     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11906              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11907              element == EL_EXPANDABLE_STEELWALL_ANY)
11908       MauerAblegerStahl(x, y);
11909     else if (element == EL_FLAMES)
11910       CheckForDragon(x, y);
11911     else if (element == EL_EXPLOSION)
11912       ; /* drawing of correct explosion animation is handled separately */
11913     else if (element == EL_ELEMENT_SNAPPING ||
11914              element == EL_DIAGONAL_SHRINKING ||
11915              element == EL_DIAGONAL_GROWING)
11916     {
11917       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11918
11919       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11920     }
11921     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11922       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11923
11924     if (IS_BELT_ACTIVE(element))
11925       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11926
11927     if (game.magic_wall_active)
11928     {
11929       int jx = local_player->jx, jy = local_player->jy;
11930
11931       /* play the element sound at the position nearest to the player */
11932       if ((element == EL_MAGIC_WALL_FULL ||
11933            element == EL_MAGIC_WALL_ACTIVE ||
11934            element == EL_MAGIC_WALL_EMPTYING ||
11935            element == EL_BD_MAGIC_WALL_FULL ||
11936            element == EL_BD_MAGIC_WALL_ACTIVE ||
11937            element == EL_BD_MAGIC_WALL_EMPTYING ||
11938            element == EL_DC_MAGIC_WALL_FULL ||
11939            element == EL_DC_MAGIC_WALL_ACTIVE ||
11940            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11941           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11942       {
11943         magic_wall_x = x;
11944         magic_wall_y = y;
11945       }
11946     }
11947   }
11948
11949 #if USE_NEW_AMOEBA_CODE
11950   /* new experimental amoeba growth stuff */
11951   if (!(FrameCounter % 8))
11952   {
11953     static unsigned int random = 1684108901;
11954
11955     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11956     {
11957       x = RND(lev_fieldx);
11958       y = RND(lev_fieldy);
11959       element = Feld[x][y];
11960
11961       if (!IS_PLAYER(x,y) &&
11962           (element == EL_EMPTY ||
11963            CAN_GROW_INTO(element) ||
11964            element == EL_QUICKSAND_EMPTY ||
11965            element == EL_QUICKSAND_FAST_EMPTY ||
11966            element == EL_ACID_SPLASH_LEFT ||
11967            element == EL_ACID_SPLASH_RIGHT))
11968       {
11969         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11970             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11971             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11972             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11973           Feld[x][y] = EL_AMOEBA_DROP;
11974       }
11975
11976       random = random * 129 + 1;
11977     }
11978   }
11979 #endif
11980
11981   game.explosions_delayed = FALSE;
11982
11983   SCAN_PLAYFIELD(x, y)
11984   {
11985     element = Feld[x][y];
11986
11987     if (ExplodeField[x][y])
11988       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11989     else if (element == EL_EXPLOSION)
11990       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11991
11992     ExplodeField[x][y] = EX_TYPE_NONE;
11993   }
11994
11995   game.explosions_delayed = TRUE;
11996
11997   if (game.magic_wall_active)
11998   {
11999     if (!(game.magic_wall_time_left % 4))
12000     {
12001       int element = Feld[magic_wall_x][magic_wall_y];
12002
12003       if (element == EL_BD_MAGIC_WALL_FULL ||
12004           element == EL_BD_MAGIC_WALL_ACTIVE ||
12005           element == EL_BD_MAGIC_WALL_EMPTYING)
12006         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12007       else if (element == EL_DC_MAGIC_WALL_FULL ||
12008                element == EL_DC_MAGIC_WALL_ACTIVE ||
12009                element == EL_DC_MAGIC_WALL_EMPTYING)
12010         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12011       else
12012         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12013     }
12014
12015     if (game.magic_wall_time_left > 0)
12016     {
12017       game.magic_wall_time_left--;
12018
12019       if (!game.magic_wall_time_left)
12020       {
12021         SCAN_PLAYFIELD(x, y)
12022         {
12023           element = Feld[x][y];
12024
12025           if (element == EL_MAGIC_WALL_ACTIVE ||
12026               element == EL_MAGIC_WALL_FULL)
12027           {
12028             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12029             TEST_DrawLevelField(x, y);
12030           }
12031           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12032                    element == EL_BD_MAGIC_WALL_FULL)
12033           {
12034             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12035             TEST_DrawLevelField(x, y);
12036           }
12037           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12038                    element == EL_DC_MAGIC_WALL_FULL)
12039           {
12040             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12041             TEST_DrawLevelField(x, y);
12042           }
12043         }
12044
12045         game.magic_wall_active = FALSE;
12046       }
12047     }
12048   }
12049
12050   if (game.light_time_left > 0)
12051   {
12052     game.light_time_left--;
12053
12054     if (game.light_time_left == 0)
12055       RedrawAllLightSwitchesAndInvisibleElements();
12056   }
12057
12058   if (game.timegate_time_left > 0)
12059   {
12060     game.timegate_time_left--;
12061
12062     if (game.timegate_time_left == 0)
12063       CloseAllOpenTimegates();
12064   }
12065
12066   if (game.lenses_time_left > 0)
12067   {
12068     game.lenses_time_left--;
12069
12070     if (game.lenses_time_left == 0)
12071       RedrawAllInvisibleElementsForLenses();
12072   }
12073
12074   if (game.magnify_time_left > 0)
12075   {
12076     game.magnify_time_left--;
12077
12078     if (game.magnify_time_left == 0)
12079       RedrawAllInvisibleElementsForMagnifier();
12080   }
12081
12082   for (i = 0; i < MAX_PLAYERS; i++)
12083   {
12084     struct PlayerInfo *player = &stored_player[i];
12085
12086     if (SHIELD_ON(player))
12087     {
12088       if (player->shield_deadly_time_left)
12089         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12090       else if (player->shield_normal_time_left)
12091         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12092     }
12093   }
12094
12095 #if USE_DELAYED_GFX_REDRAW
12096   SCAN_PLAYFIELD(x, y)
12097   {
12098     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12099     {
12100       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12101          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12102
12103       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12104         DrawLevelField(x, y);
12105
12106       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12107         DrawLevelFieldCrumbled(x, y);
12108
12109       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12110         DrawLevelFieldCrumbledNeighbours(x, y);
12111
12112       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12113         DrawTwinkleOnField(x, y);
12114     }
12115
12116     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12117   }
12118 #endif
12119
12120   DrawAllPlayers();
12121   PlayAllPlayersSound();
12122
12123   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12124   {
12125     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12126
12127     local_player->show_envelope = 0;
12128   }
12129
12130   /* use random number generator in every frame to make it less predictable */
12131   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12132     RND(1);
12133 }
12134
12135 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12136 {
12137   int min_x = x, min_y = y, max_x = x, max_y = y;
12138   int i;
12139
12140   for (i = 0; i < MAX_PLAYERS; i++)
12141   {
12142     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12143
12144     if (!stored_player[i].active || &stored_player[i] == player)
12145       continue;
12146
12147     min_x = MIN(min_x, jx);
12148     min_y = MIN(min_y, jy);
12149     max_x = MAX(max_x, jx);
12150     max_y = MAX(max_y, jy);
12151   }
12152
12153   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12154 }
12155
12156 static boolean AllPlayersInVisibleScreen()
12157 {
12158   int i;
12159
12160   for (i = 0; i < MAX_PLAYERS; i++)
12161   {
12162     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12163
12164     if (!stored_player[i].active)
12165       continue;
12166
12167     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12168       return FALSE;
12169   }
12170
12171   return TRUE;
12172 }
12173
12174 void ScrollLevel(int dx, int dy)
12175 {
12176   int scroll_offset = 2 * TILEX_VAR;
12177   int x, y;
12178
12179   BlitBitmap(drawto_field, drawto_field,
12180              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12181              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12182              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12183              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12184              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12185              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12186
12187   if (dx != 0)
12188   {
12189     x = (dx == 1 ? BX1 : BX2);
12190     for (y = BY1; y <= BY2; y++)
12191       DrawScreenField(x, y);
12192   }
12193
12194   if (dy != 0)
12195   {
12196     y = (dy == 1 ? BY1 : BY2);
12197     for (x = BX1; x <= BX2; x++)
12198       DrawScreenField(x, y);
12199   }
12200
12201   redraw_mask |= REDRAW_FIELD;
12202 }
12203
12204 static boolean canFallDown(struct PlayerInfo *player)
12205 {
12206   int jx = player->jx, jy = player->jy;
12207
12208   return (IN_LEV_FIELD(jx, jy + 1) &&
12209           (IS_FREE(jx, jy + 1) ||
12210            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12211           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12212           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12213 }
12214
12215 static boolean canPassField(int x, int y, int move_dir)
12216 {
12217   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12218   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12219   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12220   int nextx = x + dx;
12221   int nexty = y + dy;
12222   int element = Feld[x][y];
12223
12224   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12225           !CAN_MOVE(element) &&
12226           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12227           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12228           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12229 }
12230
12231 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12232 {
12233   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12234   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12235   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12236   int newx = x + dx;
12237   int newy = y + dy;
12238
12239   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12240           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12241           (IS_DIGGABLE(Feld[newx][newy]) ||
12242            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12243            canPassField(newx, newy, move_dir)));
12244 }
12245
12246 static void CheckGravityMovement(struct PlayerInfo *player)
12247 {
12248   if (player->gravity && !player->programmed_action)
12249   {
12250     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12251     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12252     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12253     int jx = player->jx, jy = player->jy;
12254     boolean player_is_moving_to_valid_field =
12255       (!player_is_snapping &&
12256        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12257         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12258     boolean player_can_fall_down = canFallDown(player);
12259
12260     if (player_can_fall_down &&
12261         !player_is_moving_to_valid_field)
12262       player->programmed_action = MV_DOWN;
12263   }
12264 }
12265
12266 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12267 {
12268   return CheckGravityMovement(player);
12269
12270   if (player->gravity && !player->programmed_action)
12271   {
12272     int jx = player->jx, jy = player->jy;
12273     boolean field_under_player_is_free =
12274       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12275     boolean player_is_standing_on_valid_field =
12276       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12277        (IS_WALKABLE(Feld[jx][jy]) &&
12278         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12279
12280     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12281       player->programmed_action = MV_DOWN;
12282   }
12283 }
12284
12285 /*
12286   MovePlayerOneStep()
12287   -----------------------------------------------------------------------------
12288   dx, dy:               direction (non-diagonal) to try to move the player to
12289   real_dx, real_dy:     direction as read from input device (can be diagonal)
12290 */
12291
12292 boolean MovePlayerOneStep(struct PlayerInfo *player,
12293                           int dx, int dy, int real_dx, int real_dy)
12294 {
12295   int jx = player->jx, jy = player->jy;
12296   int new_jx = jx + dx, new_jy = jy + dy;
12297   int can_move;
12298   boolean player_can_move = !player->cannot_move;
12299
12300   if (!player->active || (!dx && !dy))
12301     return MP_NO_ACTION;
12302
12303   player->MovDir = (dx < 0 ? MV_LEFT :
12304                     dx > 0 ? MV_RIGHT :
12305                     dy < 0 ? MV_UP :
12306                     dy > 0 ? MV_DOWN :  MV_NONE);
12307
12308   if (!IN_LEV_FIELD(new_jx, new_jy))
12309     return MP_NO_ACTION;
12310
12311   if (!player_can_move)
12312   {
12313     if (player->MovPos == 0)
12314     {
12315       player->is_moving = FALSE;
12316       player->is_digging = FALSE;
12317       player->is_collecting = FALSE;
12318       player->is_snapping = FALSE;
12319       player->is_pushing = FALSE;
12320     }
12321   }
12322
12323   if (!network.enabled && game.centered_player_nr == -1 &&
12324       !AllPlayersInSight(player, new_jx, new_jy))
12325     return MP_NO_ACTION;
12326
12327   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12328   if (can_move != MP_MOVING)
12329     return can_move;
12330
12331   /* check if DigField() has caused relocation of the player */
12332   if (player->jx != jx || player->jy != jy)
12333     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12334
12335   StorePlayer[jx][jy] = 0;
12336   player->last_jx = jx;
12337   player->last_jy = jy;
12338   player->jx = new_jx;
12339   player->jy = new_jy;
12340   StorePlayer[new_jx][new_jy] = player->element_nr;
12341
12342   if (player->move_delay_value_next != -1)
12343   {
12344     player->move_delay_value = player->move_delay_value_next;
12345     player->move_delay_value_next = -1;
12346   }
12347
12348   player->MovPos =
12349     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12350
12351   player->step_counter++;
12352
12353   PlayerVisit[jx][jy] = FrameCounter;
12354
12355   player->is_moving = TRUE;
12356
12357 #if 1
12358   /* should better be called in MovePlayer(), but this breaks some tapes */
12359   ScrollPlayer(player, SCROLL_INIT);
12360 #endif
12361
12362   return MP_MOVING;
12363 }
12364
12365 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12366 {
12367   int jx = player->jx, jy = player->jy;
12368   int old_jx = jx, old_jy = jy;
12369   int moved = MP_NO_ACTION;
12370
12371   if (!player->active)
12372     return FALSE;
12373
12374   if (!dx && !dy)
12375   {
12376     if (player->MovPos == 0)
12377     {
12378       player->is_moving = FALSE;
12379       player->is_digging = FALSE;
12380       player->is_collecting = FALSE;
12381       player->is_snapping = FALSE;
12382       player->is_pushing = FALSE;
12383     }
12384
12385     return FALSE;
12386   }
12387
12388   if (player->move_delay > 0)
12389     return FALSE;
12390
12391   player->move_delay = -1;              /* set to "uninitialized" value */
12392
12393   /* store if player is automatically moved to next field */
12394   player->is_auto_moving = (player->programmed_action != MV_NONE);
12395
12396   /* remove the last programmed player action */
12397   player->programmed_action = 0;
12398
12399   if (player->MovPos)
12400   {
12401     /* should only happen if pre-1.2 tape recordings are played */
12402     /* this is only for backward compatibility */
12403
12404     int original_move_delay_value = player->move_delay_value;
12405
12406 #if DEBUG
12407     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12408            tape.counter);
12409 #endif
12410
12411     /* scroll remaining steps with finest movement resolution */
12412     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12413
12414     while (player->MovPos)
12415     {
12416       ScrollPlayer(player, SCROLL_GO_ON);
12417       ScrollScreen(NULL, SCROLL_GO_ON);
12418
12419       AdvanceFrameAndPlayerCounters(player->index_nr);
12420
12421       DrawAllPlayers();
12422       BackToFront_WithFrameDelay(0);
12423     }
12424
12425     player->move_delay_value = original_move_delay_value;
12426   }
12427
12428   player->is_active = FALSE;
12429
12430   if (player->last_move_dir & MV_HORIZONTAL)
12431   {
12432     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12433       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12434   }
12435   else
12436   {
12437     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12438       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12439   }
12440
12441   if (!moved && !player->is_active)
12442   {
12443     player->is_moving = FALSE;
12444     player->is_digging = FALSE;
12445     player->is_collecting = FALSE;
12446     player->is_snapping = FALSE;
12447     player->is_pushing = FALSE;
12448   }
12449
12450   jx = player->jx;
12451   jy = player->jy;
12452
12453   if (moved & MP_MOVING && !ScreenMovPos &&
12454       (player->index_nr == game.centered_player_nr ||
12455        game.centered_player_nr == -1))
12456   {
12457     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12458     int offset = game.scroll_delay_value;
12459
12460     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12461     {
12462       /* actual player has left the screen -- scroll in that direction */
12463       if (jx != old_jx)         /* player has moved horizontally */
12464         scroll_x += (jx - old_jx);
12465       else                      /* player has moved vertically */
12466         scroll_y += (jy - old_jy);
12467     }
12468     else
12469     {
12470       if (jx != old_jx)         /* player has moved horizontally */
12471       {
12472         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12473             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12474           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12475
12476         /* don't scroll over playfield boundaries */
12477         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12478           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12479
12480         /* don't scroll more than one field at a time */
12481         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12482
12483         /* don't scroll against the player's moving direction */
12484         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12485             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12486           scroll_x = old_scroll_x;
12487       }
12488       else                      /* player has moved vertically */
12489       {
12490         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12491             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12492           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12493
12494         /* don't scroll over playfield boundaries */
12495         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12496           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12497
12498         /* don't scroll more than one field at a time */
12499         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12500
12501         /* don't scroll against the player's moving direction */
12502         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12503             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12504           scroll_y = old_scroll_y;
12505       }
12506     }
12507
12508     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12509     {
12510       if (!network.enabled && game.centered_player_nr == -1 &&
12511           !AllPlayersInVisibleScreen())
12512       {
12513         scroll_x = old_scroll_x;
12514         scroll_y = old_scroll_y;
12515       }
12516       else
12517       {
12518         ScrollScreen(player, SCROLL_INIT);
12519         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12520       }
12521     }
12522   }
12523
12524   player->StepFrame = 0;
12525
12526   if (moved & MP_MOVING)
12527   {
12528     if (old_jx != jx && old_jy == jy)
12529       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12530     else if (old_jx == jx && old_jy != jy)
12531       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12532
12533     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12534
12535     player->last_move_dir = player->MovDir;
12536     player->is_moving = TRUE;
12537     player->is_snapping = FALSE;
12538     player->is_switching = FALSE;
12539     player->is_dropping = FALSE;
12540     player->is_dropping_pressed = FALSE;
12541     player->drop_pressed_delay = 0;
12542
12543 #if 0
12544     /* should better be called here than above, but this breaks some tapes */
12545     ScrollPlayer(player, SCROLL_INIT);
12546 #endif
12547   }
12548   else
12549   {
12550     CheckGravityMovementWhenNotMoving(player);
12551
12552     player->is_moving = FALSE;
12553
12554     /* at this point, the player is allowed to move, but cannot move right now
12555        (e.g. because of something blocking the way) -- ensure that the player
12556        is also allowed to move in the next frame (in old versions before 3.1.1,
12557        the player was forced to wait again for eight frames before next try) */
12558
12559     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12560       player->move_delay = 0;   /* allow direct movement in the next frame */
12561   }
12562
12563   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12564     player->move_delay = player->move_delay_value;
12565
12566   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12567   {
12568     TestIfPlayerTouchesBadThing(jx, jy);
12569     TestIfPlayerTouchesCustomElement(jx, jy);
12570   }
12571
12572   if (!player->active)
12573     RemovePlayer(player);
12574
12575   return moved;
12576 }
12577
12578 void ScrollPlayer(struct PlayerInfo *player, int mode)
12579 {
12580   int jx = player->jx, jy = player->jy;
12581   int last_jx = player->last_jx, last_jy = player->last_jy;
12582   int move_stepsize = TILEX / player->move_delay_value;
12583
12584   if (!player->active)
12585     return;
12586
12587   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12588     return;
12589
12590   if (mode == SCROLL_INIT)
12591   {
12592     player->actual_frame_counter = FrameCounter;
12593     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12594
12595     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12596         Feld[last_jx][last_jy] == EL_EMPTY)
12597     {
12598       int last_field_block_delay = 0;   /* start with no blocking at all */
12599       int block_delay_adjustment = player->block_delay_adjustment;
12600
12601       /* if player blocks last field, add delay for exactly one move */
12602       if (player->block_last_field)
12603       {
12604         last_field_block_delay += player->move_delay_value;
12605
12606         /* when blocking enabled, prevent moving up despite gravity */
12607         if (player->gravity && player->MovDir == MV_UP)
12608           block_delay_adjustment = -1;
12609       }
12610
12611       /* add block delay adjustment (also possible when not blocking) */
12612       last_field_block_delay += block_delay_adjustment;
12613
12614       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12615       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12616     }
12617
12618     if (player->MovPos != 0)    /* player has not yet reached destination */
12619       return;
12620   }
12621   else if (!FrameReached(&player->actual_frame_counter, 1))
12622     return;
12623
12624   if (player->MovPos != 0)
12625   {
12626     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12627     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12628
12629     /* before DrawPlayer() to draw correct player graphic for this case */
12630     if (player->MovPos == 0)
12631       CheckGravityMovement(player);
12632   }
12633
12634   if (player->MovPos == 0)      /* player reached destination field */
12635   {
12636     if (player->move_delay_reset_counter > 0)
12637     {
12638       player->move_delay_reset_counter--;
12639
12640       if (player->move_delay_reset_counter == 0)
12641       {
12642         /* continue with normal speed after quickly moving through gate */
12643         HALVE_PLAYER_SPEED(player);
12644
12645         /* be able to make the next move without delay */
12646         player->move_delay = 0;
12647       }
12648     }
12649
12650     player->last_jx = jx;
12651     player->last_jy = jy;
12652
12653     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12654         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12655         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12656         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12657         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12658         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12659         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12660         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12661     {
12662       ExitPlayer(player);
12663
12664       if ((local_player->friends_still_needed == 0 ||
12665            IS_SP_ELEMENT(Feld[jx][jy])) &&
12666           AllPlayersGone)
12667         PlayerWins(local_player);
12668     }
12669
12670     /* this breaks one level: "machine", level 000 */
12671     {
12672       int move_direction = player->MovDir;
12673       int enter_side = MV_DIR_OPPOSITE(move_direction);
12674       int leave_side = move_direction;
12675       int old_jx = last_jx;
12676       int old_jy = last_jy;
12677       int old_element = Feld[old_jx][old_jy];
12678       int new_element = Feld[jx][jy];
12679
12680       if (IS_CUSTOM_ELEMENT(old_element))
12681         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12682                                    CE_LEFT_BY_PLAYER,
12683                                    player->index_bit, leave_side);
12684
12685       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12686                                           CE_PLAYER_LEAVES_X,
12687                                           player->index_bit, leave_side);
12688
12689       if (IS_CUSTOM_ELEMENT(new_element))
12690         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12691                                    player->index_bit, enter_side);
12692
12693       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12694                                           CE_PLAYER_ENTERS_X,
12695                                           player->index_bit, enter_side);
12696
12697       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12698                                         CE_MOVE_OF_X, move_direction);
12699     }
12700
12701     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12702     {
12703       TestIfPlayerTouchesBadThing(jx, jy);
12704       TestIfPlayerTouchesCustomElement(jx, jy);
12705
12706       /* needed because pushed element has not yet reached its destination,
12707          so it would trigger a change event at its previous field location */
12708       if (!player->is_pushing)
12709         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12710
12711       if (!player->active)
12712         RemovePlayer(player);
12713     }
12714
12715     if (!local_player->LevelSolved && level.use_step_counter)
12716     {
12717       int i;
12718
12719       TimePlayed++;
12720
12721       if (TimeLeft > 0)
12722       {
12723         TimeLeft--;
12724
12725         if (TimeLeft <= 10 && setup.time_limit)
12726           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12727
12728         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12729
12730         DisplayGameControlValues();
12731
12732         if (!TimeLeft && setup.time_limit)
12733           for (i = 0; i < MAX_PLAYERS; i++)
12734             KillPlayer(&stored_player[i]);
12735       }
12736       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12737       {
12738         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12739
12740         DisplayGameControlValues();
12741       }
12742     }
12743
12744     if (tape.single_step && tape.recording && !tape.pausing &&
12745         !player->programmed_action)
12746       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12747
12748     if (!player->programmed_action)
12749       CheckSaveEngineSnapshot(player);
12750   }
12751 }
12752
12753 void ScrollScreen(struct PlayerInfo *player, int mode)
12754 {
12755   static unsigned int screen_frame_counter = 0;
12756
12757   if (mode == SCROLL_INIT)
12758   {
12759     /* set scrolling step size according to actual player's moving speed */
12760     ScrollStepSize = TILEX / player->move_delay_value;
12761
12762     screen_frame_counter = FrameCounter;
12763     ScreenMovDir = player->MovDir;
12764     ScreenMovPos = player->MovPos;
12765     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12766     return;
12767   }
12768   else if (!FrameReached(&screen_frame_counter, 1))
12769     return;
12770
12771   if (ScreenMovPos)
12772   {
12773     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12774     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12775     redraw_mask |= REDRAW_FIELD;
12776   }
12777   else
12778     ScreenMovDir = MV_NONE;
12779 }
12780
12781 void TestIfPlayerTouchesCustomElement(int x, int y)
12782 {
12783   static int xy[4][2] =
12784   {
12785     { 0, -1 },
12786     { -1, 0 },
12787     { +1, 0 },
12788     { 0, +1 }
12789   };
12790   static int trigger_sides[4][2] =
12791   {
12792     /* center side       border side */
12793     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12794     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12795     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12796     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12797   };
12798   static int touch_dir[4] =
12799   {
12800     MV_LEFT | MV_RIGHT,
12801     MV_UP   | MV_DOWN,
12802     MV_UP   | MV_DOWN,
12803     MV_LEFT | MV_RIGHT
12804   };
12805   int center_element = Feld[x][y];      /* should always be non-moving! */
12806   int i;
12807
12808   for (i = 0; i < NUM_DIRECTIONS; i++)
12809   {
12810     int xx = x + xy[i][0];
12811     int yy = y + xy[i][1];
12812     int center_side = trigger_sides[i][0];
12813     int border_side = trigger_sides[i][1];
12814     int border_element;
12815
12816     if (!IN_LEV_FIELD(xx, yy))
12817       continue;
12818
12819     if (IS_PLAYER(x, y))                /* player found at center element */
12820     {
12821       struct PlayerInfo *player = PLAYERINFO(x, y);
12822
12823       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12824         border_element = Feld[xx][yy];          /* may be moving! */
12825       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12826         border_element = Feld[xx][yy];
12827       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12828         border_element = MovingOrBlocked2Element(xx, yy);
12829       else
12830         continue;               /* center and border element do not touch */
12831
12832       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12833                                  player->index_bit, border_side);
12834       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12835                                           CE_PLAYER_TOUCHES_X,
12836                                           player->index_bit, border_side);
12837
12838       {
12839         /* use player element that is initially defined in the level playfield,
12840            not the player element that corresponds to the runtime player number
12841            (example: a level that contains EL_PLAYER_3 as the only player would
12842            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12843         int player_element = PLAYERINFO(x, y)->initial_element;
12844
12845         CheckElementChangeBySide(xx, yy, border_element, player_element,
12846                                  CE_TOUCHING_X, border_side);
12847       }
12848     }
12849     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12850     {
12851       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12852
12853       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12854       {
12855         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12856           continue;             /* center and border element do not touch */
12857       }
12858
12859       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12860                                  player->index_bit, center_side);
12861       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12862                                           CE_PLAYER_TOUCHES_X,
12863                                           player->index_bit, center_side);
12864
12865       {
12866         /* use player element that is initially defined in the level playfield,
12867            not the player element that corresponds to the runtime player number
12868            (example: a level that contains EL_PLAYER_3 as the only player would
12869            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12870         int player_element = PLAYERINFO(xx, yy)->initial_element;
12871
12872         CheckElementChangeBySide(x, y, center_element, player_element,
12873                                  CE_TOUCHING_X, center_side);
12874       }
12875
12876       break;
12877     }
12878   }
12879 }
12880
12881 void TestIfElementTouchesCustomElement(int x, int y)
12882 {
12883   static int xy[4][2] =
12884   {
12885     { 0, -1 },
12886     { -1, 0 },
12887     { +1, 0 },
12888     { 0, +1 }
12889   };
12890   static int trigger_sides[4][2] =
12891   {
12892     /* center side      border side */
12893     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12894     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12895     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12896     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12897   };
12898   static int touch_dir[4] =
12899   {
12900     MV_LEFT | MV_RIGHT,
12901     MV_UP   | MV_DOWN,
12902     MV_UP   | MV_DOWN,
12903     MV_LEFT | MV_RIGHT
12904   };
12905   boolean change_center_element = FALSE;
12906   int center_element = Feld[x][y];      /* should always be non-moving! */
12907   int border_element_old[NUM_DIRECTIONS];
12908   int i;
12909
12910   for (i = 0; i < NUM_DIRECTIONS; i++)
12911   {
12912     int xx = x + xy[i][0];
12913     int yy = y + xy[i][1];
12914     int border_element;
12915
12916     border_element_old[i] = -1;
12917
12918     if (!IN_LEV_FIELD(xx, yy))
12919       continue;
12920
12921     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12922       border_element = Feld[xx][yy];    /* may be moving! */
12923     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12924       border_element = Feld[xx][yy];
12925     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12926       border_element = MovingOrBlocked2Element(xx, yy);
12927     else
12928       continue;                 /* center and border element do not touch */
12929
12930     border_element_old[i] = border_element;
12931   }
12932
12933   for (i = 0; i < NUM_DIRECTIONS; i++)
12934   {
12935     int xx = x + xy[i][0];
12936     int yy = y + xy[i][1];
12937     int center_side = trigger_sides[i][0];
12938     int border_element = border_element_old[i];
12939
12940     if (border_element == -1)
12941       continue;
12942
12943     /* check for change of border element */
12944     CheckElementChangeBySide(xx, yy, border_element, center_element,
12945                              CE_TOUCHING_X, center_side);
12946
12947     /* (center element cannot be player, so we dont have to check this here) */
12948   }
12949
12950   for (i = 0; i < NUM_DIRECTIONS; i++)
12951   {
12952     int xx = x + xy[i][0];
12953     int yy = y + xy[i][1];
12954     int border_side = trigger_sides[i][1];
12955     int border_element = border_element_old[i];
12956
12957     if (border_element == -1)
12958       continue;
12959
12960     /* check for change of center element (but change it only once) */
12961     if (!change_center_element)
12962       change_center_element =
12963         CheckElementChangeBySide(x, y, center_element, border_element,
12964                                  CE_TOUCHING_X, border_side);
12965
12966     if (IS_PLAYER(xx, yy))
12967     {
12968       /* use player element that is initially defined in the level playfield,
12969          not the player element that corresponds to the runtime player number
12970          (example: a level that contains EL_PLAYER_3 as the only player would
12971          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12972       int player_element = PLAYERINFO(xx, yy)->initial_element;
12973
12974       CheckElementChangeBySide(x, y, center_element, player_element,
12975                                CE_TOUCHING_X, border_side);
12976     }
12977   }
12978 }
12979
12980 void TestIfElementHitsCustomElement(int x, int y, int direction)
12981 {
12982   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12983   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12984   int hitx = x + dx, hity = y + dy;
12985   int hitting_element = Feld[x][y];
12986   int touched_element;
12987
12988   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12989     return;
12990
12991   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12992                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12993
12994   if (IN_LEV_FIELD(hitx, hity))
12995   {
12996     int opposite_direction = MV_DIR_OPPOSITE(direction);
12997     int hitting_side = direction;
12998     int touched_side = opposite_direction;
12999     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13000                           MovDir[hitx][hity] != direction ||
13001                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13002
13003     object_hit = TRUE;
13004
13005     if (object_hit)
13006     {
13007       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13008                                CE_HITTING_X, touched_side);
13009
13010       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13011                                CE_HIT_BY_X, hitting_side);
13012
13013       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13014                                CE_HIT_BY_SOMETHING, opposite_direction);
13015
13016       if (IS_PLAYER(hitx, hity))
13017       {
13018         /* use player element that is initially defined in the level playfield,
13019            not the player element that corresponds to the runtime player number
13020            (example: a level that contains EL_PLAYER_3 as the only player would
13021            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13022         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13023
13024         CheckElementChangeBySide(x, y, hitting_element, player_element,
13025                                  CE_HITTING_X, touched_side);
13026       }
13027     }
13028   }
13029
13030   /* "hitting something" is also true when hitting the playfield border */
13031   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13032                            CE_HITTING_SOMETHING, direction);
13033 }
13034
13035 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13036 {
13037   int i, kill_x = -1, kill_y = -1;
13038
13039   int bad_element = -1;
13040   static int test_xy[4][2] =
13041   {
13042     { 0, -1 },
13043     { -1, 0 },
13044     { +1, 0 },
13045     { 0, +1 }
13046   };
13047   static int test_dir[4] =
13048   {
13049     MV_UP,
13050     MV_LEFT,
13051     MV_RIGHT,
13052     MV_DOWN
13053   };
13054
13055   for (i = 0; i < NUM_DIRECTIONS; i++)
13056   {
13057     int test_x, test_y, test_move_dir, test_element;
13058
13059     test_x = good_x + test_xy[i][0];
13060     test_y = good_y + test_xy[i][1];
13061
13062     if (!IN_LEV_FIELD(test_x, test_y))
13063       continue;
13064
13065     test_move_dir =
13066       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13067
13068     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13069
13070     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13071        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13072     */
13073     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13074         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13075     {
13076       kill_x = test_x;
13077       kill_y = test_y;
13078       bad_element = test_element;
13079
13080       break;
13081     }
13082   }
13083
13084   if (kill_x != -1 || kill_y != -1)
13085   {
13086     if (IS_PLAYER(good_x, good_y))
13087     {
13088       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13089
13090       if (player->shield_deadly_time_left > 0 &&
13091           !IS_INDESTRUCTIBLE(bad_element))
13092         Bang(kill_x, kill_y);
13093       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13094         KillPlayer(player);
13095     }
13096     else
13097       Bang(good_x, good_y);
13098   }
13099 }
13100
13101 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13102 {
13103   int i, kill_x = -1, kill_y = -1;
13104   int bad_element = Feld[bad_x][bad_y];
13105   static int test_xy[4][2] =
13106   {
13107     { 0, -1 },
13108     { -1, 0 },
13109     { +1, 0 },
13110     { 0, +1 }
13111   };
13112   static int touch_dir[4] =
13113   {
13114     MV_LEFT | MV_RIGHT,
13115     MV_UP   | MV_DOWN,
13116     MV_UP   | MV_DOWN,
13117     MV_LEFT | MV_RIGHT
13118   };
13119   static int test_dir[4] =
13120   {
13121     MV_UP,
13122     MV_LEFT,
13123     MV_RIGHT,
13124     MV_DOWN
13125   };
13126
13127   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13128     return;
13129
13130   for (i = 0; i < NUM_DIRECTIONS; i++)
13131   {
13132     int test_x, test_y, test_move_dir, test_element;
13133
13134     test_x = bad_x + test_xy[i][0];
13135     test_y = bad_y + test_xy[i][1];
13136
13137     if (!IN_LEV_FIELD(test_x, test_y))
13138       continue;
13139
13140     test_move_dir =
13141       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13142
13143     test_element = Feld[test_x][test_y];
13144
13145     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13146        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13147     */
13148     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13149         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13150     {
13151       /* good thing is player or penguin that does not move away */
13152       if (IS_PLAYER(test_x, test_y))
13153       {
13154         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13155
13156         if (bad_element == EL_ROBOT && player->is_moving)
13157           continue;     /* robot does not kill player if he is moving */
13158
13159         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13160         {
13161           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13162             continue;           /* center and border element do not touch */
13163         }
13164
13165         kill_x = test_x;
13166         kill_y = test_y;
13167
13168         break;
13169       }
13170       else if (test_element == EL_PENGUIN)
13171       {
13172         kill_x = test_x;
13173         kill_y = test_y;
13174
13175         break;
13176       }
13177     }
13178   }
13179
13180   if (kill_x != -1 || kill_y != -1)
13181   {
13182     if (IS_PLAYER(kill_x, kill_y))
13183     {
13184       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13185
13186       if (player->shield_deadly_time_left > 0 &&
13187           !IS_INDESTRUCTIBLE(bad_element))
13188         Bang(bad_x, bad_y);
13189       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13190         KillPlayer(player);
13191     }
13192     else
13193       Bang(kill_x, kill_y);
13194   }
13195 }
13196
13197 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13198 {
13199   int bad_element = Feld[bad_x][bad_y];
13200   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13201   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13202   int test_x = bad_x + dx, test_y = bad_y + dy;
13203   int test_move_dir, test_element;
13204   int kill_x = -1, kill_y = -1;
13205
13206   if (!IN_LEV_FIELD(test_x, test_y))
13207     return;
13208
13209   test_move_dir =
13210     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13211
13212   test_element = Feld[test_x][test_y];
13213
13214   if (test_move_dir != bad_move_dir)
13215   {
13216     /* good thing can be player or penguin that does not move away */
13217     if (IS_PLAYER(test_x, test_y))
13218     {
13219       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13220
13221       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13222          player as being hit when he is moving towards the bad thing, because
13223          the "get hit by" condition would be lost after the player stops) */
13224       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13225         return;         /* player moves away from bad thing */
13226
13227       kill_x = test_x;
13228       kill_y = test_y;
13229     }
13230     else if (test_element == EL_PENGUIN)
13231     {
13232       kill_x = test_x;
13233       kill_y = test_y;
13234     }
13235   }
13236
13237   if (kill_x != -1 || kill_y != -1)
13238   {
13239     if (IS_PLAYER(kill_x, kill_y))
13240     {
13241       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13242
13243       if (player->shield_deadly_time_left > 0 &&
13244           !IS_INDESTRUCTIBLE(bad_element))
13245         Bang(bad_x, bad_y);
13246       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13247         KillPlayer(player);
13248     }
13249     else
13250       Bang(kill_x, kill_y);
13251   }
13252 }
13253
13254 void TestIfPlayerTouchesBadThing(int x, int y)
13255 {
13256   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13257 }
13258
13259 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13260 {
13261   TestIfGoodThingHitsBadThing(x, y, move_dir);
13262 }
13263
13264 void TestIfBadThingTouchesPlayer(int x, int y)
13265 {
13266   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13267 }
13268
13269 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13270 {
13271   TestIfBadThingHitsGoodThing(x, y, move_dir);
13272 }
13273
13274 void TestIfFriendTouchesBadThing(int x, int y)
13275 {
13276   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13277 }
13278
13279 void TestIfBadThingTouchesFriend(int x, int y)
13280 {
13281   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13282 }
13283
13284 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13285 {
13286   int i, kill_x = bad_x, kill_y = bad_y;
13287   static int xy[4][2] =
13288   {
13289     { 0, -1 },
13290     { -1, 0 },
13291     { +1, 0 },
13292     { 0, +1 }
13293   };
13294
13295   for (i = 0; i < NUM_DIRECTIONS; i++)
13296   {
13297     int x, y, element;
13298
13299     x = bad_x + xy[i][0];
13300     y = bad_y + xy[i][1];
13301     if (!IN_LEV_FIELD(x, y))
13302       continue;
13303
13304     element = Feld[x][y];
13305     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13306         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13307     {
13308       kill_x = x;
13309       kill_y = y;
13310       break;
13311     }
13312   }
13313
13314   if (kill_x != bad_x || kill_y != bad_y)
13315     Bang(bad_x, bad_y);
13316 }
13317
13318 void KillPlayer(struct PlayerInfo *player)
13319 {
13320   int jx = player->jx, jy = player->jy;
13321
13322   if (!player->active)
13323     return;
13324
13325 #if 0
13326   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13327          player->killed, player->active, player->reanimated);
13328 #endif
13329
13330   /* the following code was introduced to prevent an infinite loop when calling
13331      -> Bang()
13332      -> CheckTriggeredElementChangeExt()
13333      -> ExecuteCustomElementAction()
13334      -> KillPlayer()
13335      -> (infinitely repeating the above sequence of function calls)
13336      which occurs when killing the player while having a CE with the setting
13337      "kill player X when explosion of <player X>"; the solution using a new
13338      field "player->killed" was chosen for backwards compatibility, although
13339      clever use of the fields "player->active" etc. would probably also work */
13340 #if 1
13341   if (player->killed)
13342     return;
13343 #endif
13344
13345   player->killed = TRUE;
13346
13347   /* remove accessible field at the player's position */
13348   Feld[jx][jy] = EL_EMPTY;
13349
13350   /* deactivate shield (else Bang()/Explode() would not work right) */
13351   player->shield_normal_time_left = 0;
13352   player->shield_deadly_time_left = 0;
13353
13354 #if 0
13355   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13356          player->killed, player->active, player->reanimated);
13357 #endif
13358
13359   Bang(jx, jy);
13360
13361 #if 0
13362   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13363          player->killed, player->active, player->reanimated);
13364 #endif
13365
13366   if (player->reanimated)       /* killed player may have been reanimated */
13367     player->killed = player->reanimated = FALSE;
13368   else
13369     BuryPlayer(player);
13370 }
13371
13372 static void KillPlayerUnlessEnemyProtected(int x, int y)
13373 {
13374   if (!PLAYER_ENEMY_PROTECTED(x, y))
13375     KillPlayer(PLAYERINFO(x, y));
13376 }
13377
13378 static void KillPlayerUnlessExplosionProtected(int x, int y)
13379 {
13380   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13381     KillPlayer(PLAYERINFO(x, y));
13382 }
13383
13384 void BuryPlayer(struct PlayerInfo *player)
13385 {
13386   int jx = player->jx, jy = player->jy;
13387
13388   if (!player->active)
13389     return;
13390
13391   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13392   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13393
13394   player->GameOver = TRUE;
13395   RemovePlayer(player);
13396 }
13397
13398 void RemovePlayer(struct PlayerInfo *player)
13399 {
13400   int jx = player->jx, jy = player->jy;
13401   int i, found = FALSE;
13402
13403   player->present = FALSE;
13404   player->active = FALSE;
13405
13406   if (!ExplodeField[jx][jy])
13407     StorePlayer[jx][jy] = 0;
13408
13409   if (player->is_moving)
13410     TEST_DrawLevelField(player->last_jx, player->last_jy);
13411
13412   for (i = 0; i < MAX_PLAYERS; i++)
13413     if (stored_player[i].active)
13414       found = TRUE;
13415
13416   if (!found)
13417     AllPlayersGone = TRUE;
13418
13419   ExitX = ZX = jx;
13420   ExitY = ZY = jy;
13421 }
13422
13423 void ExitPlayer(struct PlayerInfo *player)
13424 {
13425   DrawPlayer(player);   /* needed here only to cleanup last field */
13426   RemovePlayer(player);
13427
13428   local_player->players_still_needed--;
13429 }
13430
13431 static void setFieldForSnapping(int x, int y, int element, int direction)
13432 {
13433   struct ElementInfo *ei = &element_info[element];
13434   int direction_bit = MV_DIR_TO_BIT(direction);
13435   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13436   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13437                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13438
13439   Feld[x][y] = EL_ELEMENT_SNAPPING;
13440   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13441
13442   ResetGfxAnimation(x, y);
13443
13444   GfxElement[x][y] = element;
13445   GfxAction[x][y] = action;
13446   GfxDir[x][y] = direction;
13447   GfxFrame[x][y] = -1;
13448 }
13449
13450 /*
13451   =============================================================================
13452   checkDiagonalPushing()
13453   -----------------------------------------------------------------------------
13454   check if diagonal input device direction results in pushing of object
13455   (by checking if the alternative direction is walkable, diggable, ...)
13456   =============================================================================
13457 */
13458
13459 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13460                                     int x, int y, int real_dx, int real_dy)
13461 {
13462   int jx, jy, dx, dy, xx, yy;
13463
13464   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13465     return TRUE;
13466
13467   /* diagonal direction: check alternative direction */
13468   jx = player->jx;
13469   jy = player->jy;
13470   dx = x - jx;
13471   dy = y - jy;
13472   xx = jx + (dx == 0 ? real_dx : 0);
13473   yy = jy + (dy == 0 ? real_dy : 0);
13474
13475   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13476 }
13477
13478 /*
13479   =============================================================================
13480   DigField()
13481   -----------------------------------------------------------------------------
13482   x, y:                 field next to player (non-diagonal) to try to dig to
13483   real_dx, real_dy:     direction as read from input device (can be diagonal)
13484   =============================================================================
13485 */
13486
13487 static int DigField(struct PlayerInfo *player,
13488                     int oldx, int oldy, int x, int y,
13489                     int real_dx, int real_dy, int mode)
13490 {
13491   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13492   boolean player_was_pushing = player->is_pushing;
13493   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13494   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13495   int jx = oldx, jy = oldy;
13496   int dx = x - jx, dy = y - jy;
13497   int nextx = x + dx, nexty = y + dy;
13498   int move_direction = (dx == -1 ? MV_LEFT  :
13499                         dx == +1 ? MV_RIGHT :
13500                         dy == -1 ? MV_UP    :
13501                         dy == +1 ? MV_DOWN  : MV_NONE);
13502   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13503   int dig_side = MV_DIR_OPPOSITE(move_direction);
13504   int old_element = Feld[jx][jy];
13505   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13506   int collect_count;
13507
13508   if (is_player)                /* function can also be called by EL_PENGUIN */
13509   {
13510     if (player->MovPos == 0)
13511     {
13512       player->is_digging = FALSE;
13513       player->is_collecting = FALSE;
13514     }
13515
13516     if (player->MovPos == 0)    /* last pushing move finished */
13517       player->is_pushing = FALSE;
13518
13519     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13520     {
13521       player->is_switching = FALSE;
13522       player->push_delay = -1;
13523
13524       return MP_NO_ACTION;
13525     }
13526   }
13527
13528   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13529     old_element = Back[jx][jy];
13530
13531   /* in case of element dropped at player position, check background */
13532   else if (Back[jx][jy] != EL_EMPTY &&
13533            game.engine_version >= VERSION_IDENT(2,2,0,0))
13534     old_element = Back[jx][jy];
13535
13536   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13537     return MP_NO_ACTION;        /* field has no opening in this direction */
13538
13539   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13540     return MP_NO_ACTION;        /* field has no opening in this direction */
13541
13542   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13543   {
13544     SplashAcid(x, y);
13545
13546     Feld[jx][jy] = player->artwork_element;
13547     InitMovingField(jx, jy, MV_DOWN);
13548     Store[jx][jy] = EL_ACID;
13549     ContinueMoving(jx, jy);
13550     BuryPlayer(player);
13551
13552     return MP_DONT_RUN_INTO;
13553   }
13554
13555   if (player_can_move && DONT_RUN_INTO(element))
13556   {
13557     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13558
13559     return MP_DONT_RUN_INTO;
13560   }
13561
13562   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13563     return MP_NO_ACTION;
13564
13565   collect_count = element_info[element].collect_count_initial;
13566
13567   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13568     return MP_NO_ACTION;
13569
13570   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13571     player_can_move = player_can_move_or_snap;
13572
13573   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13574       game.engine_version >= VERSION_IDENT(2,2,0,0))
13575   {
13576     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13577                                player->index_bit, dig_side);
13578     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13579                                         player->index_bit, dig_side);
13580
13581     if (element == EL_DC_LANDMINE)
13582       Bang(x, y);
13583
13584     if (Feld[x][y] != element)          /* field changed by snapping */
13585       return MP_ACTION;
13586
13587     return MP_NO_ACTION;
13588   }
13589
13590   if (player->gravity && is_player && !player->is_auto_moving &&
13591       canFallDown(player) && move_direction != MV_DOWN &&
13592       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13593     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13594
13595   if (player_can_move &&
13596       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13597   {
13598     int sound_element = SND_ELEMENT(element);
13599     int sound_action = ACTION_WALKING;
13600
13601     if (IS_RND_GATE(element))
13602     {
13603       if (!player->key[RND_GATE_NR(element)])
13604         return MP_NO_ACTION;
13605     }
13606     else if (IS_RND_GATE_GRAY(element))
13607     {
13608       if (!player->key[RND_GATE_GRAY_NR(element)])
13609         return MP_NO_ACTION;
13610     }
13611     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13612     {
13613       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13614         return MP_NO_ACTION;
13615     }
13616     else if (element == EL_EXIT_OPEN ||
13617              element == EL_EM_EXIT_OPEN ||
13618              element == EL_EM_EXIT_OPENING ||
13619              element == EL_STEEL_EXIT_OPEN ||
13620              element == EL_EM_STEEL_EXIT_OPEN ||
13621              element == EL_EM_STEEL_EXIT_OPENING ||
13622              element == EL_SP_EXIT_OPEN ||
13623              element == EL_SP_EXIT_OPENING)
13624     {
13625       sound_action = ACTION_PASSING;    /* player is passing exit */
13626     }
13627     else if (element == EL_EMPTY)
13628     {
13629       sound_action = ACTION_MOVING;             /* nothing to walk on */
13630     }
13631
13632     /* play sound from background or player, whatever is available */
13633     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13634       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13635     else
13636       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13637   }
13638   else if (player_can_move &&
13639            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13640   {
13641     if (!ACCESS_FROM(element, opposite_direction))
13642       return MP_NO_ACTION;      /* field not accessible from this direction */
13643
13644     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13645       return MP_NO_ACTION;
13646
13647     if (IS_EM_GATE(element))
13648     {
13649       if (!player->key[EM_GATE_NR(element)])
13650         return MP_NO_ACTION;
13651     }
13652     else if (IS_EM_GATE_GRAY(element))
13653     {
13654       if (!player->key[EM_GATE_GRAY_NR(element)])
13655         return MP_NO_ACTION;
13656     }
13657     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13658     {
13659       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13660         return MP_NO_ACTION;
13661     }
13662     else if (IS_EMC_GATE(element))
13663     {
13664       if (!player->key[EMC_GATE_NR(element)])
13665         return MP_NO_ACTION;
13666     }
13667     else if (IS_EMC_GATE_GRAY(element))
13668     {
13669       if (!player->key[EMC_GATE_GRAY_NR(element)])
13670         return MP_NO_ACTION;
13671     }
13672     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13673     {
13674       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13675         return MP_NO_ACTION;
13676     }
13677     else if (element == EL_DC_GATE_WHITE ||
13678              element == EL_DC_GATE_WHITE_GRAY ||
13679              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13680     {
13681       if (player->num_white_keys == 0)
13682         return MP_NO_ACTION;
13683
13684       player->num_white_keys--;
13685     }
13686     else if (IS_SP_PORT(element))
13687     {
13688       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13689           element == EL_SP_GRAVITY_PORT_RIGHT ||
13690           element == EL_SP_GRAVITY_PORT_UP ||
13691           element == EL_SP_GRAVITY_PORT_DOWN)
13692         player->gravity = !player->gravity;
13693       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13694                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13695                element == EL_SP_GRAVITY_ON_PORT_UP ||
13696                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13697         player->gravity = TRUE;
13698       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13699                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13700                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13701                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13702         player->gravity = FALSE;
13703     }
13704
13705     /* automatically move to the next field with double speed */
13706     player->programmed_action = move_direction;
13707
13708     if (player->move_delay_reset_counter == 0)
13709     {
13710       player->move_delay_reset_counter = 2;     /* two double speed steps */
13711
13712       DOUBLE_PLAYER_SPEED(player);
13713     }
13714
13715     PlayLevelSoundAction(x, y, ACTION_PASSING);
13716   }
13717   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13718   {
13719     RemoveField(x, y);
13720
13721     if (mode != DF_SNAP)
13722     {
13723       GfxElement[x][y] = GFX_ELEMENT(element);
13724       player->is_digging = TRUE;
13725     }
13726
13727     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13728
13729     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13730                                         player->index_bit, dig_side);
13731
13732     if (mode == DF_SNAP)
13733     {
13734       if (level.block_snap_field)
13735         setFieldForSnapping(x, y, element, move_direction);
13736       else
13737         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13738
13739       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13740                                           player->index_bit, dig_side);
13741     }
13742   }
13743   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13744   {
13745     RemoveField(x, y);
13746
13747     if (is_player && mode != DF_SNAP)
13748     {
13749       GfxElement[x][y] = element;
13750       player->is_collecting = TRUE;
13751     }
13752
13753     if (element == EL_SPEED_PILL)
13754     {
13755       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13756     }
13757     else if (element == EL_EXTRA_TIME && level.time > 0)
13758     {
13759       TimeLeft += level.extra_time;
13760
13761       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13762
13763       DisplayGameControlValues();
13764     }
13765     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13766     {
13767       player->shield_normal_time_left += level.shield_normal_time;
13768       if (element == EL_SHIELD_DEADLY)
13769         player->shield_deadly_time_left += level.shield_deadly_time;
13770     }
13771     else if (element == EL_DYNAMITE ||
13772              element == EL_EM_DYNAMITE ||
13773              element == EL_SP_DISK_RED)
13774     {
13775       if (player->inventory_size < MAX_INVENTORY_SIZE)
13776         player->inventory_element[player->inventory_size++] = element;
13777
13778       DrawGameDoorValues();
13779     }
13780     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13781     {
13782       player->dynabomb_count++;
13783       player->dynabombs_left++;
13784     }
13785     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13786     {
13787       player->dynabomb_size++;
13788     }
13789     else if (element == EL_DYNABOMB_INCREASE_POWER)
13790     {
13791       player->dynabomb_xl = TRUE;
13792     }
13793     else if (IS_KEY(element))
13794     {
13795       player->key[KEY_NR(element)] = TRUE;
13796
13797       DrawGameDoorValues();
13798     }
13799     else if (element == EL_DC_KEY_WHITE)
13800     {
13801       player->num_white_keys++;
13802
13803       /* display white keys? */
13804       /* DrawGameDoorValues(); */
13805     }
13806     else if (IS_ENVELOPE(element))
13807     {
13808       player->show_envelope = element;
13809     }
13810     else if (element == EL_EMC_LENSES)
13811     {
13812       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13813
13814       RedrawAllInvisibleElementsForLenses();
13815     }
13816     else if (element == EL_EMC_MAGNIFIER)
13817     {
13818       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13819
13820       RedrawAllInvisibleElementsForMagnifier();
13821     }
13822     else if (IS_DROPPABLE(element) ||
13823              IS_THROWABLE(element))     /* can be collected and dropped */
13824     {
13825       int i;
13826
13827       if (collect_count == 0)
13828         player->inventory_infinite_element = element;
13829       else
13830         for (i = 0; i < collect_count; i++)
13831           if (player->inventory_size < MAX_INVENTORY_SIZE)
13832             player->inventory_element[player->inventory_size++] = element;
13833
13834       DrawGameDoorValues();
13835     }
13836     else if (collect_count > 0)
13837     {
13838       local_player->gems_still_needed -= collect_count;
13839       if (local_player->gems_still_needed < 0)
13840         local_player->gems_still_needed = 0;
13841
13842       game.snapshot.collected_item = TRUE;
13843
13844       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13845
13846       DisplayGameControlValues();
13847     }
13848
13849     RaiseScoreElement(element);
13850     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13851
13852     if (is_player)
13853       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13854                                           player->index_bit, dig_side);
13855
13856     if (mode == DF_SNAP)
13857     {
13858       if (level.block_snap_field)
13859         setFieldForSnapping(x, y, element, move_direction);
13860       else
13861         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13862
13863       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13864                                           player->index_bit, dig_side);
13865     }
13866   }
13867   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13868   {
13869     if (mode == DF_SNAP && element != EL_BD_ROCK)
13870       return MP_NO_ACTION;
13871
13872     if (CAN_FALL(element) && dy)
13873       return MP_NO_ACTION;
13874
13875     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13876         !(element == EL_SPRING && level.use_spring_bug))
13877       return MP_NO_ACTION;
13878
13879     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13880         ((move_direction & MV_VERTICAL &&
13881           ((element_info[element].move_pattern & MV_LEFT &&
13882             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13883            (element_info[element].move_pattern & MV_RIGHT &&
13884             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13885          (move_direction & MV_HORIZONTAL &&
13886           ((element_info[element].move_pattern & MV_UP &&
13887             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13888            (element_info[element].move_pattern & MV_DOWN &&
13889             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13890       return MP_NO_ACTION;
13891
13892     /* do not push elements already moving away faster than player */
13893     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13894         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13895       return MP_NO_ACTION;
13896
13897     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13898     {
13899       if (player->push_delay_value == -1 || !player_was_pushing)
13900         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13901     }
13902     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13903     {
13904       if (player->push_delay_value == -1)
13905         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13906     }
13907     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13908     {
13909       if (!player->is_pushing)
13910         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13911     }
13912
13913     player->is_pushing = TRUE;
13914     player->is_active = TRUE;
13915
13916     if (!(IN_LEV_FIELD(nextx, nexty) &&
13917           (IS_FREE(nextx, nexty) ||
13918            (IS_SB_ELEMENT(element) &&
13919             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13920            (IS_CUSTOM_ELEMENT(element) &&
13921             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13922       return MP_NO_ACTION;
13923
13924     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13925       return MP_NO_ACTION;
13926
13927     if (player->push_delay == -1)       /* new pushing; restart delay */
13928       player->push_delay = 0;
13929
13930     if (player->push_delay < player->push_delay_value &&
13931         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13932         element != EL_SPRING && element != EL_BALLOON)
13933     {
13934       /* make sure that there is no move delay before next try to push */
13935       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13936         player->move_delay = 0;
13937
13938       return MP_NO_ACTION;
13939     }
13940
13941     if (IS_CUSTOM_ELEMENT(element) &&
13942         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13943     {
13944       if (!DigFieldByCE(nextx, nexty, element))
13945         return MP_NO_ACTION;
13946     }
13947
13948     if (IS_SB_ELEMENT(element))
13949     {
13950       if (element == EL_SOKOBAN_FIELD_FULL)
13951       {
13952         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13953         local_player->sokobanfields_still_needed++;
13954       }
13955
13956       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13957       {
13958         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13959         local_player->sokobanfields_still_needed--;
13960       }
13961
13962       Feld[x][y] = EL_SOKOBAN_OBJECT;
13963
13964       if (Back[x][y] == Back[nextx][nexty])
13965         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13966       else if (Back[x][y] != 0)
13967         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13968                                     ACTION_EMPTYING);
13969       else
13970         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13971                                     ACTION_FILLING);
13972
13973       if (local_player->sokobanfields_still_needed == 0 &&
13974           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13975       {
13976         local_player->players_still_needed = 0;
13977
13978         PlayerWins(player);
13979
13980         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13981       }
13982     }
13983     else
13984       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13985
13986     InitMovingField(x, y, move_direction);
13987     GfxAction[x][y] = ACTION_PUSHING;
13988
13989     if (mode == DF_SNAP)
13990       ContinueMoving(x, y);
13991     else
13992       MovPos[x][y] = (dx != 0 ? dx : dy);
13993
13994     Pushed[x][y] = TRUE;
13995     Pushed[nextx][nexty] = TRUE;
13996
13997     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13998       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13999     else
14000       player->push_delay_value = -1;    /* get new value later */
14001
14002     /* check for element change _after_ element has been pushed */
14003     if (game.use_change_when_pushing_bug)
14004     {
14005       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14006                                  player->index_bit, dig_side);
14007       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14008                                           player->index_bit, dig_side);
14009     }
14010   }
14011   else if (IS_SWITCHABLE(element))
14012   {
14013     if (PLAYER_SWITCHING(player, x, y))
14014     {
14015       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14016                                           player->index_bit, dig_side);
14017
14018       return MP_ACTION;
14019     }
14020
14021     player->is_switching = TRUE;
14022     player->switch_x = x;
14023     player->switch_y = y;
14024
14025     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14026
14027     if (element == EL_ROBOT_WHEEL)
14028     {
14029       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14030       ZX = x;
14031       ZY = y;
14032
14033       game.robot_wheel_active = TRUE;
14034
14035       TEST_DrawLevelField(x, y);
14036     }
14037     else if (element == EL_SP_TERMINAL)
14038     {
14039       int xx, yy;
14040
14041       SCAN_PLAYFIELD(xx, yy)
14042       {
14043         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14044         {
14045           Bang(xx, yy);
14046         }
14047         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14048         {
14049           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14050
14051           ResetGfxAnimation(xx, yy);
14052           TEST_DrawLevelField(xx, yy);
14053         }
14054       }
14055     }
14056     else if (IS_BELT_SWITCH(element))
14057     {
14058       ToggleBeltSwitch(x, y);
14059     }
14060     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14061              element == EL_SWITCHGATE_SWITCH_DOWN ||
14062              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14063              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14064     {
14065       ToggleSwitchgateSwitch(x, y);
14066     }
14067     else if (element == EL_LIGHT_SWITCH ||
14068              element == EL_LIGHT_SWITCH_ACTIVE)
14069     {
14070       ToggleLightSwitch(x, y);
14071     }
14072     else if (element == EL_TIMEGATE_SWITCH ||
14073              element == EL_DC_TIMEGATE_SWITCH)
14074     {
14075       ActivateTimegateSwitch(x, y);
14076     }
14077     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14078              element == EL_BALLOON_SWITCH_RIGHT ||
14079              element == EL_BALLOON_SWITCH_UP    ||
14080              element == EL_BALLOON_SWITCH_DOWN  ||
14081              element == EL_BALLOON_SWITCH_NONE  ||
14082              element == EL_BALLOON_SWITCH_ANY)
14083     {
14084       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14085                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14086                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14087                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14088                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14089                              move_direction);
14090     }
14091     else if (element == EL_LAMP)
14092     {
14093       Feld[x][y] = EL_LAMP_ACTIVE;
14094       local_player->lights_still_needed--;
14095
14096       ResetGfxAnimation(x, y);
14097       TEST_DrawLevelField(x, y);
14098     }
14099     else if (element == EL_TIME_ORB_FULL)
14100     {
14101       Feld[x][y] = EL_TIME_ORB_EMPTY;
14102
14103       if (level.time > 0 || level.use_time_orb_bug)
14104       {
14105         TimeLeft += level.time_orb_time;
14106         game.no_time_limit = FALSE;
14107
14108         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14109
14110         DisplayGameControlValues();
14111       }
14112
14113       ResetGfxAnimation(x, y);
14114       TEST_DrawLevelField(x, y);
14115     }
14116     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14117              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14118     {
14119       int xx, yy;
14120
14121       game.ball_state = !game.ball_state;
14122
14123       SCAN_PLAYFIELD(xx, yy)
14124       {
14125         int e = Feld[xx][yy];
14126
14127         if (game.ball_state)
14128         {
14129           if (e == EL_EMC_MAGIC_BALL)
14130             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14131           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14132             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14133         }
14134         else
14135         {
14136           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14137             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14138           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14139             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14140         }
14141       }
14142     }
14143
14144     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14145                                         player->index_bit, dig_side);
14146
14147     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14148                                         player->index_bit, dig_side);
14149
14150     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14151                                         player->index_bit, dig_side);
14152
14153     return MP_ACTION;
14154   }
14155   else
14156   {
14157     if (!PLAYER_SWITCHING(player, x, y))
14158     {
14159       player->is_switching = TRUE;
14160       player->switch_x = x;
14161       player->switch_y = y;
14162
14163       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14164                                  player->index_bit, dig_side);
14165       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14166                                           player->index_bit, dig_side);
14167
14168       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14169                                  player->index_bit, dig_side);
14170       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14171                                           player->index_bit, dig_side);
14172     }
14173
14174     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14175                                player->index_bit, dig_side);
14176     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14177                                         player->index_bit, dig_side);
14178
14179     return MP_NO_ACTION;
14180   }
14181
14182   player->push_delay = -1;
14183
14184   if (is_player)                /* function can also be called by EL_PENGUIN */
14185   {
14186     if (Feld[x][y] != element)          /* really digged/collected something */
14187     {
14188       player->is_collecting = !player->is_digging;
14189       player->is_active = TRUE;
14190     }
14191   }
14192
14193   return MP_MOVING;
14194 }
14195
14196 static boolean DigFieldByCE(int x, int y, int digging_element)
14197 {
14198   int element = Feld[x][y];
14199
14200   if (!IS_FREE(x, y))
14201   {
14202     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14203                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14204                   ACTION_BREAKING);
14205
14206     /* no element can dig solid indestructible elements */
14207     if (IS_INDESTRUCTIBLE(element) &&
14208         !IS_DIGGABLE(element) &&
14209         !IS_COLLECTIBLE(element))
14210       return FALSE;
14211
14212     if (AmoebaNr[x][y] &&
14213         (element == EL_AMOEBA_FULL ||
14214          element == EL_BD_AMOEBA ||
14215          element == EL_AMOEBA_GROWING))
14216     {
14217       AmoebaCnt[AmoebaNr[x][y]]--;
14218       AmoebaCnt2[AmoebaNr[x][y]]--;
14219     }
14220
14221     if (IS_MOVING(x, y))
14222       RemoveMovingField(x, y);
14223     else
14224     {
14225       RemoveField(x, y);
14226       TEST_DrawLevelField(x, y);
14227     }
14228
14229     /* if digged element was about to explode, prevent the explosion */
14230     ExplodeField[x][y] = EX_TYPE_NONE;
14231
14232     PlayLevelSoundAction(x, y, action);
14233   }
14234
14235   Store[x][y] = EL_EMPTY;
14236
14237   /* this makes it possible to leave the removed element again */
14238   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14239     Store[x][y] = element;
14240
14241   return TRUE;
14242 }
14243
14244 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14245 {
14246   int jx = player->jx, jy = player->jy;
14247   int x = jx + dx, y = jy + dy;
14248   int snap_direction = (dx == -1 ? MV_LEFT  :
14249                         dx == +1 ? MV_RIGHT :
14250                         dy == -1 ? MV_UP    :
14251                         dy == +1 ? MV_DOWN  : MV_NONE);
14252   boolean can_continue_snapping = (level.continuous_snapping &&
14253                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14254
14255   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14256     return FALSE;
14257
14258   if (!player->active || !IN_LEV_FIELD(x, y))
14259     return FALSE;
14260
14261   if (dx && dy)
14262     return FALSE;
14263
14264   if (!dx && !dy)
14265   {
14266     if (player->MovPos == 0)
14267       player->is_pushing = FALSE;
14268
14269     player->is_snapping = FALSE;
14270
14271     if (player->MovPos == 0)
14272     {
14273       player->is_moving = FALSE;
14274       player->is_digging = FALSE;
14275       player->is_collecting = FALSE;
14276     }
14277
14278     return FALSE;
14279   }
14280
14281   /* prevent snapping with already pressed snap key when not allowed */
14282   if (player->is_snapping && !can_continue_snapping)
14283     return FALSE;
14284
14285   player->MovDir = snap_direction;
14286
14287   if (player->MovPos == 0)
14288   {
14289     player->is_moving = FALSE;
14290     player->is_digging = FALSE;
14291     player->is_collecting = FALSE;
14292   }
14293
14294   player->is_dropping = FALSE;
14295   player->is_dropping_pressed = FALSE;
14296   player->drop_pressed_delay = 0;
14297
14298   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14299     return FALSE;
14300
14301   player->is_snapping = TRUE;
14302   player->is_active = TRUE;
14303
14304   if (player->MovPos == 0)
14305   {
14306     player->is_moving = FALSE;
14307     player->is_digging = FALSE;
14308     player->is_collecting = FALSE;
14309   }
14310
14311   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14312     TEST_DrawLevelField(player->last_jx, player->last_jy);
14313
14314   TEST_DrawLevelField(x, y);
14315
14316   return TRUE;
14317 }
14318
14319 static boolean DropElement(struct PlayerInfo *player)
14320 {
14321   int old_element, new_element;
14322   int dropx = player->jx, dropy = player->jy;
14323   int drop_direction = player->MovDir;
14324   int drop_side = drop_direction;
14325   int drop_element = get_next_dropped_element(player);
14326
14327   /* do not drop an element on top of another element; when holding drop key
14328      pressed without moving, dropped element must move away before the next
14329      element can be dropped (this is especially important if the next element
14330      is dynamite, which can be placed on background for historical reasons) */
14331   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14332     return MP_ACTION;
14333
14334   if (IS_THROWABLE(drop_element))
14335   {
14336     dropx += GET_DX_FROM_DIR(drop_direction);
14337     dropy += GET_DY_FROM_DIR(drop_direction);
14338
14339     if (!IN_LEV_FIELD(dropx, dropy))
14340       return FALSE;
14341   }
14342
14343   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14344   new_element = drop_element;           /* default: no change when dropping */
14345
14346   /* check if player is active, not moving and ready to drop */
14347   if (!player->active || player->MovPos || player->drop_delay > 0)
14348     return FALSE;
14349
14350   /* check if player has anything that can be dropped */
14351   if (new_element == EL_UNDEFINED)
14352     return FALSE;
14353
14354   /* only set if player has anything that can be dropped */
14355   player->is_dropping_pressed = TRUE;
14356
14357   /* check if drop key was pressed long enough for EM style dynamite */
14358   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14359     return FALSE;
14360
14361   /* check if anything can be dropped at the current position */
14362   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14363     return FALSE;
14364
14365   /* collected custom elements can only be dropped on empty fields */
14366   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14367     return FALSE;
14368
14369   if (old_element != EL_EMPTY)
14370     Back[dropx][dropy] = old_element;   /* store old element on this field */
14371
14372   ResetGfxAnimation(dropx, dropy);
14373   ResetRandomAnimationValue(dropx, dropy);
14374
14375   if (player->inventory_size > 0 ||
14376       player->inventory_infinite_element != EL_UNDEFINED)
14377   {
14378     if (player->inventory_size > 0)
14379     {
14380       player->inventory_size--;
14381
14382       DrawGameDoorValues();
14383
14384       if (new_element == EL_DYNAMITE)
14385         new_element = EL_DYNAMITE_ACTIVE;
14386       else if (new_element == EL_EM_DYNAMITE)
14387         new_element = EL_EM_DYNAMITE_ACTIVE;
14388       else if (new_element == EL_SP_DISK_RED)
14389         new_element = EL_SP_DISK_RED_ACTIVE;
14390     }
14391
14392     Feld[dropx][dropy] = new_element;
14393
14394     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14395       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14396                           el2img(Feld[dropx][dropy]), 0);
14397
14398     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14399
14400     /* needed if previous element just changed to "empty" in the last frame */
14401     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14402
14403     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14404                                player->index_bit, drop_side);
14405     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14406                                         CE_PLAYER_DROPS_X,
14407                                         player->index_bit, drop_side);
14408
14409     TestIfElementTouchesCustomElement(dropx, dropy);
14410   }
14411   else          /* player is dropping a dyna bomb */
14412   {
14413     player->dynabombs_left--;
14414
14415     Feld[dropx][dropy] = new_element;
14416
14417     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14418       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14419                           el2img(Feld[dropx][dropy]), 0);
14420
14421     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14422   }
14423
14424   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14425     InitField_WithBug1(dropx, dropy, FALSE);
14426
14427   new_element = Feld[dropx][dropy];     /* element might have changed */
14428
14429   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14430       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14431   {
14432     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14433       MovDir[dropx][dropy] = drop_direction;
14434
14435     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14436
14437     /* do not cause impact style collision by dropping elements that can fall */
14438     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14439   }
14440
14441   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14442   player->is_dropping = TRUE;
14443
14444   player->drop_pressed_delay = 0;
14445   player->is_dropping_pressed = FALSE;
14446
14447   player->drop_x = dropx;
14448   player->drop_y = dropy;
14449
14450   return TRUE;
14451 }
14452
14453 /* ------------------------------------------------------------------------- */
14454 /* game sound playing functions                                              */
14455 /* ------------------------------------------------------------------------- */
14456
14457 static int *loop_sound_frame = NULL;
14458 static int *loop_sound_volume = NULL;
14459
14460 void InitPlayLevelSound()
14461 {
14462   int num_sounds = getSoundListSize();
14463
14464   checked_free(loop_sound_frame);
14465   checked_free(loop_sound_volume);
14466
14467   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14468   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14469 }
14470
14471 static void PlayLevelSound(int x, int y, int nr)
14472 {
14473   int sx = SCREENX(x), sy = SCREENY(y);
14474   int volume, stereo_position;
14475   int max_distance = 8;
14476   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14477
14478   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14479       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14480     return;
14481
14482   if (!IN_LEV_FIELD(x, y) ||
14483       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14484       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14485     return;
14486
14487   volume = SOUND_MAX_VOLUME;
14488
14489   if (!IN_SCR_FIELD(sx, sy))
14490   {
14491     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14492     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14493
14494     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14495   }
14496
14497   stereo_position = (SOUND_MAX_LEFT +
14498                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14499                      (SCR_FIELDX + 2 * max_distance));
14500
14501   if (IS_LOOP_SOUND(nr))
14502   {
14503     /* This assures that quieter loop sounds do not overwrite louder ones,
14504        while restarting sound volume comparison with each new game frame. */
14505
14506     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14507       return;
14508
14509     loop_sound_volume[nr] = volume;
14510     loop_sound_frame[nr] = FrameCounter;
14511   }
14512
14513   PlaySoundExt(nr, volume, stereo_position, type);
14514 }
14515
14516 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14517 {
14518   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14519                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14520                  y < LEVELY(BY1) ? LEVELY(BY1) :
14521                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14522                  sound_action);
14523 }
14524
14525 static void PlayLevelSoundAction(int x, int y, int action)
14526 {
14527   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14528 }
14529
14530 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14531 {
14532   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14533
14534   if (sound_effect != SND_UNDEFINED)
14535     PlayLevelSound(x, y, sound_effect);
14536 }
14537
14538 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14539                                               int action)
14540 {
14541   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14542
14543   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14544     PlayLevelSound(x, y, sound_effect);
14545 }
14546
14547 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14548 {
14549   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14550
14551   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14552     PlayLevelSound(x, y, sound_effect);
14553 }
14554
14555 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14556 {
14557   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14558
14559   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14560     StopSound(sound_effect);
14561 }
14562
14563 static int getLevelMusicNr()
14564 {
14565   if (levelset.music[level_nr] != MUS_UNDEFINED)
14566     return levelset.music[level_nr];            /* from config file */
14567   else
14568     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14569 }
14570
14571 static void FadeLevelSounds()
14572 {
14573   FadeSounds();
14574 }
14575
14576 static void FadeLevelMusic()
14577 {
14578   int music_nr = getLevelMusicNr();
14579   char *curr_music = getCurrentlyPlayingMusicFilename();
14580   char *next_music = getMusicInfoEntryFilename(music_nr);
14581
14582   if (!strEqual(curr_music, next_music))
14583     FadeMusic();
14584 }
14585
14586 void FadeLevelSoundsAndMusic()
14587 {
14588   FadeLevelSounds();
14589   FadeLevelMusic();
14590 }
14591
14592 static void PlayLevelMusic()
14593 {
14594   int music_nr = getLevelMusicNr();
14595   char *curr_music = getCurrentlyPlayingMusicFilename();
14596   char *next_music = getMusicInfoEntryFilename(music_nr);
14597
14598   if (!strEqual(curr_music, next_music))
14599     PlayMusicLoop(music_nr);
14600 }
14601
14602 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14603 {
14604   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14605   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14606   int x = xx - 1 - offset;
14607   int y = yy - 1 - offset;
14608
14609   switch (sample)
14610   {
14611     case SAMPLE_blank:
14612       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14613       break;
14614
14615     case SAMPLE_roll:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14617       break;
14618
14619     case SAMPLE_stone:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14621       break;
14622
14623     case SAMPLE_nut:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14625       break;
14626
14627     case SAMPLE_crack:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14629       break;
14630
14631     case SAMPLE_bug:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14633       break;
14634
14635     case SAMPLE_tank:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14637       break;
14638
14639     case SAMPLE_android_clone:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14641       break;
14642
14643     case SAMPLE_android_move:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14645       break;
14646
14647     case SAMPLE_spring:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14649       break;
14650
14651     case SAMPLE_slurp:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14653       break;
14654
14655     case SAMPLE_eater:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14657       break;
14658
14659     case SAMPLE_eater_eat:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14661       break;
14662
14663     case SAMPLE_alien:
14664       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14665       break;
14666
14667     case SAMPLE_collect:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14669       break;
14670
14671     case SAMPLE_diamond:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14673       break;
14674
14675     case SAMPLE_squash:
14676       /* !!! CHECK THIS !!! */
14677 #if 1
14678       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14679 #else
14680       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14681 #endif
14682       break;
14683
14684     case SAMPLE_wonderfall:
14685       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14686       break;
14687
14688     case SAMPLE_drip:
14689       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14690       break;
14691
14692     case SAMPLE_push:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14694       break;
14695
14696     case SAMPLE_dirt:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14698       break;
14699
14700     case SAMPLE_acid:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14702       break;
14703
14704     case SAMPLE_ball:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14706       break;
14707
14708     case SAMPLE_grow:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14710       break;
14711
14712     case SAMPLE_wonder:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14714       break;
14715
14716     case SAMPLE_door:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14718       break;
14719
14720     case SAMPLE_exit_open:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14722       break;
14723
14724     case SAMPLE_exit_leave:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14726       break;
14727
14728     case SAMPLE_dynamite:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14730       break;
14731
14732     case SAMPLE_tick:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14734       break;
14735
14736     case SAMPLE_press:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14738       break;
14739
14740     case SAMPLE_wheel:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14742       break;
14743
14744     case SAMPLE_boom:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14746       break;
14747
14748     case SAMPLE_die:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14750       break;
14751
14752     case SAMPLE_time:
14753       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14754       break;
14755
14756     default:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14758       break;
14759   }
14760 }
14761
14762 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14763 {
14764   int element = map_element_SP_to_RND(element_sp);
14765   int action = map_action_SP_to_RND(action_sp);
14766   int offset = (setup.sp_show_border_elements ? 0 : 1);
14767   int x = xx - offset;
14768   int y = yy - offset;
14769
14770   PlayLevelSoundElementAction(x, y, element, action);
14771 }
14772
14773 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14774 {
14775   int element = map_element_MM_to_RND(element_mm);
14776   int action = map_action_MM_to_RND(action_mm);
14777   int offset = 0;
14778   int x = xx - offset;
14779   int y = yy - offset;
14780
14781   if (!IS_MM_ELEMENT(element))
14782     element = EL_MM_DEFAULT;
14783
14784   PlayLevelSoundElementAction(x, y, element, action);
14785 }
14786
14787 void PlaySound_MM(int sound_mm)
14788 {
14789   int sound = map_sound_MM_to_RND(sound_mm);
14790
14791   if (sound == SND_UNDEFINED)
14792     return;
14793
14794   PlaySound(sound);
14795 }
14796
14797 void PlaySoundLoop_MM(int sound_mm)
14798 {
14799   int sound = map_sound_MM_to_RND(sound_mm);
14800
14801   if (sound == SND_UNDEFINED)
14802     return;
14803
14804   PlaySoundLoop(sound);
14805 }
14806
14807 void StopSound_MM(int sound_mm)
14808 {
14809   int sound = map_sound_MM_to_RND(sound_mm);
14810
14811   if (sound == SND_UNDEFINED)
14812     return;
14813
14814   StopSound(sound);
14815 }
14816
14817 void RaiseScore(int value)
14818 {
14819   local_player->score += value;
14820
14821   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14822
14823   DisplayGameControlValues();
14824 }
14825
14826 void RaiseScoreElement(int element)
14827 {
14828   switch (element)
14829   {
14830     case EL_EMERALD:
14831     case EL_BD_DIAMOND:
14832     case EL_EMERALD_YELLOW:
14833     case EL_EMERALD_RED:
14834     case EL_EMERALD_PURPLE:
14835     case EL_SP_INFOTRON:
14836       RaiseScore(level.score[SC_EMERALD]);
14837       break;
14838     case EL_DIAMOND:
14839       RaiseScore(level.score[SC_DIAMOND]);
14840       break;
14841     case EL_CRYSTAL:
14842       RaiseScore(level.score[SC_CRYSTAL]);
14843       break;
14844     case EL_PEARL:
14845       RaiseScore(level.score[SC_PEARL]);
14846       break;
14847     case EL_BUG:
14848     case EL_BD_BUTTERFLY:
14849     case EL_SP_ELECTRON:
14850       RaiseScore(level.score[SC_BUG]);
14851       break;
14852     case EL_SPACESHIP:
14853     case EL_BD_FIREFLY:
14854     case EL_SP_SNIKSNAK:
14855       RaiseScore(level.score[SC_SPACESHIP]);
14856       break;
14857     case EL_YAMYAM:
14858     case EL_DARK_YAMYAM:
14859       RaiseScore(level.score[SC_YAMYAM]);
14860       break;
14861     case EL_ROBOT:
14862       RaiseScore(level.score[SC_ROBOT]);
14863       break;
14864     case EL_PACMAN:
14865       RaiseScore(level.score[SC_PACMAN]);
14866       break;
14867     case EL_NUT:
14868       RaiseScore(level.score[SC_NUT]);
14869       break;
14870     case EL_DYNAMITE:
14871     case EL_EM_DYNAMITE:
14872     case EL_SP_DISK_RED:
14873     case EL_DYNABOMB_INCREASE_NUMBER:
14874     case EL_DYNABOMB_INCREASE_SIZE:
14875     case EL_DYNABOMB_INCREASE_POWER:
14876       RaiseScore(level.score[SC_DYNAMITE]);
14877       break;
14878     case EL_SHIELD_NORMAL:
14879     case EL_SHIELD_DEADLY:
14880       RaiseScore(level.score[SC_SHIELD]);
14881       break;
14882     case EL_EXTRA_TIME:
14883       RaiseScore(level.extra_time_score);
14884       break;
14885     case EL_KEY_1:
14886     case EL_KEY_2:
14887     case EL_KEY_3:
14888     case EL_KEY_4:
14889     case EL_EM_KEY_1:
14890     case EL_EM_KEY_2:
14891     case EL_EM_KEY_3:
14892     case EL_EM_KEY_4:
14893     case EL_EMC_KEY_5:
14894     case EL_EMC_KEY_6:
14895     case EL_EMC_KEY_7:
14896     case EL_EMC_KEY_8:
14897     case EL_DC_KEY_WHITE:
14898       RaiseScore(level.score[SC_KEY]);
14899       break;
14900     default:
14901       RaiseScore(element_info[element].collect_score);
14902       break;
14903   }
14904 }
14905
14906 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14907 {
14908   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14909   {
14910     /* closing door required in case of envelope style request dialogs */
14911     if (!skip_request)
14912       CloseDoor(DOOR_CLOSE_1);
14913
14914     if (network.enabled)
14915       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14916     else
14917     {
14918       if (quick_quit)
14919         FadeSkipNextFadeIn();
14920
14921       SetGameStatus(GAME_MODE_MAIN);
14922
14923       DrawMainMenu();
14924     }
14925   }
14926   else          /* continue playing the game */
14927   {
14928     if (tape.playing && tape.deactivate_display)
14929       TapeDeactivateDisplayOff(TRUE);
14930
14931     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14932
14933     if (tape.playing && tape.deactivate_display)
14934       TapeDeactivateDisplayOn();
14935   }
14936 }
14937
14938 void RequestQuitGame(boolean ask_if_really_quit)
14939 {
14940   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14941   boolean skip_request = AllPlayersGone || quick_quit;
14942
14943   RequestQuitGameExt(skip_request, quick_quit,
14944                      "Do you really want to quit the game?");
14945 }
14946
14947 void RequestRestartGame(char *message)
14948 {
14949   game.restart_game_message = NULL;
14950
14951   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14952   {
14953     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14954   }
14955   else
14956   {
14957     SetGameStatus(GAME_MODE_MAIN);
14958
14959     DrawMainMenu();
14960   }
14961 }
14962
14963
14964 /* ------------------------------------------------------------------------- */
14965 /* random generator functions                                                */
14966 /* ------------------------------------------------------------------------- */
14967
14968 unsigned int InitEngineRandom_RND(int seed)
14969 {
14970   game.num_random_calls = 0;
14971
14972   return InitEngineRandom(seed);
14973 }
14974
14975 unsigned int RND(int max)
14976 {
14977   if (max > 0)
14978   {
14979     game.num_random_calls++;
14980
14981     return GetEngineRandom(max);
14982   }
14983
14984   return 0;
14985 }
14986
14987
14988 /* ------------------------------------------------------------------------- */
14989 /* game engine snapshot handling functions                                   */
14990 /* ------------------------------------------------------------------------- */
14991
14992 struct EngineSnapshotInfo
14993 {
14994   /* runtime values for custom element collect score */
14995   int collect_score[NUM_CUSTOM_ELEMENTS];
14996
14997   /* runtime values for group element choice position */
14998   int choice_pos[NUM_GROUP_ELEMENTS];
14999
15000   /* runtime values for belt position animations */
15001   int belt_graphic[4][NUM_BELT_PARTS];
15002   int belt_anim_mode[4][NUM_BELT_PARTS];
15003 };
15004
15005 static struct EngineSnapshotInfo engine_snapshot_rnd;
15006 static char *snapshot_level_identifier = NULL;
15007 static int snapshot_level_nr = -1;
15008
15009 static void SaveEngineSnapshotValues_RND()
15010 {
15011   static int belt_base_active_element[4] =
15012   {
15013     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15014     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15015     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15016     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15017   };
15018   int i, j;
15019
15020   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15021   {
15022     int element = EL_CUSTOM_START + i;
15023
15024     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15025   }
15026
15027   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15028   {
15029     int element = EL_GROUP_START + i;
15030
15031     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15032   }
15033
15034   for (i = 0; i < 4; i++)
15035   {
15036     for (j = 0; j < NUM_BELT_PARTS; j++)
15037     {
15038       int element = belt_base_active_element[i] + j;
15039       int graphic = el2img(element);
15040       int anim_mode = graphic_info[graphic].anim_mode;
15041
15042       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15043       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15044     }
15045   }
15046 }
15047
15048 static void LoadEngineSnapshotValues_RND()
15049 {
15050   unsigned int num_random_calls = game.num_random_calls;
15051   int i, j;
15052
15053   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15054   {
15055     int element = EL_CUSTOM_START + i;
15056
15057     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15058   }
15059
15060   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15061   {
15062     int element = EL_GROUP_START + i;
15063
15064     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15065   }
15066
15067   for (i = 0; i < 4; i++)
15068   {
15069     for (j = 0; j < NUM_BELT_PARTS; j++)
15070     {
15071       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15072       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15073
15074       graphic_info[graphic].anim_mode = anim_mode;
15075     }
15076   }
15077
15078   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15079   {
15080     InitRND(tape.random_seed);
15081     for (i = 0; i < num_random_calls; i++)
15082       RND(1);
15083   }
15084
15085   if (game.num_random_calls != num_random_calls)
15086   {
15087     Error(ERR_INFO, "number of random calls out of sync");
15088     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15089     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15090     Error(ERR_EXIT, "this should not happen -- please debug");
15091   }
15092 }
15093
15094 void FreeEngineSnapshotSingle()
15095 {
15096   FreeSnapshotSingle();
15097
15098   setString(&snapshot_level_identifier, NULL);
15099   snapshot_level_nr = -1;
15100 }
15101
15102 void FreeEngineSnapshotList()
15103 {
15104   FreeSnapshotList();
15105 }
15106
15107 ListNode *SaveEngineSnapshotBuffers()
15108 {
15109   ListNode *buffers = NULL;
15110
15111   /* copy some special values to a structure better suited for the snapshot */
15112
15113   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15114     SaveEngineSnapshotValues_RND();
15115   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15116     SaveEngineSnapshotValues_EM();
15117   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15118     SaveEngineSnapshotValues_SP(&buffers);
15119   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15120     SaveEngineSnapshotValues_MM(&buffers);
15121
15122   /* save values stored in special snapshot structure */
15123
15124   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15125     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15127     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15128   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15129     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15130   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15131     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15132
15133   /* save further RND engine values */
15134
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15137   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15138
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15143
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15149
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15153
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15155
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15157
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15160
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15179
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15182
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15186
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15189
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15195
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15198
15199 #if 0
15200   ListNode *node = engine_snapshot_list_rnd;
15201   int num_bytes = 0;
15202
15203   while (node != NULL)
15204   {
15205     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15206
15207     node = node->next;
15208   }
15209
15210   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15211 #endif
15212
15213   return buffers;
15214 }
15215
15216 void SaveEngineSnapshotSingle()
15217 {
15218   ListNode *buffers = SaveEngineSnapshotBuffers();
15219
15220   /* finally save all snapshot buffers to single snapshot */
15221   SaveSnapshotSingle(buffers);
15222
15223   /* save level identification information */
15224   setString(&snapshot_level_identifier, leveldir_current->identifier);
15225   snapshot_level_nr = level_nr;
15226 }
15227
15228 boolean CheckSaveEngineSnapshotToList()
15229 {
15230   boolean save_snapshot =
15231     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15232      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15233       game.snapshot.changed_action) ||
15234      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15235       game.snapshot.collected_item));
15236
15237   game.snapshot.changed_action = FALSE;
15238   game.snapshot.collected_item = FALSE;
15239   game.snapshot.save_snapshot = save_snapshot;
15240
15241   return save_snapshot;
15242 }
15243
15244 void SaveEngineSnapshotToList()
15245 {
15246   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15247       tape.quick_resume)
15248     return;
15249
15250   ListNode *buffers = SaveEngineSnapshotBuffers();
15251
15252   /* finally save all snapshot buffers to snapshot list */
15253   SaveSnapshotToList(buffers);
15254 }
15255
15256 void SaveEngineSnapshotToListInitial()
15257 {
15258   FreeEngineSnapshotList();
15259
15260   SaveEngineSnapshotToList();
15261 }
15262
15263 void LoadEngineSnapshotValues()
15264 {
15265   /* restore special values from snapshot structure */
15266
15267   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15268     LoadEngineSnapshotValues_RND();
15269   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15270     LoadEngineSnapshotValues_EM();
15271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15272     LoadEngineSnapshotValues_SP();
15273   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15274     LoadEngineSnapshotValues_MM();
15275 }
15276
15277 void LoadEngineSnapshotSingle()
15278 {
15279   LoadSnapshotSingle();
15280
15281   LoadEngineSnapshotValues();
15282 }
15283
15284 void LoadEngineSnapshot_Undo(int steps)
15285 {
15286   LoadSnapshotFromList_Older(steps);
15287
15288   LoadEngineSnapshotValues();
15289 }
15290
15291 void LoadEngineSnapshot_Redo(int steps)
15292 {
15293   LoadSnapshotFromList_Newer(steps);
15294
15295   LoadEngineSnapshotValues();
15296 }
15297
15298 boolean CheckEngineSnapshotSingle()
15299 {
15300   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15301           snapshot_level_nr == level_nr);
15302 }
15303
15304 boolean CheckEngineSnapshotList()
15305 {
15306   return CheckSnapshotList();
15307 }
15308
15309
15310 /* ---------- new game button stuff ---------------------------------------- */
15311
15312 static struct
15313 {
15314   int graphic;
15315   struct XY *pos;
15316   int gadget_id;
15317   boolean *setup_value;
15318   boolean allowed_on_tape;
15319   char *infotext;
15320 } gamebutton_info[NUM_GAME_BUTTONS] =
15321 {
15322   {
15323     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15324     GAME_CTRL_ID_STOP,                          NULL,
15325     TRUE,                                       "stop game"
15326   },
15327   {
15328     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15329     GAME_CTRL_ID_PAUSE,                         NULL,
15330     TRUE,                                       "pause game"
15331   },
15332   {
15333     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15334     GAME_CTRL_ID_PLAY,                          NULL,
15335     TRUE,                                       "play game"
15336   },
15337   {
15338     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15339     GAME_CTRL_ID_UNDO,                          NULL,
15340     TRUE,                                       "undo step"
15341   },
15342   {
15343     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15344     GAME_CTRL_ID_REDO,                          NULL,
15345     TRUE,                                       "redo step"
15346   },
15347   {
15348     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15349     GAME_CTRL_ID_SAVE,                          NULL,
15350     TRUE,                                       "save game"
15351   },
15352   {
15353     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15354     GAME_CTRL_ID_PAUSE2,                        NULL,
15355     TRUE,                                       "pause game"
15356   },
15357   {
15358     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15359     GAME_CTRL_ID_LOAD,                          NULL,
15360     TRUE,                                       "load game"
15361   },
15362   {
15363     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15364     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15365     FALSE,                                      "stop game"
15366   },
15367   {
15368     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15369     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15370     FALSE,                                      "pause game"
15371   },
15372   {
15373     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15374     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15375     FALSE,                                      "play game"
15376   },
15377   {
15378     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15379     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15380     TRUE,                                       "background music on/off"
15381   },
15382   {
15383     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15384     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15385     TRUE,                                       "sound loops on/off"
15386   },
15387   {
15388     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15389     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15390     TRUE,                                       "normal sounds on/off"
15391   },
15392   {
15393     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15394     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15395     FALSE,                                      "background music on/off"
15396   },
15397   {
15398     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15399     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15400     FALSE,                                      "sound loops on/off"
15401   },
15402   {
15403     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15404     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15405     FALSE,                                      "normal sounds on/off"
15406   }
15407 };
15408
15409 void CreateGameButtons()
15410 {
15411   int i;
15412
15413   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15414   {
15415     int graphic = gamebutton_info[i].graphic;
15416     struct GraphicInfo *gfx = &graphic_info[graphic];
15417     struct XY *pos = gamebutton_info[i].pos;
15418     struct GadgetInfo *gi;
15419     int button_type;
15420     boolean checked;
15421     unsigned int event_mask;
15422     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15423     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15424     int base_x = (on_tape ? VX : DX);
15425     int base_y = (on_tape ? VY : DY);
15426     int gd_x   = gfx->src_x;
15427     int gd_y   = gfx->src_y;
15428     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15429     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15430     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15431     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15432     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15433     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15434     int id = i;
15435
15436     if (gfx->bitmap == NULL)
15437     {
15438       game_gadget[id] = NULL;
15439
15440       continue;
15441     }
15442
15443     if (id == GAME_CTRL_ID_STOP ||
15444         id == GAME_CTRL_ID_PANEL_STOP ||
15445         id == GAME_CTRL_ID_PLAY ||
15446         id == GAME_CTRL_ID_PANEL_PLAY ||
15447         id == GAME_CTRL_ID_SAVE ||
15448         id == GAME_CTRL_ID_LOAD)
15449     {
15450       button_type = GD_TYPE_NORMAL_BUTTON;
15451       checked = FALSE;
15452       event_mask = GD_EVENT_RELEASED;
15453     }
15454     else if (id == GAME_CTRL_ID_UNDO ||
15455              id == GAME_CTRL_ID_REDO)
15456     {
15457       button_type = GD_TYPE_NORMAL_BUTTON;
15458       checked = FALSE;
15459       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15460     }
15461     else
15462     {
15463       button_type = GD_TYPE_CHECK_BUTTON;
15464       checked = (gamebutton_info[i].setup_value != NULL ?
15465                  *gamebutton_info[i].setup_value : FALSE);
15466       event_mask = GD_EVENT_PRESSED;
15467     }
15468
15469     gi = CreateGadget(GDI_CUSTOM_ID, id,
15470                       GDI_IMAGE_ID, graphic,
15471                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15472                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15473                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15474                       GDI_WIDTH, gfx->width,
15475                       GDI_HEIGHT, gfx->height,
15476                       GDI_TYPE, button_type,
15477                       GDI_STATE, GD_BUTTON_UNPRESSED,
15478                       GDI_CHECKED, checked,
15479                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15480                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15481                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15482                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15483                       GDI_DIRECT_DRAW, FALSE,
15484                       GDI_EVENT_MASK, event_mask,
15485                       GDI_CALLBACK_ACTION, HandleGameButtons,
15486                       GDI_END);
15487
15488     if (gi == NULL)
15489       Error(ERR_EXIT, "cannot create gadget");
15490
15491     game_gadget[id] = gi;
15492   }
15493 }
15494
15495 void FreeGameButtons()
15496 {
15497   int i;
15498
15499   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15500     FreeGadget(game_gadget[i]);
15501 }
15502
15503 static void UnmapGameButtonsAtSamePosition(int id)
15504 {
15505   int i;
15506
15507   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15508     if (i != id &&
15509         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15510         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15511       UnmapGadget(game_gadget[i]);
15512 }
15513
15514 static void UnmapGameButtonsAtSamePosition_All()
15515 {
15516   if (setup.show_snapshot_buttons)
15517   {
15518     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15519     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15520     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15521   }
15522   else
15523   {
15524     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15525     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15526     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15527
15528     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15529     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15530     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15531   }
15532 }
15533
15534 static void MapGameButtonsAtSamePosition(int id)
15535 {
15536   int i;
15537
15538   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15539     if (i != id &&
15540         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15541         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15542       MapGadget(game_gadget[i]);
15543
15544   UnmapGameButtonsAtSamePosition_All();
15545 }
15546
15547 void MapUndoRedoButtons()
15548 {
15549   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15550   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15551
15552   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15553   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15554
15555   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15556 }
15557
15558 void UnmapUndoRedoButtons()
15559 {
15560   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15561   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15562
15563   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15564   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15565
15566   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15567 }
15568
15569 void MapGameButtonsExt(boolean on_tape)
15570 {
15571   int i;
15572
15573   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15574     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15575         i != GAME_CTRL_ID_UNDO &&
15576         i != GAME_CTRL_ID_REDO)
15577       MapGadget(game_gadget[i]);
15578
15579   UnmapGameButtonsAtSamePosition_All();
15580
15581   RedrawGameButtons();
15582 }
15583
15584 void UnmapGameButtonsExt(boolean on_tape)
15585 {
15586   int i;
15587
15588   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15589     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15590       UnmapGadget(game_gadget[i]);
15591 }
15592
15593 void RedrawGameButtonsExt(boolean on_tape)
15594 {
15595   int i;
15596
15597   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15598     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15599       RedrawGadget(game_gadget[i]);
15600
15601   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15602   redraw_mask &= ~REDRAW_ALL;
15603 }
15604
15605 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15606 {
15607   if (gi == NULL)
15608     return;
15609
15610   gi->checked = state;
15611 }
15612
15613 void RedrawSoundButtonGadget(int id)
15614 {
15615   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15616              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15617              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15618              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15619              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15620              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15621              id);
15622
15623   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15624   RedrawGadget(game_gadget[id2]);
15625 }
15626
15627 void MapGameButtons()
15628 {
15629   MapGameButtonsExt(FALSE);
15630 }
15631
15632 void UnmapGameButtons()
15633 {
15634   UnmapGameButtonsExt(FALSE);
15635 }
15636
15637 void RedrawGameButtons()
15638 {
15639   RedrawGameButtonsExt(FALSE);
15640 }
15641
15642 void MapGameButtonsOnTape()
15643 {
15644   MapGameButtonsExt(TRUE);
15645 }
15646
15647 void UnmapGameButtonsOnTape()
15648 {
15649   UnmapGameButtonsExt(TRUE);
15650 }
15651
15652 void RedrawGameButtonsOnTape()
15653 {
15654   RedrawGameButtonsExt(TRUE);
15655 }
15656
15657 void GameUndoRedoExt()
15658 {
15659   ClearPlayerAction();
15660
15661   tape.pausing = TRUE;
15662
15663   RedrawPlayfield();
15664   UpdateAndDisplayGameControlValues();
15665
15666   DrawCompleteVideoDisplay();
15667   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15668   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15669   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15670
15671   BackToFront();
15672 }
15673
15674 void GameUndo(int steps)
15675 {
15676   if (!CheckEngineSnapshotList())
15677     return;
15678
15679   LoadEngineSnapshot_Undo(steps);
15680
15681   GameUndoRedoExt();
15682 }
15683
15684 void GameRedo(int steps)
15685 {
15686   if (!CheckEngineSnapshotList())
15687     return;
15688
15689   LoadEngineSnapshot_Redo(steps);
15690
15691   GameUndoRedoExt();
15692 }
15693
15694 static void HandleGameButtonsExt(int id, int button)
15695 {
15696   static boolean game_undo_executed = FALSE;
15697   int steps = BUTTON_STEPSIZE(button);
15698   boolean handle_game_buttons =
15699     (game_status == GAME_MODE_PLAYING ||
15700      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15701
15702   if (!handle_game_buttons)
15703     return;
15704
15705   switch (id)
15706   {
15707     case GAME_CTRL_ID_STOP:
15708     case GAME_CTRL_ID_PANEL_STOP:
15709       if (game_status == GAME_MODE_MAIN)
15710         break;
15711
15712       if (tape.playing)
15713         TapeStop();
15714       else
15715         RequestQuitGame(TRUE);
15716
15717       break;
15718
15719     case GAME_CTRL_ID_PAUSE:
15720     case GAME_CTRL_ID_PAUSE2:
15721     case GAME_CTRL_ID_PANEL_PAUSE:
15722       if (network.enabled && game_status == GAME_MODE_PLAYING)
15723       {
15724         if (tape.pausing)
15725           SendToServer_ContinuePlaying();
15726         else
15727           SendToServer_PausePlaying();
15728       }
15729       else
15730         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15731
15732       game_undo_executed = FALSE;
15733
15734       break;
15735
15736     case GAME_CTRL_ID_PLAY:
15737     case GAME_CTRL_ID_PANEL_PLAY:
15738       if (game_status == GAME_MODE_MAIN)
15739       {
15740         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15741       }
15742       else if (tape.pausing)
15743       {
15744         if (network.enabled)
15745           SendToServer_ContinuePlaying();
15746         else
15747           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15748       }
15749       break;
15750
15751     case GAME_CTRL_ID_UNDO:
15752       // Important: When using "save snapshot when collecting an item" mode,
15753       // load last (current) snapshot for first "undo" after pressing "pause"
15754       // (else the last-but-one snapshot would be loaded, because the snapshot
15755       // pointer already points to the last snapshot when pressing "pause",
15756       // which is fine for "every step/move" mode, but not for "every collect")
15757       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15758           !game_undo_executed)
15759         steps--;
15760
15761       game_undo_executed = TRUE;
15762
15763       GameUndo(steps);
15764       break;
15765
15766     case GAME_CTRL_ID_REDO:
15767       GameRedo(steps);
15768       break;
15769
15770     case GAME_CTRL_ID_SAVE:
15771       TapeQuickSave();
15772       break;
15773
15774     case GAME_CTRL_ID_LOAD:
15775       TapeQuickLoad();
15776       break;
15777
15778     case SOUND_CTRL_ID_MUSIC:
15779     case SOUND_CTRL_ID_PANEL_MUSIC:
15780       if (setup.sound_music)
15781       { 
15782         setup.sound_music = FALSE;
15783
15784         FadeMusic();
15785       }
15786       else if (audio.music_available)
15787       { 
15788         setup.sound = setup.sound_music = TRUE;
15789
15790         SetAudioMode(setup.sound);
15791
15792         if (game_status == GAME_MODE_PLAYING)
15793           PlayLevelMusic();
15794       }
15795
15796       RedrawSoundButtonGadget(id);
15797
15798       break;
15799
15800     case SOUND_CTRL_ID_LOOPS:
15801     case SOUND_CTRL_ID_PANEL_LOOPS:
15802       if (setup.sound_loops)
15803         setup.sound_loops = FALSE;
15804       else if (audio.loops_available)
15805       {
15806         setup.sound = setup.sound_loops = TRUE;
15807
15808         SetAudioMode(setup.sound);
15809       }
15810
15811       RedrawSoundButtonGadget(id);
15812
15813       break;
15814
15815     case SOUND_CTRL_ID_SIMPLE:
15816     case SOUND_CTRL_ID_PANEL_SIMPLE:
15817       if (setup.sound_simple)
15818         setup.sound_simple = FALSE;
15819       else if (audio.sound_available)
15820       {
15821         setup.sound = setup.sound_simple = TRUE;
15822
15823         SetAudioMode(setup.sound);
15824       }
15825
15826       RedrawSoundButtonGadget(id);
15827
15828       break;
15829
15830     default:
15831       break;
15832   }
15833 }
15834
15835 static void HandleGameButtons(struct GadgetInfo *gi)
15836 {
15837   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15838 }
15839
15840 void HandleSoundButtonKeys(Key key)
15841 {
15842   if (key == setup.shortcut.sound_simple)
15843     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15844   else if (key == setup.shortcut.sound_loops)
15845     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15846   else if (key == setup.shortcut.sound_music)
15847     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15848 }