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