disabled auto-incrementing level number when playing in network mode
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 void InitGameControlValues()
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 void UpdatePlayfieldElementCount()
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 void UpdateGameControlValues()
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   /* use "level.file_info.nr" instead of "level_nr" (for network games) */
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level.file_info.nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       /* only one player in Supaplex game engine */
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   /* update game panel control frames */
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 void DisplayGameControlValues()
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   /* copy default game door content to main double buffer */
2533
2534   /* !!! CHECK AGAIN !!! */
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   /* redraw game control buttons */
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           /* use dynamic number of digits */
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          /* (used for normal state) */
2728         int font2 = pos->font_alt;      /* (used for active state) */
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           /* don't truncate output if "chars" is zero or less */
2740           size = strlen(s);
2741
2742           /* dynamically correct text alignment */
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues()
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 void UpdateGameDoorValues()
2771 {
2772   UpdateGameControlValues();
2773 }
2774
2775 void DrawGameDoorValues()
2776 {
2777   DisplayGameControlValues();
2778 }
2779
2780
2781 /*
2782   =============================================================================
2783   InitGameEngine()
2784   -----------------------------------------------------------------------------
2785   initialize game engine due to level / tape version number
2786   =============================================================================
2787 */
2788
2789 static void InitGameEngine()
2790 {
2791   int i, j, k, l, x, y;
2792
2793   /* set game engine from tape file when re-playing, else from level file */
2794   game.engine_version = (tape.playing ? tape.engine_version :
2795                          level.game_version);
2796
2797   /* set single or multi-player game mode (needed for re-playing tapes) */
2798   game.team_mode = setup.team_mode;
2799
2800   if (tape.playing)
2801   {
2802     int num_players = 0;
2803
2804     for (i = 0; i < MAX_PLAYERS; i++)
2805       if (tape.player_participates[i])
2806         num_players++;
2807
2808     /* multi-player tapes contain input data for more than one player */
2809     game.team_mode = (num_players > 1);
2810   }
2811
2812   /* ---------------------------------------------------------------------- */
2813   /* set flags for bugs and changes according to active game engine version */
2814   /* ---------------------------------------------------------------------- */
2815
2816   /*
2817     Summary of bugfix/change:
2818     Fixed handling for custom elements that change when pushed by the player.
2819
2820     Fixed/changed in version:
2821     3.1.0
2822
2823     Description:
2824     Before 3.1.0, custom elements that "change when pushing" changed directly
2825     after the player started pushing them (until then handled in "DigField()").
2826     Since 3.1.0, these custom elements are not changed until the "pushing"
2827     move of the element is finished (now handled in "ContinueMoving()").
2828
2829     Affected levels/tapes:
2830     The first condition is generally needed for all levels/tapes before version
2831     3.1.0, which might use the old behaviour before it was changed; known tapes
2832     that are affected are some tapes from the level set "Walpurgis Gardens" by
2833     Jamie Cullen.
2834     The second condition is an exception from the above case and is needed for
2835     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2836     above (including some development versions of 3.1.0), but before it was
2837     known that this change would break tapes like the above and was fixed in
2838     3.1.1, so that the changed behaviour was active although the engine version
2839     while recording maybe was before 3.1.0. There is at least one tape that is
2840     affected by this exception, which is the tape for the one-level set "Bug
2841     Machine" by Juergen Bonhagen.
2842   */
2843
2844   game.use_change_when_pushing_bug =
2845     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2846      !(tape.playing &&
2847        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2848        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2849
2850   /*
2851     Summary of bugfix/change:
2852     Fixed handling for blocking the field the player leaves when moving.
2853
2854     Fixed/changed in version:
2855     3.1.1
2856
2857     Description:
2858     Before 3.1.1, when "block last field when moving" was enabled, the field
2859     the player is leaving when moving was blocked for the time of the move,
2860     and was directly unblocked afterwards. This resulted in the last field
2861     being blocked for exactly one less than the number of frames of one player
2862     move. Additionally, even when blocking was disabled, the last field was
2863     blocked for exactly one frame.
2864     Since 3.1.1, due to changes in player movement handling, the last field
2865     is not blocked at all when blocking is disabled. When blocking is enabled,
2866     the last field is blocked for exactly the number of frames of one player
2867     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2868     last field is blocked for exactly one more than the number of frames of
2869     one player move.
2870
2871     Affected levels/tapes:
2872     (!!! yet to be determined -- probably many !!!)
2873   */
2874
2875   game.use_block_last_field_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,1,0));
2877
2878   game_em.use_single_button =
2879     (game.engine_version > VERSION_IDENT(4,0,0,2));
2880
2881   game_em.use_snap_key_bug =
2882     (game.engine_version < VERSION_IDENT(4,0,1,0));
2883
2884   /* ---------------------------------------------------------------------- */
2885
2886   /* set maximal allowed number of custom element changes per game frame */
2887   game.max_num_changes_per_frame = 1;
2888
2889   /* default scan direction: scan playfield from top/left to bottom/right */
2890   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2891
2892   /* dynamically adjust element properties according to game engine version */
2893   InitElementPropertiesEngine(game.engine_version);
2894
2895 #if 0
2896   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2897   printf("          tape version == %06d [%s] [file: %06d]\n",
2898          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2899          tape.file_version);
2900   printf("       => game.engine_version == %06d\n", game.engine_version);
2901 #endif
2902
2903   /* ---------- initialize player's initial move delay --------------------- */
2904
2905   /* dynamically adjust player properties according to level information */
2906   for (i = 0; i < MAX_PLAYERS; i++)
2907     game.initial_move_delay_value[i] =
2908       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2909
2910   /* dynamically adjust player properties according to game engine version */
2911   for (i = 0; i < MAX_PLAYERS; i++)
2912     game.initial_move_delay[i] =
2913       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2914        game.initial_move_delay_value[i] : 0);
2915
2916   /* ---------- initialize player's initial push delay --------------------- */
2917
2918   /* dynamically adjust player properties according to game engine version */
2919   game.initial_push_delay_value =
2920     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2921
2922   /* ---------- initialize changing elements ------------------------------- */
2923
2924   /* initialize changing elements information */
2925   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2926   {
2927     struct ElementInfo *ei = &element_info[i];
2928
2929     /* this pointer might have been changed in the level editor */
2930     ei->change = &ei->change_page[0];
2931
2932     if (!IS_CUSTOM_ELEMENT(i))
2933     {
2934       ei->change->target_element = EL_EMPTY_SPACE;
2935       ei->change->delay_fixed = 0;
2936       ei->change->delay_random = 0;
2937       ei->change->delay_frames = 1;
2938     }
2939
2940     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2941     {
2942       ei->has_change_event[j] = FALSE;
2943
2944       ei->event_page_nr[j] = 0;
2945       ei->event_page[j] = &ei->change_page[0];
2946     }
2947   }
2948
2949   /* add changing elements from pre-defined list */
2950   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2951   {
2952     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2953     struct ElementInfo *ei = &element_info[ch_delay->element];
2954
2955     ei->change->target_element       = ch_delay->target_element;
2956     ei->change->delay_fixed          = ch_delay->change_delay;
2957
2958     ei->change->pre_change_function  = ch_delay->pre_change_function;
2959     ei->change->change_function      = ch_delay->change_function;
2960     ei->change->post_change_function = ch_delay->post_change_function;
2961
2962     ei->change->can_change = TRUE;
2963     ei->change->can_change_or_has_action = TRUE;
2964
2965     ei->has_change_event[CE_DELAY] = TRUE;
2966
2967     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2968     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969   }
2970
2971   /* ---------- initialize internal run-time variables --------------------- */
2972
2973   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974   {
2975     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976
2977     for (j = 0; j < ei->num_change_pages; j++)
2978     {
2979       ei->change_page[j].can_change_or_has_action =
2980         (ei->change_page[j].can_change |
2981          ei->change_page[j].has_action);
2982     }
2983   }
2984
2985   /* add change events from custom element configuration */
2986   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2987   {
2988     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2989
2990     for (j = 0; j < ei->num_change_pages; j++)
2991     {
2992       if (!ei->change_page[j].can_change_or_has_action)
2993         continue;
2994
2995       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2996       {
2997         /* only add event page for the first page found with this event */
2998         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2999         {
3000           ei->has_change_event[k] = TRUE;
3001
3002           ei->event_page_nr[k] = j;
3003           ei->event_page[k] = &ei->change_page[j];
3004         }
3005       }
3006     }
3007   }
3008
3009   /* ---------- initialize reference elements in change conditions --------- */
3010
3011   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3012   {
3013     int element = EL_CUSTOM_START + i;
3014     struct ElementInfo *ei = &element_info[element];
3015
3016     for (j = 0; j < ei->num_change_pages; j++)
3017     {
3018       int trigger_element = ei->change_page[j].initial_trigger_element;
3019
3020       if (trigger_element >= EL_PREV_CE_8 &&
3021           trigger_element <= EL_NEXT_CE_8)
3022         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3023
3024       ei->change_page[j].trigger_element = trigger_element;
3025     }
3026   }
3027
3028   /* ---------- initialize run-time trigger player and element ------------- */
3029
3030   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3031   {
3032     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3033
3034     for (j = 0; j < ei->num_change_pages; j++)
3035     {
3036       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3037       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3038       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3039       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3040       ei->change_page[j].actual_trigger_ce_value = 0;
3041       ei->change_page[j].actual_trigger_ce_score = 0;
3042     }
3043   }
3044
3045   /* ---------- initialize trigger events ---------------------------------- */
3046
3047   /* initialize trigger events information */
3048   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3049     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3050       trigger_events[i][j] = FALSE;
3051
3052   /* add trigger events from element change event properties */
3053   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054   {
3055     struct ElementInfo *ei = &element_info[i];
3056
3057     for (j = 0; j < ei->num_change_pages; j++)
3058     {
3059       if (!ei->change_page[j].can_change_or_has_action)
3060         continue;
3061
3062       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3063       {
3064         int trigger_element = ei->change_page[j].trigger_element;
3065
3066         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3067         {
3068           if (ei->change_page[j].has_event[k])
3069           {
3070             if (IS_GROUP_ELEMENT(trigger_element))
3071             {
3072               struct ElementGroupInfo *group =
3073                 element_info[trigger_element].group;
3074
3075               for (l = 0; l < group->num_elements_resolved; l++)
3076                 trigger_events[group->element_resolved[l]][k] = TRUE;
3077             }
3078             else if (trigger_element == EL_ANY_ELEMENT)
3079               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3080                 trigger_events[l][k] = TRUE;
3081             else
3082               trigger_events[trigger_element][k] = TRUE;
3083           }
3084         }
3085       }
3086     }
3087   }
3088
3089   /* ---------- initialize push delay -------------------------------------- */
3090
3091   /* initialize push delay values to default */
3092   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3093   {
3094     if (!IS_CUSTOM_ELEMENT(i))
3095     {
3096       /* set default push delay values (corrected since version 3.0.7-1) */
3097       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3098       {
3099         element_info[i].push_delay_fixed = 2;
3100         element_info[i].push_delay_random = 8;
3101       }
3102       else
3103       {
3104         element_info[i].push_delay_fixed = 8;
3105         element_info[i].push_delay_random = 8;
3106       }
3107     }
3108   }
3109
3110   /* set push delay value for certain elements from pre-defined list */
3111   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3112   {
3113     int e = push_delay_list[i].element;
3114
3115     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3116     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117   }
3118
3119   /* set push delay value for Supaplex elements for newer engine versions */
3120   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3121   {
3122     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123     {
3124       if (IS_SP_ELEMENT(i))
3125       {
3126         /* set SP push delay to just enough to push under a falling zonk */
3127         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3128
3129         element_info[i].push_delay_fixed  = delay;
3130         element_info[i].push_delay_random = 0;
3131       }
3132     }
3133   }
3134
3135   /* ---------- initialize move stepsize ----------------------------------- */
3136
3137   /* initialize move stepsize values to default */
3138   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3139     if (!IS_CUSTOM_ELEMENT(i))
3140       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3141
3142   /* set move stepsize value for certain elements from pre-defined list */
3143   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3144   {
3145     int e = move_stepsize_list[i].element;
3146
3147     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148   }
3149
3150   /* ---------- initialize collect score ----------------------------------- */
3151
3152   /* initialize collect score values for custom elements from initial value */
3153   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3154     if (IS_CUSTOM_ELEMENT(i))
3155       element_info[i].collect_score = element_info[i].collect_score_initial;
3156
3157   /* ---------- initialize collect count ----------------------------------- */
3158
3159   /* initialize collect count values for non-custom elements */
3160   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3161     if (!IS_CUSTOM_ELEMENT(i))
3162       element_info[i].collect_count_initial = 0;
3163
3164   /* add collect count values for all elements from pre-defined list */
3165   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3166     element_info[collect_count_list[i].element].collect_count_initial =
3167       collect_count_list[i].count;
3168
3169   /* ---------- initialize access direction -------------------------------- */
3170
3171   /* initialize access direction values to default (access from every side) */
3172   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3173     if (!IS_CUSTOM_ELEMENT(i))
3174       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3175
3176   /* set access direction value for certain elements from pre-defined list */
3177   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3178     element_info[access_direction_list[i].element].access_direction =
3179       access_direction_list[i].direction;
3180
3181   /* ---------- initialize explosion content ------------------------------- */
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183   {
3184     if (IS_CUSTOM_ELEMENT(i))
3185       continue;
3186
3187     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3188     {
3189       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3190
3191       element_info[i].content.e[x][y] =
3192         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3193          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3194          i == EL_PLAYER_3 ? EL_EMERALD :
3195          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3196          i == EL_MOLE ? EL_EMERALD_RED :
3197          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3198          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3199          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3200          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3201          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3202          i == EL_WALL_EMERALD ? EL_EMERALD :
3203          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3204          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3205          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3206          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3207          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3208          i == EL_WALL_PEARL ? EL_PEARL :
3209          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3210          EL_EMPTY);
3211     }
3212   }
3213
3214   /* ---------- initialize recursion detection ------------------------------ */
3215   recursion_loop_depth = 0;
3216   recursion_loop_detected = FALSE;
3217   recursion_loop_element = EL_UNDEFINED;
3218
3219   /* ---------- initialize graphics engine ---------------------------------- */
3220   game.scroll_delay_value =
3221     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3222      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3223   game.scroll_delay_value =
3224     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3225
3226   /* ---------- initialize game engine snapshots ---------------------------- */
3227   for (i = 0; i < MAX_PLAYERS; i++)
3228     game.snapshot.last_action[i] = 0;
3229   game.snapshot.changed_action = FALSE;
3230   game.snapshot.collected_item = FALSE;
3231   game.snapshot.mode =
3232     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3233      SNAPSHOT_MODE_EVERY_STEP :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3235      SNAPSHOT_MODE_EVERY_MOVE :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3237      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3238   game.snapshot.save_snapshot = FALSE;
3239
3240   /* ---------- initialize level time for Supaplex engine ------------------- */
3241   /* Supaplex levels with time limit currently unsupported -- should be added */
3242   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3243     level.time = 0;
3244 }
3245
3246 int get_num_special_action(int element, int action_first, int action_last)
3247 {
3248   int num_special_action = 0;
3249   int i, j;
3250
3251   for (i = action_first; i <= action_last; i++)
3252   {
3253     boolean found = FALSE;
3254
3255     for (j = 0; j < NUM_DIRECTIONS; j++)
3256       if (el_act_dir2img(element, i, j) !=
3257           el_act_dir2img(element, ACTION_DEFAULT, j))
3258         found = TRUE;
3259
3260     if (found)
3261       num_special_action++;
3262     else
3263       break;
3264   }
3265
3266   return num_special_action;
3267 }
3268
3269
3270 /*
3271   =============================================================================
3272   InitGame()
3273   -----------------------------------------------------------------------------
3274   initialize and start new game
3275   =============================================================================
3276 */
3277
3278 #if DEBUG_INIT_PLAYER
3279 static void DebugPrintPlayerStatus(char *message)
3280 {
3281   int i;
3282
3283   if (!options.debug)
3284     return;
3285
3286   printf("%s:\n", message);
3287
3288   for (i = 0; i < MAX_PLAYERS; i++)
3289   {
3290     struct PlayerInfo *player = &stored_player[i];
3291
3292     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3293            i + 1,
3294            player->present,
3295            player->connected,
3296            player->connected_locally,
3297            player->connected_network,
3298            player->active);
3299
3300     if (local_player == player)
3301       printf(" (local player)");
3302
3303     printf("\n");
3304   }
3305 }
3306 #endif
3307
3308 void InitGame()
3309 {
3310   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3312   int fade_mask = REDRAW_FIELD;
3313
3314   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3315   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3316   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3317   int initial_move_dir = MV_DOWN;
3318   int i, j, x, y;
3319
3320   // required here to update video display before fading (FIX THIS)
3321   DrawMaskedBorder(REDRAW_DOOR_2);
3322
3323   if (!game.restart_level)
3324     CloseDoor(DOOR_CLOSE_1);
3325
3326   SetGameStatus(GAME_MODE_PLAYING);
3327
3328   if (level_editor_test_game)
3329     FadeSkipNextFadeIn();
3330   else
3331     FadeSetEnterScreen();
3332
3333   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3334     fade_mask = REDRAW_ALL;
3335
3336   FadeLevelSoundsAndMusic();
3337
3338   ExpireSoundLoops(TRUE);
3339
3340   if (!level_editor_test_game)
3341     FadeOut(fade_mask);
3342
3343   /* needed if different viewport properties defined for playing */
3344   ChangeViewportPropertiesIfNeeded();
3345
3346   ClearField();
3347
3348   DrawCompleteVideoDisplay();
3349
3350   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3351
3352   InitGameEngine();
3353   InitGameControlValues();
3354
3355   /* don't play tapes over network */
3356   network_playing = (network.enabled && !tape.playing);
3357
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359   {
3360     struct PlayerInfo *player = &stored_player[i];
3361
3362     player->index_nr = i;
3363     player->index_bit = (1 << i);
3364     player->element_nr = EL_PLAYER_1 + i;
3365
3366     player->present = FALSE;
3367     player->active = FALSE;
3368     player->mapped = FALSE;
3369
3370     player->killed = FALSE;
3371     player->reanimated = FALSE;
3372
3373     player->action = 0;
3374     player->effective_action = 0;
3375     player->programmed_action = 0;
3376
3377     player->mouse_action.lx = 0;
3378     player->mouse_action.ly = 0;
3379     player->mouse_action.button = 0;
3380     player->mouse_action.button_hint = 0;
3381
3382     player->effective_mouse_action.lx = 0;
3383     player->effective_mouse_action.ly = 0;
3384     player->effective_mouse_action.button = 0;
3385     player->effective_mouse_action.button_hint = 0;
3386
3387     player->score = 0;
3388     player->score_final = 0;
3389
3390     player->health = MAX_HEALTH;
3391     player->health_final = MAX_HEALTH;
3392
3393     player->gems_still_needed = level.gems_needed;
3394     player->sokobanfields_still_needed = 0;
3395     player->lights_still_needed = 0;
3396     player->players_still_needed = 0;
3397     player->friends_still_needed = 0;
3398
3399     for (j = 0; j < MAX_NUM_KEYS; j++)
3400       player->key[j] = FALSE;
3401
3402     player->num_white_keys = 0;
3403
3404     player->dynabomb_count = 0;
3405     player->dynabomb_size = 1;
3406     player->dynabombs_left = 0;
3407     player->dynabomb_xl = FALSE;
3408
3409     player->MovDir = initial_move_dir;
3410     player->MovPos = 0;
3411     player->GfxPos = 0;
3412     player->GfxDir = initial_move_dir;
3413     player->GfxAction = ACTION_DEFAULT;
3414     player->Frame = 0;
3415     player->StepFrame = 0;
3416
3417     player->initial_element = player->element_nr;
3418     player->artwork_element =
3419       (level.use_artwork_element[i] ? level.artwork_element[i] :
3420        player->element_nr);
3421     player->use_murphy = FALSE;
3422
3423     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3424     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3425
3426     player->gravity = level.initial_player_gravity[i];
3427
3428     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3429
3430     player->actual_frame_counter = 0;
3431
3432     player->step_counter = 0;
3433
3434     player->last_move_dir = initial_move_dir;
3435
3436     player->is_active = FALSE;
3437
3438     player->is_waiting = FALSE;
3439     player->is_moving = FALSE;
3440     player->is_auto_moving = FALSE;
3441     player->is_digging = FALSE;
3442     player->is_snapping = FALSE;
3443     player->is_collecting = FALSE;
3444     player->is_pushing = FALSE;
3445     player->is_switching = FALSE;
3446     player->is_dropping = FALSE;
3447     player->is_dropping_pressed = FALSE;
3448
3449     player->is_bored = FALSE;
3450     player->is_sleeping = FALSE;
3451
3452     player->was_waiting = TRUE;
3453     player->was_moving = FALSE;
3454     player->was_snapping = FALSE;
3455     player->was_dropping = FALSE;
3456
3457     player->force_dropping = FALSE;
3458
3459     player->frame_counter_bored = -1;
3460     player->frame_counter_sleeping = -1;
3461
3462     player->anim_delay_counter = 0;
3463     player->post_delay_counter = 0;
3464
3465     player->dir_waiting = initial_move_dir;
3466     player->action_waiting = ACTION_DEFAULT;
3467     player->last_action_waiting = ACTION_DEFAULT;
3468     player->special_action_bored = ACTION_DEFAULT;
3469     player->special_action_sleeping = ACTION_DEFAULT;
3470
3471     player->switch_x = -1;
3472     player->switch_y = -1;
3473
3474     player->drop_x = -1;
3475     player->drop_y = -1;
3476
3477     player->show_envelope = 0;
3478
3479     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3480
3481     player->push_delay       = -1;      /* initialized when pushing starts */
3482     player->push_delay_value = game.initial_push_delay_value;
3483
3484     player->drop_delay = 0;
3485     player->drop_pressed_delay = 0;
3486
3487     player->last_jx = -1;
3488     player->last_jy = -1;
3489     player->jx = -1;
3490     player->jy = -1;
3491
3492     player->shield_normal_time_left = 0;
3493     player->shield_deadly_time_left = 0;
3494
3495     player->inventory_infinite_element = EL_UNDEFINED;
3496     player->inventory_size = 0;
3497
3498     if (level.use_initial_inventory[i])
3499     {
3500       for (j = 0; j < level.initial_inventory_size[i]; j++)
3501       {
3502         int element = level.initial_inventory_content[i][j];
3503         int collect_count = element_info[element].collect_count_initial;
3504         int k;
3505
3506         if (!IS_CUSTOM_ELEMENT(element))
3507           collect_count = 1;
3508
3509         if (collect_count == 0)
3510           player->inventory_infinite_element = element;
3511         else
3512           for (k = 0; k < collect_count; k++)
3513             if (player->inventory_size < MAX_INVENTORY_SIZE)
3514               player->inventory_element[player->inventory_size++] = element;
3515       }
3516     }
3517
3518     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3519     SnapField(player, 0, 0);
3520
3521     player->LevelSolved = FALSE;
3522     player->GameOver = FALSE;
3523
3524     player->LevelSolved_GameWon = FALSE;
3525     player->LevelSolved_GameEnd = FALSE;
3526     player->LevelSolved_PanelOff = FALSE;
3527     player->LevelSolved_SaveTape = FALSE;
3528     player->LevelSolved_SaveScore = FALSE;
3529
3530     player->LevelSolved_CountingTime = 0;
3531     player->LevelSolved_CountingScore = 0;
3532     player->LevelSolved_CountingHealth = 0;
3533
3534     map_player_action[i] = i;
3535   }
3536
3537   network_player_action_received = FALSE;
3538
3539   /* initial null action */
3540   if (network_playing)
3541     SendToServer_MovePlayer(MV_NONE);
3542
3543   ZX = ZY = -1;
3544   ExitX = ExitY = -1;
3545
3546   FrameCounter = 0;
3547   TimeFrames = 0;
3548   TimePlayed = 0;
3549   TimeLeft = level.time;
3550   TapeTime = 0;
3551
3552   ScreenMovDir = MV_NONE;
3553   ScreenMovPos = 0;
3554   ScreenGfxPos = 0;
3555
3556   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3557
3558   AllPlayersGone = FALSE;
3559
3560   game.no_time_limit = (level.time == 0);
3561
3562   game.yamyam_content_nr = 0;
3563   game.robot_wheel_active = FALSE;
3564   game.magic_wall_active = FALSE;
3565   game.magic_wall_time_left = 0;
3566   game.light_time_left = 0;
3567   game.timegate_time_left = 0;
3568   game.switchgate_pos = 0;
3569   game.wind_direction = level.wind_direction_initial;
3570
3571   game.lenses_time_left = 0;
3572   game.magnify_time_left = 0;
3573
3574   game.ball_state = level.ball_state_initial;
3575   game.ball_content_nr = 0;
3576
3577   game.envelope_active = FALSE;
3578
3579   for (i = 0; i < NUM_BELTS; i++)
3580   {
3581     game.belt_dir[i] = MV_NONE;
3582     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3583   }
3584
3585   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3586     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3587
3588 #if DEBUG_INIT_PLAYER
3589   DebugPrintPlayerStatus("Player status at level initialization");
3590 #endif
3591
3592   SCAN_PLAYFIELD(x, y)
3593   {
3594     Feld[x][y] = level.field[x][y];
3595     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3596     ChangeDelay[x][y] = 0;
3597     ChangePage[x][y] = -1;
3598     CustomValue[x][y] = 0;              /* initialized in InitField() */
3599     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3600     AmoebaNr[x][y] = 0;
3601     WasJustMoving[x][y] = 0;
3602     WasJustFalling[x][y] = 0;
3603     CheckCollision[x][y] = 0;
3604     CheckImpact[x][y] = 0;
3605     Stop[x][y] = FALSE;
3606     Pushed[x][y] = FALSE;
3607
3608     ChangeCount[x][y] = 0;
3609     ChangeEvent[x][y] = -1;
3610
3611     ExplodePhase[x][y] = 0;
3612     ExplodeDelay[x][y] = 0;
3613     ExplodeField[x][y] = EX_TYPE_NONE;
3614
3615     RunnerVisit[x][y] = 0;
3616     PlayerVisit[x][y] = 0;
3617
3618     GfxFrame[x][y] = 0;
3619     GfxRandom[x][y] = INIT_GFX_RANDOM();
3620     GfxElement[x][y] = EL_UNDEFINED;
3621     GfxAction[x][y] = ACTION_DEFAULT;
3622     GfxDir[x][y] = MV_NONE;
3623     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3624   }
3625
3626   SCAN_PLAYFIELD(x, y)
3627   {
3628     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3629       emulate_bd = FALSE;
3630     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3631       emulate_sb = FALSE;
3632     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3633       emulate_sp = FALSE;
3634
3635     InitField(x, y, TRUE);
3636
3637     ResetGfxAnimation(x, y);
3638   }
3639
3640   InitBeltMovement();
3641
3642   for (i = 0; i < MAX_PLAYERS; i++)
3643   {
3644     struct PlayerInfo *player = &stored_player[i];
3645
3646     /* set number of special actions for bored and sleeping animation */
3647     player->num_special_action_bored =
3648       get_num_special_action(player->artwork_element,
3649                              ACTION_BORING_1, ACTION_BORING_LAST);
3650     player->num_special_action_sleeping =
3651       get_num_special_action(player->artwork_element,
3652                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3653   }
3654
3655   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3656                     emulate_sb ? EMU_SOKOBAN :
3657                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3658
3659   /* initialize type of slippery elements */
3660   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3661   {
3662     if (!IS_CUSTOM_ELEMENT(i))
3663     {
3664       /* default: elements slip down either to the left or right randomly */
3665       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3666
3667       /* SP style elements prefer to slip down on the left side */
3668       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3669         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3670
3671       /* BD style elements prefer to slip down on the left side */
3672       if (game.emulation == EMU_BOULDERDASH)
3673         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3674     }
3675   }
3676
3677   /* initialize explosion and ignition delay */
3678   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3679   {
3680     if (!IS_CUSTOM_ELEMENT(i))
3681     {
3682       int num_phase = 8;
3683       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3684                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3685                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3686       int last_phase = (num_phase + 1) * delay;
3687       int half_phase = (num_phase / 2) * delay;
3688
3689       element_info[i].explosion_delay = last_phase - 1;
3690       element_info[i].ignition_delay = half_phase;
3691
3692       if (i == EL_BLACK_ORB)
3693         element_info[i].ignition_delay = 1;
3694     }
3695   }
3696
3697   /* correct non-moving belts to start moving left */
3698   for (i = 0; i < NUM_BELTS; i++)
3699     if (game.belt_dir[i] == MV_NONE)
3700       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3701
3702 #if USE_NEW_PLAYER_ASSIGNMENTS
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     stored_player[i].connected = FALSE;
3706
3707     /* in network game mode, the local player might not be the first player */
3708     if (stored_player[i].connected_locally)
3709       local_player = &stored_player[i];
3710   }
3711
3712   if (!network.enabled)
3713     local_player->connected = TRUE;
3714
3715   if (tape.playing)
3716   {
3717     for (i = 0; i < MAX_PLAYERS; i++)
3718       stored_player[i].connected = tape.player_participates[i];
3719   }
3720   else if (network.enabled)
3721   {
3722     /* add team mode players connected over the network (needed for correct
3723        assignment of player figures from level to locally playing players) */
3724
3725     for (i = 0; i < MAX_PLAYERS; i++)
3726       if (stored_player[i].connected_network)
3727         stored_player[i].connected = TRUE;
3728   }
3729   else if (game.team_mode)
3730   {
3731     /* try to guess locally connected team mode players (needed for correct
3732        assignment of player figures from level to locally playing players) */
3733
3734     for (i = 0; i < MAX_PLAYERS; i++)
3735       if (setup.input[i].use_joystick ||
3736           setup.input[i].key.left != KSYM_UNDEFINED)
3737         stored_player[i].connected = TRUE;
3738   }
3739
3740 #if DEBUG_INIT_PLAYER
3741   DebugPrintPlayerStatus("Player status after level initialization");
3742 #endif
3743
3744 #if DEBUG_INIT_PLAYER
3745   if (options.debug)
3746     printf("Reassigning players ...\n");
3747 #endif
3748
3749   /* check if any connected player was not found in playfield */
3750   for (i = 0; i < MAX_PLAYERS; i++)
3751   {
3752     struct PlayerInfo *player = &stored_player[i];
3753
3754     if (player->connected && !player->present)
3755     {
3756       struct PlayerInfo *field_player = NULL;
3757
3758 #if DEBUG_INIT_PLAYER
3759       if (options.debug)
3760         printf("- looking for field player for player %d ...\n", i + 1);
3761 #endif
3762
3763       /* assign first free player found that is present in the playfield */
3764
3765       /* first try: look for unmapped playfield player that is not connected */
3766       for (j = 0; j < MAX_PLAYERS; j++)
3767         if (field_player == NULL &&
3768             stored_player[j].present &&
3769             !stored_player[j].mapped &&
3770             !stored_player[j].connected)
3771           field_player = &stored_player[j];
3772
3773       /* second try: look for *any* unmapped playfield player */
3774       for (j = 0; j < MAX_PLAYERS; j++)
3775         if (field_player == NULL &&
3776             stored_player[j].present &&
3777             !stored_player[j].mapped)
3778           field_player = &stored_player[j];
3779
3780       if (field_player != NULL)
3781       {
3782         int jx = field_player->jx, jy = field_player->jy;
3783
3784 #if DEBUG_INIT_PLAYER
3785         if (options.debug)
3786           printf("- found player %d\n", field_player->index_nr + 1);
3787 #endif
3788
3789         player->present = FALSE;
3790         player->active = FALSE;
3791
3792         field_player->present = TRUE;
3793         field_player->active = TRUE;
3794
3795         /*
3796         player->initial_element = field_player->initial_element;
3797         player->artwork_element = field_player->artwork_element;
3798
3799         player->block_last_field       = field_player->block_last_field;
3800         player->block_delay_adjustment = field_player->block_delay_adjustment;
3801         */
3802
3803         StorePlayer[jx][jy] = field_player->element_nr;
3804
3805         field_player->jx = field_player->last_jx = jx;
3806         field_player->jy = field_player->last_jy = jy;
3807
3808         if (local_player == player)
3809           local_player = field_player;
3810
3811         map_player_action[field_player->index_nr] = i;
3812
3813         field_player->mapped = TRUE;
3814
3815 #if DEBUG_INIT_PLAYER
3816         if (options.debug)
3817           printf("- map_player_action[%d] == %d\n",
3818                  field_player->index_nr + 1, i + 1);
3819 #endif
3820       }
3821     }
3822
3823     if (player->connected && player->present)
3824       player->mapped = TRUE;
3825   }
3826
3827 #if DEBUG_INIT_PLAYER
3828   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3829 #endif
3830
3831 #else
3832
3833   /* check if any connected player was not found in playfield */
3834   for (i = 0; i < MAX_PLAYERS; i++)
3835   {
3836     struct PlayerInfo *player = &stored_player[i];
3837
3838     if (player->connected && !player->present)
3839     {
3840       for (j = 0; j < MAX_PLAYERS; j++)
3841       {
3842         struct PlayerInfo *field_player = &stored_player[j];
3843         int jx = field_player->jx, jy = field_player->jy;
3844
3845         /* assign first free player found that is present in the playfield */
3846         if (field_player->present && !field_player->connected)
3847         {
3848           player->present = TRUE;
3849           player->active = TRUE;
3850
3851           field_player->present = FALSE;
3852           field_player->active = FALSE;
3853
3854           player->initial_element = field_player->initial_element;
3855           player->artwork_element = field_player->artwork_element;
3856
3857           player->block_last_field       = field_player->block_last_field;
3858           player->block_delay_adjustment = field_player->block_delay_adjustment;
3859
3860           StorePlayer[jx][jy] = player->element_nr;
3861
3862           player->jx = player->last_jx = jx;
3863           player->jy = player->last_jy = jy;
3864
3865           break;
3866         }
3867       }
3868     }
3869   }
3870 #endif
3871
3872 #if 0
3873   printf("::: local_player->present == %d\n", local_player->present);
3874 #endif
3875
3876   /* set focus to local player for network games, else to all players */
3877   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3878   game.centered_player_nr_next = game.centered_player_nr;
3879   game.set_centered_player = FALSE;
3880
3881   if (network_playing && tape.recording)
3882   {
3883     /* store client dependent player focus when recording network games */
3884     tape.centered_player_nr_next = game.centered_player_nr_next;
3885     tape.set_centered_player = TRUE;
3886   }
3887
3888   if (tape.playing)
3889   {
3890     /* when playing a tape, eliminate all players who do not participate */
3891
3892 #if USE_NEW_PLAYER_ASSIGNMENTS
3893
3894     if (!game.team_mode)
3895     {
3896       for (i = 0; i < MAX_PLAYERS; i++)
3897       {
3898         if (stored_player[i].active &&
3899             !tape.player_participates[map_player_action[i]])
3900         {
3901           struct PlayerInfo *player = &stored_player[i];
3902           int jx = player->jx, jy = player->jy;
3903
3904 #if DEBUG_INIT_PLAYER
3905           if (options.debug)
3906             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3907 #endif
3908
3909           player->active = FALSE;
3910           StorePlayer[jx][jy] = 0;
3911           Feld[jx][jy] = EL_EMPTY;
3912         }
3913       }
3914     }
3915
3916 #else
3917
3918     for (i = 0; i < MAX_PLAYERS; i++)
3919     {
3920       if (stored_player[i].active &&
3921           !tape.player_participates[i])
3922       {
3923         struct PlayerInfo *player = &stored_player[i];
3924         int jx = player->jx, jy = player->jy;
3925
3926         player->active = FALSE;
3927         StorePlayer[jx][jy] = 0;
3928         Feld[jx][jy] = EL_EMPTY;
3929       }
3930     }
3931 #endif
3932   }
3933   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3934   {
3935     /* when in single player mode, eliminate all but the local player */
3936
3937     for (i = 0; i < MAX_PLAYERS; i++)
3938     {
3939       struct PlayerInfo *player = &stored_player[i];
3940
3941       if (player->active && player != local_player)
3942       {
3943         int jx = player->jx, jy = player->jy;
3944
3945         player->active = FALSE;
3946         player->present = FALSE;
3947
3948         StorePlayer[jx][jy] = 0;
3949         Feld[jx][jy] = EL_EMPTY;
3950       }
3951     }
3952   }
3953
3954   for (i = 0; i < MAX_PLAYERS; i++)
3955     if (stored_player[i].active)
3956       local_player->players_still_needed++;
3957
3958   /* when recording the game, store which players take part in the game */
3959   if (tape.recording)
3960   {
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962     for (i = 0; i < MAX_PLAYERS; i++)
3963       if (stored_player[i].connected)
3964         tape.player_participates[i] = TRUE;
3965 #else
3966     for (i = 0; i < MAX_PLAYERS; i++)
3967       if (stored_player[i].active)
3968         tape.player_participates[i] = TRUE;
3969 #endif
3970   }
3971
3972 #if DEBUG_INIT_PLAYER
3973   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3974 #endif
3975
3976   if (BorderElement == EL_EMPTY)
3977   {
3978     SBX_Left = 0;
3979     SBX_Right = lev_fieldx - SCR_FIELDX;
3980     SBY_Upper = 0;
3981     SBY_Lower = lev_fieldy - SCR_FIELDY;
3982   }
3983   else
3984   {
3985     SBX_Left = -1;
3986     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3987     SBY_Upper = -1;
3988     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3989   }
3990
3991   if (full_lev_fieldx <= SCR_FIELDX)
3992     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3993   if (full_lev_fieldy <= SCR_FIELDY)
3994     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3995
3996   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3997     SBX_Left--;
3998   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3999     SBY_Upper--;
4000
4001   /* if local player not found, look for custom element that might create
4002      the player (make some assumptions about the right custom element) */
4003   if (!local_player->present)
4004   {
4005     int start_x = 0, start_y = 0;
4006     int found_rating = 0;
4007     int found_element = EL_UNDEFINED;
4008     int player_nr = local_player->index_nr;
4009
4010     SCAN_PLAYFIELD(x, y)
4011     {
4012       int element = Feld[x][y];
4013       int content;
4014       int xx, yy;
4015       boolean is_player;
4016
4017       if (level.use_start_element[player_nr] &&
4018           level.start_element[player_nr] == element &&
4019           found_rating < 4)
4020       {
4021         start_x = x;
4022         start_y = y;
4023
4024         found_rating = 4;
4025         found_element = element;
4026       }
4027
4028       if (!IS_CUSTOM_ELEMENT(element))
4029         continue;
4030
4031       if (CAN_CHANGE(element))
4032       {
4033         for (i = 0; i < element_info[element].num_change_pages; i++)
4034         {
4035           /* check for player created from custom element as single target */
4036           content = element_info[element].change_page[i].target_element;
4037           is_player = ELEM_IS_PLAYER(content);
4038
4039           if (is_player && (found_rating < 3 ||
4040                             (found_rating == 3 && element < found_element)))
4041           {
4042             start_x = x;
4043             start_y = y;
4044
4045             found_rating = 3;
4046             found_element = element;
4047           }
4048         }
4049       }
4050
4051       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4052       {
4053         /* check for player created from custom element as explosion content */
4054         content = element_info[element].content.e[xx][yy];
4055         is_player = ELEM_IS_PLAYER(content);
4056
4057         if (is_player && (found_rating < 2 ||
4058                           (found_rating == 2 && element < found_element)))
4059         {
4060           start_x = x + xx - 1;
4061           start_y = y + yy - 1;
4062
4063           found_rating = 2;
4064           found_element = element;
4065         }
4066
4067         if (!CAN_CHANGE(element))
4068           continue;
4069
4070         for (i = 0; i < element_info[element].num_change_pages; i++)
4071         {
4072           /* check for player created from custom element as extended target */
4073           content =
4074             element_info[element].change_page[i].target_content.e[xx][yy];
4075
4076           is_player = ELEM_IS_PLAYER(content);
4077
4078           if (is_player && (found_rating < 1 ||
4079                             (found_rating == 1 && element < found_element)))
4080           {
4081             start_x = x + xx - 1;
4082             start_y = y + yy - 1;
4083
4084             found_rating = 1;
4085             found_element = element;
4086           }
4087         }
4088       }
4089     }
4090
4091     scroll_x = SCROLL_POSITION_X(start_x);
4092     scroll_y = SCROLL_POSITION_Y(start_y);
4093   }
4094   else
4095   {
4096     scroll_x = SCROLL_POSITION_X(local_player->jx);
4097     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4098   }
4099
4100   /* !!! FIX THIS (START) !!! */
4101   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4102   {
4103     InitGameEngine_EM();
4104   }
4105   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4106   {
4107     InitGameEngine_SP();
4108   }
4109   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4110   {
4111     InitGameEngine_MM();
4112   }
4113   else
4114   {
4115     DrawLevel(REDRAW_FIELD);
4116     DrawAllPlayers();
4117
4118     /* after drawing the level, correct some elements */
4119     if (game.timegate_time_left == 0)
4120       CloseAllOpenTimegates();
4121   }
4122
4123   /* blit playfield from scroll buffer to normal back buffer for fading in */
4124   BlitScreenToBitmap(backbuffer);
4125   /* !!! FIX THIS (END) !!! */
4126
4127   DrawMaskedBorder(fade_mask);
4128
4129   FadeIn(fade_mask);
4130
4131 #if 1
4132   // full screen redraw is required at this point in the following cases:
4133   // - special editor door undrawn when game was started from level editor
4134   // - drawing area (playfield) was changed and has to be removed completely
4135   redraw_mask = REDRAW_ALL;
4136   BackToFront();
4137 #endif
4138
4139   if (!game.restart_level)
4140   {
4141     /* copy default game door content to main double buffer */
4142
4143     /* !!! CHECK AGAIN !!! */
4144     SetPanelBackground();
4145     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4146     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4147   }
4148
4149   SetPanelBackground();
4150   SetDrawBackgroundMask(REDRAW_DOOR_1);
4151
4152   UpdateAndDisplayGameControlValues();
4153
4154   if (!game.restart_level)
4155   {
4156     UnmapGameButtons();
4157     UnmapTapeButtons();
4158
4159     FreeGameButtons();
4160     CreateGameButtons();
4161
4162     MapGameButtons();
4163     MapTapeButtons();
4164
4165     /* copy actual game door content to door double buffer for OpenDoor() */
4166     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4167
4168     OpenDoor(DOOR_OPEN_ALL);
4169
4170     KeyboardAutoRepeatOffUnlessAutoplay();
4171
4172 #if DEBUG_INIT_PLAYER
4173     DebugPrintPlayerStatus("Player status (final)");
4174 #endif
4175   }
4176
4177   UnmapAllGadgets();
4178
4179   MapGameButtons();
4180   MapTapeButtons();
4181
4182   if (!game.restart_level && !tape.playing)
4183   {
4184     LevelStats_incPlayed(level_nr);
4185
4186     SaveLevelSetup_SeriesInfo();
4187   }
4188
4189   game.restart_level = FALSE;
4190   game.restart_game_message = NULL;
4191
4192   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4193     InitGameActions_MM();
4194
4195   SaveEngineSnapshotToListInitial();
4196
4197   if (!game.restart_level)
4198   {
4199     PlaySound(SND_GAME_STARTING);
4200
4201     if (setup.sound_music)
4202       PlayLevelMusic();
4203   }
4204 }
4205
4206 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4207                         int actual_player_x, int actual_player_y)
4208 {
4209   /* this is used for non-R'n'D game engines to update certain engine values */
4210
4211   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4212   {
4213     actual_player_x = correctLevelPosX_EM(actual_player_x);
4214     actual_player_y = correctLevelPosY_EM(actual_player_y);
4215   }
4216
4217   /* needed to determine if sounds are played within the visible screen area */
4218   scroll_x = actual_scroll_x;
4219   scroll_y = actual_scroll_y;
4220
4221   /* needed to get player position for "follow finger" playing input method */
4222   local_player->jx = actual_player_x;
4223   local_player->jy = actual_player_y;
4224 }
4225
4226 void InitMovDir(int x, int y)
4227 {
4228   int i, element = Feld[x][y];
4229   static int xy[4][2] =
4230   {
4231     {  0, +1 },
4232     { +1,  0 },
4233     {  0, -1 },
4234     { -1,  0 }
4235   };
4236   static int direction[3][4] =
4237   {
4238     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4239     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4240     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4241   };
4242
4243   switch (element)
4244   {
4245     case EL_BUG_RIGHT:
4246     case EL_BUG_UP:
4247     case EL_BUG_LEFT:
4248     case EL_BUG_DOWN:
4249       Feld[x][y] = EL_BUG;
4250       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4251       break;
4252
4253     case EL_SPACESHIP_RIGHT:
4254     case EL_SPACESHIP_UP:
4255     case EL_SPACESHIP_LEFT:
4256     case EL_SPACESHIP_DOWN:
4257       Feld[x][y] = EL_SPACESHIP;
4258       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4259       break;
4260
4261     case EL_BD_BUTTERFLY_RIGHT:
4262     case EL_BD_BUTTERFLY_UP:
4263     case EL_BD_BUTTERFLY_LEFT:
4264     case EL_BD_BUTTERFLY_DOWN:
4265       Feld[x][y] = EL_BD_BUTTERFLY;
4266       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4267       break;
4268
4269     case EL_BD_FIREFLY_RIGHT:
4270     case EL_BD_FIREFLY_UP:
4271     case EL_BD_FIREFLY_LEFT:
4272     case EL_BD_FIREFLY_DOWN:
4273       Feld[x][y] = EL_BD_FIREFLY;
4274       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4275       break;
4276
4277     case EL_PACMAN_RIGHT:
4278     case EL_PACMAN_UP:
4279     case EL_PACMAN_LEFT:
4280     case EL_PACMAN_DOWN:
4281       Feld[x][y] = EL_PACMAN;
4282       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4283       break;
4284
4285     case EL_YAMYAM_LEFT:
4286     case EL_YAMYAM_RIGHT:
4287     case EL_YAMYAM_UP:
4288     case EL_YAMYAM_DOWN:
4289       Feld[x][y] = EL_YAMYAM;
4290       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4291       break;
4292
4293     case EL_SP_SNIKSNAK:
4294       MovDir[x][y] = MV_UP;
4295       break;
4296
4297     case EL_SP_ELECTRON:
4298       MovDir[x][y] = MV_LEFT;
4299       break;
4300
4301     case EL_MOLE_LEFT:
4302     case EL_MOLE_RIGHT:
4303     case EL_MOLE_UP:
4304     case EL_MOLE_DOWN:
4305       Feld[x][y] = EL_MOLE;
4306       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4307       break;
4308
4309     default:
4310       if (IS_CUSTOM_ELEMENT(element))
4311       {
4312         struct ElementInfo *ei = &element_info[element];
4313         int move_direction_initial = ei->move_direction_initial;
4314         int move_pattern = ei->move_pattern;
4315
4316         if (move_direction_initial == MV_START_PREVIOUS)
4317         {
4318           if (MovDir[x][y] != MV_NONE)
4319             return;
4320
4321           move_direction_initial = MV_START_AUTOMATIC;
4322         }
4323
4324         if (move_direction_initial == MV_START_RANDOM)
4325           MovDir[x][y] = 1 << RND(4);
4326         else if (move_direction_initial & MV_ANY_DIRECTION)
4327           MovDir[x][y] = move_direction_initial;
4328         else if (move_pattern == MV_ALL_DIRECTIONS ||
4329                  move_pattern == MV_TURNING_LEFT ||
4330                  move_pattern == MV_TURNING_RIGHT ||
4331                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4332                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4333                  move_pattern == MV_TURNING_RANDOM)
4334           MovDir[x][y] = 1 << RND(4);
4335         else if (move_pattern == MV_HORIZONTAL)
4336           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4337         else if (move_pattern == MV_VERTICAL)
4338           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4339         else if (move_pattern & MV_ANY_DIRECTION)
4340           MovDir[x][y] = element_info[element].move_pattern;
4341         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4342                  move_pattern == MV_ALONG_RIGHT_SIDE)
4343         {
4344           /* use random direction as default start direction */
4345           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4346             MovDir[x][y] = 1 << RND(4);
4347
4348           for (i = 0; i < NUM_DIRECTIONS; i++)
4349           {
4350             int x1 = x + xy[i][0];
4351             int y1 = y + xy[i][1];
4352
4353             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4354             {
4355               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4356                 MovDir[x][y] = direction[0][i];
4357               else
4358                 MovDir[x][y] = direction[1][i];
4359
4360               break;
4361             }
4362           }
4363         }                
4364       }
4365       else
4366       {
4367         MovDir[x][y] = 1 << RND(4);
4368
4369         if (element != EL_BUG &&
4370             element != EL_SPACESHIP &&
4371             element != EL_BD_BUTTERFLY &&
4372             element != EL_BD_FIREFLY)
4373           break;
4374
4375         for (i = 0; i < NUM_DIRECTIONS; i++)
4376         {
4377           int x1 = x + xy[i][0];
4378           int y1 = y + xy[i][1];
4379
4380           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4381           {
4382             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4383             {
4384               MovDir[x][y] = direction[0][i];
4385               break;
4386             }
4387             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4388                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4389             {
4390               MovDir[x][y] = direction[1][i];
4391               break;
4392             }
4393           }
4394         }
4395       }
4396       break;
4397   }
4398
4399   GfxDir[x][y] = MovDir[x][y];
4400 }
4401
4402 void InitAmoebaNr(int x, int y)
4403 {
4404   int i;
4405   int group_nr = AmoebeNachbarNr(x, y);
4406
4407   if (group_nr == 0)
4408   {
4409     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4410     {
4411       if (AmoebaCnt[i] == 0)
4412       {
4413         group_nr = i;
4414         break;
4415       }
4416     }
4417   }
4418
4419   AmoebaNr[x][y] = group_nr;
4420   AmoebaCnt[group_nr]++;
4421   AmoebaCnt2[group_nr]++;
4422 }
4423
4424 static void PlayerWins(struct PlayerInfo *player)
4425 {
4426   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4427       local_player->players_still_needed > 0)
4428     return;
4429
4430   player->LevelSolved = TRUE;
4431   player->GameOver = TRUE;
4432
4433   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4434                          level.native_em_level->lev->score :
4435                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4436                          game_mm.score :
4437                          player->score);
4438   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4439                           MM_HEALTH(game_mm.laser_overload_value) :
4440                           player->health);
4441
4442   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4443                                       TimeLeft);
4444   player->LevelSolved_CountingScore = player->score_final;
4445   player->LevelSolved_CountingHealth = player->health_final;
4446 }
4447
4448 void GameWon()
4449 {
4450   static int time_count_steps;
4451   static int time, time_final;
4452   static int score, score_final;
4453   static int health, health_final;
4454   static int game_over_delay_1 = 0;
4455   static int game_over_delay_2 = 0;
4456   static int game_over_delay_3 = 0;
4457   int game_over_delay_value_1 = 50;
4458   int game_over_delay_value_2 = 25;
4459   int game_over_delay_value_3 = 50;
4460
4461   if (!local_player->LevelSolved_GameWon)
4462   {
4463     int i;
4464
4465     /* do not start end game actions before the player stops moving (to exit) */
4466     if (local_player->MovPos)
4467       return;
4468
4469     local_player->LevelSolved_GameWon = TRUE;
4470     local_player->LevelSolved_SaveTape = tape.recording;
4471     local_player->LevelSolved_SaveScore = !tape.playing;
4472
4473     if (!tape.playing)
4474     {
4475       LevelStats_incSolved(level_nr);
4476
4477       SaveLevelSetup_SeriesInfo();
4478     }
4479
4480     if (tape.auto_play)         /* tape might already be stopped here */
4481       tape.auto_play_level_solved = TRUE;
4482
4483     TapeStop();
4484
4485     game_over_delay_1 = 0;
4486     game_over_delay_2 = 0;
4487     game_over_delay_3 = game_over_delay_value_3;
4488
4489     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4490     score = score_final = local_player->score_final;
4491     health = health_final = local_player->health_final;
4492
4493     if (level.score[SC_TIME_BONUS] > 0)
4494     {
4495       if (TimeLeft > 0)
4496       {
4497         time_final = 0;
4498         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4499       }
4500       else if (game.no_time_limit && TimePlayed < 999)
4501       {
4502         time_final = 999;
4503         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4504       }
4505
4506       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4507
4508       game_over_delay_1 = game_over_delay_value_1;
4509
4510       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4511       {
4512         health_final = 0;
4513         score_final += health * level.score[SC_TIME_BONUS];
4514
4515         game_over_delay_2 = game_over_delay_value_2;
4516       }
4517
4518       local_player->score_final = score_final;
4519       local_player->health_final = health_final;
4520     }
4521
4522     if (level_editor_test_game)
4523     {
4524       time = time_final;
4525       score = score_final;
4526
4527       local_player->LevelSolved_CountingTime = time;
4528       local_player->LevelSolved_CountingScore = score;
4529
4530       game_panel_controls[GAME_PANEL_TIME].value = time;
4531       game_panel_controls[GAME_PANEL_SCORE].value = score;
4532
4533       DisplayGameControlValues();
4534     }
4535
4536     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4537     {
4538       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4539       {
4540         /* close exit door after last player */
4541         if ((AllPlayersGone &&
4542              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4543               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4544               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4545             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4546             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4547         {
4548           int element = Feld[ExitX][ExitY];
4549
4550           Feld[ExitX][ExitY] =
4551             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4552              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4553              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4554              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4555              EL_EM_STEEL_EXIT_CLOSING);
4556
4557           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4558         }
4559
4560         /* player disappears */
4561         DrawLevelField(ExitX, ExitY);
4562       }
4563
4564       for (i = 0; i < MAX_PLAYERS; i++)
4565       {
4566         struct PlayerInfo *player = &stored_player[i];
4567
4568         if (player->present)
4569         {
4570           RemovePlayer(player);
4571
4572           /* player disappears */
4573           DrawLevelField(player->jx, player->jy);
4574         }
4575       }
4576     }
4577
4578     PlaySound(SND_GAME_WINNING);
4579   }
4580
4581   if (game_over_delay_1 > 0)
4582   {
4583     game_over_delay_1--;
4584
4585     return;
4586   }
4587
4588   if (time != time_final)
4589   {
4590     int time_to_go = ABS(time_final - time);
4591     int time_count_dir = (time < time_final ? +1 : -1);
4592
4593     if (time_to_go < time_count_steps)
4594       time_count_steps = 1;
4595
4596     time  += time_count_steps * time_count_dir;
4597     score += time_count_steps * level.score[SC_TIME_BONUS];
4598
4599     local_player->LevelSolved_CountingTime = time;
4600     local_player->LevelSolved_CountingScore = score;
4601
4602     game_panel_controls[GAME_PANEL_TIME].value = time;
4603     game_panel_controls[GAME_PANEL_SCORE].value = score;
4604
4605     DisplayGameControlValues();
4606
4607     if (time == time_final)
4608       StopSound(SND_GAME_LEVELTIME_BONUS);
4609     else if (setup.sound_loops)
4610       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4611     else
4612       PlaySound(SND_GAME_LEVELTIME_BONUS);
4613
4614     return;
4615   }
4616
4617   if (game_over_delay_2 > 0)
4618   {
4619     game_over_delay_2--;
4620
4621     return;
4622   }
4623
4624   if (health != health_final)
4625   {
4626     int health_count_dir = (health < health_final ? +1 : -1);
4627
4628     health += health_count_dir;
4629     score  += level.score[SC_TIME_BONUS];
4630
4631     local_player->LevelSolved_CountingHealth = health;
4632     local_player->LevelSolved_CountingScore = score;
4633
4634     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4635     game_panel_controls[GAME_PANEL_SCORE].value = score;
4636
4637     DisplayGameControlValues();
4638
4639     if (health == health_final)
4640       StopSound(SND_GAME_LEVELTIME_BONUS);
4641     else if (setup.sound_loops)
4642       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4643     else
4644       PlaySound(SND_GAME_LEVELTIME_BONUS);
4645
4646     return;
4647   }
4648
4649   local_player->LevelSolved_PanelOff = TRUE;
4650
4651   if (game_over_delay_3 > 0)
4652   {
4653     game_over_delay_3--;
4654
4655     return;
4656   }
4657
4658   GameEnd();
4659 }
4660
4661 void GameEnd()
4662 {
4663   int hi_pos;
4664   int last_level_nr = level_nr;
4665
4666   local_player->LevelSolved_GameEnd = TRUE;
4667
4668   if (local_player->LevelSolved_SaveTape)
4669   {
4670     /* make sure that request dialog to save tape does not open door again */
4671     if (!global.use_envelope_request)
4672       CloseDoor(DOOR_CLOSE_1);
4673
4674     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4675   }
4676
4677   /* if no tape is to be saved, close both doors simultaneously */
4678   CloseDoor(DOOR_CLOSE_ALL);
4679
4680   if (level_editor_test_game)
4681   {
4682     SetGameStatus(GAME_MODE_MAIN);
4683
4684     DrawMainMenu();
4685
4686     return;
4687   }
4688
4689   if (!local_player->LevelSolved_SaveScore)
4690   {
4691     SetGameStatus(GAME_MODE_MAIN);
4692
4693     DrawMainMenu();
4694
4695     return;
4696   }
4697
4698   if (level_nr == leveldir_current->handicap_level)
4699   {
4700     leveldir_current->handicap_level++;
4701
4702     SaveLevelSetup_SeriesInfo();
4703   }
4704
4705   if (setup.increment_levels &&
4706       level_nr < leveldir_current->last_level &&
4707       !network_playing)
4708   {
4709     level_nr++;         /* advance to next level */
4710     TapeErase();        /* start with empty tape */
4711
4712     if (setup.auto_play_next_level)
4713     {
4714       LoadLevel(level_nr);
4715
4716       SaveLevelSetup_SeriesInfo();
4717     }
4718   }
4719
4720   hi_pos = NewHiScore(last_level_nr);
4721
4722   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4723   {
4724     SetGameStatus(GAME_MODE_SCORES);
4725
4726     DrawHallOfFame(last_level_nr, hi_pos);
4727   }
4728   else if (setup.auto_play_next_level && setup.increment_levels &&
4729            !network_playing)
4730   {
4731     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4732   }
4733   else
4734   {
4735     SetGameStatus(GAME_MODE_MAIN);
4736
4737     DrawMainMenu();
4738   }
4739 }
4740
4741 int NewHiScore(int level_nr)
4742 {
4743   int k, l;
4744   int position = -1;
4745   boolean one_score_entry_per_name = !program.many_scores_per_name;
4746
4747   LoadScore(level_nr);
4748
4749   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4750       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4751     return -1;
4752
4753   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4754   {
4755     if (local_player->score_final > highscore[k].Score)
4756     {
4757       /* player has made it to the hall of fame */
4758
4759       if (k < MAX_SCORE_ENTRIES - 1)
4760       {
4761         int m = MAX_SCORE_ENTRIES - 1;
4762
4763         if (one_score_entry_per_name)
4764         {
4765           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4766             if (strEqual(setup.player_name, highscore[l].Name))
4767               m = l;
4768
4769           if (m == k)   /* player's new highscore overwrites his old one */
4770             goto put_into_list;
4771         }
4772
4773         for (l = m; l > k; l--)
4774         {
4775           strcpy(highscore[l].Name, highscore[l - 1].Name);
4776           highscore[l].Score = highscore[l - 1].Score;
4777         }
4778       }
4779
4780       put_into_list:
4781
4782       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4783       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4784       highscore[k].Score = local_player->score_final; 
4785       position = k;
4786
4787       break;
4788     }
4789     else if (one_score_entry_per_name &&
4790              !strncmp(setup.player_name, highscore[k].Name,
4791                       MAX_PLAYER_NAME_LEN))
4792       break;    /* player already there with a higher score */
4793   }
4794
4795   if (position >= 0) 
4796     SaveScore(level_nr);
4797
4798   return position;
4799 }
4800
4801 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4802 {
4803   int element = Feld[x][y];
4804   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4805   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4806   int horiz_move = (dx != 0);
4807   int sign = (horiz_move ? dx : dy);
4808   int step = sign * element_info[element].move_stepsize;
4809
4810   /* special values for move stepsize for spring and things on conveyor belt */
4811   if (horiz_move)
4812   {
4813     if (CAN_FALL(element) &&
4814         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4815       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4816     else if (element == EL_SPRING)
4817       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4818   }
4819
4820   return step;
4821 }
4822
4823 inline static int getElementMoveStepsize(int x, int y)
4824 {
4825   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4826 }
4827
4828 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4829 {
4830   if (player->GfxAction != action || player->GfxDir != dir)
4831   {
4832     player->GfxAction = action;
4833     player->GfxDir = dir;
4834     player->Frame = 0;
4835     player->StepFrame = 0;
4836   }
4837 }
4838
4839 static void ResetGfxFrame(int x, int y)
4840 {
4841   // profiling showed that "autotest" spends 10~20% of its time in this function
4842   if (DrawingDeactivatedField())
4843     return;
4844
4845   int element = Feld[x][y];
4846   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4847
4848   if (graphic_info[graphic].anim_global_sync)
4849     GfxFrame[x][y] = FrameCounter;
4850   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4851     GfxFrame[x][y] = CustomValue[x][y];
4852   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4853     GfxFrame[x][y] = element_info[element].collect_score;
4854   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4855     GfxFrame[x][y] = ChangeDelay[x][y];
4856 }
4857
4858 static void ResetGfxAnimation(int x, int y)
4859 {
4860   GfxAction[x][y] = ACTION_DEFAULT;
4861   GfxDir[x][y] = MovDir[x][y];
4862   GfxFrame[x][y] = 0;
4863
4864   ResetGfxFrame(x, y);
4865 }
4866
4867 static void ResetRandomAnimationValue(int x, int y)
4868 {
4869   GfxRandom[x][y] = INIT_GFX_RANDOM();
4870 }
4871
4872 void InitMovingField(int x, int y, int direction)
4873 {
4874   int element = Feld[x][y];
4875   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4876   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4877   int newx = x + dx;
4878   int newy = y + dy;
4879   boolean is_moving_before, is_moving_after;
4880
4881   /* check if element was/is moving or being moved before/after mode change */
4882   is_moving_before = (WasJustMoving[x][y] != 0);
4883   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4884
4885   /* reset animation only for moving elements which change direction of moving
4886      or which just started or stopped moving
4887      (else CEs with property "can move" / "not moving" are reset each frame) */
4888   if (is_moving_before != is_moving_after ||
4889       direction != MovDir[x][y])
4890     ResetGfxAnimation(x, y);
4891
4892   MovDir[x][y] = direction;
4893   GfxDir[x][y] = direction;
4894
4895   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4896                      direction == MV_DOWN && CAN_FALL(element) ?
4897                      ACTION_FALLING : ACTION_MOVING);
4898
4899   /* this is needed for CEs with property "can move" / "not moving" */
4900
4901   if (is_moving_after)
4902   {
4903     if (Feld[newx][newy] == EL_EMPTY)
4904       Feld[newx][newy] = EL_BLOCKED;
4905
4906     MovDir[newx][newy] = MovDir[x][y];
4907
4908     CustomValue[newx][newy] = CustomValue[x][y];
4909
4910     GfxFrame[newx][newy] = GfxFrame[x][y];
4911     GfxRandom[newx][newy] = GfxRandom[x][y];
4912     GfxAction[newx][newy] = GfxAction[x][y];
4913     GfxDir[newx][newy] = GfxDir[x][y];
4914   }
4915 }
4916
4917 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4918 {
4919   int direction = MovDir[x][y];
4920   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4921   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4922
4923   *goes_to_x = newx;
4924   *goes_to_y = newy;
4925 }
4926
4927 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4928 {
4929   int oldx = x, oldy = y;
4930   int direction = MovDir[x][y];
4931
4932   if (direction == MV_LEFT)
4933     oldx++;
4934   else if (direction == MV_RIGHT)
4935     oldx--;
4936   else if (direction == MV_UP)
4937     oldy++;
4938   else if (direction == MV_DOWN)
4939     oldy--;
4940
4941   *comes_from_x = oldx;
4942   *comes_from_y = oldy;
4943 }
4944
4945 int MovingOrBlocked2Element(int x, int y)
4946 {
4947   int element = Feld[x][y];
4948
4949   if (element == EL_BLOCKED)
4950   {
4951     int oldx, oldy;
4952
4953     Blocked2Moving(x, y, &oldx, &oldy);
4954     return Feld[oldx][oldy];
4955   }
4956   else
4957     return element;
4958 }
4959
4960 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4961 {
4962   /* like MovingOrBlocked2Element(), but if element is moving
4963      and (x,y) is the field the moving element is just leaving,
4964      return EL_BLOCKED instead of the element value */
4965   int element = Feld[x][y];
4966
4967   if (IS_MOVING(x, y))
4968   {
4969     if (element == EL_BLOCKED)
4970     {
4971       int oldx, oldy;
4972
4973       Blocked2Moving(x, y, &oldx, &oldy);
4974       return Feld[oldx][oldy];
4975     }
4976     else
4977       return EL_BLOCKED;
4978   }
4979   else
4980     return element;
4981 }
4982
4983 static void RemoveField(int x, int y)
4984 {
4985   Feld[x][y] = EL_EMPTY;
4986
4987   MovPos[x][y] = 0;
4988   MovDir[x][y] = 0;
4989   MovDelay[x][y] = 0;
4990
4991   CustomValue[x][y] = 0;
4992
4993   AmoebaNr[x][y] = 0;
4994   ChangeDelay[x][y] = 0;
4995   ChangePage[x][y] = -1;
4996   Pushed[x][y] = FALSE;
4997
4998   GfxElement[x][y] = EL_UNDEFINED;
4999   GfxAction[x][y] = ACTION_DEFAULT;
5000   GfxDir[x][y] = MV_NONE;
5001 }
5002
5003 void RemoveMovingField(int x, int y)
5004 {
5005   int oldx = x, oldy = y, newx = x, newy = y;
5006   int element = Feld[x][y];
5007   int next_element = EL_UNDEFINED;
5008
5009   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5010     return;
5011
5012   if (IS_MOVING(x, y))
5013   {
5014     Moving2Blocked(x, y, &newx, &newy);
5015
5016     if (Feld[newx][newy] != EL_BLOCKED)
5017     {
5018       /* element is moving, but target field is not free (blocked), but
5019          already occupied by something different (example: acid pool);
5020          in this case, only remove the moving field, but not the target */
5021
5022       RemoveField(oldx, oldy);
5023
5024       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5025
5026       TEST_DrawLevelField(oldx, oldy);
5027
5028       return;
5029     }
5030   }
5031   else if (element == EL_BLOCKED)
5032   {
5033     Blocked2Moving(x, y, &oldx, &oldy);
5034     if (!IS_MOVING(oldx, oldy))
5035       return;
5036   }
5037
5038   if (element == EL_BLOCKED &&
5039       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5040        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5041        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5042        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5043        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5044        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5045     next_element = get_next_element(Feld[oldx][oldy]);
5046
5047   RemoveField(oldx, oldy);
5048   RemoveField(newx, newy);
5049
5050   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5051
5052   if (next_element != EL_UNDEFINED)
5053     Feld[oldx][oldy] = next_element;
5054
5055   TEST_DrawLevelField(oldx, oldy);
5056   TEST_DrawLevelField(newx, newy);
5057 }
5058
5059 void DrawDynamite(int x, int y)
5060 {
5061   int sx = SCREENX(x), sy = SCREENY(y);
5062   int graphic = el2img(Feld[x][y]);
5063   int frame;
5064
5065   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5066     return;
5067
5068   if (IS_WALKABLE_INSIDE(Back[x][y]))
5069     return;
5070
5071   if (Back[x][y])
5072     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5073   else if (Store[x][y])
5074     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5075
5076   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5077
5078   if (Back[x][y] || Store[x][y])
5079     DrawGraphicThruMask(sx, sy, graphic, frame);
5080   else
5081     DrawGraphic(sx, sy, graphic, frame);
5082 }
5083
5084 void CheckDynamite(int x, int y)
5085 {
5086   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5087   {
5088     MovDelay[x][y]--;
5089
5090     if (MovDelay[x][y] != 0)
5091     {
5092       DrawDynamite(x, y);
5093       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5094
5095       return;
5096     }
5097   }
5098
5099   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5100
5101   Bang(x, y);
5102 }
5103
5104 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5105 {
5106   boolean num_checked_players = 0;
5107   int i;
5108
5109   for (i = 0; i < MAX_PLAYERS; i++)
5110   {
5111     if (stored_player[i].active)
5112     {
5113       int sx = stored_player[i].jx;
5114       int sy = stored_player[i].jy;
5115
5116       if (num_checked_players == 0)
5117       {
5118         *sx1 = *sx2 = sx;
5119         *sy1 = *sy2 = sy;
5120       }
5121       else
5122       {
5123         *sx1 = MIN(*sx1, sx);
5124         *sy1 = MIN(*sy1, sy);
5125         *sx2 = MAX(*sx2, sx);
5126         *sy2 = MAX(*sy2, sy);
5127       }
5128
5129       num_checked_players++;
5130     }
5131   }
5132 }
5133
5134 static boolean checkIfAllPlayersFitToScreen_RND()
5135 {
5136   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5137
5138   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5139
5140   return (sx2 - sx1 < SCR_FIELDX &&
5141           sy2 - sy1 < SCR_FIELDY);
5142 }
5143
5144 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5145 {
5146   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5147
5148   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5149
5150   *sx = (sx1 + sx2) / 2;
5151   *sy = (sy1 + sy2) / 2;
5152 }
5153
5154 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5155                         boolean center_screen, boolean quick_relocation)
5156 {
5157   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5158   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5159   boolean no_delay = (tape.warp_forward);
5160   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5161   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5162   int new_scroll_x, new_scroll_y;
5163
5164   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5165   {
5166     /* case 1: quick relocation inside visible screen (without scrolling) */
5167
5168     RedrawPlayfield();
5169
5170     return;
5171   }
5172
5173   if (!level.shifted_relocation || center_screen)
5174   {
5175     /* relocation _with_ centering of screen */
5176
5177     new_scroll_x = SCROLL_POSITION_X(x);
5178     new_scroll_y = SCROLL_POSITION_Y(y);
5179   }
5180   else
5181   {
5182     /* relocation _without_ centering of screen */
5183
5184     int center_scroll_x = SCROLL_POSITION_X(old_x);
5185     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5186     int offset_x = x + (scroll_x - center_scroll_x);
5187     int offset_y = y + (scroll_y - center_scroll_y);
5188
5189     /* for new screen position, apply previous offset to center position */
5190     new_scroll_x = SCROLL_POSITION_X(offset_x);
5191     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5192   }
5193
5194   if (quick_relocation)
5195   {
5196     /* case 2: quick relocation (redraw without visible scrolling) */
5197
5198     scroll_x = new_scroll_x;
5199     scroll_y = new_scroll_y;
5200
5201     RedrawPlayfield();
5202
5203     return;
5204   }
5205
5206   /* case 3: visible relocation (with scrolling to new position) */
5207
5208   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5209
5210   SetVideoFrameDelay(wait_delay_value);
5211
5212   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5213   {
5214     int dx = 0, dy = 0;
5215     int fx = FX, fy = FY;
5216
5217     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5218     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5219
5220     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5221       break;
5222
5223     scroll_x -= dx;
5224     scroll_y -= dy;
5225
5226     fx += dx * TILEX / 2;
5227     fy += dy * TILEY / 2;
5228
5229     ScrollLevel(dx, dy);
5230     DrawAllPlayers();
5231
5232     /* scroll in two steps of half tile size to make things smoother */
5233     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5234
5235     /* scroll second step to align at full tile size */
5236     BlitScreenToBitmap(window);
5237   }
5238
5239   DrawAllPlayers();
5240   BackToFront();
5241
5242   SetVideoFrameDelay(frame_delay_value_old);
5243 }
5244
5245 void RelocatePlayer(int jx, int jy, int el_player_raw)
5246 {
5247   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5248   int player_nr = GET_PLAYER_NR(el_player);
5249   struct PlayerInfo *player = &stored_player[player_nr];
5250   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5251   boolean no_delay = (tape.warp_forward);
5252   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5253   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5254   int old_jx = player->jx;
5255   int old_jy = player->jy;
5256   int old_element = Feld[old_jx][old_jy];
5257   int element = Feld[jx][jy];
5258   boolean player_relocated = (old_jx != jx || old_jy != jy);
5259
5260   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5261   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5262   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5263   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5264   int leave_side_horiz = move_dir_horiz;
5265   int leave_side_vert  = move_dir_vert;
5266   int enter_side = enter_side_horiz | enter_side_vert;
5267   int leave_side = leave_side_horiz | leave_side_vert;
5268
5269   if (player->GameOver)         /* do not reanimate dead player */
5270     return;
5271
5272   if (!player_relocated)        /* no need to relocate the player */
5273     return;
5274
5275   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5276   {
5277     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5278     DrawLevelField(jx, jy);
5279   }
5280
5281   if (player->present)
5282   {
5283     while (player->MovPos)
5284     {
5285       ScrollPlayer(player, SCROLL_GO_ON);
5286       ScrollScreen(NULL, SCROLL_GO_ON);
5287
5288       AdvanceFrameAndPlayerCounters(player->index_nr);
5289
5290       DrawPlayer(player);
5291
5292       BackToFront_WithFrameDelay(wait_delay_value);
5293     }
5294
5295     DrawPlayer(player);         /* needed here only to cleanup last field */
5296     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5297
5298     player->is_moving = FALSE;
5299   }
5300
5301   if (IS_CUSTOM_ELEMENT(old_element))
5302     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5303                                CE_LEFT_BY_PLAYER,
5304                                player->index_bit, leave_side);
5305
5306   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5307                                       CE_PLAYER_LEAVES_X,
5308                                       player->index_bit, leave_side);
5309
5310   Feld[jx][jy] = el_player;
5311   InitPlayerField(jx, jy, el_player, TRUE);
5312
5313   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5314      possible that the relocation target field did not contain a player element,
5315      but a walkable element, to which the new player was relocated -- in this
5316      case, restore that (already initialized!) element on the player field */
5317   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5318   {
5319     Feld[jx][jy] = element;     /* restore previously existing element */
5320   }
5321
5322   /* only visually relocate centered player */
5323   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5324                      FALSE, level.instant_relocation);
5325
5326   TestIfPlayerTouchesBadThing(jx, jy);
5327   TestIfPlayerTouchesCustomElement(jx, jy);
5328
5329   if (IS_CUSTOM_ELEMENT(element))
5330     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5331                                player->index_bit, enter_side);
5332
5333   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5334                                       player->index_bit, enter_side);
5335
5336   if (player->is_switching)
5337   {
5338     /* ensure that relocation while still switching an element does not cause
5339        a new element to be treated as also switched directly after relocation
5340        (this is important for teleporter switches that teleport the player to
5341        a place where another teleporter switch is in the same direction, which
5342        would then incorrectly be treated as immediately switched before the
5343        direction key that caused the switch was released) */
5344
5345     player->switch_x += jx - old_jx;
5346     player->switch_y += jy - old_jy;
5347   }
5348 }
5349
5350 void Explode(int ex, int ey, int phase, int mode)
5351 {
5352   int x, y;
5353   int last_phase;
5354   int border_element;
5355
5356   /* !!! eliminate this variable !!! */
5357   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5358
5359   if (game.explosions_delayed)
5360   {
5361     ExplodeField[ex][ey] = mode;
5362     return;
5363   }
5364
5365   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5366   {
5367     int center_element = Feld[ex][ey];
5368     int artwork_element, explosion_element;     /* set these values later */
5369
5370     /* remove things displayed in background while burning dynamite */
5371     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5372       Back[ex][ey] = 0;
5373
5374     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5375     {
5376       /* put moving element to center field (and let it explode there) */
5377       center_element = MovingOrBlocked2Element(ex, ey);
5378       RemoveMovingField(ex, ey);
5379       Feld[ex][ey] = center_element;
5380     }
5381
5382     /* now "center_element" is finally determined -- set related values now */
5383     artwork_element = center_element;           /* for custom player artwork */
5384     explosion_element = center_element;         /* for custom player artwork */
5385
5386     if (IS_PLAYER(ex, ey))
5387     {
5388       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5389
5390       artwork_element = stored_player[player_nr].artwork_element;
5391
5392       if (level.use_explosion_element[player_nr])
5393       {
5394         explosion_element = level.explosion_element[player_nr];
5395         artwork_element = explosion_element;
5396       }
5397     }
5398
5399     if (mode == EX_TYPE_NORMAL ||
5400         mode == EX_TYPE_CENTER ||
5401         mode == EX_TYPE_CROSS)
5402       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5403
5404     last_phase = element_info[explosion_element].explosion_delay + 1;
5405
5406     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5407     {
5408       int xx = x - ex + 1;
5409       int yy = y - ey + 1;
5410       int element;
5411
5412       if (!IN_LEV_FIELD(x, y) ||
5413           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5414           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5415         continue;
5416
5417       element = Feld[x][y];
5418
5419       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5420       {
5421         element = MovingOrBlocked2Element(x, y);
5422
5423         if (!IS_EXPLOSION_PROOF(element))
5424           RemoveMovingField(x, y);
5425       }
5426
5427       /* indestructible elements can only explode in center (but not flames) */
5428       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5429                                            mode == EX_TYPE_BORDER)) ||
5430           element == EL_FLAMES)
5431         continue;
5432
5433       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5434          behaviour, for example when touching a yamyam that explodes to rocks
5435          with active deadly shield, a rock is created under the player !!! */
5436       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5437 #if 0
5438       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5439           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5440            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5441 #else
5442       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5443 #endif
5444       {
5445         if (IS_ACTIVE_BOMB(element))
5446         {
5447           /* re-activate things under the bomb like gate or penguin */
5448           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5449           Back[x][y] = 0;
5450         }
5451
5452         continue;
5453       }
5454
5455       /* save walkable background elements while explosion on same tile */
5456       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5457           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5458         Back[x][y] = element;
5459
5460       /* ignite explodable elements reached by other explosion */
5461       if (element == EL_EXPLOSION)
5462         element = Store2[x][y];
5463
5464       if (AmoebaNr[x][y] &&
5465           (element == EL_AMOEBA_FULL ||
5466            element == EL_BD_AMOEBA ||
5467            element == EL_AMOEBA_GROWING))
5468       {
5469         AmoebaCnt[AmoebaNr[x][y]]--;
5470         AmoebaCnt2[AmoebaNr[x][y]]--;
5471       }
5472
5473       RemoveField(x, y);
5474
5475       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5476       {
5477         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5478
5479         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5480
5481         if (PLAYERINFO(ex, ey)->use_murphy)
5482           Store[x][y] = EL_EMPTY;
5483       }
5484
5485       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5486          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5487       else if (ELEM_IS_PLAYER(center_element))
5488         Store[x][y] = EL_EMPTY;
5489       else if (center_element == EL_YAMYAM)
5490         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5491       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5492         Store[x][y] = element_info[center_element].content.e[xx][yy];
5493 #if 1
5494       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5495          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5496          otherwise) -- FIX THIS !!! */
5497       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5498         Store[x][y] = element_info[element].content.e[1][1];
5499 #else
5500       else if (!CAN_EXPLODE(element))
5501         Store[x][y] = element_info[element].content.e[1][1];
5502 #endif
5503       else
5504         Store[x][y] = EL_EMPTY;
5505
5506       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5507           center_element == EL_AMOEBA_TO_DIAMOND)
5508         Store2[x][y] = element;
5509
5510       Feld[x][y] = EL_EXPLOSION;
5511       GfxElement[x][y] = artwork_element;
5512
5513       ExplodePhase[x][y] = 1;
5514       ExplodeDelay[x][y] = last_phase;
5515
5516       Stop[x][y] = TRUE;
5517     }
5518
5519     if (center_element == EL_YAMYAM)
5520       game.yamyam_content_nr =
5521         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5522
5523     return;
5524   }
5525
5526   if (Stop[ex][ey])
5527     return;
5528
5529   x = ex;
5530   y = ey;
5531
5532   if (phase == 1)
5533     GfxFrame[x][y] = 0;         /* restart explosion animation */
5534
5535   last_phase = ExplodeDelay[x][y];
5536
5537   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5538
5539   /* this can happen if the player leaves an explosion just in time */
5540   if (GfxElement[x][y] == EL_UNDEFINED)
5541     GfxElement[x][y] = EL_EMPTY;
5542
5543   border_element = Store2[x][y];
5544   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5545     border_element = StorePlayer[x][y];
5546
5547   if (phase == element_info[border_element].ignition_delay ||
5548       phase == last_phase)
5549   {
5550     boolean border_explosion = FALSE;
5551
5552     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5553         !PLAYER_EXPLOSION_PROTECTED(x, y))
5554     {
5555       KillPlayerUnlessExplosionProtected(x, y);
5556       border_explosion = TRUE;
5557     }
5558     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5559     {
5560       Feld[x][y] = Store2[x][y];
5561       Store2[x][y] = 0;
5562       Bang(x, y);
5563       border_explosion = TRUE;
5564     }
5565     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5566     {
5567       AmoebeUmwandeln(x, y);
5568       Store2[x][y] = 0;
5569       border_explosion = TRUE;
5570     }
5571
5572     /* if an element just explodes due to another explosion (chain-reaction),
5573        do not immediately end the new explosion when it was the last frame of
5574        the explosion (as it would be done in the following "if"-statement!) */
5575     if (border_explosion && phase == last_phase)
5576       return;
5577   }
5578
5579   if (phase == last_phase)
5580   {
5581     int element;
5582
5583     element = Feld[x][y] = Store[x][y];
5584     Store[x][y] = Store2[x][y] = 0;
5585     GfxElement[x][y] = EL_UNDEFINED;
5586
5587     /* player can escape from explosions and might therefore be still alive */
5588     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5589         element <= EL_PLAYER_IS_EXPLODING_4)
5590     {
5591       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5592       int explosion_element = EL_PLAYER_1 + player_nr;
5593       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5594       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5595
5596       if (level.use_explosion_element[player_nr])
5597         explosion_element = level.explosion_element[player_nr];
5598
5599       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5600                     element_info[explosion_element].content.e[xx][yy]);
5601     }
5602
5603     /* restore probably existing indestructible background element */
5604     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5605       element = Feld[x][y] = Back[x][y];
5606     Back[x][y] = 0;
5607
5608     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5609     GfxDir[x][y] = MV_NONE;
5610     ChangeDelay[x][y] = 0;
5611     ChangePage[x][y] = -1;
5612
5613     CustomValue[x][y] = 0;
5614
5615     InitField_WithBug2(x, y, FALSE);
5616
5617     TEST_DrawLevelField(x, y);
5618
5619     TestIfElementTouchesCustomElement(x, y);
5620
5621     if (GFX_CRUMBLED(element))
5622       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5623
5624     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5625       StorePlayer[x][y] = 0;
5626
5627     if (ELEM_IS_PLAYER(element))
5628       RelocatePlayer(x, y, element);
5629   }
5630   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5631   {
5632     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5633     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5634
5635     if (phase == delay)
5636       TEST_DrawLevelFieldCrumbled(x, y);
5637
5638     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5639     {
5640       DrawLevelElement(x, y, Back[x][y]);
5641       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5642     }
5643     else if (IS_WALKABLE_UNDER(Back[x][y]))
5644     {
5645       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5646       DrawLevelElementThruMask(x, y, Back[x][y]);
5647     }
5648     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5649       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5650   }
5651 }
5652
5653 void DynaExplode(int ex, int ey)
5654 {
5655   int i, j;
5656   int dynabomb_element = Feld[ex][ey];
5657   int dynabomb_size = 1;
5658   boolean dynabomb_xl = FALSE;
5659   struct PlayerInfo *player;
5660   static int xy[4][2] =
5661   {
5662     { 0, -1 },
5663     { -1, 0 },
5664     { +1, 0 },
5665     { 0, +1 }
5666   };
5667
5668   if (IS_ACTIVE_BOMB(dynabomb_element))
5669   {
5670     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5671     dynabomb_size = player->dynabomb_size;
5672     dynabomb_xl = player->dynabomb_xl;
5673     player->dynabombs_left++;
5674   }
5675
5676   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5677
5678   for (i = 0; i < NUM_DIRECTIONS; i++)
5679   {
5680     for (j = 1; j <= dynabomb_size; j++)
5681     {
5682       int x = ex + j * xy[i][0];
5683       int y = ey + j * xy[i][1];
5684       int element;
5685
5686       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5687         break;
5688
5689       element = Feld[x][y];
5690
5691       /* do not restart explosions of fields with active bombs */
5692       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5693         continue;
5694
5695       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5696
5697       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5698           !IS_DIGGABLE(element) && !dynabomb_xl)
5699         break;
5700     }
5701   }
5702 }
5703
5704 void Bang(int x, int y)
5705 {
5706   int element = MovingOrBlocked2Element(x, y);
5707   int explosion_type = EX_TYPE_NORMAL;
5708
5709   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5710   {
5711     struct PlayerInfo *player = PLAYERINFO(x, y);
5712
5713     element = Feld[x][y] = player->initial_element;
5714
5715     if (level.use_explosion_element[player->index_nr])
5716     {
5717       int explosion_element = level.explosion_element[player->index_nr];
5718
5719       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5720         explosion_type = EX_TYPE_CROSS;
5721       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5722         explosion_type = EX_TYPE_CENTER;
5723     }
5724   }
5725
5726   switch (element)
5727   {
5728     case EL_BUG:
5729     case EL_SPACESHIP:
5730     case EL_BD_BUTTERFLY:
5731     case EL_BD_FIREFLY:
5732     case EL_YAMYAM:
5733     case EL_DARK_YAMYAM:
5734     case EL_ROBOT:
5735     case EL_PACMAN:
5736     case EL_MOLE:
5737       RaiseScoreElement(element);
5738       break;
5739
5740     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5741     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5742     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5743     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5744     case EL_DYNABOMB_INCREASE_NUMBER:
5745     case EL_DYNABOMB_INCREASE_SIZE:
5746     case EL_DYNABOMB_INCREASE_POWER:
5747       explosion_type = EX_TYPE_DYNA;
5748       break;
5749
5750     case EL_DC_LANDMINE:
5751       explosion_type = EX_TYPE_CENTER;
5752       break;
5753
5754     case EL_PENGUIN:
5755     case EL_LAMP:
5756     case EL_LAMP_ACTIVE:
5757     case EL_AMOEBA_TO_DIAMOND:
5758       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5759         explosion_type = EX_TYPE_CENTER;
5760       break;
5761
5762     default:
5763       if (element_info[element].explosion_type == EXPLODES_CROSS)
5764         explosion_type = EX_TYPE_CROSS;
5765       else if (element_info[element].explosion_type == EXPLODES_1X1)
5766         explosion_type = EX_TYPE_CENTER;
5767       break;
5768   }
5769
5770   if (explosion_type == EX_TYPE_DYNA)
5771     DynaExplode(x, y);
5772   else
5773     Explode(x, y, EX_PHASE_START, explosion_type);
5774
5775   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5776 }
5777
5778 void SplashAcid(int x, int y)
5779 {
5780   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5781       (!IN_LEV_FIELD(x - 1, y - 2) ||
5782        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5783     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5784
5785   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5786       (!IN_LEV_FIELD(x + 1, y - 2) ||
5787        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5788     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5789
5790   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5791 }
5792
5793 static void InitBeltMovement()
5794 {
5795   static int belt_base_element[4] =
5796   {
5797     EL_CONVEYOR_BELT_1_LEFT,
5798     EL_CONVEYOR_BELT_2_LEFT,
5799     EL_CONVEYOR_BELT_3_LEFT,
5800     EL_CONVEYOR_BELT_4_LEFT
5801   };
5802   static int belt_base_active_element[4] =
5803   {
5804     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5805     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5806     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5807     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5808   };
5809
5810   int x, y, i, j;
5811
5812   /* set frame order for belt animation graphic according to belt direction */
5813   for (i = 0; i < NUM_BELTS; i++)
5814   {
5815     int belt_nr = i;
5816
5817     for (j = 0; j < NUM_BELT_PARTS; j++)
5818     {
5819       int element = belt_base_active_element[belt_nr] + j;
5820       int graphic_1 = el2img(element);
5821       int graphic_2 = el2panelimg(element);
5822
5823       if (game.belt_dir[i] == MV_LEFT)
5824       {
5825         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5826         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5827       }
5828       else
5829       {
5830         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5831         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5832       }
5833     }
5834   }
5835
5836   SCAN_PLAYFIELD(x, y)
5837   {
5838     int element = Feld[x][y];
5839
5840     for (i = 0; i < NUM_BELTS; i++)
5841     {
5842       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5843       {
5844         int e_belt_nr = getBeltNrFromBeltElement(element);
5845         int belt_nr = i;
5846
5847         if (e_belt_nr == belt_nr)
5848         {
5849           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5850
5851           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5852         }
5853       }
5854     }
5855   }
5856 }
5857
5858 static void ToggleBeltSwitch(int x, int y)
5859 {
5860   static int belt_base_element[4] =
5861   {
5862     EL_CONVEYOR_BELT_1_LEFT,
5863     EL_CONVEYOR_BELT_2_LEFT,
5864     EL_CONVEYOR_BELT_3_LEFT,
5865     EL_CONVEYOR_BELT_4_LEFT
5866   };
5867   static int belt_base_active_element[4] =
5868   {
5869     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5870     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5871     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5872     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5873   };
5874   static int belt_base_switch_element[4] =
5875   {
5876     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5877     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5878     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5879     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5880   };
5881   static int belt_move_dir[4] =
5882   {
5883     MV_LEFT,
5884     MV_NONE,
5885     MV_RIGHT,
5886     MV_NONE,
5887   };
5888
5889   int element = Feld[x][y];
5890   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5891   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5892   int belt_dir = belt_move_dir[belt_dir_nr];
5893   int xx, yy, i;
5894
5895   if (!IS_BELT_SWITCH(element))
5896     return;
5897
5898   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5899   game.belt_dir[belt_nr] = belt_dir;
5900
5901   if (belt_dir_nr == 3)
5902     belt_dir_nr = 1;
5903
5904   /* set frame order for belt animation graphic according to belt direction */
5905   for (i = 0; i < NUM_BELT_PARTS; i++)
5906   {
5907     int element = belt_base_active_element[belt_nr] + i;
5908     int graphic_1 = el2img(element);
5909     int graphic_2 = el2panelimg(element);
5910
5911     if (belt_dir == MV_LEFT)
5912     {
5913       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5914       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5915     }
5916     else
5917     {
5918       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5919       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5920     }
5921   }
5922
5923   SCAN_PLAYFIELD(xx, yy)
5924   {
5925     int element = Feld[xx][yy];
5926
5927     if (IS_BELT_SWITCH(element))
5928     {
5929       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5930
5931       if (e_belt_nr == belt_nr)
5932       {
5933         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5934         TEST_DrawLevelField(xx, yy);
5935       }
5936     }
5937     else if (IS_BELT(element) && belt_dir != MV_NONE)
5938     {
5939       int e_belt_nr = getBeltNrFromBeltElement(element);
5940
5941       if (e_belt_nr == belt_nr)
5942       {
5943         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5944
5945         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5946         TEST_DrawLevelField(xx, yy);
5947       }
5948     }
5949     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5950     {
5951       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5952
5953       if (e_belt_nr == belt_nr)
5954       {
5955         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5956
5957         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5958         TEST_DrawLevelField(xx, yy);
5959       }
5960     }
5961   }
5962 }
5963
5964 static void ToggleSwitchgateSwitch(int x, int y)
5965 {
5966   int xx, yy;
5967
5968   game.switchgate_pos = !game.switchgate_pos;
5969
5970   SCAN_PLAYFIELD(xx, yy)
5971   {
5972     int element = Feld[xx][yy];
5973
5974     if (element == EL_SWITCHGATE_SWITCH_UP)
5975     {
5976       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5977       TEST_DrawLevelField(xx, yy);
5978     }
5979     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5980     {
5981       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5982       TEST_DrawLevelField(xx, yy);
5983     }
5984     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5985     {
5986       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5987       TEST_DrawLevelField(xx, yy);
5988     }
5989     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5990     {
5991       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5992       TEST_DrawLevelField(xx, yy);
5993     }
5994     else if (element == EL_SWITCHGATE_OPEN ||
5995              element == EL_SWITCHGATE_OPENING)
5996     {
5997       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5998
5999       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6000     }
6001     else if (element == EL_SWITCHGATE_CLOSED ||
6002              element == EL_SWITCHGATE_CLOSING)
6003     {
6004       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6005
6006       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6007     }
6008   }
6009 }
6010
6011 static int getInvisibleActiveFromInvisibleElement(int element)
6012 {
6013   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6014           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6015           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6016           element);
6017 }
6018
6019 static int getInvisibleFromInvisibleActiveElement(int element)
6020 {
6021   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6022           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6023           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6024           element);
6025 }
6026
6027 static void RedrawAllLightSwitchesAndInvisibleElements()
6028 {
6029   int x, y;
6030
6031   SCAN_PLAYFIELD(x, y)
6032   {
6033     int element = Feld[x][y];
6034
6035     if (element == EL_LIGHT_SWITCH &&
6036         game.light_time_left > 0)
6037     {
6038       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6039       TEST_DrawLevelField(x, y);
6040     }
6041     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6042              game.light_time_left == 0)
6043     {
6044       Feld[x][y] = EL_LIGHT_SWITCH;
6045       TEST_DrawLevelField(x, y);
6046     }
6047     else if (element == EL_EMC_DRIPPER &&
6048              game.light_time_left > 0)
6049     {
6050       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6051       TEST_DrawLevelField(x, y);
6052     }
6053     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6054              game.light_time_left == 0)
6055     {
6056       Feld[x][y] = EL_EMC_DRIPPER;
6057       TEST_DrawLevelField(x, y);
6058     }
6059     else if (element == EL_INVISIBLE_STEELWALL ||
6060              element == EL_INVISIBLE_WALL ||
6061              element == EL_INVISIBLE_SAND)
6062     {
6063       if (game.light_time_left > 0)
6064         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6065
6066       TEST_DrawLevelField(x, y);
6067
6068       /* uncrumble neighbour fields, if needed */
6069       if (element == EL_INVISIBLE_SAND)
6070         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6071     }
6072     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6073              element == EL_INVISIBLE_WALL_ACTIVE ||
6074              element == EL_INVISIBLE_SAND_ACTIVE)
6075     {
6076       if (game.light_time_left == 0)
6077         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6078
6079       TEST_DrawLevelField(x, y);
6080
6081       /* re-crumble neighbour fields, if needed */
6082       if (element == EL_INVISIBLE_SAND)
6083         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6084     }
6085   }
6086 }
6087
6088 static void RedrawAllInvisibleElementsForLenses()
6089 {
6090   int x, y;
6091
6092   SCAN_PLAYFIELD(x, y)
6093   {
6094     int element = Feld[x][y];
6095
6096     if (element == EL_EMC_DRIPPER &&
6097         game.lenses_time_left > 0)
6098     {
6099       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6100       TEST_DrawLevelField(x, y);
6101     }
6102     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6103              game.lenses_time_left == 0)
6104     {
6105       Feld[x][y] = EL_EMC_DRIPPER;
6106       TEST_DrawLevelField(x, y);
6107     }
6108     else if (element == EL_INVISIBLE_STEELWALL ||
6109              element == EL_INVISIBLE_WALL ||
6110              element == EL_INVISIBLE_SAND)
6111     {
6112       if (game.lenses_time_left > 0)
6113         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6114
6115       TEST_DrawLevelField(x, y);
6116
6117       /* uncrumble neighbour fields, if needed */
6118       if (element == EL_INVISIBLE_SAND)
6119         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6120     }
6121     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6122              element == EL_INVISIBLE_WALL_ACTIVE ||
6123              element == EL_INVISIBLE_SAND_ACTIVE)
6124     {
6125       if (game.lenses_time_left == 0)
6126         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6127
6128       TEST_DrawLevelField(x, y);
6129
6130       /* re-crumble neighbour fields, if needed */
6131       if (element == EL_INVISIBLE_SAND)
6132         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6133     }
6134   }
6135 }
6136
6137 static void RedrawAllInvisibleElementsForMagnifier()
6138 {
6139   int x, y;
6140
6141   SCAN_PLAYFIELD(x, y)
6142   {
6143     int element = Feld[x][y];
6144
6145     if (element == EL_EMC_FAKE_GRASS &&
6146         game.magnify_time_left > 0)
6147     {
6148       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6149       TEST_DrawLevelField(x, y);
6150     }
6151     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6152              game.magnify_time_left == 0)
6153     {
6154       Feld[x][y] = EL_EMC_FAKE_GRASS;
6155       TEST_DrawLevelField(x, y);
6156     }
6157     else if (IS_GATE_GRAY(element) &&
6158              game.magnify_time_left > 0)
6159     {
6160       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6161                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6162                     IS_EM_GATE_GRAY(element) ?
6163                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6164                     IS_EMC_GATE_GRAY(element) ?
6165                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6166                     IS_DC_GATE_GRAY(element) ?
6167                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6168                     element);
6169       TEST_DrawLevelField(x, y);
6170     }
6171     else if (IS_GATE_GRAY_ACTIVE(element) &&
6172              game.magnify_time_left == 0)
6173     {
6174       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6175                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6176                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6177                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6178                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6179                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6180                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6181                     EL_DC_GATE_WHITE_GRAY :
6182                     element);
6183       TEST_DrawLevelField(x, y);
6184     }
6185   }
6186 }
6187
6188 static void ToggleLightSwitch(int x, int y)
6189 {
6190   int element = Feld[x][y];
6191
6192   game.light_time_left =
6193     (element == EL_LIGHT_SWITCH ?
6194      level.time_light * FRAMES_PER_SECOND : 0);
6195
6196   RedrawAllLightSwitchesAndInvisibleElements();
6197 }
6198
6199 static void ActivateTimegateSwitch(int x, int y)
6200 {
6201   int xx, yy;
6202
6203   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6204
6205   SCAN_PLAYFIELD(xx, yy)
6206   {
6207     int element = Feld[xx][yy];
6208
6209     if (element == EL_TIMEGATE_CLOSED ||
6210         element == EL_TIMEGATE_CLOSING)
6211     {
6212       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6213       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6214     }
6215
6216     /*
6217     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6218     {
6219       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6220       TEST_DrawLevelField(xx, yy);
6221     }
6222     */
6223
6224   }
6225
6226   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6227                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6228 }
6229
6230 void Impact(int x, int y)
6231 {
6232   boolean last_line = (y == lev_fieldy - 1);
6233   boolean object_hit = FALSE;
6234   boolean impact = (last_line || object_hit);
6235   int element = Feld[x][y];
6236   int smashed = EL_STEELWALL;
6237
6238   if (!last_line)       /* check if element below was hit */
6239   {
6240     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6241       return;
6242
6243     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6244                                          MovDir[x][y + 1] != MV_DOWN ||
6245                                          MovPos[x][y + 1] <= TILEY / 2));
6246
6247     /* do not smash moving elements that left the smashed field in time */
6248     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6249         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6250       object_hit = FALSE;
6251
6252 #if USE_QUICKSAND_IMPACT_BUGFIX
6253     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6254     {
6255       RemoveMovingField(x, y + 1);
6256       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6257       Feld[x][y + 2] = EL_ROCK;
6258       TEST_DrawLevelField(x, y + 2);
6259
6260       object_hit = TRUE;
6261     }
6262
6263     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6264     {
6265       RemoveMovingField(x, y + 1);
6266       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6267       Feld[x][y + 2] = EL_ROCK;
6268       TEST_DrawLevelField(x, y + 2);
6269
6270       object_hit = TRUE;
6271     }
6272 #endif
6273
6274     if (object_hit)
6275       smashed = MovingOrBlocked2Element(x, y + 1);
6276
6277     impact = (last_line || object_hit);
6278   }
6279
6280   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6281   {
6282     SplashAcid(x, y + 1);
6283     return;
6284   }
6285
6286   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6287   /* only reset graphic animation if graphic really changes after impact */
6288   if (impact &&
6289       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6290   {
6291     ResetGfxAnimation(x, y);
6292     TEST_DrawLevelField(x, y);
6293   }
6294
6295   if (impact && CAN_EXPLODE_IMPACT(element))
6296   {
6297     Bang(x, y);
6298     return;
6299   }
6300   else if (impact && element == EL_PEARL &&
6301            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6302   {
6303     ResetGfxAnimation(x, y);
6304
6305     Feld[x][y] = EL_PEARL_BREAKING;
6306     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6307     return;
6308   }
6309   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6310   {
6311     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6312
6313     return;
6314   }
6315
6316   if (impact && element == EL_AMOEBA_DROP)
6317   {
6318     if (object_hit && IS_PLAYER(x, y + 1))
6319       KillPlayerUnlessEnemyProtected(x, y + 1);
6320     else if (object_hit && smashed == EL_PENGUIN)
6321       Bang(x, y + 1);
6322     else
6323     {
6324       Feld[x][y] = EL_AMOEBA_GROWING;
6325       Store[x][y] = EL_AMOEBA_WET;
6326
6327       ResetRandomAnimationValue(x, y);
6328     }
6329     return;
6330   }
6331
6332   if (object_hit)               /* check which object was hit */
6333   {
6334     if ((CAN_PASS_MAGIC_WALL(element) && 
6335          (smashed == EL_MAGIC_WALL ||
6336           smashed == EL_BD_MAGIC_WALL)) ||
6337         (CAN_PASS_DC_MAGIC_WALL(element) &&
6338          smashed == EL_DC_MAGIC_WALL))
6339     {
6340       int xx, yy;
6341       int activated_magic_wall =
6342         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6343          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6344          EL_DC_MAGIC_WALL_ACTIVE);
6345
6346       /* activate magic wall / mill */
6347       SCAN_PLAYFIELD(xx, yy)
6348       {
6349         if (Feld[xx][yy] == smashed)
6350           Feld[xx][yy] = activated_magic_wall;
6351       }
6352
6353       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6354       game.magic_wall_active = TRUE;
6355
6356       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6357                             SND_MAGIC_WALL_ACTIVATING :
6358                             smashed == EL_BD_MAGIC_WALL ?
6359                             SND_BD_MAGIC_WALL_ACTIVATING :
6360                             SND_DC_MAGIC_WALL_ACTIVATING));
6361     }
6362
6363     if (IS_PLAYER(x, y + 1))
6364     {
6365       if (CAN_SMASH_PLAYER(element))
6366       {
6367         KillPlayerUnlessEnemyProtected(x, y + 1);
6368         return;
6369       }
6370     }
6371     else if (smashed == EL_PENGUIN)
6372     {
6373       if (CAN_SMASH_PLAYER(element))
6374       {
6375         Bang(x, y + 1);
6376         return;
6377       }
6378     }
6379     else if (element == EL_BD_DIAMOND)
6380     {
6381       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6382       {
6383         Bang(x, y + 1);
6384         return;
6385       }
6386     }
6387     else if (((element == EL_SP_INFOTRON ||
6388                element == EL_SP_ZONK) &&
6389               (smashed == EL_SP_SNIKSNAK ||
6390                smashed == EL_SP_ELECTRON ||
6391                smashed == EL_SP_DISK_ORANGE)) ||
6392              (element == EL_SP_INFOTRON &&
6393               smashed == EL_SP_DISK_YELLOW))
6394     {
6395       Bang(x, y + 1);
6396       return;
6397     }
6398     else if (CAN_SMASH_EVERYTHING(element))
6399     {
6400       if (IS_CLASSIC_ENEMY(smashed) ||
6401           CAN_EXPLODE_SMASHED(smashed))
6402       {
6403         Bang(x, y + 1);
6404         return;
6405       }
6406       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6407       {
6408         if (smashed == EL_LAMP ||
6409             smashed == EL_LAMP_ACTIVE)
6410         {
6411           Bang(x, y + 1);
6412           return;
6413         }
6414         else if (smashed == EL_NUT)
6415         {
6416           Feld[x][y + 1] = EL_NUT_BREAKING;
6417           PlayLevelSound(x, y, SND_NUT_BREAKING);
6418           RaiseScoreElement(EL_NUT);
6419           return;
6420         }
6421         else if (smashed == EL_PEARL)
6422         {
6423           ResetGfxAnimation(x, y);
6424
6425           Feld[x][y + 1] = EL_PEARL_BREAKING;
6426           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6427           return;
6428         }
6429         else if (smashed == EL_DIAMOND)
6430         {
6431           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6432           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6433           return;
6434         }
6435         else if (IS_BELT_SWITCH(smashed))
6436         {
6437           ToggleBeltSwitch(x, y + 1);
6438         }
6439         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6440                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6441                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6442                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6443         {
6444           ToggleSwitchgateSwitch(x, y + 1);
6445         }
6446         else if (smashed == EL_LIGHT_SWITCH ||
6447                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6448         {
6449           ToggleLightSwitch(x, y + 1);
6450         }
6451         else
6452         {
6453           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6454
6455           CheckElementChangeBySide(x, y + 1, smashed, element,
6456                                    CE_SWITCHED, CH_SIDE_TOP);
6457           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6458                                             CH_SIDE_TOP);
6459         }
6460       }
6461       else
6462       {
6463         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6464       }
6465     }
6466   }
6467
6468   /* play sound of magic wall / mill */
6469   if (!last_line &&
6470       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6471        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6472        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6473   {
6474     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6475       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6476     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6477       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6478     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6479       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6480
6481     return;
6482   }
6483
6484   /* play sound of object that hits the ground */
6485   if (last_line || object_hit)
6486     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6487 }
6488
6489 inline static void TurnRoundExt(int x, int y)
6490 {
6491   static struct
6492   {
6493     int dx, dy;
6494   } move_xy[] =
6495   {
6496     {  0,  0 },
6497     { -1,  0 },
6498     { +1,  0 },
6499     {  0,  0 },
6500     {  0, -1 },
6501     {  0,  0 }, { 0, 0 }, { 0, 0 },
6502     {  0, +1 }
6503   };
6504   static struct
6505   {
6506     int left, right, back;
6507   } turn[] =
6508   {
6509     { 0,        0,              0        },
6510     { MV_DOWN,  MV_UP,          MV_RIGHT },
6511     { MV_UP,    MV_DOWN,        MV_LEFT  },
6512     { 0,        0,              0        },
6513     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6514     { 0,        0,              0        },
6515     { 0,        0,              0        },
6516     { 0,        0,              0        },
6517     { MV_RIGHT, MV_LEFT,        MV_UP    }
6518   };
6519
6520   int element = Feld[x][y];
6521   int move_pattern = element_info[element].move_pattern;
6522
6523   int old_move_dir = MovDir[x][y];
6524   int left_dir  = turn[old_move_dir].left;
6525   int right_dir = turn[old_move_dir].right;
6526   int back_dir  = turn[old_move_dir].back;
6527
6528   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6529   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6530   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6531   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6532
6533   int left_x  = x + left_dx,  left_y  = y + left_dy;
6534   int right_x = x + right_dx, right_y = y + right_dy;
6535   int move_x  = x + move_dx,  move_y  = y + move_dy;
6536
6537   int xx, yy;
6538
6539   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6540   {
6541     TestIfBadThingTouchesOtherBadThing(x, y);
6542
6543     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6544       MovDir[x][y] = right_dir;
6545     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6546       MovDir[x][y] = left_dir;
6547
6548     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6549       MovDelay[x][y] = 9;
6550     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6551       MovDelay[x][y] = 1;
6552   }
6553   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6554   {
6555     TestIfBadThingTouchesOtherBadThing(x, y);
6556
6557     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6558       MovDir[x][y] = left_dir;
6559     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6560       MovDir[x][y] = right_dir;
6561
6562     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6563       MovDelay[x][y] = 9;
6564     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6565       MovDelay[x][y] = 1;
6566   }
6567   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6568   {
6569     TestIfBadThingTouchesOtherBadThing(x, y);
6570
6571     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6572       MovDir[x][y] = left_dir;
6573     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6574       MovDir[x][y] = right_dir;
6575
6576     if (MovDir[x][y] != old_move_dir)
6577       MovDelay[x][y] = 9;
6578   }
6579   else if (element == EL_YAMYAM)
6580   {
6581     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6582     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6583
6584     if (can_turn_left && can_turn_right)
6585       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6586     else if (can_turn_left)
6587       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6588     else if (can_turn_right)
6589       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6590     else
6591       MovDir[x][y] = back_dir;
6592
6593     MovDelay[x][y] = 16 + 16 * RND(3);
6594   }
6595   else if (element == EL_DARK_YAMYAM)
6596   {
6597     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6598                                                          left_x, left_y);
6599     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6600                                                          right_x, right_y);
6601
6602     if (can_turn_left && can_turn_right)
6603       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6604     else if (can_turn_left)
6605       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6606     else if (can_turn_right)
6607       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6608     else
6609       MovDir[x][y] = back_dir;
6610
6611     MovDelay[x][y] = 16 + 16 * RND(3);
6612   }
6613   else if (element == EL_PACMAN)
6614   {
6615     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6616     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6617
6618     if (can_turn_left && can_turn_right)
6619       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6620     else if (can_turn_left)
6621       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6622     else if (can_turn_right)
6623       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6624     else
6625       MovDir[x][y] = back_dir;
6626
6627     MovDelay[x][y] = 6 + RND(40);
6628   }
6629   else if (element == EL_PIG)
6630   {
6631     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6632     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6633     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6634     boolean should_turn_left, should_turn_right, should_move_on;
6635     int rnd_value = 24;
6636     int rnd = RND(rnd_value);
6637
6638     should_turn_left = (can_turn_left &&
6639                         (!can_move_on ||
6640                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6641                                                    y + back_dy + left_dy)));
6642     should_turn_right = (can_turn_right &&
6643                          (!can_move_on ||
6644                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6645                                                     y + back_dy + right_dy)));
6646     should_move_on = (can_move_on &&
6647                       (!can_turn_left ||
6648                        !can_turn_right ||
6649                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6650                                                  y + move_dy + left_dy) ||
6651                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6652                                                  y + move_dy + right_dy)));
6653
6654     if (should_turn_left || should_turn_right || should_move_on)
6655     {
6656       if (should_turn_left && should_turn_right && should_move_on)
6657         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6658                         rnd < 2 * rnd_value / 3 ? right_dir :
6659                         old_move_dir);
6660       else if (should_turn_left && should_turn_right)
6661         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6662       else if (should_turn_left && should_move_on)
6663         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6664       else if (should_turn_right && should_move_on)
6665         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6666       else if (should_turn_left)
6667         MovDir[x][y] = left_dir;
6668       else if (should_turn_right)
6669         MovDir[x][y] = right_dir;
6670       else if (should_move_on)
6671         MovDir[x][y] = old_move_dir;
6672     }
6673     else if (can_move_on && rnd > rnd_value / 8)
6674       MovDir[x][y] = old_move_dir;
6675     else if (can_turn_left && can_turn_right)
6676       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6677     else if (can_turn_left && rnd > rnd_value / 8)
6678       MovDir[x][y] = left_dir;
6679     else if (can_turn_right && rnd > rnd_value/8)
6680       MovDir[x][y] = right_dir;
6681     else
6682       MovDir[x][y] = back_dir;
6683
6684     xx = x + move_xy[MovDir[x][y]].dx;
6685     yy = y + move_xy[MovDir[x][y]].dy;
6686
6687     if (!IN_LEV_FIELD(xx, yy) ||
6688         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6689       MovDir[x][y] = old_move_dir;
6690
6691     MovDelay[x][y] = 0;
6692   }
6693   else if (element == EL_DRAGON)
6694   {
6695     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6696     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6697     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6698     int rnd_value = 24;
6699     int rnd = RND(rnd_value);
6700
6701     if (can_move_on && rnd > rnd_value / 8)
6702       MovDir[x][y] = old_move_dir;
6703     else if (can_turn_left && can_turn_right)
6704       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6705     else if (can_turn_left && rnd > rnd_value / 8)
6706       MovDir[x][y] = left_dir;
6707     else if (can_turn_right && rnd > rnd_value / 8)
6708       MovDir[x][y] = right_dir;
6709     else
6710       MovDir[x][y] = back_dir;
6711
6712     xx = x + move_xy[MovDir[x][y]].dx;
6713     yy = y + move_xy[MovDir[x][y]].dy;
6714
6715     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6716       MovDir[x][y] = old_move_dir;
6717
6718     MovDelay[x][y] = 0;
6719   }
6720   else if (element == EL_MOLE)
6721   {
6722     boolean can_move_on =
6723       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6724                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6725                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6726     if (!can_move_on)
6727     {
6728       boolean can_turn_left =
6729         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6730                               IS_AMOEBOID(Feld[left_x][left_y])));
6731
6732       boolean can_turn_right =
6733         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6734                               IS_AMOEBOID(Feld[right_x][right_y])));
6735
6736       if (can_turn_left && can_turn_right)
6737         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6738       else if (can_turn_left)
6739         MovDir[x][y] = left_dir;
6740       else
6741         MovDir[x][y] = right_dir;
6742     }
6743
6744     if (MovDir[x][y] != old_move_dir)
6745       MovDelay[x][y] = 9;
6746   }
6747   else if (element == EL_BALLOON)
6748   {
6749     MovDir[x][y] = game.wind_direction;
6750     MovDelay[x][y] = 0;
6751   }
6752   else if (element == EL_SPRING)
6753   {
6754     if (MovDir[x][y] & MV_HORIZONTAL)
6755     {
6756       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6757           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6758       {
6759         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6760         ResetGfxAnimation(move_x, move_y);
6761         TEST_DrawLevelField(move_x, move_y);
6762
6763         MovDir[x][y] = back_dir;
6764       }
6765       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6766                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6767         MovDir[x][y] = MV_NONE;
6768     }
6769
6770     MovDelay[x][y] = 0;
6771   }
6772   else if (element == EL_ROBOT ||
6773            element == EL_SATELLITE ||
6774            element == EL_PENGUIN ||
6775            element == EL_EMC_ANDROID)
6776   {
6777     int attr_x = -1, attr_y = -1;
6778
6779     if (AllPlayersGone)
6780     {
6781       attr_x = ExitX;
6782       attr_y = ExitY;
6783     }
6784     else
6785     {
6786       int i;
6787
6788       for (i = 0; i < MAX_PLAYERS; i++)
6789       {
6790         struct PlayerInfo *player = &stored_player[i];
6791         int jx = player->jx, jy = player->jy;
6792
6793         if (!player->active)
6794           continue;
6795
6796         if (attr_x == -1 ||
6797             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6798         {
6799           attr_x = jx;
6800           attr_y = jy;
6801         }
6802       }
6803     }
6804
6805     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6806         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6807          game.engine_version < VERSION_IDENT(3,1,0,0)))
6808     {
6809       attr_x = ZX;
6810       attr_y = ZY;
6811     }
6812
6813     if (element == EL_PENGUIN)
6814     {
6815       int i;
6816       static int xy[4][2] =
6817       {
6818         { 0, -1 },
6819         { -1, 0 },
6820         { +1, 0 },
6821         { 0, +1 }
6822       };
6823
6824       for (i = 0; i < NUM_DIRECTIONS; i++)
6825       {
6826         int ex = x + xy[i][0];
6827         int ey = y + xy[i][1];
6828
6829         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6830                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6831                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6832                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6833         {
6834           attr_x = ex;
6835           attr_y = ey;
6836           break;
6837         }
6838       }
6839     }
6840
6841     MovDir[x][y] = MV_NONE;
6842     if (attr_x < x)
6843       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6844     else if (attr_x > x)
6845       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6846     if (attr_y < y)
6847       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6848     else if (attr_y > y)
6849       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6850
6851     if (element == EL_ROBOT)
6852     {
6853       int newx, newy;
6854
6855       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6856         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6857       Moving2Blocked(x, y, &newx, &newy);
6858
6859       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6860         MovDelay[x][y] = 8 + 8 * !RND(3);
6861       else
6862         MovDelay[x][y] = 16;
6863     }
6864     else if (element == EL_PENGUIN)
6865     {
6866       int newx, newy;
6867
6868       MovDelay[x][y] = 1;
6869
6870       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6871       {
6872         boolean first_horiz = RND(2);
6873         int new_move_dir = MovDir[x][y];
6874
6875         MovDir[x][y] =
6876           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6877         Moving2Blocked(x, y, &newx, &newy);
6878
6879         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6880           return;
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] = old_move_dir;
6890         return;
6891       }
6892     }
6893     else if (element == EL_SATELLITE)
6894     {
6895       int newx, newy;
6896
6897       MovDelay[x][y] = 1;
6898
6899       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6900       {
6901         boolean first_horiz = RND(2);
6902         int new_move_dir = MovDir[x][y];
6903
6904         MovDir[x][y] =
6905           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6906         Moving2Blocked(x, y, &newx, &newy);
6907
6908         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6909           return;
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] = old_move_dir;
6919         return;
6920       }
6921     }
6922     else if (element == EL_EMC_ANDROID)
6923     {
6924       static int check_pos[16] =
6925       {
6926         -1,             /*  0 => (invalid)          */
6927         7,              /*  1 => MV_LEFT            */
6928         3,              /*  2 => MV_RIGHT           */
6929         -1,             /*  3 => (invalid)          */
6930         1,              /*  4 =>            MV_UP   */
6931         0,              /*  5 => MV_LEFT  | MV_UP   */
6932         2,              /*  6 => MV_RIGHT | MV_UP   */
6933         -1,             /*  7 => (invalid)          */
6934         5,              /*  8 =>            MV_DOWN */
6935         6,              /*  9 => MV_LEFT  | MV_DOWN */
6936         4,              /* 10 => MV_RIGHT | MV_DOWN */
6937         -1,             /* 11 => (invalid)          */
6938         -1,             /* 12 => (invalid)          */
6939         -1,             /* 13 => (invalid)          */
6940         -1,             /* 14 => (invalid)          */
6941         -1,             /* 15 => (invalid)          */
6942       };
6943       static struct
6944       {
6945         int dx, dy;
6946         int dir;
6947       } check_xy[8] =
6948       {
6949         { -1, -1,       MV_LEFT  | MV_UP   },
6950         {  0, -1,                  MV_UP   },
6951         { +1, -1,       MV_RIGHT | MV_UP   },
6952         { +1,  0,       MV_RIGHT           },
6953         { +1, +1,       MV_RIGHT | MV_DOWN },
6954         {  0, +1,                  MV_DOWN },
6955         { -1, +1,       MV_LEFT  | MV_DOWN },
6956         { -1,  0,       MV_LEFT            },
6957       };
6958       int start_pos, check_order;
6959       boolean can_clone = FALSE;
6960       int i;
6961
6962       /* check if there is any free field around current position */
6963       for (i = 0; i < 8; i++)
6964       {
6965         int newx = x + check_xy[i].dx;
6966         int newy = y + check_xy[i].dy;
6967
6968         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6969         {
6970           can_clone = TRUE;
6971
6972           break;
6973         }
6974       }
6975
6976       if (can_clone)            /* randomly find an element to clone */
6977       {
6978         can_clone = FALSE;
6979
6980         start_pos = check_pos[RND(8)];
6981         check_order = (RND(2) ? -1 : +1);
6982
6983         for (i = 0; i < 8; i++)
6984         {
6985           int pos_raw = start_pos + i * check_order;
6986           int pos = (pos_raw + 8) % 8;
6987           int newx = x + check_xy[pos].dx;
6988           int newy = y + check_xy[pos].dy;
6989
6990           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6991           {
6992             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6993             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6994
6995             Store[x][y] = Feld[newx][newy];
6996
6997             can_clone = TRUE;
6998
6999             break;
7000           }
7001         }
7002       }
7003
7004       if (can_clone)            /* randomly find a direction to move */
7005       {
7006         can_clone = FALSE;
7007
7008         start_pos = check_pos[RND(8)];
7009         check_order = (RND(2) ? -1 : +1);
7010
7011         for (i = 0; i < 8; i++)
7012         {
7013           int pos_raw = start_pos + i * check_order;
7014           int pos = (pos_raw + 8) % 8;
7015           int newx = x + check_xy[pos].dx;
7016           int newy = y + check_xy[pos].dy;
7017           int new_move_dir = check_xy[pos].dir;
7018
7019           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7020           {
7021             MovDir[x][y] = new_move_dir;
7022             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7023
7024             can_clone = TRUE;
7025
7026             break;
7027           }
7028         }
7029       }
7030
7031       if (can_clone)            /* cloning and moving successful */
7032         return;
7033
7034       /* cannot clone -- try to move towards player */
7035
7036       start_pos = check_pos[MovDir[x][y] & 0x0f];
7037       check_order = (RND(2) ? -1 : +1);
7038
7039       for (i = 0; i < 3; i++)
7040       {
7041         /* first check start_pos, then previous/next or (next/previous) pos */
7042         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7043         int pos = (pos_raw + 8) % 8;
7044         int newx = x + check_xy[pos].dx;
7045         int newy = y + check_xy[pos].dy;
7046         int new_move_dir = check_xy[pos].dir;
7047
7048         if (IS_PLAYER(newx, newy))
7049           break;
7050
7051         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7052         {
7053           MovDir[x][y] = new_move_dir;
7054           MovDelay[x][y] = level.android_move_time * 8 + 1;
7055
7056           break;
7057         }
7058       }
7059     }
7060   }
7061   else if (move_pattern == MV_TURNING_LEFT ||
7062            move_pattern == MV_TURNING_RIGHT ||
7063            move_pattern == MV_TURNING_LEFT_RIGHT ||
7064            move_pattern == MV_TURNING_RIGHT_LEFT ||
7065            move_pattern == MV_TURNING_RANDOM ||
7066            move_pattern == MV_ALL_DIRECTIONS)
7067   {
7068     boolean can_turn_left =
7069       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7070     boolean can_turn_right =
7071       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7072
7073     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7074       return;
7075
7076     if (move_pattern == MV_TURNING_LEFT)
7077       MovDir[x][y] = left_dir;
7078     else if (move_pattern == MV_TURNING_RIGHT)
7079       MovDir[x][y] = right_dir;
7080     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7081       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7082     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7083       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7084     else if (move_pattern == MV_TURNING_RANDOM)
7085       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7086                       can_turn_right && !can_turn_left ? right_dir :
7087                       RND(2) ? left_dir : right_dir);
7088     else if (can_turn_left && can_turn_right)
7089       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7090     else if (can_turn_left)
7091       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7092     else if (can_turn_right)
7093       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7094     else
7095       MovDir[x][y] = back_dir;
7096
7097     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7098   }
7099   else if (move_pattern == MV_HORIZONTAL ||
7100            move_pattern == MV_VERTICAL)
7101   {
7102     if (move_pattern & old_move_dir)
7103       MovDir[x][y] = back_dir;
7104     else if (move_pattern == MV_HORIZONTAL)
7105       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7106     else if (move_pattern == MV_VERTICAL)
7107       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7108
7109     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7110   }
7111   else if (move_pattern & MV_ANY_DIRECTION)
7112   {
7113     MovDir[x][y] = move_pattern;
7114     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7115   }
7116   else if (move_pattern & MV_WIND_DIRECTION)
7117   {
7118     MovDir[x][y] = game.wind_direction;
7119     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7120   }
7121   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7122   {
7123     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7124       MovDir[x][y] = left_dir;
7125     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7126       MovDir[x][y] = right_dir;
7127
7128     if (MovDir[x][y] != old_move_dir)
7129       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7130   }
7131   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7132   {
7133     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7134       MovDir[x][y] = right_dir;
7135     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7136       MovDir[x][y] = left_dir;
7137
7138     if (MovDir[x][y] != old_move_dir)
7139       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7140   }
7141   else if (move_pattern == MV_TOWARDS_PLAYER ||
7142            move_pattern == MV_AWAY_FROM_PLAYER)
7143   {
7144     int attr_x = -1, attr_y = -1;
7145     int newx, newy;
7146     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7147
7148     if (AllPlayersGone)
7149     {
7150       attr_x = ExitX;
7151       attr_y = ExitY;
7152     }
7153     else
7154     {
7155       int i;
7156
7157       for (i = 0; i < MAX_PLAYERS; i++)
7158       {
7159         struct PlayerInfo *player = &stored_player[i];
7160         int jx = player->jx, jy = player->jy;
7161
7162         if (!player->active)
7163           continue;
7164
7165         if (attr_x == -1 ||
7166             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7167         {
7168           attr_x = jx;
7169           attr_y = jy;
7170         }
7171       }
7172     }
7173
7174     MovDir[x][y] = MV_NONE;
7175     if (attr_x < x)
7176       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7177     else if (attr_x > x)
7178       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7179     if (attr_y < y)
7180       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7181     else if (attr_y > y)
7182       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7183
7184     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7185
7186     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7187     {
7188       boolean first_horiz = RND(2);
7189       int new_move_dir = MovDir[x][y];
7190
7191       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7192       {
7193         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7194         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7195
7196         return;
7197       }
7198
7199       MovDir[x][y] =
7200         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7201       Moving2Blocked(x, y, &newx, &newy);
7202
7203       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7204         return;
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] = old_move_dir;
7214     }
7215   }
7216   else if (move_pattern == MV_WHEN_PUSHED ||
7217            move_pattern == MV_WHEN_DROPPED)
7218   {
7219     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7220       MovDir[x][y] = MV_NONE;
7221
7222     MovDelay[x][y] = 0;
7223   }
7224   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7225   {
7226     static int test_xy[7][2] =
7227     {
7228       { 0, -1 },
7229       { -1, 0 },
7230       { +1, 0 },
7231       { 0, +1 },
7232       { 0, -1 },
7233       { -1, 0 },
7234       { +1, 0 },
7235     };
7236     static int test_dir[7] =
7237     {
7238       MV_UP,
7239       MV_LEFT,
7240       MV_RIGHT,
7241       MV_DOWN,
7242       MV_UP,
7243       MV_LEFT,
7244       MV_RIGHT,
7245     };
7246     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7247     int move_preference = -1000000;     /* start with very low preference */
7248     int new_move_dir = MV_NONE;
7249     int start_test = RND(4);
7250     int i;
7251
7252     for (i = 0; i < NUM_DIRECTIONS; i++)
7253     {
7254       int move_dir = test_dir[start_test + i];
7255       int move_dir_preference;
7256
7257       xx = x + test_xy[start_test + i][0];
7258       yy = y + test_xy[start_test + i][1];
7259
7260       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7261           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7262       {
7263         new_move_dir = move_dir;
7264
7265         break;
7266       }
7267
7268       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7269         continue;
7270
7271       move_dir_preference = -1 * RunnerVisit[xx][yy];
7272       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7273         move_dir_preference = PlayerVisit[xx][yy];
7274
7275       if (move_dir_preference > move_preference)
7276       {
7277         /* prefer field that has not been visited for the longest time */
7278         move_preference = move_dir_preference;
7279         new_move_dir = move_dir;
7280       }
7281       else if (move_dir_preference == move_preference &&
7282                move_dir == old_move_dir)
7283       {
7284         /* prefer last direction when all directions are preferred equally */
7285         move_preference = move_dir_preference;
7286         new_move_dir = move_dir;
7287       }
7288     }
7289
7290     MovDir[x][y] = new_move_dir;
7291     if (old_move_dir != new_move_dir)
7292       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7293   }
7294 }
7295
7296 static void TurnRound(int x, int y)
7297 {
7298   int direction = MovDir[x][y];
7299
7300   TurnRoundExt(x, y);
7301
7302   GfxDir[x][y] = MovDir[x][y];
7303
7304   if (direction != MovDir[x][y])
7305     GfxFrame[x][y] = 0;
7306
7307   if (MovDelay[x][y])
7308     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7309
7310   ResetGfxFrame(x, y);
7311 }
7312
7313 static boolean JustBeingPushed(int x, int y)
7314 {
7315   int i;
7316
7317   for (i = 0; i < MAX_PLAYERS; i++)
7318   {
7319     struct PlayerInfo *player = &stored_player[i];
7320
7321     if (player->active && player->is_pushing && player->MovPos)
7322     {
7323       int next_jx = player->jx + (player->jx - player->last_jx);
7324       int next_jy = player->jy + (player->jy - player->last_jy);
7325
7326       if (x == next_jx && y == next_jy)
7327         return TRUE;
7328     }
7329   }
7330
7331   return FALSE;
7332 }
7333
7334 void StartMoving(int x, int y)
7335 {
7336   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7337   int element = Feld[x][y];
7338
7339   if (Stop[x][y])
7340     return;
7341
7342   if (MovDelay[x][y] == 0)
7343     GfxAction[x][y] = ACTION_DEFAULT;
7344
7345   if (CAN_FALL(element) && y < lev_fieldy - 1)
7346   {
7347     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7348         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7349       if (JustBeingPushed(x, y))
7350         return;
7351
7352     if (element == EL_QUICKSAND_FULL)
7353     {
7354       if (IS_FREE(x, y + 1))
7355       {
7356         InitMovingField(x, y, MV_DOWN);
7357         started_moving = TRUE;
7358
7359         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7360 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7361         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7362           Store[x][y] = EL_ROCK;
7363 #else
7364         Store[x][y] = EL_ROCK;
7365 #endif
7366
7367         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7368       }
7369       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7370       {
7371         if (!MovDelay[x][y])
7372         {
7373           MovDelay[x][y] = TILEY + 1;
7374
7375           ResetGfxAnimation(x, y);
7376           ResetGfxAnimation(x, y + 1);
7377         }
7378
7379         if (MovDelay[x][y])
7380         {
7381           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7382           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7383
7384           MovDelay[x][y]--;
7385           if (MovDelay[x][y])
7386             return;
7387         }
7388
7389         Feld[x][y] = EL_QUICKSAND_EMPTY;
7390         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7391         Store[x][y + 1] = Store[x][y];
7392         Store[x][y] = 0;
7393
7394         PlayLevelSoundAction(x, y, ACTION_FILLING);
7395       }
7396       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7397       {
7398         if (!MovDelay[x][y])
7399         {
7400           MovDelay[x][y] = TILEY + 1;
7401
7402           ResetGfxAnimation(x, y);
7403           ResetGfxAnimation(x, y + 1);
7404         }
7405
7406         if (MovDelay[x][y])
7407         {
7408           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7409           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7410
7411           MovDelay[x][y]--;
7412           if (MovDelay[x][y])
7413             return;
7414         }
7415
7416         Feld[x][y] = EL_QUICKSAND_EMPTY;
7417         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7418         Store[x][y + 1] = Store[x][y];
7419         Store[x][y] = 0;
7420
7421         PlayLevelSoundAction(x, y, ACTION_FILLING);
7422       }
7423     }
7424     else if (element == EL_QUICKSAND_FAST_FULL)
7425     {
7426       if (IS_FREE(x, y + 1))
7427       {
7428         InitMovingField(x, y, MV_DOWN);
7429         started_moving = TRUE;
7430
7431         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7432 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7433         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7434           Store[x][y] = EL_ROCK;
7435 #else
7436         Store[x][y] = EL_ROCK;
7437 #endif
7438
7439         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7440       }
7441       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7442       {
7443         if (!MovDelay[x][y])
7444         {
7445           MovDelay[x][y] = TILEY + 1;
7446
7447           ResetGfxAnimation(x, y);
7448           ResetGfxAnimation(x, y + 1);
7449         }
7450
7451         if (MovDelay[x][y])
7452         {
7453           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7454           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7455
7456           MovDelay[x][y]--;
7457           if (MovDelay[x][y])
7458             return;
7459         }
7460
7461         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7462         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7463         Store[x][y + 1] = Store[x][y];
7464         Store[x][y] = 0;
7465
7466         PlayLevelSoundAction(x, y, ACTION_FILLING);
7467       }
7468       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7469       {
7470         if (!MovDelay[x][y])
7471         {
7472           MovDelay[x][y] = TILEY + 1;
7473
7474           ResetGfxAnimation(x, y);
7475           ResetGfxAnimation(x, y + 1);
7476         }
7477
7478         if (MovDelay[x][y])
7479         {
7480           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7481           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7482
7483           MovDelay[x][y]--;
7484           if (MovDelay[x][y])
7485             return;
7486         }
7487
7488         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7489         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7490         Store[x][y + 1] = Store[x][y];
7491         Store[x][y] = 0;
7492
7493         PlayLevelSoundAction(x, y, ACTION_FILLING);
7494       }
7495     }
7496     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7497              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7498     {
7499       InitMovingField(x, y, MV_DOWN);
7500       started_moving = TRUE;
7501
7502       Feld[x][y] = EL_QUICKSAND_FILLING;
7503       Store[x][y] = element;
7504
7505       PlayLevelSoundAction(x, y, ACTION_FILLING);
7506     }
7507     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7508              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7509     {
7510       InitMovingField(x, y, MV_DOWN);
7511       started_moving = TRUE;
7512
7513       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7514       Store[x][y] = element;
7515
7516       PlayLevelSoundAction(x, y, ACTION_FILLING);
7517     }
7518     else if (element == EL_MAGIC_WALL_FULL)
7519     {
7520       if (IS_FREE(x, y + 1))
7521       {
7522         InitMovingField(x, y, MV_DOWN);
7523         started_moving = TRUE;
7524
7525         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7526         Store[x][y] = EL_CHANGED(Store[x][y]);
7527       }
7528       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7529       {
7530         if (!MovDelay[x][y])
7531           MovDelay[x][y] = TILEY / 4 + 1;
7532
7533         if (MovDelay[x][y])
7534         {
7535           MovDelay[x][y]--;
7536           if (MovDelay[x][y])
7537             return;
7538         }
7539
7540         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7541         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7542         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7543         Store[x][y] = 0;
7544       }
7545     }
7546     else if (element == EL_BD_MAGIC_WALL_FULL)
7547     {
7548       if (IS_FREE(x, y + 1))
7549       {
7550         InitMovingField(x, y, MV_DOWN);
7551         started_moving = TRUE;
7552
7553         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7554         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7555       }
7556       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7557       {
7558         if (!MovDelay[x][y])
7559           MovDelay[x][y] = TILEY / 4 + 1;
7560
7561         if (MovDelay[x][y])
7562         {
7563           MovDelay[x][y]--;
7564           if (MovDelay[x][y])
7565             return;
7566         }
7567
7568         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7569         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7570         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7571         Store[x][y] = 0;
7572       }
7573     }
7574     else if (element == EL_DC_MAGIC_WALL_FULL)
7575     {
7576       if (IS_FREE(x, y + 1))
7577       {
7578         InitMovingField(x, y, MV_DOWN);
7579         started_moving = TRUE;
7580
7581         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7582         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7583       }
7584       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7585       {
7586         if (!MovDelay[x][y])
7587           MovDelay[x][y] = TILEY / 4 + 1;
7588
7589         if (MovDelay[x][y])
7590         {
7591           MovDelay[x][y]--;
7592           if (MovDelay[x][y])
7593             return;
7594         }
7595
7596         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7597         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7598         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7599         Store[x][y] = 0;
7600       }
7601     }
7602     else if ((CAN_PASS_MAGIC_WALL(element) &&
7603               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7604                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7605              (CAN_PASS_DC_MAGIC_WALL(element) &&
7606               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7607
7608     {
7609       InitMovingField(x, y, MV_DOWN);
7610       started_moving = TRUE;
7611
7612       Feld[x][y] =
7613         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7614          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7615          EL_DC_MAGIC_WALL_FILLING);
7616       Store[x][y] = element;
7617     }
7618     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7619     {
7620       SplashAcid(x, y + 1);
7621
7622       InitMovingField(x, y, MV_DOWN);
7623       started_moving = TRUE;
7624
7625       Store[x][y] = EL_ACID;
7626     }
7627     else if (
7628              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7629               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7630              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7631               CAN_FALL(element) && WasJustFalling[x][y] &&
7632               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7633
7634              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7635               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7636               (Feld[x][y + 1] == EL_BLOCKED)))
7637     {
7638       /* this is needed for a special case not covered by calling "Impact()"
7639          from "ContinueMoving()": if an element moves to a tile directly below
7640          another element which was just falling on that tile (which was empty
7641          in the previous frame), the falling element above would just stop
7642          instead of smashing the element below (in previous version, the above
7643          element was just checked for "moving" instead of "falling", resulting
7644          in incorrect smashes caused by horizontal movement of the above
7645          element; also, the case of the player being the element to smash was
7646          simply not covered here... :-/ ) */
7647
7648       CheckCollision[x][y] = 0;
7649       CheckImpact[x][y] = 0;
7650
7651       Impact(x, y);
7652     }
7653     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7654     {
7655       if (MovDir[x][y] == MV_NONE)
7656       {
7657         InitMovingField(x, y, MV_DOWN);
7658         started_moving = TRUE;
7659       }
7660     }
7661     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7662     {
7663       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7664         MovDir[x][y] = MV_DOWN;
7665
7666       InitMovingField(x, y, MV_DOWN);
7667       started_moving = TRUE;
7668     }
7669     else if (element == EL_AMOEBA_DROP)
7670     {
7671       Feld[x][y] = EL_AMOEBA_GROWING;
7672       Store[x][y] = EL_AMOEBA_WET;
7673     }
7674     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7675               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7676              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7677              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7678     {
7679       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7680                                 (IS_FREE(x - 1, y + 1) ||
7681                                  Feld[x - 1][y + 1] == EL_ACID));
7682       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7683                                 (IS_FREE(x + 1, y + 1) ||
7684                                  Feld[x + 1][y + 1] == EL_ACID));
7685       boolean can_fall_any  = (can_fall_left || can_fall_right);
7686       boolean can_fall_both = (can_fall_left && can_fall_right);
7687       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7688
7689       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7690       {
7691         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7692           can_fall_right = FALSE;
7693         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7694           can_fall_left = FALSE;
7695         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7696           can_fall_right = FALSE;
7697         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7698           can_fall_left = FALSE;
7699
7700         can_fall_any  = (can_fall_left || can_fall_right);
7701         can_fall_both = FALSE;
7702       }
7703
7704       if (can_fall_both)
7705       {
7706         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7707           can_fall_right = FALSE;       /* slip down on left side */
7708         else
7709           can_fall_left = !(can_fall_right = RND(2));
7710
7711         can_fall_both = FALSE;
7712       }
7713
7714       if (can_fall_any)
7715       {
7716         /* if not determined otherwise, prefer left side for slipping down */
7717         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7718         started_moving = TRUE;
7719       }
7720     }
7721     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7722     {
7723       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7724       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7725       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7726       int belt_dir = game.belt_dir[belt_nr];
7727
7728       if ((belt_dir == MV_LEFT  && left_is_free) ||
7729           (belt_dir == MV_RIGHT && right_is_free))
7730       {
7731         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7732
7733         InitMovingField(x, y, belt_dir);
7734         started_moving = TRUE;
7735
7736         Pushed[x][y] = TRUE;
7737         Pushed[nextx][y] = TRUE;
7738
7739         GfxAction[x][y] = ACTION_DEFAULT;
7740       }
7741       else
7742       {
7743         MovDir[x][y] = 0;       /* if element was moving, stop it */
7744       }
7745     }
7746   }
7747
7748   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7749   if (CAN_MOVE(element) && !started_moving)
7750   {
7751     int move_pattern = element_info[element].move_pattern;
7752     int newx, newy;
7753
7754     Moving2Blocked(x, y, &newx, &newy);
7755
7756     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7757       return;
7758
7759     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7760         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7761     {
7762       WasJustMoving[x][y] = 0;
7763       CheckCollision[x][y] = 0;
7764
7765       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7766
7767       if (Feld[x][y] != element)        /* element has changed */
7768         return;
7769     }
7770
7771     if (!MovDelay[x][y])        /* start new movement phase */
7772     {
7773       /* all objects that can change their move direction after each step
7774          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7775
7776       if (element != EL_YAMYAM &&
7777           element != EL_DARK_YAMYAM &&
7778           element != EL_PACMAN &&
7779           !(move_pattern & MV_ANY_DIRECTION) &&
7780           move_pattern != MV_TURNING_LEFT &&
7781           move_pattern != MV_TURNING_RIGHT &&
7782           move_pattern != MV_TURNING_LEFT_RIGHT &&
7783           move_pattern != MV_TURNING_RIGHT_LEFT &&
7784           move_pattern != MV_TURNING_RANDOM)
7785       {
7786         TurnRound(x, y);
7787
7788         if (MovDelay[x][y] && (element == EL_BUG ||
7789                                element == EL_SPACESHIP ||
7790                                element == EL_SP_SNIKSNAK ||
7791                                element == EL_SP_ELECTRON ||
7792                                element == EL_MOLE))
7793           TEST_DrawLevelField(x, y);
7794       }
7795     }
7796
7797     if (MovDelay[x][y])         /* wait some time before next movement */
7798     {
7799       MovDelay[x][y]--;
7800
7801       if (element == EL_ROBOT ||
7802           element == EL_YAMYAM ||
7803           element == EL_DARK_YAMYAM)
7804       {
7805         DrawLevelElementAnimationIfNeeded(x, y, element);
7806         PlayLevelSoundAction(x, y, ACTION_WAITING);
7807       }
7808       else if (element == EL_SP_ELECTRON)
7809         DrawLevelElementAnimationIfNeeded(x, y, element);
7810       else if (element == EL_DRAGON)
7811       {
7812         int i;
7813         int dir = MovDir[x][y];
7814         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7815         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7816         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7817                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7818                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7819                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7820         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7821
7822         GfxAction[x][y] = ACTION_ATTACKING;
7823
7824         if (IS_PLAYER(x, y))
7825           DrawPlayerField(x, y);
7826         else
7827           TEST_DrawLevelField(x, y);
7828
7829         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7830
7831         for (i = 1; i <= 3; i++)
7832         {
7833           int xx = x + i * dx;
7834           int yy = y + i * dy;
7835           int sx = SCREENX(xx);
7836           int sy = SCREENY(yy);
7837           int flame_graphic = graphic + (i - 1);
7838
7839           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7840             break;
7841
7842           if (MovDelay[x][y])
7843           {
7844             int flamed = MovingOrBlocked2Element(xx, yy);
7845
7846             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7847               Bang(xx, yy);
7848             else
7849               RemoveMovingField(xx, yy);
7850
7851             ChangeDelay[xx][yy] = 0;
7852
7853             Feld[xx][yy] = EL_FLAMES;
7854
7855             if (IN_SCR_FIELD(sx, sy))
7856             {
7857               TEST_DrawLevelFieldCrumbled(xx, yy);
7858               DrawGraphic(sx, sy, flame_graphic, frame);
7859             }
7860           }
7861           else
7862           {
7863             if (Feld[xx][yy] == EL_FLAMES)
7864               Feld[xx][yy] = EL_EMPTY;
7865             TEST_DrawLevelField(xx, yy);
7866           }
7867         }
7868       }
7869
7870       if (MovDelay[x][y])       /* element still has to wait some time */
7871       {
7872         PlayLevelSoundAction(x, y, ACTION_WAITING);
7873
7874         return;
7875       }
7876     }
7877
7878     /* now make next step */
7879
7880     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7881
7882     if (DONT_COLLIDE_WITH(element) &&
7883         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7884         !PLAYER_ENEMY_PROTECTED(newx, newy))
7885     {
7886       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7887
7888       return;
7889     }
7890
7891     else if (CAN_MOVE_INTO_ACID(element) &&
7892              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7893              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7894              (MovDir[x][y] == MV_DOWN ||
7895               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7896     {
7897       SplashAcid(newx, newy);
7898       Store[x][y] = EL_ACID;
7899     }
7900     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7901     {
7902       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7903           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7904           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7905           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7906       {
7907         RemoveField(x, y);
7908         TEST_DrawLevelField(x, y);
7909
7910         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7911         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7912           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7913
7914         local_player->friends_still_needed--;
7915         if (!local_player->friends_still_needed &&
7916             !local_player->GameOver && AllPlayersGone)
7917           PlayerWins(local_player);
7918
7919         return;
7920       }
7921       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7922       {
7923         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7924           TEST_DrawLevelField(newx, newy);
7925         else
7926           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7927       }
7928       else if (!IS_FREE(newx, newy))
7929       {
7930         GfxAction[x][y] = ACTION_WAITING;
7931
7932         if (IS_PLAYER(x, y))
7933           DrawPlayerField(x, y);
7934         else
7935           TEST_DrawLevelField(x, y);
7936
7937         return;
7938       }
7939     }
7940     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7941     {
7942       if (IS_FOOD_PIG(Feld[newx][newy]))
7943       {
7944         if (IS_MOVING(newx, newy))
7945           RemoveMovingField(newx, newy);
7946         else
7947         {
7948           Feld[newx][newy] = EL_EMPTY;
7949           TEST_DrawLevelField(newx, newy);
7950         }
7951
7952         PlayLevelSound(x, y, SND_PIG_DIGGING);
7953       }
7954       else if (!IS_FREE(newx, newy))
7955       {
7956         if (IS_PLAYER(x, y))
7957           DrawPlayerField(x, y);
7958         else
7959           TEST_DrawLevelField(x, y);
7960
7961         return;
7962       }
7963     }
7964     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7965     {
7966       if (Store[x][y] != EL_EMPTY)
7967       {
7968         boolean can_clone = FALSE;
7969         int xx, yy;
7970
7971         /* check if element to clone is still there */
7972         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7973         {
7974           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7975           {
7976             can_clone = TRUE;
7977
7978             break;
7979           }
7980         }
7981
7982         /* cannot clone or target field not free anymore -- do not clone */
7983         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7984           Store[x][y] = EL_EMPTY;
7985       }
7986
7987       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7988       {
7989         if (IS_MV_DIAGONAL(MovDir[x][y]))
7990         {
7991           int diagonal_move_dir = MovDir[x][y];
7992           int stored = Store[x][y];
7993           int change_delay = 8;
7994           int graphic;
7995
7996           /* android is moving diagonally */
7997
7998           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7999
8000           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8001           GfxElement[x][y] = EL_EMC_ANDROID;
8002           GfxAction[x][y] = ACTION_SHRINKING;
8003           GfxDir[x][y] = diagonal_move_dir;
8004           ChangeDelay[x][y] = change_delay;
8005
8006           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8007                                    GfxDir[x][y]);
8008
8009           DrawLevelGraphicAnimation(x, y, graphic);
8010           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8011
8012           if (Feld[newx][newy] == EL_ACID)
8013           {
8014             SplashAcid(newx, newy);
8015
8016             return;
8017           }
8018
8019           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8020
8021           Store[newx][newy] = EL_EMC_ANDROID;
8022           GfxElement[newx][newy] = EL_EMC_ANDROID;
8023           GfxAction[newx][newy] = ACTION_GROWING;
8024           GfxDir[newx][newy] = diagonal_move_dir;
8025           ChangeDelay[newx][newy] = change_delay;
8026
8027           graphic = el_act_dir2img(GfxElement[newx][newy],
8028                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8029
8030           DrawLevelGraphicAnimation(newx, newy, graphic);
8031           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8032
8033           return;
8034         }
8035         else
8036         {
8037           Feld[newx][newy] = EL_EMPTY;
8038           TEST_DrawLevelField(newx, newy);
8039
8040           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8041         }
8042       }
8043       else if (!IS_FREE(newx, newy))
8044       {
8045         return;
8046       }
8047     }
8048     else if (IS_CUSTOM_ELEMENT(element) &&
8049              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8050     {
8051       if (!DigFieldByCE(newx, newy, element))
8052         return;
8053
8054       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8055       {
8056         RunnerVisit[x][y] = FrameCounter;
8057         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8058       }
8059     }
8060     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8061     {
8062       if (!IS_FREE(newx, newy))
8063       {
8064         if (IS_PLAYER(x, y))
8065           DrawPlayerField(x, y);
8066         else
8067           TEST_DrawLevelField(x, y);
8068
8069         return;
8070       }
8071       else
8072       {
8073         boolean wanna_flame = !RND(10);
8074         int dx = newx - x, dy = newy - y;
8075         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8076         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8077         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8078                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8079         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8080                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8081
8082         if ((wanna_flame ||
8083              IS_CLASSIC_ENEMY(element1) ||
8084              IS_CLASSIC_ENEMY(element2)) &&
8085             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8086             element1 != EL_FLAMES && element2 != EL_FLAMES)
8087         {
8088           ResetGfxAnimation(x, y);
8089           GfxAction[x][y] = ACTION_ATTACKING;
8090
8091           if (IS_PLAYER(x, y))
8092             DrawPlayerField(x, y);
8093           else
8094             TEST_DrawLevelField(x, y);
8095
8096           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8097
8098           MovDelay[x][y] = 50;
8099
8100           Feld[newx][newy] = EL_FLAMES;
8101           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8102             Feld[newx1][newy1] = EL_FLAMES;
8103           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8104             Feld[newx2][newy2] = EL_FLAMES;
8105
8106           return;
8107         }
8108       }
8109     }
8110     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8111              Feld[newx][newy] == EL_DIAMOND)
8112     {
8113       if (IS_MOVING(newx, newy))
8114         RemoveMovingField(newx, newy);
8115       else
8116       {
8117         Feld[newx][newy] = EL_EMPTY;
8118         TEST_DrawLevelField(newx, newy);
8119       }
8120
8121       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8122     }
8123     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8124              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8125     {
8126       if (AmoebaNr[newx][newy])
8127       {
8128         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8129         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8130             Feld[newx][newy] == EL_BD_AMOEBA)
8131           AmoebaCnt[AmoebaNr[newx][newy]]--;
8132       }
8133
8134       if (IS_MOVING(newx, newy))
8135       {
8136         RemoveMovingField(newx, newy);
8137       }
8138       else
8139       {
8140         Feld[newx][newy] = EL_EMPTY;
8141         TEST_DrawLevelField(newx, newy);
8142       }
8143
8144       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8145     }
8146     else if ((element == EL_PACMAN || element == EL_MOLE)
8147              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8148     {
8149       if (AmoebaNr[newx][newy])
8150       {
8151         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8152         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8153             Feld[newx][newy] == EL_BD_AMOEBA)
8154           AmoebaCnt[AmoebaNr[newx][newy]]--;
8155       }
8156
8157       if (element == EL_MOLE)
8158       {
8159         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8160         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8161
8162         ResetGfxAnimation(x, y);
8163         GfxAction[x][y] = ACTION_DIGGING;
8164         TEST_DrawLevelField(x, y);
8165
8166         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8167
8168         return;                         /* wait for shrinking amoeba */
8169       }
8170       else      /* element == EL_PACMAN */
8171       {
8172         Feld[newx][newy] = EL_EMPTY;
8173         TEST_DrawLevelField(newx, newy);
8174         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8175       }
8176     }
8177     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8178              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8179               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8180     {
8181       /* wait for shrinking amoeba to completely disappear */
8182       return;
8183     }
8184     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8185     {
8186       /* object was running against a wall */
8187
8188       TurnRound(x, y);
8189
8190       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8191         DrawLevelElementAnimation(x, y, element);
8192
8193       if (DONT_TOUCH(element))
8194         TestIfBadThingTouchesPlayer(x, y);
8195
8196       return;
8197     }
8198
8199     InitMovingField(x, y, MovDir[x][y]);
8200
8201     PlayLevelSoundAction(x, y, ACTION_MOVING);
8202   }
8203
8204   if (MovDir[x][y])
8205     ContinueMoving(x, y);
8206 }
8207
8208 void ContinueMoving(int x, int y)
8209 {
8210   int element = Feld[x][y];
8211   struct ElementInfo *ei = &element_info[element];
8212   int direction = MovDir[x][y];
8213   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8214   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8215   int newx = x + dx, newy = y + dy;
8216   int stored = Store[x][y];
8217   int stored_new = Store[newx][newy];
8218   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8219   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8220   boolean last_line = (newy == lev_fieldy - 1);
8221
8222   MovPos[x][y] += getElementMoveStepsize(x, y);
8223
8224   if (pushed_by_player) /* special case: moving object pushed by player */
8225     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8226
8227   if (ABS(MovPos[x][y]) < TILEX)
8228   {
8229     TEST_DrawLevelField(x, y);
8230
8231     return;     /* element is still moving */
8232   }
8233
8234   /* element reached destination field */
8235
8236   Feld[x][y] = EL_EMPTY;
8237   Feld[newx][newy] = element;
8238   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8239
8240   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8241   {
8242     element = Feld[newx][newy] = EL_ACID;
8243   }
8244   else if (element == EL_MOLE)
8245   {
8246     Feld[x][y] = EL_SAND;
8247
8248     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8249   }
8250   else if (element == EL_QUICKSAND_FILLING)
8251   {
8252     element = Feld[newx][newy] = get_next_element(element);
8253     Store[newx][newy] = Store[x][y];
8254   }
8255   else if (element == EL_QUICKSAND_EMPTYING)
8256   {
8257     Feld[x][y] = get_next_element(element);
8258     element = Feld[newx][newy] = Store[x][y];
8259   }
8260   else if (element == EL_QUICKSAND_FAST_FILLING)
8261   {
8262     element = Feld[newx][newy] = get_next_element(element);
8263     Store[newx][newy] = Store[x][y];
8264   }
8265   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8266   {
8267     Feld[x][y] = get_next_element(element);
8268     element = Feld[newx][newy] = Store[x][y];
8269   }
8270   else if (element == EL_MAGIC_WALL_FILLING)
8271   {
8272     element = Feld[newx][newy] = get_next_element(element);
8273     if (!game.magic_wall_active)
8274       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8275     Store[newx][newy] = Store[x][y];
8276   }
8277   else if (element == EL_MAGIC_WALL_EMPTYING)
8278   {
8279     Feld[x][y] = get_next_element(element);
8280     if (!game.magic_wall_active)
8281       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8282     element = Feld[newx][newy] = Store[x][y];
8283
8284     InitField(newx, newy, FALSE);
8285   }
8286   else if (element == EL_BD_MAGIC_WALL_FILLING)
8287   {
8288     element = Feld[newx][newy] = get_next_element(element);
8289     if (!game.magic_wall_active)
8290       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8291     Store[newx][newy] = Store[x][y];
8292   }
8293   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8294   {
8295     Feld[x][y] = get_next_element(element);
8296     if (!game.magic_wall_active)
8297       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8298     element = Feld[newx][newy] = Store[x][y];
8299
8300     InitField(newx, newy, FALSE);
8301   }
8302   else if (element == EL_DC_MAGIC_WALL_FILLING)
8303   {
8304     element = Feld[newx][newy] = get_next_element(element);
8305     if (!game.magic_wall_active)
8306       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8307     Store[newx][newy] = Store[x][y];
8308   }
8309   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8310   {
8311     Feld[x][y] = get_next_element(element);
8312     if (!game.magic_wall_active)
8313       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8314     element = Feld[newx][newy] = Store[x][y];
8315
8316     InitField(newx, newy, FALSE);
8317   }
8318   else if (element == EL_AMOEBA_DROPPING)
8319   {
8320     Feld[x][y] = get_next_element(element);
8321     element = Feld[newx][newy] = Store[x][y];
8322   }
8323   else if (element == EL_SOKOBAN_OBJECT)
8324   {
8325     if (Back[x][y])
8326       Feld[x][y] = Back[x][y];
8327
8328     if (Back[newx][newy])
8329       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8330
8331     Back[x][y] = Back[newx][newy] = 0;
8332   }
8333
8334   Store[x][y] = EL_EMPTY;
8335   MovPos[x][y] = 0;
8336   MovDir[x][y] = 0;
8337   MovDelay[x][y] = 0;
8338
8339   MovDelay[newx][newy] = 0;
8340
8341   if (CAN_CHANGE_OR_HAS_ACTION(element))
8342   {
8343     /* copy element change control values to new field */
8344     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8345     ChangePage[newx][newy]  = ChangePage[x][y];
8346     ChangeCount[newx][newy] = ChangeCount[x][y];
8347     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8348   }
8349
8350   CustomValue[newx][newy] = CustomValue[x][y];
8351
8352   ChangeDelay[x][y] = 0;
8353   ChangePage[x][y] = -1;
8354   ChangeCount[x][y] = 0;
8355   ChangeEvent[x][y] = -1;
8356
8357   CustomValue[x][y] = 0;
8358
8359   /* copy animation control values to new field */
8360   GfxFrame[newx][newy]  = GfxFrame[x][y];
8361   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8362   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8363   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8364
8365   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8366
8367   /* some elements can leave other elements behind after moving */
8368   if (ei->move_leave_element != EL_EMPTY &&
8369       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8370       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8371   {
8372     int move_leave_element = ei->move_leave_element;
8373
8374     /* this makes it possible to leave the removed element again */
8375     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8376       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8377
8378     Feld[x][y] = move_leave_element;
8379
8380     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8381       MovDir[x][y] = direction;
8382
8383     InitField(x, y, FALSE);
8384
8385     if (GFX_CRUMBLED(Feld[x][y]))
8386       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8387
8388     if (ELEM_IS_PLAYER(move_leave_element))
8389       RelocatePlayer(x, y, move_leave_element);
8390   }
8391
8392   /* do this after checking for left-behind element */
8393   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8394
8395   if (!CAN_MOVE(element) ||
8396       (CAN_FALL(element) && direction == MV_DOWN &&
8397        (element == EL_SPRING ||
8398         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8399         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8400     GfxDir[x][y] = MovDir[newx][newy] = 0;
8401
8402   TEST_DrawLevelField(x, y);
8403   TEST_DrawLevelField(newx, newy);
8404
8405   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8406
8407   /* prevent pushed element from moving on in pushed direction */
8408   if (pushed_by_player && CAN_MOVE(element) &&
8409       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8410       !(element_info[element].move_pattern & direction))
8411     TurnRound(newx, newy);
8412
8413   /* prevent elements on conveyor belt from moving on in last direction */
8414   if (pushed_by_conveyor && CAN_FALL(element) &&
8415       direction & MV_HORIZONTAL)
8416     MovDir[newx][newy] = 0;
8417
8418   if (!pushed_by_player)
8419   {
8420     int nextx = newx + dx, nexty = newy + dy;
8421     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8422
8423     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8424
8425     if (CAN_FALL(element) && direction == MV_DOWN)
8426       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8427
8428     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8429       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8430
8431     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8432       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8433   }
8434
8435   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8436   {
8437     TestIfBadThingTouchesPlayer(newx, newy);
8438     TestIfBadThingTouchesFriend(newx, newy);
8439
8440     if (!IS_CUSTOM_ELEMENT(element))
8441       TestIfBadThingTouchesOtherBadThing(newx, newy);
8442   }
8443   else if (element == EL_PENGUIN)
8444     TestIfFriendTouchesBadThing(newx, newy);
8445
8446   if (DONT_GET_HIT_BY(element))
8447   {
8448     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8449   }
8450
8451   /* give the player one last chance (one more frame) to move away */
8452   if (CAN_FALL(element) && direction == MV_DOWN &&
8453       (last_line || (!IS_FREE(x, newy + 1) &&
8454                      (!IS_PLAYER(x, newy + 1) ||
8455                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8456     Impact(x, newy);
8457
8458   if (pushed_by_player && !game.use_change_when_pushing_bug)
8459   {
8460     int push_side = MV_DIR_OPPOSITE(direction);
8461     struct PlayerInfo *player = PLAYERINFO(x, y);
8462
8463     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8464                                player->index_bit, push_side);
8465     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8466                                         player->index_bit, push_side);
8467   }
8468
8469   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8470     MovDelay[newx][newy] = 1;
8471
8472   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8473
8474   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8475   TestIfElementHitsCustomElement(newx, newy, direction);
8476   TestIfPlayerTouchesCustomElement(newx, newy);
8477   TestIfElementTouchesCustomElement(newx, newy);
8478
8479   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8480       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8481     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8482                              MV_DIR_OPPOSITE(direction));
8483 }
8484
8485 int AmoebeNachbarNr(int ax, int ay)
8486 {
8487   int i;
8488   int element = Feld[ax][ay];
8489   int group_nr = 0;
8490   static int xy[4][2] =
8491   {
8492     { 0, -1 },
8493     { -1, 0 },
8494     { +1, 0 },
8495     { 0, +1 }
8496   };
8497
8498   for (i = 0; i < NUM_DIRECTIONS; i++)
8499   {
8500     int x = ax + xy[i][0];
8501     int y = ay + xy[i][1];
8502
8503     if (!IN_LEV_FIELD(x, y))
8504       continue;
8505
8506     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8507       group_nr = AmoebaNr[x][y];
8508   }
8509
8510   return group_nr;
8511 }
8512
8513 void AmoebenVereinigen(int ax, int ay)
8514 {
8515   int i, x, y, xx, yy;
8516   int new_group_nr = AmoebaNr[ax][ay];
8517   static int xy[4][2] =
8518   {
8519     { 0, -1 },
8520     { -1, 0 },
8521     { +1, 0 },
8522     { 0, +1 }
8523   };
8524
8525   if (new_group_nr == 0)
8526     return;
8527
8528   for (i = 0; i < NUM_DIRECTIONS; i++)
8529   {
8530     x = ax + xy[i][0];
8531     y = ay + xy[i][1];
8532
8533     if (!IN_LEV_FIELD(x, y))
8534       continue;
8535
8536     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8537          Feld[x][y] == EL_BD_AMOEBA ||
8538          Feld[x][y] == EL_AMOEBA_DEAD) &&
8539         AmoebaNr[x][y] != new_group_nr)
8540     {
8541       int old_group_nr = AmoebaNr[x][y];
8542
8543       if (old_group_nr == 0)
8544         return;
8545
8546       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8547       AmoebaCnt[old_group_nr] = 0;
8548       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8549       AmoebaCnt2[old_group_nr] = 0;
8550
8551       SCAN_PLAYFIELD(xx, yy)
8552       {
8553         if (AmoebaNr[xx][yy] == old_group_nr)
8554           AmoebaNr[xx][yy] = new_group_nr;
8555       }
8556     }
8557   }
8558 }
8559
8560 void AmoebeUmwandeln(int ax, int ay)
8561 {
8562   int i, x, y;
8563
8564   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8565   {
8566     int group_nr = AmoebaNr[ax][ay];
8567
8568 #ifdef DEBUG
8569     if (group_nr == 0)
8570     {
8571       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8572       printf("AmoebeUmwandeln(): This should never happen!\n");
8573       return;
8574     }
8575 #endif
8576
8577     SCAN_PLAYFIELD(x, y)
8578     {
8579       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8580       {
8581         AmoebaNr[x][y] = 0;
8582         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8583       }
8584     }
8585
8586     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8587                             SND_AMOEBA_TURNING_TO_GEM :
8588                             SND_AMOEBA_TURNING_TO_ROCK));
8589     Bang(ax, ay);
8590   }
8591   else
8592   {
8593     static int xy[4][2] =
8594     {
8595       { 0, -1 },
8596       { -1, 0 },
8597       { +1, 0 },
8598       { 0, +1 }
8599     };
8600
8601     for (i = 0; i < NUM_DIRECTIONS; i++)
8602     {
8603       x = ax + xy[i][0];
8604       y = ay + xy[i][1];
8605
8606       if (!IN_LEV_FIELD(x, y))
8607         continue;
8608
8609       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8610       {
8611         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8612                               SND_AMOEBA_TURNING_TO_GEM :
8613                               SND_AMOEBA_TURNING_TO_ROCK));
8614         Bang(x, y);
8615       }
8616     }
8617   }
8618 }
8619
8620 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8621 {
8622   int x, y;
8623   int group_nr = AmoebaNr[ax][ay];
8624   boolean done = FALSE;
8625
8626 #ifdef DEBUG
8627   if (group_nr == 0)
8628   {
8629     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8630     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8631     return;
8632   }
8633 #endif
8634
8635   SCAN_PLAYFIELD(x, y)
8636   {
8637     if (AmoebaNr[x][y] == group_nr &&
8638         (Feld[x][y] == EL_AMOEBA_DEAD ||
8639          Feld[x][y] == EL_BD_AMOEBA ||
8640          Feld[x][y] == EL_AMOEBA_GROWING))
8641     {
8642       AmoebaNr[x][y] = 0;
8643       Feld[x][y] = new_element;
8644       InitField(x, y, FALSE);
8645       TEST_DrawLevelField(x, y);
8646       done = TRUE;
8647     }
8648   }
8649
8650   if (done)
8651     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8652                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8653                             SND_BD_AMOEBA_TURNING_TO_GEM));
8654 }
8655
8656 void AmoebeWaechst(int x, int y)
8657 {
8658   static unsigned int sound_delay = 0;
8659   static unsigned int sound_delay_value = 0;
8660
8661   if (!MovDelay[x][y])          /* start new growing cycle */
8662   {
8663     MovDelay[x][y] = 7;
8664
8665     if (DelayReached(&sound_delay, sound_delay_value))
8666     {
8667       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8668       sound_delay_value = 30;
8669     }
8670   }
8671
8672   if (MovDelay[x][y])           /* wait some time before growing bigger */
8673   {
8674     MovDelay[x][y]--;
8675     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8676     {
8677       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8678                                            6 - MovDelay[x][y]);
8679
8680       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8681     }
8682
8683     if (!MovDelay[x][y])
8684     {
8685       Feld[x][y] = Store[x][y];
8686       Store[x][y] = 0;
8687       TEST_DrawLevelField(x, y);
8688     }
8689   }
8690 }
8691
8692 void AmoebaDisappearing(int x, int y)
8693 {
8694   static unsigned int sound_delay = 0;
8695   static unsigned int sound_delay_value = 0;
8696
8697   if (!MovDelay[x][y])          /* start new shrinking cycle */
8698   {
8699     MovDelay[x][y] = 7;
8700
8701     if (DelayReached(&sound_delay, sound_delay_value))
8702       sound_delay_value = 30;
8703   }
8704
8705   if (MovDelay[x][y])           /* wait some time before shrinking */
8706   {
8707     MovDelay[x][y]--;
8708     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8709     {
8710       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8711                                            6 - MovDelay[x][y]);
8712
8713       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8714     }
8715
8716     if (!MovDelay[x][y])
8717     {
8718       Feld[x][y] = EL_EMPTY;
8719       TEST_DrawLevelField(x, y);
8720
8721       /* don't let mole enter this field in this cycle;
8722          (give priority to objects falling to this field from above) */
8723       Stop[x][y] = TRUE;
8724     }
8725   }
8726 }
8727
8728 void AmoebeAbleger(int ax, int ay)
8729 {
8730   int i;
8731   int element = Feld[ax][ay];
8732   int graphic = el2img(element);
8733   int newax = ax, neway = ay;
8734   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8735   static int xy[4][2] =
8736   {
8737     { 0, -1 },
8738     { -1, 0 },
8739     { +1, 0 },
8740     { 0, +1 }
8741   };
8742
8743   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8744   {
8745     Feld[ax][ay] = EL_AMOEBA_DEAD;
8746     TEST_DrawLevelField(ax, ay);
8747     return;
8748   }
8749
8750   if (IS_ANIMATED(graphic))
8751     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8752
8753   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8754     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8755
8756   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8757   {
8758     MovDelay[ax][ay]--;
8759     if (MovDelay[ax][ay])
8760       return;
8761   }
8762
8763   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8764   {
8765     int start = RND(4);
8766     int x = ax + xy[start][0];
8767     int y = ay + xy[start][1];
8768
8769     if (!IN_LEV_FIELD(x, y))
8770       return;
8771
8772     if (IS_FREE(x, y) ||
8773         CAN_GROW_INTO(Feld[x][y]) ||
8774         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8775         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8776     {
8777       newax = x;
8778       neway = y;
8779     }
8780
8781     if (newax == ax && neway == ay)
8782       return;
8783   }
8784   else                          /* normal or "filled" (BD style) amoeba */
8785   {
8786     int start = RND(4);
8787     boolean waiting_for_player = FALSE;
8788
8789     for (i = 0; i < NUM_DIRECTIONS; i++)
8790     {
8791       int j = (start + i) % 4;
8792       int x = ax + xy[j][0];
8793       int y = ay + xy[j][1];
8794
8795       if (!IN_LEV_FIELD(x, y))
8796         continue;
8797
8798       if (IS_FREE(x, y) ||
8799           CAN_GROW_INTO(Feld[x][y]) ||
8800           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8801           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8802       {
8803         newax = x;
8804         neway = y;
8805         break;
8806       }
8807       else if (IS_PLAYER(x, y))
8808         waiting_for_player = TRUE;
8809     }
8810
8811     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8812     {
8813       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8814       {
8815         Feld[ax][ay] = EL_AMOEBA_DEAD;
8816         TEST_DrawLevelField(ax, ay);
8817         AmoebaCnt[AmoebaNr[ax][ay]]--;
8818
8819         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8820         {
8821           if (element == EL_AMOEBA_FULL)
8822             AmoebeUmwandeln(ax, ay);
8823           else if (element == EL_BD_AMOEBA)
8824             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8825         }
8826       }
8827       return;
8828     }
8829     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8830     {
8831       /* amoeba gets larger by growing in some direction */
8832
8833       int new_group_nr = AmoebaNr[ax][ay];
8834
8835 #ifdef DEBUG
8836   if (new_group_nr == 0)
8837   {
8838     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8839     printf("AmoebeAbleger(): This should never happen!\n");
8840     return;
8841   }
8842 #endif
8843
8844       AmoebaNr[newax][neway] = new_group_nr;
8845       AmoebaCnt[new_group_nr]++;
8846       AmoebaCnt2[new_group_nr]++;
8847
8848       /* if amoeba touches other amoeba(s) after growing, unify them */
8849       AmoebenVereinigen(newax, neway);
8850
8851       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8852       {
8853         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8854         return;
8855       }
8856     }
8857   }
8858
8859   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8860       (neway == lev_fieldy - 1 && newax != ax))
8861   {
8862     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8863     Store[newax][neway] = element;
8864   }
8865   else if (neway == ay || element == EL_EMC_DRIPPER)
8866   {
8867     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8868
8869     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8870   }
8871   else
8872   {
8873     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8874     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8875     Store[ax][ay] = EL_AMOEBA_DROP;
8876     ContinueMoving(ax, ay);
8877     return;
8878   }
8879
8880   TEST_DrawLevelField(newax, neway);
8881 }
8882
8883 void Life(int ax, int ay)
8884 {
8885   int x1, y1, x2, y2;
8886   int life_time = 40;
8887   int element = Feld[ax][ay];
8888   int graphic = el2img(element);
8889   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8890                          level.biomaze);
8891   boolean changed = FALSE;
8892
8893   if (IS_ANIMATED(graphic))
8894     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8895
8896   if (Stop[ax][ay])
8897     return;
8898
8899   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8900     MovDelay[ax][ay] = life_time;
8901
8902   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8903   {
8904     MovDelay[ax][ay]--;
8905     if (MovDelay[ax][ay])
8906       return;
8907   }
8908
8909   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8910   {
8911     int xx = ax+x1, yy = ay+y1;
8912     int nachbarn = 0;
8913
8914     if (!IN_LEV_FIELD(xx, yy))
8915       continue;
8916
8917     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8918     {
8919       int x = xx+x2, y = yy+y2;
8920
8921       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8922         continue;
8923
8924       if (((Feld[x][y] == element ||
8925             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8926            !Stop[x][y]) ||
8927           (IS_FREE(x, y) && Stop[x][y]))
8928         nachbarn++;
8929     }
8930
8931     if (xx == ax && yy == ay)           /* field in the middle */
8932     {
8933       if (nachbarn < life_parameter[0] ||
8934           nachbarn > life_parameter[1])
8935       {
8936         Feld[xx][yy] = EL_EMPTY;
8937         if (!Stop[xx][yy])
8938           TEST_DrawLevelField(xx, yy);
8939         Stop[xx][yy] = TRUE;
8940         changed = TRUE;
8941       }
8942     }
8943     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8944     {                                   /* free border field */
8945       if (nachbarn >= life_parameter[2] &&
8946           nachbarn <= life_parameter[3])
8947       {
8948         Feld[xx][yy] = element;
8949         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8950         if (!Stop[xx][yy])
8951           TEST_DrawLevelField(xx, yy);
8952         Stop[xx][yy] = TRUE;
8953         changed = TRUE;
8954       }
8955     }
8956   }
8957
8958   if (changed)
8959     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8960                    SND_GAME_OF_LIFE_GROWING);
8961 }
8962
8963 static void InitRobotWheel(int x, int y)
8964 {
8965   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8966 }
8967
8968 static void RunRobotWheel(int x, int y)
8969 {
8970   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8971 }
8972
8973 static void StopRobotWheel(int x, int y)
8974 {
8975   if (ZX == x && ZY == y)
8976   {
8977     ZX = ZY = -1;
8978
8979     game.robot_wheel_active = FALSE;
8980   }
8981 }
8982
8983 static void InitTimegateWheel(int x, int y)
8984 {
8985   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8986 }
8987
8988 static void RunTimegateWheel(int x, int y)
8989 {
8990   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8991 }
8992
8993 static void InitMagicBallDelay(int x, int y)
8994 {
8995   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8996 }
8997
8998 static void ActivateMagicBall(int bx, int by)
8999 {
9000   int x, y;
9001
9002   if (level.ball_random)
9003   {
9004     int pos_border = RND(8);    /* select one of the eight border elements */
9005     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9006     int xx = pos_content % 3;
9007     int yy = pos_content / 3;
9008
9009     x = bx - 1 + xx;
9010     y = by - 1 + yy;
9011
9012     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9013       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9014   }
9015   else
9016   {
9017     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9018     {
9019       int xx = x - bx + 1;
9020       int yy = y - by + 1;
9021
9022       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9023         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9024     }
9025   }
9026
9027   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9028 }
9029
9030 void CheckExit(int x, int y)
9031 {
9032   if (local_player->gems_still_needed > 0 ||
9033       local_player->sokobanfields_still_needed > 0 ||
9034       local_player->lights_still_needed > 0)
9035   {
9036     int element = Feld[x][y];
9037     int graphic = el2img(element);
9038
9039     if (IS_ANIMATED(graphic))
9040       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9041
9042     return;
9043   }
9044
9045   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9046     return;
9047
9048   Feld[x][y] = EL_EXIT_OPENING;
9049
9050   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9051 }
9052
9053 void CheckExitEM(int x, int y)
9054 {
9055   if (local_player->gems_still_needed > 0 ||
9056       local_player->sokobanfields_still_needed > 0 ||
9057       local_player->lights_still_needed > 0)
9058   {
9059     int element = Feld[x][y];
9060     int graphic = el2img(element);
9061
9062     if (IS_ANIMATED(graphic))
9063       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9064
9065     return;
9066   }
9067
9068   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9069     return;
9070
9071   Feld[x][y] = EL_EM_EXIT_OPENING;
9072
9073   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9074 }
9075
9076 void CheckExitSteel(int x, int y)
9077 {
9078   if (local_player->gems_still_needed > 0 ||
9079       local_player->sokobanfields_still_needed > 0 ||
9080       local_player->lights_still_needed > 0)
9081   {
9082     int element = Feld[x][y];
9083     int graphic = el2img(element);
9084
9085     if (IS_ANIMATED(graphic))
9086       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9087
9088     return;
9089   }
9090
9091   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9092     return;
9093
9094   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9095
9096   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9097 }
9098
9099 void CheckExitSteelEM(int x, int y)
9100 {
9101   if (local_player->gems_still_needed > 0 ||
9102       local_player->sokobanfields_still_needed > 0 ||
9103       local_player->lights_still_needed > 0)
9104   {
9105     int element = Feld[x][y];
9106     int graphic = el2img(element);
9107
9108     if (IS_ANIMATED(graphic))
9109       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9110
9111     return;
9112   }
9113
9114   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9115     return;
9116
9117   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9118
9119   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9120 }
9121
9122 void CheckExitSP(int x, int y)
9123 {
9124   if (local_player->gems_still_needed > 0)
9125   {
9126     int element = Feld[x][y];
9127     int graphic = el2img(element);
9128
9129     if (IS_ANIMATED(graphic))
9130       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9131
9132     return;
9133   }
9134
9135   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9136     return;
9137
9138   Feld[x][y] = EL_SP_EXIT_OPENING;
9139
9140   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9141 }
9142
9143 static void CloseAllOpenTimegates()
9144 {
9145   int x, y;
9146
9147   SCAN_PLAYFIELD(x, y)
9148   {
9149     int element = Feld[x][y];
9150
9151     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9152     {
9153       Feld[x][y] = EL_TIMEGATE_CLOSING;
9154
9155       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9156     }
9157   }
9158 }
9159
9160 void DrawTwinkleOnField(int x, int y)
9161 {
9162   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9163     return;
9164
9165   if (Feld[x][y] == EL_BD_DIAMOND)
9166     return;
9167
9168   if (MovDelay[x][y] == 0)      /* next animation frame */
9169     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9170
9171   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9172   {
9173     MovDelay[x][y]--;
9174
9175     DrawLevelElementAnimation(x, y, Feld[x][y]);
9176
9177     if (MovDelay[x][y] != 0)
9178     {
9179       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9180                                            10 - MovDelay[x][y]);
9181
9182       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9183     }
9184   }
9185 }
9186
9187 void MauerWaechst(int x, int y)
9188 {
9189   int delay = 6;
9190
9191   if (!MovDelay[x][y])          /* next animation frame */
9192     MovDelay[x][y] = 3 * delay;
9193
9194   if (MovDelay[x][y])           /* wait some time before next frame */
9195   {
9196     MovDelay[x][y]--;
9197
9198     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9199     {
9200       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9201       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9202
9203       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9204     }
9205
9206     if (!MovDelay[x][y])
9207     {
9208       if (MovDir[x][y] == MV_LEFT)
9209       {
9210         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9211           TEST_DrawLevelField(x - 1, y);
9212       }
9213       else if (MovDir[x][y] == MV_RIGHT)
9214       {
9215         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9216           TEST_DrawLevelField(x + 1, y);
9217       }
9218       else if (MovDir[x][y] == MV_UP)
9219       {
9220         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9221           TEST_DrawLevelField(x, y - 1);
9222       }
9223       else
9224       {
9225         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9226           TEST_DrawLevelField(x, y + 1);
9227       }
9228
9229       Feld[x][y] = Store[x][y];
9230       Store[x][y] = 0;
9231       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9232       TEST_DrawLevelField(x, y);
9233     }
9234   }
9235 }
9236
9237 void MauerAbleger(int ax, int ay)
9238 {
9239   int element = Feld[ax][ay];
9240   int graphic = el2img(element);
9241   boolean oben_frei = FALSE, unten_frei = FALSE;
9242   boolean links_frei = FALSE, rechts_frei = FALSE;
9243   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9244   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9245   boolean new_wall = FALSE;
9246
9247   if (IS_ANIMATED(graphic))
9248     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9249
9250   if (!MovDelay[ax][ay])        /* start building new wall */
9251     MovDelay[ax][ay] = 6;
9252
9253   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9254   {
9255     MovDelay[ax][ay]--;
9256     if (MovDelay[ax][ay])
9257       return;
9258   }
9259
9260   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9261     oben_frei = TRUE;
9262   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9263     unten_frei = TRUE;
9264   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9265     links_frei = TRUE;
9266   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9267     rechts_frei = TRUE;
9268
9269   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9270       element == EL_EXPANDABLE_WALL_ANY)
9271   {
9272     if (oben_frei)
9273     {
9274       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9275       Store[ax][ay-1] = element;
9276       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9277       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9278         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9279                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9280       new_wall = TRUE;
9281     }
9282     if (unten_frei)
9283     {
9284       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9285       Store[ax][ay+1] = element;
9286       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9287       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9288         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9289                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9290       new_wall = TRUE;
9291     }
9292   }
9293
9294   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9295       element == EL_EXPANDABLE_WALL_ANY ||
9296       element == EL_EXPANDABLE_WALL ||
9297       element == EL_BD_EXPANDABLE_WALL)
9298   {
9299     if (links_frei)
9300     {
9301       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9302       Store[ax-1][ay] = element;
9303       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9304       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9305         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9306                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9307       new_wall = TRUE;
9308     }
9309
9310     if (rechts_frei)
9311     {
9312       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9313       Store[ax+1][ay] = element;
9314       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9315       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9316         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9317                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9318       new_wall = TRUE;
9319     }
9320   }
9321
9322   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9323     TEST_DrawLevelField(ax, ay);
9324
9325   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9326     oben_massiv = TRUE;
9327   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9328     unten_massiv = TRUE;
9329   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9330     links_massiv = TRUE;
9331   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9332     rechts_massiv = TRUE;
9333
9334   if (((oben_massiv && unten_massiv) ||
9335        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9336        element == EL_EXPANDABLE_WALL) &&
9337       ((links_massiv && rechts_massiv) ||
9338        element == EL_EXPANDABLE_WALL_VERTICAL))
9339     Feld[ax][ay] = EL_WALL;
9340
9341   if (new_wall)
9342     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9343 }
9344
9345 void MauerAblegerStahl(int ax, int ay)
9346 {
9347   int element = Feld[ax][ay];
9348   int graphic = el2img(element);
9349   boolean oben_frei = FALSE, unten_frei = FALSE;
9350   boolean links_frei = FALSE, rechts_frei = FALSE;
9351   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9352   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9353   boolean new_wall = FALSE;
9354
9355   if (IS_ANIMATED(graphic))
9356     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9357
9358   if (!MovDelay[ax][ay])        /* start building new wall */
9359     MovDelay[ax][ay] = 6;
9360
9361   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9362   {
9363     MovDelay[ax][ay]--;
9364     if (MovDelay[ax][ay])
9365       return;
9366   }
9367
9368   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9369     oben_frei = TRUE;
9370   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9371     unten_frei = TRUE;
9372   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9373     links_frei = TRUE;
9374   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9375     rechts_frei = TRUE;
9376
9377   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9378       element == EL_EXPANDABLE_STEELWALL_ANY)
9379   {
9380     if (oben_frei)
9381     {
9382       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9383       Store[ax][ay-1] = element;
9384       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9385       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9386         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9387                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9388       new_wall = TRUE;
9389     }
9390     if (unten_frei)
9391     {
9392       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9393       Store[ax][ay+1] = element;
9394       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9395       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9396         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9397                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9398       new_wall = TRUE;
9399     }
9400   }
9401
9402   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9403       element == EL_EXPANDABLE_STEELWALL_ANY)
9404   {
9405     if (links_frei)
9406     {
9407       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9408       Store[ax-1][ay] = element;
9409       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9410       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9411         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9412                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9413       new_wall = TRUE;
9414     }
9415
9416     if (rechts_frei)
9417     {
9418       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9419       Store[ax+1][ay] = element;
9420       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9421       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9422         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9423                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9424       new_wall = TRUE;
9425     }
9426   }
9427
9428   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9429     oben_massiv = TRUE;
9430   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9431     unten_massiv = TRUE;
9432   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9433     links_massiv = TRUE;
9434   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9435     rechts_massiv = TRUE;
9436
9437   if (((oben_massiv && unten_massiv) ||
9438        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9439       ((links_massiv && rechts_massiv) ||
9440        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9441     Feld[ax][ay] = EL_STEELWALL;
9442
9443   if (new_wall)
9444     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9445 }
9446
9447 void CheckForDragon(int x, int y)
9448 {
9449   int i, j;
9450   boolean dragon_found = FALSE;
9451   static int xy[4][2] =
9452   {
9453     { 0, -1 },
9454     { -1, 0 },
9455     { +1, 0 },
9456     { 0, +1 }
9457   };
9458
9459   for (i = 0; i < NUM_DIRECTIONS; i++)
9460   {
9461     for (j = 0; j < 4; j++)
9462     {
9463       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9464
9465       if (IN_LEV_FIELD(xx, yy) &&
9466           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9467       {
9468         if (Feld[xx][yy] == EL_DRAGON)
9469           dragon_found = TRUE;
9470       }
9471       else
9472         break;
9473     }
9474   }
9475
9476   if (!dragon_found)
9477   {
9478     for (i = 0; i < NUM_DIRECTIONS; i++)
9479     {
9480       for (j = 0; j < 3; j++)
9481       {
9482         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9483   
9484         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9485         {
9486           Feld[xx][yy] = EL_EMPTY;
9487           TEST_DrawLevelField(xx, yy);
9488         }
9489         else
9490           break;
9491       }
9492     }
9493   }
9494 }
9495
9496 static void InitBuggyBase(int x, int y)
9497 {
9498   int element = Feld[x][y];
9499   int activating_delay = FRAMES_PER_SECOND / 4;
9500
9501   ChangeDelay[x][y] =
9502     (element == EL_SP_BUGGY_BASE ?
9503      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9504      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9505      activating_delay :
9506      element == EL_SP_BUGGY_BASE_ACTIVE ?
9507      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9508 }
9509
9510 static void WarnBuggyBase(int x, int y)
9511 {
9512   int i;
9513   static int xy[4][2] =
9514   {
9515     { 0, -1 },
9516     { -1, 0 },
9517     { +1, 0 },
9518     { 0, +1 }
9519   };
9520
9521   for (i = 0; i < NUM_DIRECTIONS; i++)
9522   {
9523     int xx = x + xy[i][0];
9524     int yy = y + xy[i][1];
9525
9526     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9527     {
9528       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9529
9530       break;
9531     }
9532   }
9533 }
9534
9535 static void InitTrap(int x, int y)
9536 {
9537   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9538 }
9539
9540 static void ActivateTrap(int x, int y)
9541 {
9542   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9543 }
9544
9545 static void ChangeActiveTrap(int x, int y)
9546 {
9547   int graphic = IMG_TRAP_ACTIVE;
9548
9549   /* if new animation frame was drawn, correct crumbled sand border */
9550   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9551     TEST_DrawLevelFieldCrumbled(x, y);
9552 }
9553
9554 static int getSpecialActionElement(int element, int number, int base_element)
9555 {
9556   return (element != EL_EMPTY ? element :
9557           number != -1 ? base_element + number - 1 :
9558           EL_EMPTY);
9559 }
9560
9561 static int getModifiedActionNumber(int value_old, int operator, int operand,
9562                                    int value_min, int value_max)
9563 {
9564   int value_new = (operator == CA_MODE_SET      ? operand :
9565                    operator == CA_MODE_ADD      ? value_old + operand :
9566                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9567                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9568                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9569                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9570                    value_old);
9571
9572   return (value_new < value_min ? value_min :
9573           value_new > value_max ? value_max :
9574           value_new);
9575 }
9576
9577 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9578 {
9579   struct ElementInfo *ei = &element_info[element];
9580   struct ElementChangeInfo *change = &ei->change_page[page];
9581   int target_element = change->target_element;
9582   int action_type = change->action_type;
9583   int action_mode = change->action_mode;
9584   int action_arg = change->action_arg;
9585   int action_element = change->action_element;
9586   int i;
9587
9588   if (!change->has_action)
9589     return;
9590
9591   /* ---------- determine action paramater values -------------------------- */
9592
9593   int level_time_value =
9594     (level.time > 0 ? TimeLeft :
9595      TimePlayed);
9596
9597   int action_arg_element_raw =
9598     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9599      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9600      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9601      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9602      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9603      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9604      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9605      EL_EMPTY);
9606   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9607
9608   int action_arg_direction =
9609     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9610      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9611      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9612      change->actual_trigger_side :
9613      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9614      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9615      MV_NONE);
9616
9617   int action_arg_number_min =
9618     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9619      CA_ARG_MIN);
9620
9621   int action_arg_number_max =
9622     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9623      action_type == CA_SET_LEVEL_GEMS ? 999 :
9624      action_type == CA_SET_LEVEL_TIME ? 9999 :
9625      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9626      action_type == CA_SET_CE_VALUE ? 9999 :
9627      action_type == CA_SET_CE_SCORE ? 9999 :
9628      CA_ARG_MAX);
9629
9630   int action_arg_number_reset =
9631     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9632      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9633      action_type == CA_SET_LEVEL_TIME ? level.time :
9634      action_type == CA_SET_LEVEL_SCORE ? 0 :
9635      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9636      action_type == CA_SET_CE_SCORE ? 0 :
9637      0);
9638
9639   int action_arg_number =
9640     (action_arg <= CA_ARG_MAX ? action_arg :
9641      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9642      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9643      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9644      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9645      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9646      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9647      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9648      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9649      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9650      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9651      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9652      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9653      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9654      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9655      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9656      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9657      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9658      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9659      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9660      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9661      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9662      -1);
9663
9664   int action_arg_number_old =
9665     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9666      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9667      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9668      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9669      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9670      0);
9671
9672   int action_arg_number_new =
9673     getModifiedActionNumber(action_arg_number_old,
9674                             action_mode, action_arg_number,
9675                             action_arg_number_min, action_arg_number_max);
9676
9677   int trigger_player_bits =
9678     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9679      change->actual_trigger_player_bits : change->trigger_player);
9680
9681   int action_arg_player_bits =
9682     (action_arg >= CA_ARG_PLAYER_1 &&
9683      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9684      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9685      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9686      PLAYER_BITS_ANY);
9687
9688   /* ---------- execute action  -------------------------------------------- */
9689
9690   switch (action_type)
9691   {
9692     case CA_NO_ACTION:
9693     {
9694       return;
9695     }
9696
9697     /* ---------- level actions  ------------------------------------------- */
9698
9699     case CA_RESTART_LEVEL:
9700     {
9701       game.restart_level = TRUE;
9702
9703       break;
9704     }
9705
9706     case CA_SHOW_ENVELOPE:
9707     {
9708       int element = getSpecialActionElement(action_arg_element,
9709                                             action_arg_number, EL_ENVELOPE_1);
9710
9711       if (IS_ENVELOPE(element))
9712         local_player->show_envelope = element;
9713
9714       break;
9715     }
9716
9717     case CA_SET_LEVEL_TIME:
9718     {
9719       if (level.time > 0)       /* only modify limited time value */
9720       {
9721         TimeLeft = action_arg_number_new;
9722
9723         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9724
9725         DisplayGameControlValues();
9726
9727         if (!TimeLeft && setup.time_limit)
9728           for (i = 0; i < MAX_PLAYERS; i++)
9729             KillPlayer(&stored_player[i]);
9730       }
9731
9732       break;
9733     }
9734
9735     case CA_SET_LEVEL_SCORE:
9736     {
9737       local_player->score = action_arg_number_new;
9738
9739       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9740
9741       DisplayGameControlValues();
9742
9743       break;
9744     }
9745
9746     case CA_SET_LEVEL_GEMS:
9747     {
9748       local_player->gems_still_needed = action_arg_number_new;
9749
9750       game.snapshot.collected_item = TRUE;
9751
9752       game_panel_controls[GAME_PANEL_GEMS].value =
9753         local_player->gems_still_needed;
9754
9755       DisplayGameControlValues();
9756
9757       break;
9758     }
9759
9760     case CA_SET_LEVEL_WIND:
9761     {
9762       game.wind_direction = action_arg_direction;
9763
9764       break;
9765     }
9766
9767     case CA_SET_LEVEL_RANDOM_SEED:
9768     {
9769       /* ensure that setting a new random seed while playing is predictable */
9770       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9771
9772       break;
9773     }
9774
9775     /* ---------- player actions  ------------------------------------------ */
9776
9777     case CA_MOVE_PLAYER:
9778     {
9779       /* automatically move to the next field in specified direction */
9780       for (i = 0; i < MAX_PLAYERS; i++)
9781         if (trigger_player_bits & (1 << i))
9782           stored_player[i].programmed_action = action_arg_direction;
9783
9784       break;
9785     }
9786
9787     case CA_EXIT_PLAYER:
9788     {
9789       for (i = 0; i < MAX_PLAYERS; i++)
9790         if (action_arg_player_bits & (1 << i))
9791           ExitPlayer(&stored_player[i]);
9792
9793       if (AllPlayersGone)
9794         PlayerWins(local_player);
9795
9796       break;
9797     }
9798
9799     case CA_KILL_PLAYER:
9800     {
9801       for (i = 0; i < MAX_PLAYERS; i++)
9802         if (action_arg_player_bits & (1 << i))
9803           KillPlayer(&stored_player[i]);
9804
9805       break;
9806     }
9807
9808     case CA_SET_PLAYER_KEYS:
9809     {
9810       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9811       int element = getSpecialActionElement(action_arg_element,
9812                                             action_arg_number, EL_KEY_1);
9813
9814       if (IS_KEY(element))
9815       {
9816         for (i = 0; i < MAX_PLAYERS; i++)
9817         {
9818           if (trigger_player_bits & (1 << i))
9819           {
9820             stored_player[i].key[KEY_NR(element)] = key_state;
9821
9822             DrawGameDoorValues();
9823           }
9824         }
9825       }
9826
9827       break;
9828     }
9829
9830     case CA_SET_PLAYER_SPEED:
9831     {
9832       for (i = 0; i < MAX_PLAYERS; i++)
9833       {
9834         if (trigger_player_bits & (1 << i))
9835         {
9836           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9837
9838           if (action_arg == CA_ARG_SPEED_FASTER &&
9839               stored_player[i].cannot_move)
9840           {
9841             action_arg_number = STEPSIZE_VERY_SLOW;
9842           }
9843           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9844                    action_arg == CA_ARG_SPEED_FASTER)
9845           {
9846             action_arg_number = 2;
9847             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9848                            CA_MODE_MULTIPLY);
9849           }
9850           else if (action_arg == CA_ARG_NUMBER_RESET)
9851           {
9852             action_arg_number = level.initial_player_stepsize[i];
9853           }
9854
9855           move_stepsize =
9856             getModifiedActionNumber(move_stepsize,
9857                                     action_mode,
9858                                     action_arg_number,
9859                                     action_arg_number_min,
9860                                     action_arg_number_max);
9861
9862           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9863         }
9864       }
9865
9866       break;
9867     }
9868
9869     case CA_SET_PLAYER_SHIELD:
9870     {
9871       for (i = 0; i < MAX_PLAYERS; i++)
9872       {
9873         if (trigger_player_bits & (1 << i))
9874         {
9875           if (action_arg == CA_ARG_SHIELD_OFF)
9876           {
9877             stored_player[i].shield_normal_time_left = 0;
9878             stored_player[i].shield_deadly_time_left = 0;
9879           }
9880           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9881           {
9882             stored_player[i].shield_normal_time_left = 999999;
9883           }
9884           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9885           {
9886             stored_player[i].shield_normal_time_left = 999999;
9887             stored_player[i].shield_deadly_time_left = 999999;
9888           }
9889         }
9890       }
9891
9892       break;
9893     }
9894
9895     case CA_SET_PLAYER_GRAVITY:
9896     {
9897       for (i = 0; i < MAX_PLAYERS; i++)
9898       {
9899         if (trigger_player_bits & (1 << i))
9900         {
9901           stored_player[i].gravity =
9902             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9903              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9904              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9905              stored_player[i].gravity);
9906         }
9907       }
9908
9909       break;
9910     }
9911
9912     case CA_SET_PLAYER_ARTWORK:
9913     {
9914       for (i = 0; i < MAX_PLAYERS; i++)
9915       {
9916         if (trigger_player_bits & (1 << i))
9917         {
9918           int artwork_element = action_arg_element;
9919
9920           if (action_arg == CA_ARG_ELEMENT_RESET)
9921             artwork_element =
9922               (level.use_artwork_element[i] ? level.artwork_element[i] :
9923                stored_player[i].element_nr);
9924
9925           if (stored_player[i].artwork_element != artwork_element)
9926             stored_player[i].Frame = 0;
9927
9928           stored_player[i].artwork_element = artwork_element;
9929
9930           SetPlayerWaiting(&stored_player[i], FALSE);
9931
9932           /* set number of special actions for bored and sleeping animation */
9933           stored_player[i].num_special_action_bored =
9934             get_num_special_action(artwork_element,
9935                                    ACTION_BORING_1, ACTION_BORING_LAST);
9936           stored_player[i].num_special_action_sleeping =
9937             get_num_special_action(artwork_element,
9938                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9939         }
9940       }
9941
9942       break;
9943     }
9944
9945     case CA_SET_PLAYER_INVENTORY:
9946     {
9947       for (i = 0; i < MAX_PLAYERS; i++)
9948       {
9949         struct PlayerInfo *player = &stored_player[i];
9950         int j, k;
9951
9952         if (trigger_player_bits & (1 << i))
9953         {
9954           int inventory_element = action_arg_element;
9955
9956           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9957               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9958               action_arg == CA_ARG_ELEMENT_ACTION)
9959           {
9960             int element = inventory_element;
9961             int collect_count = element_info[element].collect_count_initial;
9962
9963             if (!IS_CUSTOM_ELEMENT(element))
9964               collect_count = 1;
9965
9966             if (collect_count == 0)
9967               player->inventory_infinite_element = element;
9968             else
9969               for (k = 0; k < collect_count; k++)
9970                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9971                   player->inventory_element[player->inventory_size++] =
9972                     element;
9973           }
9974           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9975                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9976                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9977           {
9978             if (player->inventory_infinite_element != EL_UNDEFINED &&
9979                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9980                                      action_arg_element_raw))
9981               player->inventory_infinite_element = EL_UNDEFINED;
9982
9983             for (k = 0, j = 0; j < player->inventory_size; j++)
9984             {
9985               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9986                                         action_arg_element_raw))
9987                 player->inventory_element[k++] = player->inventory_element[j];
9988             }
9989
9990             player->inventory_size = k;
9991           }
9992           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9993           {
9994             if (player->inventory_size > 0)
9995             {
9996               for (j = 0; j < player->inventory_size - 1; j++)
9997                 player->inventory_element[j] = player->inventory_element[j + 1];
9998
9999               player->inventory_size--;
10000             }
10001           }
10002           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10003           {
10004             if (player->inventory_size > 0)
10005               player->inventory_size--;
10006           }
10007           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10008           {
10009             player->inventory_infinite_element = EL_UNDEFINED;
10010             player->inventory_size = 0;
10011           }
10012           else if (action_arg == CA_ARG_INVENTORY_RESET)
10013           {
10014             player->inventory_infinite_element = EL_UNDEFINED;
10015             player->inventory_size = 0;
10016
10017             if (level.use_initial_inventory[i])
10018             {
10019               for (j = 0; j < level.initial_inventory_size[i]; j++)
10020               {
10021                 int element = level.initial_inventory_content[i][j];
10022                 int collect_count = element_info[element].collect_count_initial;
10023
10024                 if (!IS_CUSTOM_ELEMENT(element))
10025                   collect_count = 1;
10026
10027                 if (collect_count == 0)
10028                   player->inventory_infinite_element = element;
10029                 else
10030                   for (k = 0; k < collect_count; k++)
10031                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10032                       player->inventory_element[player->inventory_size++] =
10033                         element;
10034               }
10035             }
10036           }
10037         }
10038       }
10039
10040       break;
10041     }
10042
10043     /* ---------- CE actions  ---------------------------------------------- */
10044
10045     case CA_SET_CE_VALUE:
10046     {
10047       int last_ce_value = CustomValue[x][y];
10048
10049       CustomValue[x][y] = action_arg_number_new;
10050
10051       if (CustomValue[x][y] != last_ce_value)
10052       {
10053         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10054         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10055
10056         if (CustomValue[x][y] == 0)
10057         {
10058           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10059           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10060         }
10061       }
10062
10063       break;
10064     }
10065
10066     case CA_SET_CE_SCORE:
10067     {
10068       int last_ce_score = ei->collect_score;
10069
10070       ei->collect_score = action_arg_number_new;
10071
10072       if (ei->collect_score != last_ce_score)
10073       {
10074         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10075         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10076
10077         if (ei->collect_score == 0)
10078         {
10079           int xx, yy;
10080
10081           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10082           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10083
10084           /*
10085             This is a very special case that seems to be a mixture between
10086             CheckElementChange() and CheckTriggeredElementChange(): while
10087             the first one only affects single elements that are triggered
10088             directly, the second one affects multiple elements in the playfield
10089             that are triggered indirectly by another element. This is a third
10090             case: Changing the CE score always affects multiple identical CEs,
10091             so every affected CE must be checked, not only the single CE for
10092             which the CE score was changed in the first place (as every instance
10093             of that CE shares the same CE score, and therefore also can change)!
10094           */
10095           SCAN_PLAYFIELD(xx, yy)
10096           {
10097             if (Feld[xx][yy] == element)
10098               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10099                                  CE_SCORE_GETS_ZERO);
10100           }
10101         }
10102       }
10103
10104       break;
10105     }
10106
10107     case CA_SET_CE_ARTWORK:
10108     {
10109       int artwork_element = action_arg_element;
10110       boolean reset_frame = FALSE;
10111       int xx, yy;
10112
10113       if (action_arg == CA_ARG_ELEMENT_RESET)
10114         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10115                            element);
10116
10117       if (ei->gfx_element != artwork_element)
10118         reset_frame = TRUE;
10119
10120       ei->gfx_element = artwork_element;
10121
10122       SCAN_PLAYFIELD(xx, yy)
10123       {
10124         if (Feld[xx][yy] == element)
10125         {
10126           if (reset_frame)
10127           {
10128             ResetGfxAnimation(xx, yy);
10129             ResetRandomAnimationValue(xx, yy);
10130           }
10131
10132           TEST_DrawLevelField(xx, yy);
10133         }
10134       }
10135
10136       break;
10137     }
10138
10139     /* ---------- engine actions  ------------------------------------------ */
10140
10141     case CA_SET_ENGINE_SCAN_MODE:
10142     {
10143       InitPlayfieldScanMode(action_arg);
10144
10145       break;
10146     }
10147
10148     default:
10149       break;
10150   }
10151 }
10152
10153 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10154 {
10155   int old_element = Feld[x][y];
10156   int new_element = GetElementFromGroupElement(element);
10157   int previous_move_direction = MovDir[x][y];
10158   int last_ce_value = CustomValue[x][y];
10159   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10160   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10161   boolean add_player_onto_element = (new_element_is_player &&
10162                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10163                                      IS_WALKABLE(old_element));
10164
10165   if (!add_player_onto_element)
10166   {
10167     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10168       RemoveMovingField(x, y);
10169     else
10170       RemoveField(x, y);
10171
10172     Feld[x][y] = new_element;
10173
10174     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10175       MovDir[x][y] = previous_move_direction;
10176
10177     if (element_info[new_element].use_last_ce_value)
10178       CustomValue[x][y] = last_ce_value;
10179
10180     InitField_WithBug1(x, y, FALSE);
10181
10182     new_element = Feld[x][y];   /* element may have changed */
10183
10184     ResetGfxAnimation(x, y);
10185     ResetRandomAnimationValue(x, y);
10186
10187     TEST_DrawLevelField(x, y);
10188
10189     if (GFX_CRUMBLED(new_element))
10190       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10191   }
10192
10193   /* check if element under the player changes from accessible to unaccessible
10194      (needed for special case of dropping element which then changes) */
10195   /* (must be checked after creating new element for walkable group elements) */
10196   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10197       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10198   {
10199     Bang(x, y);
10200
10201     return;
10202   }
10203
10204   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10205   if (new_element_is_player)
10206     RelocatePlayer(x, y, new_element);
10207
10208   if (is_change)
10209     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10210
10211   TestIfBadThingTouchesPlayer(x, y);
10212   TestIfPlayerTouchesCustomElement(x, y);
10213   TestIfElementTouchesCustomElement(x, y);
10214 }
10215
10216 static void CreateField(int x, int y, int element)
10217 {
10218   CreateFieldExt(x, y, element, FALSE);
10219 }
10220
10221 static void CreateElementFromChange(int x, int y, int element)
10222 {
10223   element = GET_VALID_RUNTIME_ELEMENT(element);
10224
10225   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10226   {
10227     int old_element = Feld[x][y];
10228
10229     /* prevent changed element from moving in same engine frame
10230        unless both old and new element can either fall or move */
10231     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10232         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10233       Stop[x][y] = TRUE;
10234   }
10235
10236   CreateFieldExt(x, y, element, TRUE);
10237 }
10238
10239 static boolean ChangeElement(int x, int y, int element, int page)
10240 {
10241   struct ElementInfo *ei = &element_info[element];
10242   struct ElementChangeInfo *change = &ei->change_page[page];
10243   int ce_value = CustomValue[x][y];
10244   int ce_score = ei->collect_score;
10245   int target_element;
10246   int old_element = Feld[x][y];
10247
10248   /* always use default change event to prevent running into a loop */
10249   if (ChangeEvent[x][y] == -1)
10250     ChangeEvent[x][y] = CE_DELAY;
10251
10252   if (ChangeEvent[x][y] == CE_DELAY)
10253   {
10254     /* reset actual trigger element, trigger player and action element */
10255     change->actual_trigger_element = EL_EMPTY;
10256     change->actual_trigger_player = EL_EMPTY;
10257     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10258     change->actual_trigger_side = CH_SIDE_NONE;
10259     change->actual_trigger_ce_value = 0;
10260     change->actual_trigger_ce_score = 0;
10261   }
10262
10263   /* do not change elements more than a specified maximum number of changes */
10264   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10265     return FALSE;
10266
10267   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10268
10269   if (change->explode)
10270   {
10271     Bang(x, y);
10272
10273     return TRUE;
10274   }
10275
10276   if (change->use_target_content)
10277   {
10278     boolean complete_replace = TRUE;
10279     boolean can_replace[3][3];
10280     int xx, yy;
10281
10282     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10283     {
10284       boolean is_empty;
10285       boolean is_walkable;
10286       boolean is_diggable;
10287       boolean is_collectible;
10288       boolean is_removable;
10289       boolean is_destructible;
10290       int ex = x + xx - 1;
10291       int ey = y + yy - 1;
10292       int content_element = change->target_content.e[xx][yy];
10293       int e;
10294
10295       can_replace[xx][yy] = TRUE;
10296
10297       if (ex == x && ey == y)   /* do not check changing element itself */
10298         continue;
10299
10300       if (content_element == EL_EMPTY_SPACE)
10301       {
10302         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10303
10304         continue;
10305       }
10306
10307       if (!IN_LEV_FIELD(ex, ey))
10308       {
10309         can_replace[xx][yy] = FALSE;
10310         complete_replace = FALSE;
10311
10312         continue;
10313       }
10314
10315       e = Feld[ex][ey];
10316
10317       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10318         e = MovingOrBlocked2Element(ex, ey);
10319
10320       is_empty = (IS_FREE(ex, ey) ||
10321                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10322
10323       is_walkable     = (is_empty || IS_WALKABLE(e));
10324       is_diggable     = (is_empty || IS_DIGGABLE(e));
10325       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10326       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10327       is_removable    = (is_diggable || is_collectible);
10328
10329       can_replace[xx][yy] =
10330         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10331           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10332           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10333           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10334           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10335           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10336          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10337
10338       if (!can_replace[xx][yy])
10339         complete_replace = FALSE;
10340     }
10341
10342     if (!change->only_if_complete || complete_replace)
10343     {
10344       boolean something_has_changed = FALSE;
10345
10346       if (change->only_if_complete && change->use_random_replace &&
10347           RND(100) < change->random_percentage)
10348         return FALSE;
10349
10350       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10351       {
10352         int ex = x + xx - 1;
10353         int ey = y + yy - 1;
10354         int content_element;
10355
10356         if (can_replace[xx][yy] && (!change->use_random_replace ||
10357                                     RND(100) < change->random_percentage))
10358         {
10359           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10360             RemoveMovingField(ex, ey);
10361
10362           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10363
10364           content_element = change->target_content.e[xx][yy];
10365           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10366                                               ce_value, ce_score);
10367
10368           CreateElementFromChange(ex, ey, target_element);
10369
10370           something_has_changed = TRUE;
10371
10372           /* for symmetry reasons, freeze newly created border elements */
10373           if (ex != x || ey != y)
10374             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10375         }
10376       }
10377
10378       if (something_has_changed)
10379       {
10380         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10381         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10382       }
10383     }
10384   }
10385   else
10386   {
10387     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10388                                         ce_value, ce_score);
10389
10390     if (element == EL_DIAGONAL_GROWING ||
10391         element == EL_DIAGONAL_SHRINKING)
10392     {
10393       target_element = Store[x][y];
10394
10395       Store[x][y] = EL_EMPTY;
10396     }
10397
10398     CreateElementFromChange(x, y, target_element);
10399
10400     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10401     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10402   }
10403
10404   /* this uses direct change before indirect change */
10405   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10406
10407   return TRUE;
10408 }
10409
10410 static void HandleElementChange(int x, int y, int page)
10411 {
10412   int element = MovingOrBlocked2Element(x, y);
10413   struct ElementInfo *ei = &element_info[element];
10414   struct ElementChangeInfo *change = &ei->change_page[page];
10415   boolean handle_action_before_change = FALSE;
10416
10417 #ifdef DEBUG
10418   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10419       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10420   {
10421     printf("\n\n");
10422     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10423            x, y, element, element_info[element].token_name);
10424     printf("HandleElementChange(): This should never happen!\n");
10425     printf("\n\n");
10426   }
10427 #endif
10428
10429   /* this can happen with classic bombs on walkable, changing elements */
10430   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10431   {
10432     return;
10433   }
10434
10435   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10436   {
10437     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10438
10439     if (change->can_change)
10440     {
10441       /* !!! not clear why graphic animation should be reset at all here !!! */
10442       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10443       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10444
10445       /*
10446         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10447
10448         When using an animation frame delay of 1 (this only happens with
10449         "sp_zonk.moving.left/right" in the classic graphics), the default
10450         (non-moving) animation shows wrong animation frames (while the
10451         moving animation, like "sp_zonk.moving.left/right", is correct,
10452         so this graphical bug never shows up with the classic graphics).
10453         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10454         be drawn instead of the correct frames 0,1,2,3. This is caused by
10455         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10456         an element change: First when the change delay ("ChangeDelay[][]")
10457         counter has reached zero after decrementing, then a second time in
10458         the next frame (after "GfxFrame[][]" was already incremented) when
10459         "ChangeDelay[][]" is reset to the initial delay value again.
10460
10461         This causes frame 0 to be drawn twice, while the last frame won't
10462         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10463
10464         As some animations may already be cleverly designed around this bug
10465         (at least the "Snake Bite" snake tail animation does this), it cannot
10466         simply be fixed here without breaking such existing animations.
10467         Unfortunately, it cannot easily be detected if a graphics set was
10468         designed "before" or "after" the bug was fixed. As a workaround,
10469         a new graphics set option "game.graphics_engine_version" was added
10470         to be able to specify the game's major release version for which the
10471         graphics set was designed, which can then be used to decide if the
10472         bugfix should be used (version 4 and above) or not (version 3 or
10473         below, or if no version was specified at all, as with old sets).
10474
10475         (The wrong/fixed animation frames can be tested with the test level set
10476         "test_gfxframe" and level "000", which contains a specially prepared
10477         custom element at level position (x/y) == (11/9) which uses the zonk
10478         animation mentioned above. Using "game.graphics_engine_version: 4"
10479         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10480         This can also be seen from the debug output for this test element.)
10481       */
10482
10483       /* when a custom element is about to change (for example by change delay),
10484          do not reset graphic animation when the custom element is moving */
10485       if (game.graphics_engine_version < 4 &&
10486           !IS_MOVING(x, y))
10487       {
10488         ResetGfxAnimation(x, y);
10489         ResetRandomAnimationValue(x, y);
10490       }
10491
10492       if (change->pre_change_function)
10493         change->pre_change_function(x, y);
10494     }
10495   }
10496
10497   ChangeDelay[x][y]--;
10498
10499   if (ChangeDelay[x][y] != 0)           /* continue element change */
10500   {
10501     if (change->can_change)
10502     {
10503       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10504
10505       if (IS_ANIMATED(graphic))
10506         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10507
10508       if (change->change_function)
10509         change->change_function(x, y);
10510     }
10511   }
10512   else                                  /* finish element change */
10513   {
10514     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10515     {
10516       page = ChangePage[x][y];
10517       ChangePage[x][y] = -1;
10518
10519       change = &ei->change_page[page];
10520     }
10521
10522     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10523     {
10524       ChangeDelay[x][y] = 1;            /* try change after next move step */
10525       ChangePage[x][y] = page;          /* remember page to use for change */
10526
10527       return;
10528     }
10529
10530     /* special case: set new level random seed before changing element */
10531     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10532       handle_action_before_change = TRUE;
10533
10534     if (change->has_action && handle_action_before_change)
10535       ExecuteCustomElementAction(x, y, element, page);
10536
10537     if (change->can_change)
10538     {
10539       if (ChangeElement(x, y, element, page))
10540       {
10541         if (change->post_change_function)
10542           change->post_change_function(x, y);
10543       }
10544     }
10545
10546     if (change->has_action && !handle_action_before_change)
10547       ExecuteCustomElementAction(x, y, element, page);
10548   }
10549 }
10550
10551 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10552                                               int trigger_element,
10553                                               int trigger_event,
10554                                               int trigger_player,
10555                                               int trigger_side,
10556                                               int trigger_page)
10557 {
10558   boolean change_done_any = FALSE;
10559   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10560   int i;
10561
10562   if (!(trigger_events[trigger_element][trigger_event]))
10563     return FALSE;
10564
10565   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10566
10567   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10568   {
10569     int element = EL_CUSTOM_START + i;
10570     boolean change_done = FALSE;
10571     int p;
10572
10573     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10574         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10575       continue;
10576
10577     for (p = 0; p < element_info[element].num_change_pages; p++)
10578     {
10579       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10580
10581       if (change->can_change_or_has_action &&
10582           change->has_event[trigger_event] &&
10583           change->trigger_side & trigger_side &&
10584           change->trigger_player & trigger_player &&
10585           change->trigger_page & trigger_page_bits &&
10586           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10587       {
10588         change->actual_trigger_element = trigger_element;
10589         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10590         change->actual_trigger_player_bits = trigger_player;
10591         change->actual_trigger_side = trigger_side;
10592         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10593         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10594
10595         if ((change->can_change && !change_done) || change->has_action)
10596         {
10597           int x, y;
10598
10599           SCAN_PLAYFIELD(x, y)
10600           {
10601             if (Feld[x][y] == element)
10602             {
10603               if (change->can_change && !change_done)
10604               {
10605                 /* if element already changed in this frame, not only prevent
10606                    another element change (checked in ChangeElement()), but
10607                    also prevent additional element actions for this element */
10608
10609                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10610                     !level.use_action_after_change_bug)
10611                   continue;
10612
10613                 ChangeDelay[x][y] = 1;
10614                 ChangeEvent[x][y] = trigger_event;
10615
10616                 HandleElementChange(x, y, p);
10617               }
10618               else if (change->has_action)
10619               {
10620                 /* if element already changed in this frame, not only prevent
10621                    another element change (checked in ChangeElement()), but
10622                    also prevent additional element actions for this element */
10623
10624                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10625                     !level.use_action_after_change_bug)
10626                   continue;
10627
10628                 ExecuteCustomElementAction(x, y, element, p);
10629                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10630               }
10631             }
10632           }
10633
10634           if (change->can_change)
10635           {
10636             change_done = TRUE;
10637             change_done_any = TRUE;
10638           }
10639         }
10640       }
10641     }
10642   }
10643
10644   RECURSION_LOOP_DETECTION_END();
10645
10646   return change_done_any;
10647 }
10648
10649 static boolean CheckElementChangeExt(int x, int y,
10650                                      int element,
10651                                      int trigger_element,
10652                                      int trigger_event,
10653                                      int trigger_player,
10654                                      int trigger_side)
10655 {
10656   boolean change_done = FALSE;
10657   int p;
10658
10659   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10660       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10661     return FALSE;
10662
10663   if (Feld[x][y] == EL_BLOCKED)
10664   {
10665     Blocked2Moving(x, y, &x, &y);
10666     element = Feld[x][y];
10667   }
10668
10669   /* check if element has already changed or is about to change after moving */
10670   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10671        Feld[x][y] != element) ||
10672
10673       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10674        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10675         ChangePage[x][y] != -1)))
10676     return FALSE;
10677
10678   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10679
10680   for (p = 0; p < element_info[element].num_change_pages; p++)
10681   {
10682     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10683
10684     /* check trigger element for all events where the element that is checked
10685        for changing interacts with a directly adjacent element -- this is
10686        different to element changes that affect other elements to change on the
10687        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10688     boolean check_trigger_element =
10689       (trigger_event == CE_TOUCHING_X ||
10690        trigger_event == CE_HITTING_X ||
10691        trigger_event == CE_HIT_BY_X ||
10692        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10693
10694     if (change->can_change_or_has_action &&
10695         change->has_event[trigger_event] &&
10696         change->trigger_side & trigger_side &&
10697         change->trigger_player & trigger_player &&
10698         (!check_trigger_element ||
10699          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10700     {
10701       change->actual_trigger_element = trigger_element;
10702       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10703       change->actual_trigger_player_bits = trigger_player;
10704       change->actual_trigger_side = trigger_side;
10705       change->actual_trigger_ce_value = CustomValue[x][y];
10706       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10707
10708       /* special case: trigger element not at (x,y) position for some events */
10709       if (check_trigger_element)
10710       {
10711         static struct
10712         {
10713           int dx, dy;
10714         } move_xy[] =
10715           {
10716             {  0,  0 },
10717             { -1,  0 },
10718             { +1,  0 },
10719             {  0,  0 },
10720             {  0, -1 },
10721             {  0,  0 }, { 0, 0 }, { 0, 0 },
10722             {  0, +1 }
10723           };
10724
10725         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10726         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10727
10728         change->actual_trigger_ce_value = CustomValue[xx][yy];
10729         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10730       }
10731
10732       if (change->can_change && !change_done)
10733       {
10734         ChangeDelay[x][y] = 1;
10735         ChangeEvent[x][y] = trigger_event;
10736
10737         HandleElementChange(x, y, p);
10738
10739         change_done = TRUE;
10740       }
10741       else if (change->has_action)
10742       {
10743         ExecuteCustomElementAction(x, y, element, p);
10744         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10745       }
10746     }
10747   }
10748
10749   RECURSION_LOOP_DETECTION_END();
10750
10751   return change_done;
10752 }
10753
10754 static void PlayPlayerSound(struct PlayerInfo *player)
10755 {
10756   int jx = player->jx, jy = player->jy;
10757   int sound_element = player->artwork_element;
10758   int last_action = player->last_action_waiting;
10759   int action = player->action_waiting;
10760
10761   if (player->is_waiting)
10762   {
10763     if (action != last_action)
10764       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10765     else
10766       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10767   }
10768   else
10769   {
10770     if (action != last_action)
10771       StopSound(element_info[sound_element].sound[last_action]);
10772
10773     if (last_action == ACTION_SLEEPING)
10774       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10775   }
10776 }
10777
10778 static void PlayAllPlayersSound()
10779 {
10780   int i;
10781
10782   for (i = 0; i < MAX_PLAYERS; i++)
10783     if (stored_player[i].active)
10784       PlayPlayerSound(&stored_player[i]);
10785 }
10786
10787 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10788 {
10789   boolean last_waiting = player->is_waiting;
10790   int move_dir = player->MovDir;
10791
10792   player->dir_waiting = move_dir;
10793   player->last_action_waiting = player->action_waiting;
10794
10795   if (is_waiting)
10796   {
10797     if (!last_waiting)          /* not waiting -> waiting */
10798     {
10799       player->is_waiting = TRUE;
10800
10801       player->frame_counter_bored =
10802         FrameCounter +
10803         game.player_boring_delay_fixed +
10804         GetSimpleRandom(game.player_boring_delay_random);
10805       player->frame_counter_sleeping =
10806         FrameCounter +
10807         game.player_sleeping_delay_fixed +
10808         GetSimpleRandom(game.player_sleeping_delay_random);
10809
10810       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10811     }
10812
10813     if (game.player_sleeping_delay_fixed +
10814         game.player_sleeping_delay_random > 0 &&
10815         player->anim_delay_counter == 0 &&
10816         player->post_delay_counter == 0 &&
10817         FrameCounter >= player->frame_counter_sleeping)
10818       player->is_sleeping = TRUE;
10819     else if (game.player_boring_delay_fixed +
10820              game.player_boring_delay_random > 0 &&
10821              FrameCounter >= player->frame_counter_bored)
10822       player->is_bored = TRUE;
10823
10824     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10825                               player->is_bored ? ACTION_BORING :
10826                               ACTION_WAITING);
10827
10828     if (player->is_sleeping && player->use_murphy)
10829     {
10830       /* special case for sleeping Murphy when leaning against non-free tile */
10831
10832       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10833           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10834            !IS_MOVING(player->jx - 1, player->jy)))
10835         move_dir = MV_LEFT;
10836       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10837                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10838                 !IS_MOVING(player->jx + 1, player->jy)))
10839         move_dir = MV_RIGHT;
10840       else
10841         player->is_sleeping = FALSE;
10842
10843       player->dir_waiting = move_dir;
10844     }
10845
10846     if (player->is_sleeping)
10847     {
10848       if (player->num_special_action_sleeping > 0)
10849       {
10850         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10851         {
10852           int last_special_action = player->special_action_sleeping;
10853           int num_special_action = player->num_special_action_sleeping;
10854           int special_action =
10855             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10856              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10857              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10858              last_special_action + 1 : ACTION_SLEEPING);
10859           int special_graphic =
10860             el_act_dir2img(player->artwork_element, special_action, move_dir);
10861
10862           player->anim_delay_counter =
10863             graphic_info[special_graphic].anim_delay_fixed +
10864             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10865           player->post_delay_counter =
10866             graphic_info[special_graphic].post_delay_fixed +
10867             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10868
10869           player->special_action_sleeping = special_action;
10870         }
10871
10872         if (player->anim_delay_counter > 0)
10873         {
10874           player->action_waiting = player->special_action_sleeping;
10875           player->anim_delay_counter--;
10876         }
10877         else if (player->post_delay_counter > 0)
10878         {
10879           player->post_delay_counter--;
10880         }
10881       }
10882     }
10883     else if (player->is_bored)
10884     {
10885       if (player->num_special_action_bored > 0)
10886       {
10887         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10888         {
10889           int special_action =
10890             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10891           int special_graphic =
10892             el_act_dir2img(player->artwork_element, special_action, move_dir);
10893
10894           player->anim_delay_counter =
10895             graphic_info[special_graphic].anim_delay_fixed +
10896             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10897           player->post_delay_counter =
10898             graphic_info[special_graphic].post_delay_fixed +
10899             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10900
10901           player->special_action_bored = special_action;
10902         }
10903
10904         if (player->anim_delay_counter > 0)
10905         {
10906           player->action_waiting = player->special_action_bored;
10907           player->anim_delay_counter--;
10908         }
10909         else if (player->post_delay_counter > 0)
10910         {
10911           player->post_delay_counter--;
10912         }
10913       }
10914     }
10915   }
10916   else if (last_waiting)        /* waiting -> not waiting */
10917   {
10918     player->is_waiting = FALSE;
10919     player->is_bored = FALSE;
10920     player->is_sleeping = FALSE;
10921
10922     player->frame_counter_bored = -1;
10923     player->frame_counter_sleeping = -1;
10924
10925     player->anim_delay_counter = 0;
10926     player->post_delay_counter = 0;
10927
10928     player->dir_waiting = player->MovDir;
10929     player->action_waiting = ACTION_DEFAULT;
10930
10931     player->special_action_bored = ACTION_DEFAULT;
10932     player->special_action_sleeping = ACTION_DEFAULT;
10933   }
10934 }
10935
10936 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10937 {
10938   if ((!player->is_moving  && player->was_moving) ||
10939       (player->MovPos == 0 && player->was_moving) ||
10940       (player->is_snapping && !player->was_snapping) ||
10941       (player->is_dropping && !player->was_dropping))
10942   {
10943     if (!CheckSaveEngineSnapshotToList())
10944       return;
10945
10946     player->was_moving = FALSE;
10947     player->was_snapping = TRUE;
10948     player->was_dropping = TRUE;
10949   }
10950   else
10951   {
10952     if (player->is_moving)
10953       player->was_moving = TRUE;
10954
10955     if (!player->is_snapping)
10956       player->was_snapping = FALSE;
10957
10958     if (!player->is_dropping)
10959       player->was_dropping = FALSE;
10960   }
10961 }
10962
10963 static void CheckSingleStepMode(struct PlayerInfo *player)
10964 {
10965   if (tape.single_step && tape.recording && !tape.pausing)
10966   {
10967     /* as it is called "single step mode", just return to pause mode when the
10968        player stopped moving after one tile (or never starts moving at all) */
10969     if (!player->is_moving &&
10970         !player->is_pushing &&
10971         !player->is_dropping_pressed)
10972     {
10973       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10974       SnapField(player, 0, 0);                  /* stop snapping */
10975     }
10976   }
10977
10978   CheckSaveEngineSnapshot(player);
10979 }
10980
10981 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10982 {
10983   int left      = player_action & JOY_LEFT;
10984   int right     = player_action & JOY_RIGHT;
10985   int up        = player_action & JOY_UP;
10986   int down      = player_action & JOY_DOWN;
10987   int button1   = player_action & JOY_BUTTON_1;
10988   int button2   = player_action & JOY_BUTTON_2;
10989   int dx        = (left ? -1 : right ? 1 : 0);
10990   int dy        = (up   ? -1 : down  ? 1 : 0);
10991
10992   if (!player->active || tape.pausing)
10993     return 0;
10994
10995   if (player_action)
10996   {
10997     if (button1)
10998       SnapField(player, dx, dy);
10999     else
11000     {
11001       if (button2)
11002         DropElement(player);
11003
11004       MovePlayer(player, dx, dy);
11005     }
11006
11007     CheckSingleStepMode(player);
11008
11009     SetPlayerWaiting(player, FALSE);
11010
11011     return player_action;
11012   }
11013   else
11014   {
11015     /* no actions for this player (no input at player's configured device) */
11016
11017     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11018     SnapField(player, 0, 0);
11019     CheckGravityMovementWhenNotMoving(player);
11020
11021     if (player->MovPos == 0)
11022       SetPlayerWaiting(player, TRUE);
11023
11024     if (player->MovPos == 0)    /* needed for tape.playing */
11025       player->is_moving = FALSE;
11026
11027     player->is_dropping = FALSE;
11028     player->is_dropping_pressed = FALSE;
11029     player->drop_pressed_delay = 0;
11030
11031     CheckSingleStepMode(player);
11032
11033     return 0;
11034   }
11035 }
11036
11037 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11038                                          byte *tape_action)
11039 {
11040   if (!tape.use_mouse)
11041     return;
11042
11043   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11044   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11045   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11046 }
11047
11048 static void SetTapeActionFromMouseAction(byte *tape_action,
11049                                          struct MouseActionInfo *mouse_action)
11050 {
11051   if (!tape.use_mouse)
11052     return;
11053
11054   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11055   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11056   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11057 }
11058
11059 static void CheckLevelTime()
11060 {
11061   int i;
11062
11063   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11064   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11065   {
11066     if (level.native_em_level->lev->home == 0)  /* all players at home */
11067     {
11068       PlayerWins(local_player);
11069
11070       AllPlayersGone = TRUE;
11071
11072       level.native_em_level->lev->home = -1;
11073     }
11074
11075     if (level.native_em_level->ply[0]->alive == 0 &&
11076         level.native_em_level->ply[1]->alive == 0 &&
11077         level.native_em_level->ply[2]->alive == 0 &&
11078         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11079       AllPlayersGone = TRUE;
11080   }
11081   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11082   {
11083     if (game_sp.LevelSolved &&
11084         !game_sp.GameOver)                              /* game won */
11085     {
11086       PlayerWins(local_player);
11087
11088       game_sp.GameOver = TRUE;
11089
11090       AllPlayersGone = TRUE;
11091     }
11092
11093     if (game_sp.GameOver)                               /* game lost */
11094       AllPlayersGone = TRUE;
11095   }
11096   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11097   {
11098     if (game_mm.level_solved &&
11099         !game_mm.game_over)                             /* game won */
11100     {
11101       PlayerWins(local_player);
11102
11103       game_mm.game_over = TRUE;
11104
11105       AllPlayersGone = TRUE;
11106     }
11107
11108     if (game_mm.game_over)                              /* game lost */
11109       AllPlayersGone = TRUE;
11110   }
11111
11112   if (TimeFrames >= FRAMES_PER_SECOND)
11113   {
11114     TimeFrames = 0;
11115     TapeTime++;
11116
11117     for (i = 0; i < MAX_PLAYERS; i++)
11118     {
11119       struct PlayerInfo *player = &stored_player[i];
11120
11121       if (SHIELD_ON(player))
11122       {
11123         player->shield_normal_time_left--;
11124
11125         if (player->shield_deadly_time_left > 0)
11126           player->shield_deadly_time_left--;
11127       }
11128     }
11129
11130     if (!local_player->LevelSolved && !level.use_step_counter)
11131     {
11132       TimePlayed++;
11133
11134       if (TimeLeft > 0)
11135       {
11136         TimeLeft--;
11137
11138         if (TimeLeft <= 10 && setup.time_limit)
11139           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11140
11141         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11142            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11143
11144         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11145
11146         if (!TimeLeft && setup.time_limit)
11147         {
11148           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11149             level.native_em_level->lev->killed_out_of_time = TRUE;
11150           else
11151             for (i = 0; i < MAX_PLAYERS; i++)
11152               KillPlayer(&stored_player[i]);
11153         }
11154       }
11155       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11156       {
11157         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11158       }
11159
11160       level.native_em_level->lev->time =
11161         (game.no_time_limit ? TimePlayed : TimeLeft);
11162     }
11163
11164     if (tape.recording || tape.playing)
11165       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11166   }
11167
11168   if (tape.recording || tape.playing)
11169     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11170
11171   UpdateAndDisplayGameControlValues();
11172 }
11173
11174 void AdvanceFrameAndPlayerCounters(int player_nr)
11175 {
11176   int i;
11177
11178   /* advance frame counters (global frame counter and time frame counter) */
11179   FrameCounter++;
11180   TimeFrames++;
11181
11182   /* advance player counters (counters for move delay, move animation etc.) */
11183   for (i = 0; i < MAX_PLAYERS; i++)
11184   {
11185     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11186     int move_delay_value = stored_player[i].move_delay_value;
11187     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11188
11189     if (!advance_player_counters)       /* not all players may be affected */
11190       continue;
11191
11192     if (move_frames == 0)       /* less than one move per game frame */
11193     {
11194       int stepsize = TILEX / move_delay_value;
11195       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11196       int count = (stored_player[i].is_moving ?
11197                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11198
11199       if (count % delay == 0)
11200         move_frames = 1;
11201     }
11202
11203     stored_player[i].Frame += move_frames;
11204
11205     if (stored_player[i].MovPos != 0)
11206       stored_player[i].StepFrame += move_frames;
11207
11208     if (stored_player[i].move_delay > 0)
11209       stored_player[i].move_delay--;
11210
11211     /* due to bugs in previous versions, counter must count up, not down */
11212     if (stored_player[i].push_delay != -1)
11213       stored_player[i].push_delay++;
11214
11215     if (stored_player[i].drop_delay > 0)
11216       stored_player[i].drop_delay--;
11217
11218     if (stored_player[i].is_dropping_pressed)
11219       stored_player[i].drop_pressed_delay++;
11220   }
11221 }
11222
11223 void StartGameActions(boolean init_network_game, boolean record_tape,
11224                       int random_seed)
11225 {
11226   unsigned int new_random_seed = InitRND(random_seed);
11227
11228   if (record_tape)
11229     TapeStartRecording(new_random_seed);
11230
11231   if (init_network_game)
11232   {
11233     SendToServer_LevelFile();
11234     SendToServer_StartPlaying();
11235
11236     return;
11237   }
11238
11239   InitGame();
11240 }
11241
11242 void GameActionsExt()
11243 {
11244 #if 0
11245   static unsigned int game_frame_delay = 0;
11246 #endif
11247   unsigned int game_frame_delay_value;
11248   byte *recorded_player_action;
11249   byte summarized_player_action = 0;
11250   byte tape_action[MAX_PLAYERS];
11251   int i;
11252
11253   /* detect endless loops, caused by custom element programming */
11254   if (recursion_loop_detected && recursion_loop_depth == 0)
11255   {
11256     char *message = getStringCat3("Internal Error! Element ",
11257                                   EL_NAME(recursion_loop_element),
11258                                   " caused endless loop! Quit the game?");
11259
11260     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11261           EL_NAME(recursion_loop_element));
11262
11263     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11264
11265     recursion_loop_detected = FALSE;    /* if game should be continued */
11266
11267     free(message);
11268
11269     return;
11270   }
11271
11272   if (game.restart_level)
11273     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11274
11275   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11276   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11277   {
11278     if (level.native_em_level->lev->home == 0)  /* all players at home */
11279     {
11280       PlayerWins(local_player);
11281
11282       AllPlayersGone = TRUE;
11283
11284       level.native_em_level->lev->home = -1;
11285     }
11286
11287     if (level.native_em_level->ply[0]->alive == 0 &&
11288         level.native_em_level->ply[1]->alive == 0 &&
11289         level.native_em_level->ply[2]->alive == 0 &&
11290         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11291       AllPlayersGone = TRUE;
11292   }
11293   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11294   {
11295     if (game_sp.LevelSolved &&
11296         !game_sp.GameOver)                              /* game won */
11297     {
11298       PlayerWins(local_player);
11299
11300       game_sp.GameOver = TRUE;
11301
11302       AllPlayersGone = TRUE;
11303     }
11304
11305     if (game_sp.GameOver)                               /* game lost */
11306       AllPlayersGone = TRUE;
11307   }
11308   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11309   {
11310     if (game_mm.level_solved &&
11311         !game_mm.game_over)                             /* game won */
11312     {
11313       PlayerWins(local_player);
11314
11315       game_mm.game_over = TRUE;
11316
11317       AllPlayersGone = TRUE;
11318     }
11319
11320     if (game_mm.game_over)                              /* game lost */
11321       AllPlayersGone = TRUE;
11322   }
11323
11324   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11325     GameWon();
11326
11327   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11328     TapeStop();
11329
11330   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11331     return;
11332
11333   game_frame_delay_value =
11334     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11335
11336   if (tape.playing && tape.warp_forward && !tape.pausing)
11337     game_frame_delay_value = 0;
11338
11339   SetVideoFrameDelay(game_frame_delay_value);
11340
11341 #if 0
11342 #if 0
11343   /* ---------- main game synchronization point ---------- */
11344
11345   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11346
11347   printf("::: skip == %d\n", skip);
11348
11349 #else
11350   /* ---------- main game synchronization point ---------- */
11351
11352   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11353 #endif
11354 #endif
11355
11356   if (network_playing && !network_player_action_received)
11357   {
11358     /* try to get network player actions in time */
11359
11360     /* last chance to get network player actions without main loop delay */
11361     HandleNetworking();
11362
11363     /* game was quit by network peer */
11364     if (game_status != GAME_MODE_PLAYING)
11365       return;
11366
11367     if (!network_player_action_received)
11368       return;           /* failed to get network player actions in time */
11369
11370     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11371   }
11372
11373   if (tape.pausing)
11374     return;
11375
11376   /* at this point we know that we really continue executing the game */
11377
11378   network_player_action_received = FALSE;
11379
11380   /* when playing tape, read previously recorded player input from tape data */
11381   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11382
11383   local_player->effective_mouse_action = local_player->mouse_action;
11384
11385   if (recorded_player_action != NULL)
11386     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11387                                  recorded_player_action);
11388
11389   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11390   if (tape.pausing)
11391     return;
11392
11393   if (tape.set_centered_player)
11394   {
11395     game.centered_player_nr_next = tape.centered_player_nr_next;
11396     game.set_centered_player = TRUE;
11397   }
11398
11399   for (i = 0; i < MAX_PLAYERS; i++)
11400   {
11401     summarized_player_action |= stored_player[i].action;
11402
11403     if (!network_playing && (game.team_mode || tape.playing))
11404       stored_player[i].effective_action = stored_player[i].action;
11405   }
11406
11407   if (network_playing)
11408     SendToServer_MovePlayer(summarized_player_action);
11409
11410   // summarize all actions at local players mapped input device position
11411   // (this allows using different input devices in single player mode)
11412   if (!network.enabled && !game.team_mode)
11413     stored_player[map_player_action[local_player->index_nr]].effective_action =
11414       summarized_player_action;
11415
11416   if (tape.recording &&
11417       setup.team_mode &&
11418       setup.input_on_focus &&
11419       game.centered_player_nr != -1)
11420   {
11421     for (i = 0; i < MAX_PLAYERS; i++)
11422       stored_player[i].effective_action =
11423         (i == game.centered_player_nr ? summarized_player_action : 0);
11424   }
11425
11426   if (recorded_player_action != NULL)
11427     for (i = 0; i < MAX_PLAYERS; i++)
11428       stored_player[i].effective_action = recorded_player_action[i];
11429
11430   for (i = 0; i < MAX_PLAYERS; i++)
11431   {
11432     tape_action[i] = stored_player[i].effective_action;
11433
11434     /* (this may happen in the RND game engine if a player was not present on
11435        the playfield on level start, but appeared later from a custom element */
11436     if (setup.team_mode &&
11437         tape.recording &&
11438         tape_action[i] &&
11439         !tape.player_participates[i])
11440       tape.player_participates[i] = TRUE;
11441   }
11442
11443   SetTapeActionFromMouseAction(tape_action,
11444                                &local_player->effective_mouse_action);
11445
11446   /* only record actions from input devices, but not programmed actions */
11447   if (tape.recording)
11448     TapeRecordAction(tape_action);
11449
11450 #if USE_NEW_PLAYER_ASSIGNMENTS
11451   // !!! also map player actions in single player mode !!!
11452   // if (game.team_mode)
11453   if (1)
11454   {
11455     byte mapped_action[MAX_PLAYERS];
11456
11457 #if DEBUG_PLAYER_ACTIONS
11458     printf(":::");
11459     for (i = 0; i < MAX_PLAYERS; i++)
11460       printf(" %d, ", stored_player[i].effective_action);
11461 #endif
11462
11463     for (i = 0; i < MAX_PLAYERS; i++)
11464       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11465
11466     for (i = 0; i < MAX_PLAYERS; i++)
11467       stored_player[i].effective_action = mapped_action[i];
11468
11469 #if DEBUG_PLAYER_ACTIONS
11470     printf(" =>");
11471     for (i = 0; i < MAX_PLAYERS; i++)
11472       printf(" %d, ", stored_player[i].effective_action);
11473     printf("\n");
11474 #endif
11475   }
11476 #if DEBUG_PLAYER_ACTIONS
11477   else
11478   {
11479     printf(":::");
11480     for (i = 0; i < MAX_PLAYERS; i++)
11481       printf(" %d, ", stored_player[i].effective_action);
11482     printf("\n");
11483   }
11484 #endif
11485 #endif
11486
11487   for (i = 0; i < MAX_PLAYERS; i++)
11488   {
11489     // allow engine snapshot in case of changed movement attempt
11490     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11491         (stored_player[i].effective_action & KEY_MOTION))
11492       game.snapshot.changed_action = TRUE;
11493
11494     // allow engine snapshot in case of snapping/dropping attempt
11495     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11496         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11497       game.snapshot.changed_action = TRUE;
11498
11499     game.snapshot.last_action[i] = stored_player[i].effective_action;
11500   }
11501
11502   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11503   {
11504     GameActions_EM_Main();
11505   }
11506   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11507   {
11508     GameActions_SP_Main();
11509   }
11510   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11511   {
11512     GameActions_MM_Main();
11513   }
11514   else
11515   {
11516     GameActions_RND_Main();
11517   }
11518
11519   BlitScreenToBitmap(backbuffer);
11520
11521   CheckLevelTime();
11522
11523   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11524
11525   if (global.show_frames_per_second)
11526   {
11527     static unsigned int fps_counter = 0;
11528     static int fps_frames = 0;
11529     unsigned int fps_delay_ms = Counter() - fps_counter;
11530
11531     fps_frames++;
11532
11533     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11534     {
11535       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11536
11537       fps_frames = 0;
11538       fps_counter = Counter();
11539
11540       /* always draw FPS to screen after FPS value was updated */
11541       redraw_mask |= REDRAW_FPS;
11542     }
11543
11544     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11545     if (GetDrawDeactivationMask() == REDRAW_NONE)
11546       redraw_mask |= REDRAW_FPS;
11547   }
11548 }
11549
11550 static void GameActions_CheckSaveEngineSnapshot()
11551 {
11552   if (!game.snapshot.save_snapshot)
11553     return;
11554
11555   // clear flag for saving snapshot _before_ saving snapshot
11556   game.snapshot.save_snapshot = FALSE;
11557
11558   SaveEngineSnapshotToList();
11559 }
11560
11561 void GameActions()
11562 {
11563   GameActionsExt();
11564
11565   GameActions_CheckSaveEngineSnapshot();
11566 }
11567
11568 void GameActions_EM_Main()
11569 {
11570   byte effective_action[MAX_PLAYERS];
11571   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11572   int i;
11573
11574   for (i = 0; i < MAX_PLAYERS; i++)
11575     effective_action[i] = stored_player[i].effective_action;
11576
11577   GameActions_EM(effective_action, warp_mode);
11578 }
11579
11580 void GameActions_SP_Main()
11581 {
11582   byte effective_action[MAX_PLAYERS];
11583   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11584   int i;
11585
11586   for (i = 0; i < MAX_PLAYERS; i++)
11587     effective_action[i] = stored_player[i].effective_action;
11588
11589   GameActions_SP(effective_action, warp_mode);
11590
11591   for (i = 0; i < MAX_PLAYERS; i++)
11592   {
11593     if (stored_player[i].force_dropping)
11594       stored_player[i].action |= KEY_BUTTON_DROP;
11595
11596     stored_player[i].force_dropping = FALSE;
11597   }
11598 }
11599
11600 void GameActions_MM_Main()
11601 {
11602   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11603
11604   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11605 }
11606
11607 void GameActions_RND_Main()
11608 {
11609   GameActions_RND();
11610 }
11611
11612 void GameActions_RND()
11613 {
11614   int magic_wall_x = 0, magic_wall_y = 0;
11615   int i, x, y, element, graphic, last_gfx_frame;
11616
11617   InitPlayfieldScanModeVars();
11618
11619   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11620   {
11621     SCAN_PLAYFIELD(x, y)
11622     {
11623       ChangeCount[x][y] = 0;
11624       ChangeEvent[x][y] = -1;
11625     }
11626   }
11627
11628   if (game.set_centered_player)
11629   {
11630     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11631
11632     /* switching to "all players" only possible if all players fit to screen */
11633     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11634     {
11635       game.centered_player_nr_next = game.centered_player_nr;
11636       game.set_centered_player = FALSE;
11637     }
11638
11639     /* do not switch focus to non-existing (or non-active) player */
11640     if (game.centered_player_nr_next >= 0 &&
11641         !stored_player[game.centered_player_nr_next].active)
11642     {
11643       game.centered_player_nr_next = game.centered_player_nr;
11644       game.set_centered_player = FALSE;
11645     }
11646   }
11647
11648   if (game.set_centered_player &&
11649       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11650   {
11651     int sx, sy;
11652
11653     if (game.centered_player_nr_next == -1)
11654     {
11655       setScreenCenteredToAllPlayers(&sx, &sy);
11656     }
11657     else
11658     {
11659       sx = stored_player[game.centered_player_nr_next].jx;
11660       sy = stored_player[game.centered_player_nr_next].jy;
11661     }
11662
11663     game.centered_player_nr = game.centered_player_nr_next;
11664     game.set_centered_player = FALSE;
11665
11666     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11667     DrawGameDoorValues();
11668   }
11669
11670   for (i = 0; i < MAX_PLAYERS; i++)
11671   {
11672     int actual_player_action = stored_player[i].effective_action;
11673
11674 #if 1
11675     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11676        - rnd_equinox_tetrachloride 048
11677        - rnd_equinox_tetrachloride_ii 096
11678        - rnd_emanuel_schmieg 002
11679        - doctor_sloan_ww 001, 020
11680     */
11681     if (stored_player[i].MovPos == 0)
11682       CheckGravityMovement(&stored_player[i]);
11683 #endif
11684
11685     /* overwrite programmed action with tape action */
11686     if (stored_player[i].programmed_action)
11687       actual_player_action = stored_player[i].programmed_action;
11688
11689     PlayerActions(&stored_player[i], actual_player_action);
11690
11691     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11692   }
11693
11694   ScrollScreen(NULL, SCROLL_GO_ON);
11695
11696   /* for backwards compatibility, the following code emulates a fixed bug that
11697      occured when pushing elements (causing elements that just made their last
11698      pushing step to already (if possible) make their first falling step in the
11699      same game frame, which is bad); this code is also needed to use the famous
11700      "spring push bug" which is used in older levels and might be wanted to be
11701      used also in newer levels, but in this case the buggy pushing code is only
11702      affecting the "spring" element and no other elements */
11703
11704   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11705   {
11706     for (i = 0; i < MAX_PLAYERS; i++)
11707     {
11708       struct PlayerInfo *player = &stored_player[i];
11709       int x = player->jx;
11710       int y = player->jy;
11711
11712       if (player->active && player->is_pushing && player->is_moving &&
11713           IS_MOVING(x, y) &&
11714           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11715            Feld[x][y] == EL_SPRING))
11716       {
11717         ContinueMoving(x, y);
11718
11719         /* continue moving after pushing (this is actually a bug) */
11720         if (!IS_MOVING(x, y))
11721           Stop[x][y] = FALSE;
11722       }
11723     }
11724   }
11725
11726   SCAN_PLAYFIELD(x, y)
11727   {
11728     ChangeCount[x][y] = 0;
11729     ChangeEvent[x][y] = -1;
11730
11731     /* this must be handled before main playfield loop */
11732     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11733     {
11734       MovDelay[x][y]--;
11735       if (MovDelay[x][y] <= 0)
11736         RemoveField(x, y);
11737     }
11738
11739     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11740     {
11741       MovDelay[x][y]--;
11742       if (MovDelay[x][y] <= 0)
11743       {
11744         RemoveField(x, y);
11745         TEST_DrawLevelField(x, y);
11746
11747         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11748       }
11749     }
11750
11751 #if DEBUG
11752     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11753     {
11754       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11755       printf("GameActions(): This should never happen!\n");
11756
11757       ChangePage[x][y] = -1;
11758     }
11759 #endif
11760
11761     Stop[x][y] = FALSE;
11762     if (WasJustMoving[x][y] > 0)
11763       WasJustMoving[x][y]--;
11764     if (WasJustFalling[x][y] > 0)
11765       WasJustFalling[x][y]--;
11766     if (CheckCollision[x][y] > 0)
11767       CheckCollision[x][y]--;
11768     if (CheckImpact[x][y] > 0)
11769       CheckImpact[x][y]--;
11770
11771     GfxFrame[x][y]++;
11772
11773     /* reset finished pushing action (not done in ContinueMoving() to allow
11774        continuous pushing animation for elements with zero push delay) */
11775     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11776     {
11777       ResetGfxAnimation(x, y);
11778       TEST_DrawLevelField(x, y);
11779     }
11780
11781 #if DEBUG
11782     if (IS_BLOCKED(x, y))
11783     {
11784       int oldx, oldy;
11785
11786       Blocked2Moving(x, y, &oldx, &oldy);
11787       if (!IS_MOVING(oldx, oldy))
11788       {
11789         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11790         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11791         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11792         printf("GameActions(): This should never happen!\n");
11793       }
11794     }
11795 #endif
11796   }
11797
11798   SCAN_PLAYFIELD(x, y)
11799   {
11800     element = Feld[x][y];
11801     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11802     last_gfx_frame = GfxFrame[x][y];
11803
11804     ResetGfxFrame(x, y);
11805
11806     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11807       DrawLevelGraphicAnimation(x, y, graphic);
11808
11809     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11810         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11811       ResetRandomAnimationValue(x, y);
11812
11813     SetRandomAnimationValue(x, y);
11814
11815     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11816
11817     if (IS_INACTIVE(element))
11818     {
11819       if (IS_ANIMATED(graphic))
11820         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11821
11822       continue;
11823     }
11824
11825     /* this may take place after moving, so 'element' may have changed */
11826     if (IS_CHANGING(x, y) &&
11827         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11828     {
11829       int page = element_info[element].event_page_nr[CE_DELAY];
11830
11831       HandleElementChange(x, y, page);
11832
11833       element = Feld[x][y];
11834       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11835     }
11836
11837     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11838     {
11839       StartMoving(x, y);
11840
11841       element = Feld[x][y];
11842       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11843
11844       if (IS_ANIMATED(graphic) &&
11845           !IS_MOVING(x, y) &&
11846           !Stop[x][y])
11847         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11848
11849       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11850         TEST_DrawTwinkleOnField(x, y);
11851     }
11852     else if (element == EL_ACID)
11853     {
11854       if (!Stop[x][y])
11855         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11856     }
11857     else if ((element == EL_EXIT_OPEN ||
11858               element == EL_EM_EXIT_OPEN ||
11859               element == EL_SP_EXIT_OPEN ||
11860               element == EL_STEEL_EXIT_OPEN ||
11861               element == EL_EM_STEEL_EXIT_OPEN ||
11862               element == EL_SP_TERMINAL ||
11863               element == EL_SP_TERMINAL_ACTIVE ||
11864               element == EL_EXTRA_TIME ||
11865               element == EL_SHIELD_NORMAL ||
11866               element == EL_SHIELD_DEADLY) &&
11867              IS_ANIMATED(graphic))
11868       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11869     else if (IS_MOVING(x, y))
11870       ContinueMoving(x, y);
11871     else if (IS_ACTIVE_BOMB(element))
11872       CheckDynamite(x, y);
11873     else if (element == EL_AMOEBA_GROWING)
11874       AmoebeWaechst(x, y);
11875     else if (element == EL_AMOEBA_SHRINKING)
11876       AmoebaDisappearing(x, y);
11877
11878 #if !USE_NEW_AMOEBA_CODE
11879     else if (IS_AMOEBALIVE(element))
11880       AmoebeAbleger(x, y);
11881 #endif
11882
11883     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11884       Life(x, y);
11885     else if (element == EL_EXIT_CLOSED)
11886       CheckExit(x, y);
11887     else if (element == EL_EM_EXIT_CLOSED)
11888       CheckExitEM(x, y);
11889     else if (element == EL_STEEL_EXIT_CLOSED)
11890       CheckExitSteel(x, y);
11891     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11892       CheckExitSteelEM(x, y);
11893     else if (element == EL_SP_EXIT_CLOSED)
11894       CheckExitSP(x, y);
11895     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11896              element == EL_EXPANDABLE_STEELWALL_GROWING)
11897       MauerWaechst(x, y);
11898     else if (element == EL_EXPANDABLE_WALL ||
11899              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11900              element == EL_EXPANDABLE_WALL_VERTICAL ||
11901              element == EL_EXPANDABLE_WALL_ANY ||
11902              element == EL_BD_EXPANDABLE_WALL)
11903       MauerAbleger(x, y);
11904     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11905              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11906              element == EL_EXPANDABLE_STEELWALL_ANY)
11907       MauerAblegerStahl(x, y);
11908     else if (element == EL_FLAMES)
11909       CheckForDragon(x, y);
11910     else if (element == EL_EXPLOSION)
11911       ; /* drawing of correct explosion animation is handled separately */
11912     else if (element == EL_ELEMENT_SNAPPING ||
11913              element == EL_DIAGONAL_SHRINKING ||
11914              element == EL_DIAGONAL_GROWING)
11915     {
11916       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11917
11918       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11919     }
11920     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11921       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11922
11923     if (IS_BELT_ACTIVE(element))
11924       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11925
11926     if (game.magic_wall_active)
11927     {
11928       int jx = local_player->jx, jy = local_player->jy;
11929
11930       /* play the element sound at the position nearest to the player */
11931       if ((element == EL_MAGIC_WALL_FULL ||
11932            element == EL_MAGIC_WALL_ACTIVE ||
11933            element == EL_MAGIC_WALL_EMPTYING ||
11934            element == EL_BD_MAGIC_WALL_FULL ||
11935            element == EL_BD_MAGIC_WALL_ACTIVE ||
11936            element == EL_BD_MAGIC_WALL_EMPTYING ||
11937            element == EL_DC_MAGIC_WALL_FULL ||
11938            element == EL_DC_MAGIC_WALL_ACTIVE ||
11939            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11940           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11941       {
11942         magic_wall_x = x;
11943         magic_wall_y = y;
11944       }
11945     }
11946   }
11947
11948 #if USE_NEW_AMOEBA_CODE
11949   /* new experimental amoeba growth stuff */
11950   if (!(FrameCounter % 8))
11951   {
11952     static unsigned int random = 1684108901;
11953
11954     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11955     {
11956       x = RND(lev_fieldx);
11957       y = RND(lev_fieldy);
11958       element = Feld[x][y];
11959
11960       if (!IS_PLAYER(x,y) &&
11961           (element == EL_EMPTY ||
11962            CAN_GROW_INTO(element) ||
11963            element == EL_QUICKSAND_EMPTY ||
11964            element == EL_QUICKSAND_FAST_EMPTY ||
11965            element == EL_ACID_SPLASH_LEFT ||
11966            element == EL_ACID_SPLASH_RIGHT))
11967       {
11968         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11969             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11970             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11971             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11972           Feld[x][y] = EL_AMOEBA_DROP;
11973       }
11974
11975       random = random * 129 + 1;
11976     }
11977   }
11978 #endif
11979
11980   game.explosions_delayed = FALSE;
11981
11982   SCAN_PLAYFIELD(x, y)
11983   {
11984     element = Feld[x][y];
11985
11986     if (ExplodeField[x][y])
11987       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11988     else if (element == EL_EXPLOSION)
11989       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11990
11991     ExplodeField[x][y] = EX_TYPE_NONE;
11992   }
11993
11994   game.explosions_delayed = TRUE;
11995
11996   if (game.magic_wall_active)
11997   {
11998     if (!(game.magic_wall_time_left % 4))
11999     {
12000       int element = Feld[magic_wall_x][magic_wall_y];
12001
12002       if (element == EL_BD_MAGIC_WALL_FULL ||
12003           element == EL_BD_MAGIC_WALL_ACTIVE ||
12004           element == EL_BD_MAGIC_WALL_EMPTYING)
12005         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12006       else if (element == EL_DC_MAGIC_WALL_FULL ||
12007                element == EL_DC_MAGIC_WALL_ACTIVE ||
12008                element == EL_DC_MAGIC_WALL_EMPTYING)
12009         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12010       else
12011         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12012     }
12013
12014     if (game.magic_wall_time_left > 0)
12015     {
12016       game.magic_wall_time_left--;
12017
12018       if (!game.magic_wall_time_left)
12019       {
12020         SCAN_PLAYFIELD(x, y)
12021         {
12022           element = Feld[x][y];
12023
12024           if (element == EL_MAGIC_WALL_ACTIVE ||
12025               element == EL_MAGIC_WALL_FULL)
12026           {
12027             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12028             TEST_DrawLevelField(x, y);
12029           }
12030           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12031                    element == EL_BD_MAGIC_WALL_FULL)
12032           {
12033             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12034             TEST_DrawLevelField(x, y);
12035           }
12036           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12037                    element == EL_DC_MAGIC_WALL_FULL)
12038           {
12039             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12040             TEST_DrawLevelField(x, y);
12041           }
12042         }
12043
12044         game.magic_wall_active = FALSE;
12045       }
12046     }
12047   }
12048
12049   if (game.light_time_left > 0)
12050   {
12051     game.light_time_left--;
12052
12053     if (game.light_time_left == 0)
12054       RedrawAllLightSwitchesAndInvisibleElements();
12055   }
12056
12057   if (game.timegate_time_left > 0)
12058   {
12059     game.timegate_time_left--;
12060
12061     if (game.timegate_time_left == 0)
12062       CloseAllOpenTimegates();
12063   }
12064
12065   if (game.lenses_time_left > 0)
12066   {
12067     game.lenses_time_left--;
12068
12069     if (game.lenses_time_left == 0)
12070       RedrawAllInvisibleElementsForLenses();
12071   }
12072
12073   if (game.magnify_time_left > 0)
12074   {
12075     game.magnify_time_left--;
12076
12077     if (game.magnify_time_left == 0)
12078       RedrawAllInvisibleElementsForMagnifier();
12079   }
12080
12081   for (i = 0; i < MAX_PLAYERS; i++)
12082   {
12083     struct PlayerInfo *player = &stored_player[i];
12084
12085     if (SHIELD_ON(player))
12086     {
12087       if (player->shield_deadly_time_left)
12088         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12089       else if (player->shield_normal_time_left)
12090         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12091     }
12092   }
12093
12094 #if USE_DELAYED_GFX_REDRAW
12095   SCAN_PLAYFIELD(x, y)
12096   {
12097     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12098     {
12099       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12100          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12101
12102       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12103         DrawLevelField(x, y);
12104
12105       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12106         DrawLevelFieldCrumbled(x, y);
12107
12108       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12109         DrawLevelFieldCrumbledNeighbours(x, y);
12110
12111       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12112         DrawTwinkleOnField(x, y);
12113     }
12114
12115     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12116   }
12117 #endif
12118
12119   DrawAllPlayers();
12120   PlayAllPlayersSound();
12121
12122   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12123   {
12124     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12125
12126     local_player->show_envelope = 0;
12127   }
12128
12129   /* use random number generator in every frame to make it less predictable */
12130   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12131     RND(1);
12132 }
12133
12134 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12135 {
12136   int min_x = x, min_y = y, max_x = x, max_y = y;
12137   int i;
12138
12139   for (i = 0; i < MAX_PLAYERS; i++)
12140   {
12141     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12142
12143     if (!stored_player[i].active || &stored_player[i] == player)
12144       continue;
12145
12146     min_x = MIN(min_x, jx);
12147     min_y = MIN(min_y, jy);
12148     max_x = MAX(max_x, jx);
12149     max_y = MAX(max_y, jy);
12150   }
12151
12152   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12153 }
12154
12155 static boolean AllPlayersInVisibleScreen()
12156 {
12157   int i;
12158
12159   for (i = 0; i < MAX_PLAYERS; i++)
12160   {
12161     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12162
12163     if (!stored_player[i].active)
12164       continue;
12165
12166     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12167       return FALSE;
12168   }
12169
12170   return TRUE;
12171 }
12172
12173 void ScrollLevel(int dx, int dy)
12174 {
12175   int scroll_offset = 2 * TILEX_VAR;
12176   int x, y;
12177
12178   BlitBitmap(drawto_field, drawto_field,
12179              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12180              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12181              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12182              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12183              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12184              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12185
12186   if (dx != 0)
12187   {
12188     x = (dx == 1 ? BX1 : BX2);
12189     for (y = BY1; y <= BY2; y++)
12190       DrawScreenField(x, y);
12191   }
12192
12193   if (dy != 0)
12194   {
12195     y = (dy == 1 ? BY1 : BY2);
12196     for (x = BX1; x <= BX2; x++)
12197       DrawScreenField(x, y);
12198   }
12199
12200   redraw_mask |= REDRAW_FIELD;
12201 }
12202
12203 static boolean canFallDown(struct PlayerInfo *player)
12204 {
12205   int jx = player->jx, jy = player->jy;
12206
12207   return (IN_LEV_FIELD(jx, jy + 1) &&
12208           (IS_FREE(jx, jy + 1) ||
12209            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12210           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12211           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12212 }
12213
12214 static boolean canPassField(int x, int y, int move_dir)
12215 {
12216   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12217   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12218   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12219   int nextx = x + dx;
12220   int nexty = y + dy;
12221   int element = Feld[x][y];
12222
12223   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12224           !CAN_MOVE(element) &&
12225           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12226           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12227           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12228 }
12229
12230 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12231 {
12232   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12233   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12234   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12235   int newx = x + dx;
12236   int newy = y + dy;
12237
12238   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12239           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12240           (IS_DIGGABLE(Feld[newx][newy]) ||
12241            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12242            canPassField(newx, newy, move_dir)));
12243 }
12244
12245 static void CheckGravityMovement(struct PlayerInfo *player)
12246 {
12247   if (player->gravity && !player->programmed_action)
12248   {
12249     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12250     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12251     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12252     int jx = player->jx, jy = player->jy;
12253     boolean player_is_moving_to_valid_field =
12254       (!player_is_snapping &&
12255        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12256         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12257     boolean player_can_fall_down = canFallDown(player);
12258
12259     if (player_can_fall_down &&
12260         !player_is_moving_to_valid_field)
12261       player->programmed_action = MV_DOWN;
12262   }
12263 }
12264
12265 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12266 {
12267   return CheckGravityMovement(player);
12268
12269   if (player->gravity && !player->programmed_action)
12270   {
12271     int jx = player->jx, jy = player->jy;
12272     boolean field_under_player_is_free =
12273       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12274     boolean player_is_standing_on_valid_field =
12275       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12276        (IS_WALKABLE(Feld[jx][jy]) &&
12277         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12278
12279     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12280       player->programmed_action = MV_DOWN;
12281   }
12282 }
12283
12284 /*
12285   MovePlayerOneStep()
12286   -----------------------------------------------------------------------------
12287   dx, dy:               direction (non-diagonal) to try to move the player to
12288   real_dx, real_dy:     direction as read from input device (can be diagonal)
12289 */
12290
12291 boolean MovePlayerOneStep(struct PlayerInfo *player,
12292                           int dx, int dy, int real_dx, int real_dy)
12293 {
12294   int jx = player->jx, jy = player->jy;
12295   int new_jx = jx + dx, new_jy = jy + dy;
12296   int can_move;
12297   boolean player_can_move = !player->cannot_move;
12298
12299   if (!player->active || (!dx && !dy))
12300     return MP_NO_ACTION;
12301
12302   player->MovDir = (dx < 0 ? MV_LEFT :
12303                     dx > 0 ? MV_RIGHT :
12304                     dy < 0 ? MV_UP :
12305                     dy > 0 ? MV_DOWN :  MV_NONE);
12306
12307   if (!IN_LEV_FIELD(new_jx, new_jy))
12308     return MP_NO_ACTION;
12309
12310   if (!player_can_move)
12311   {
12312     if (player->MovPos == 0)
12313     {
12314       player->is_moving = FALSE;
12315       player->is_digging = FALSE;
12316       player->is_collecting = FALSE;
12317       player->is_snapping = FALSE;
12318       player->is_pushing = FALSE;
12319     }
12320   }
12321
12322   if (!network.enabled && game.centered_player_nr == -1 &&
12323       !AllPlayersInSight(player, new_jx, new_jy))
12324     return MP_NO_ACTION;
12325
12326   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12327   if (can_move != MP_MOVING)
12328     return can_move;
12329
12330   /* check if DigField() has caused relocation of the player */
12331   if (player->jx != jx || player->jy != jy)
12332     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12333
12334   StorePlayer[jx][jy] = 0;
12335   player->last_jx = jx;
12336   player->last_jy = jy;
12337   player->jx = new_jx;
12338   player->jy = new_jy;
12339   StorePlayer[new_jx][new_jy] = player->element_nr;
12340
12341   if (player->move_delay_value_next != -1)
12342   {
12343     player->move_delay_value = player->move_delay_value_next;
12344     player->move_delay_value_next = -1;
12345   }
12346
12347   player->MovPos =
12348     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12349
12350   player->step_counter++;
12351
12352   PlayerVisit[jx][jy] = FrameCounter;
12353
12354   player->is_moving = TRUE;
12355
12356 #if 1
12357   /* should better be called in MovePlayer(), but this breaks some tapes */
12358   ScrollPlayer(player, SCROLL_INIT);
12359 #endif
12360
12361   return MP_MOVING;
12362 }
12363
12364 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12365 {
12366   int jx = player->jx, jy = player->jy;
12367   int old_jx = jx, old_jy = jy;
12368   int moved = MP_NO_ACTION;
12369
12370   if (!player->active)
12371     return FALSE;
12372
12373   if (!dx && !dy)
12374   {
12375     if (player->MovPos == 0)
12376     {
12377       player->is_moving = FALSE;
12378       player->is_digging = FALSE;
12379       player->is_collecting = FALSE;
12380       player->is_snapping = FALSE;
12381       player->is_pushing = FALSE;
12382     }
12383
12384     return FALSE;
12385   }
12386
12387   if (player->move_delay > 0)
12388     return FALSE;
12389
12390   player->move_delay = -1;              /* set to "uninitialized" value */
12391
12392   /* store if player is automatically moved to next field */
12393   player->is_auto_moving = (player->programmed_action != MV_NONE);
12394
12395   /* remove the last programmed player action */
12396   player->programmed_action = 0;
12397
12398   if (player->MovPos)
12399   {
12400     /* should only happen if pre-1.2 tape recordings are played */
12401     /* this is only for backward compatibility */
12402
12403     int original_move_delay_value = player->move_delay_value;
12404
12405 #if DEBUG
12406     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12407            tape.counter);
12408 #endif
12409
12410     /* scroll remaining steps with finest movement resolution */
12411     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12412
12413     while (player->MovPos)
12414     {
12415       ScrollPlayer(player, SCROLL_GO_ON);
12416       ScrollScreen(NULL, SCROLL_GO_ON);
12417
12418       AdvanceFrameAndPlayerCounters(player->index_nr);
12419
12420       DrawAllPlayers();
12421       BackToFront_WithFrameDelay(0);
12422     }
12423
12424     player->move_delay_value = original_move_delay_value;
12425   }
12426
12427   player->is_active = FALSE;
12428
12429   if (player->last_move_dir & MV_HORIZONTAL)
12430   {
12431     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12432       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12433   }
12434   else
12435   {
12436     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12437       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12438   }
12439
12440   if (!moved && !player->is_active)
12441   {
12442     player->is_moving = FALSE;
12443     player->is_digging = FALSE;
12444     player->is_collecting = FALSE;
12445     player->is_snapping = FALSE;
12446     player->is_pushing = FALSE;
12447   }
12448
12449   jx = player->jx;
12450   jy = player->jy;
12451
12452   if (moved & MP_MOVING && !ScreenMovPos &&
12453       (player->index_nr == game.centered_player_nr ||
12454        game.centered_player_nr == -1))
12455   {
12456     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12457     int offset = game.scroll_delay_value;
12458
12459     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12460     {
12461       /* actual player has left the screen -- scroll in that direction */
12462       if (jx != old_jx)         /* player has moved horizontally */
12463         scroll_x += (jx - old_jx);
12464       else                      /* player has moved vertically */
12465         scroll_y += (jy - old_jy);
12466     }
12467     else
12468     {
12469       if (jx != old_jx)         /* player has moved horizontally */
12470       {
12471         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12472             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12473           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12474
12475         /* don't scroll over playfield boundaries */
12476         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12477           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12478
12479         /* don't scroll more than one field at a time */
12480         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12481
12482         /* don't scroll against the player's moving direction */
12483         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12484             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12485           scroll_x = old_scroll_x;
12486       }
12487       else                      /* player has moved vertically */
12488       {
12489         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12490             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12491           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12492
12493         /* don't scroll over playfield boundaries */
12494         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12495           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12496
12497         /* don't scroll more than one field at a time */
12498         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12499
12500         /* don't scroll against the player's moving direction */
12501         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12502             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12503           scroll_y = old_scroll_y;
12504       }
12505     }
12506
12507     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12508     {
12509       if (!network.enabled && game.centered_player_nr == -1 &&
12510           !AllPlayersInVisibleScreen())
12511       {
12512         scroll_x = old_scroll_x;
12513         scroll_y = old_scroll_y;
12514       }
12515       else
12516       {
12517         ScrollScreen(player, SCROLL_INIT);
12518         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12519       }
12520     }
12521   }
12522
12523   player->StepFrame = 0;
12524
12525   if (moved & MP_MOVING)
12526   {
12527     if (old_jx != jx && old_jy == jy)
12528       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12529     else if (old_jx == jx && old_jy != jy)
12530       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12531
12532     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12533
12534     player->last_move_dir = player->MovDir;
12535     player->is_moving = TRUE;
12536     player->is_snapping = FALSE;
12537     player->is_switching = FALSE;
12538     player->is_dropping = FALSE;
12539     player->is_dropping_pressed = FALSE;
12540     player->drop_pressed_delay = 0;
12541
12542 #if 0
12543     /* should better be called here than above, but this breaks some tapes */
12544     ScrollPlayer(player, SCROLL_INIT);
12545 #endif
12546   }
12547   else
12548   {
12549     CheckGravityMovementWhenNotMoving(player);
12550
12551     player->is_moving = FALSE;
12552
12553     /* at this point, the player is allowed to move, but cannot move right now
12554        (e.g. because of something blocking the way) -- ensure that the player
12555        is also allowed to move in the next frame (in old versions before 3.1.1,
12556        the player was forced to wait again for eight frames before next try) */
12557
12558     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12559       player->move_delay = 0;   /* allow direct movement in the next frame */
12560   }
12561
12562   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12563     player->move_delay = player->move_delay_value;
12564
12565   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12566   {
12567     TestIfPlayerTouchesBadThing(jx, jy);
12568     TestIfPlayerTouchesCustomElement(jx, jy);
12569   }
12570
12571   if (!player->active)
12572     RemovePlayer(player);
12573
12574   return moved;
12575 }
12576
12577 void ScrollPlayer(struct PlayerInfo *player, int mode)
12578 {
12579   int jx = player->jx, jy = player->jy;
12580   int last_jx = player->last_jx, last_jy = player->last_jy;
12581   int move_stepsize = TILEX / player->move_delay_value;
12582
12583   if (!player->active)
12584     return;
12585
12586   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12587     return;
12588
12589   if (mode == SCROLL_INIT)
12590   {
12591     player->actual_frame_counter = FrameCounter;
12592     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12593
12594     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12595         Feld[last_jx][last_jy] == EL_EMPTY)
12596     {
12597       int last_field_block_delay = 0;   /* start with no blocking at all */
12598       int block_delay_adjustment = player->block_delay_adjustment;
12599
12600       /* if player blocks last field, add delay for exactly one move */
12601       if (player->block_last_field)
12602       {
12603         last_field_block_delay += player->move_delay_value;
12604
12605         /* when blocking enabled, prevent moving up despite gravity */
12606         if (player->gravity && player->MovDir == MV_UP)
12607           block_delay_adjustment = -1;
12608       }
12609
12610       /* add block delay adjustment (also possible when not blocking) */
12611       last_field_block_delay += block_delay_adjustment;
12612
12613       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12614       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12615     }
12616
12617     if (player->MovPos != 0)    /* player has not yet reached destination */
12618       return;
12619   }
12620   else if (!FrameReached(&player->actual_frame_counter, 1))
12621     return;
12622
12623   if (player->MovPos != 0)
12624   {
12625     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12626     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12627
12628     /* before DrawPlayer() to draw correct player graphic for this case */
12629     if (player->MovPos == 0)
12630       CheckGravityMovement(player);
12631   }
12632
12633   if (player->MovPos == 0)      /* player reached destination field */
12634   {
12635     if (player->move_delay_reset_counter > 0)
12636     {
12637       player->move_delay_reset_counter--;
12638
12639       if (player->move_delay_reset_counter == 0)
12640       {
12641         /* continue with normal speed after quickly moving through gate */
12642         HALVE_PLAYER_SPEED(player);
12643
12644         /* be able to make the next move without delay */
12645         player->move_delay = 0;
12646       }
12647     }
12648
12649     player->last_jx = jx;
12650     player->last_jy = jy;
12651
12652     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12653         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12654         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12655         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12656         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12657         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12658         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12659         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12660     {
12661       ExitPlayer(player);
12662
12663       if ((local_player->friends_still_needed == 0 ||
12664            IS_SP_ELEMENT(Feld[jx][jy])) &&
12665           AllPlayersGone)
12666         PlayerWins(local_player);
12667     }
12668
12669     /* this breaks one level: "machine", level 000 */
12670     {
12671       int move_direction = player->MovDir;
12672       int enter_side = MV_DIR_OPPOSITE(move_direction);
12673       int leave_side = move_direction;
12674       int old_jx = last_jx;
12675       int old_jy = last_jy;
12676       int old_element = Feld[old_jx][old_jy];
12677       int new_element = Feld[jx][jy];
12678
12679       if (IS_CUSTOM_ELEMENT(old_element))
12680         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12681                                    CE_LEFT_BY_PLAYER,
12682                                    player->index_bit, leave_side);
12683
12684       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12685                                           CE_PLAYER_LEAVES_X,
12686                                           player->index_bit, leave_side);
12687
12688       if (IS_CUSTOM_ELEMENT(new_element))
12689         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12690                                    player->index_bit, enter_side);
12691
12692       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12693                                           CE_PLAYER_ENTERS_X,
12694                                           player->index_bit, enter_side);
12695
12696       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12697                                         CE_MOVE_OF_X, move_direction);
12698     }
12699
12700     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12701     {
12702       TestIfPlayerTouchesBadThing(jx, jy);
12703       TestIfPlayerTouchesCustomElement(jx, jy);
12704
12705       /* needed because pushed element has not yet reached its destination,
12706          so it would trigger a change event at its previous field location */
12707       if (!player->is_pushing)
12708         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12709
12710       if (!player->active)
12711         RemovePlayer(player);
12712     }
12713
12714     if (!local_player->LevelSolved && level.use_step_counter)
12715     {
12716       int i;
12717
12718       TimePlayed++;
12719
12720       if (TimeLeft > 0)
12721       {
12722         TimeLeft--;
12723
12724         if (TimeLeft <= 10 && setup.time_limit)
12725           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12726
12727         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12728
12729         DisplayGameControlValues();
12730
12731         if (!TimeLeft && setup.time_limit)
12732           for (i = 0; i < MAX_PLAYERS; i++)
12733             KillPlayer(&stored_player[i]);
12734       }
12735       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12736       {
12737         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12738
12739         DisplayGameControlValues();
12740       }
12741     }
12742
12743     if (tape.single_step && tape.recording && !tape.pausing &&
12744         !player->programmed_action)
12745       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12746
12747     if (!player->programmed_action)
12748       CheckSaveEngineSnapshot(player);
12749   }
12750 }
12751
12752 void ScrollScreen(struct PlayerInfo *player, int mode)
12753 {
12754   static unsigned int screen_frame_counter = 0;
12755
12756   if (mode == SCROLL_INIT)
12757   {
12758     /* set scrolling step size according to actual player's moving speed */
12759     ScrollStepSize = TILEX / player->move_delay_value;
12760
12761     screen_frame_counter = FrameCounter;
12762     ScreenMovDir = player->MovDir;
12763     ScreenMovPos = player->MovPos;
12764     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12765     return;
12766   }
12767   else if (!FrameReached(&screen_frame_counter, 1))
12768     return;
12769
12770   if (ScreenMovPos)
12771   {
12772     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12773     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12774     redraw_mask |= REDRAW_FIELD;
12775   }
12776   else
12777     ScreenMovDir = MV_NONE;
12778 }
12779
12780 void TestIfPlayerTouchesCustomElement(int x, int y)
12781 {
12782   static int xy[4][2] =
12783   {
12784     { 0, -1 },
12785     { -1, 0 },
12786     { +1, 0 },
12787     { 0, +1 }
12788   };
12789   static int trigger_sides[4][2] =
12790   {
12791     /* center side       border side */
12792     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12793     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12794     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12795     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12796   };
12797   static int touch_dir[4] =
12798   {
12799     MV_LEFT | MV_RIGHT,
12800     MV_UP   | MV_DOWN,
12801     MV_UP   | MV_DOWN,
12802     MV_LEFT | MV_RIGHT
12803   };
12804   int center_element = Feld[x][y];      /* should always be non-moving! */
12805   int i;
12806
12807   for (i = 0; i < NUM_DIRECTIONS; i++)
12808   {
12809     int xx = x + xy[i][0];
12810     int yy = y + xy[i][1];
12811     int center_side = trigger_sides[i][0];
12812     int border_side = trigger_sides[i][1];
12813     int border_element;
12814
12815     if (!IN_LEV_FIELD(xx, yy))
12816       continue;
12817
12818     if (IS_PLAYER(x, y))                /* player found at center element */
12819     {
12820       struct PlayerInfo *player = PLAYERINFO(x, y);
12821
12822       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12823         border_element = Feld[xx][yy];          /* may be moving! */
12824       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12825         border_element = Feld[xx][yy];
12826       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12827         border_element = MovingOrBlocked2Element(xx, yy);
12828       else
12829         continue;               /* center and border element do not touch */
12830
12831       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12832                                  player->index_bit, border_side);
12833       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12834                                           CE_PLAYER_TOUCHES_X,
12835                                           player->index_bit, border_side);
12836
12837       {
12838         /* use player element that is initially defined in the level playfield,
12839            not the player element that corresponds to the runtime player number
12840            (example: a level that contains EL_PLAYER_3 as the only player would
12841            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12842         int player_element = PLAYERINFO(x, y)->initial_element;
12843
12844         CheckElementChangeBySide(xx, yy, border_element, player_element,
12845                                  CE_TOUCHING_X, border_side);
12846       }
12847     }
12848     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12849     {
12850       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12851
12852       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12853       {
12854         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12855           continue;             /* center and border element do not touch */
12856       }
12857
12858       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12859                                  player->index_bit, center_side);
12860       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12861                                           CE_PLAYER_TOUCHES_X,
12862                                           player->index_bit, center_side);
12863
12864       {
12865         /* use player element that is initially defined in the level playfield,
12866            not the player element that corresponds to the runtime player number
12867            (example: a level that contains EL_PLAYER_3 as the only player would
12868            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12869         int player_element = PLAYERINFO(xx, yy)->initial_element;
12870
12871         CheckElementChangeBySide(x, y, center_element, player_element,
12872                                  CE_TOUCHING_X, center_side);
12873       }
12874
12875       break;
12876     }
12877   }
12878 }
12879
12880 void TestIfElementTouchesCustomElement(int x, int y)
12881 {
12882   static int xy[4][2] =
12883   {
12884     { 0, -1 },
12885     { -1, 0 },
12886     { +1, 0 },
12887     { 0, +1 }
12888   };
12889   static int trigger_sides[4][2] =
12890   {
12891     /* center side      border side */
12892     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12893     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12894     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12895     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12896   };
12897   static int touch_dir[4] =
12898   {
12899     MV_LEFT | MV_RIGHT,
12900     MV_UP   | MV_DOWN,
12901     MV_UP   | MV_DOWN,
12902     MV_LEFT | MV_RIGHT
12903   };
12904   boolean change_center_element = FALSE;
12905   int center_element = Feld[x][y];      /* should always be non-moving! */
12906   int border_element_old[NUM_DIRECTIONS];
12907   int i;
12908
12909   for (i = 0; i < NUM_DIRECTIONS; i++)
12910   {
12911     int xx = x + xy[i][0];
12912     int yy = y + xy[i][1];
12913     int border_element;
12914
12915     border_element_old[i] = -1;
12916
12917     if (!IN_LEV_FIELD(xx, yy))
12918       continue;
12919
12920     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12921       border_element = Feld[xx][yy];    /* may be moving! */
12922     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12923       border_element = Feld[xx][yy];
12924     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12925       border_element = MovingOrBlocked2Element(xx, yy);
12926     else
12927       continue;                 /* center and border element do not touch */
12928
12929     border_element_old[i] = border_element;
12930   }
12931
12932   for (i = 0; i < NUM_DIRECTIONS; i++)
12933   {
12934     int xx = x + xy[i][0];
12935     int yy = y + xy[i][1];
12936     int center_side = trigger_sides[i][0];
12937     int border_element = border_element_old[i];
12938
12939     if (border_element == -1)
12940       continue;
12941
12942     /* check for change of border element */
12943     CheckElementChangeBySide(xx, yy, border_element, center_element,
12944                              CE_TOUCHING_X, center_side);
12945
12946     /* (center element cannot be player, so we dont have to check this here) */
12947   }
12948
12949   for (i = 0; i < NUM_DIRECTIONS; i++)
12950   {
12951     int xx = x + xy[i][0];
12952     int yy = y + xy[i][1];
12953     int border_side = trigger_sides[i][1];
12954     int border_element = border_element_old[i];
12955
12956     if (border_element == -1)
12957       continue;
12958
12959     /* check for change of center element (but change it only once) */
12960     if (!change_center_element)
12961       change_center_element =
12962         CheckElementChangeBySide(x, y, center_element, border_element,
12963                                  CE_TOUCHING_X, border_side);
12964
12965     if (IS_PLAYER(xx, yy))
12966     {
12967       /* use player element that is initially defined in the level playfield,
12968          not the player element that corresponds to the runtime player number
12969          (example: a level that contains EL_PLAYER_3 as the only player would
12970          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12971       int player_element = PLAYERINFO(xx, yy)->initial_element;
12972
12973       CheckElementChangeBySide(x, y, center_element, player_element,
12974                                CE_TOUCHING_X, border_side);
12975     }
12976   }
12977 }
12978
12979 void TestIfElementHitsCustomElement(int x, int y, int direction)
12980 {
12981   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12982   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12983   int hitx = x + dx, hity = y + dy;
12984   int hitting_element = Feld[x][y];
12985   int touched_element;
12986
12987   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12988     return;
12989
12990   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12991                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12992
12993   if (IN_LEV_FIELD(hitx, hity))
12994   {
12995     int opposite_direction = MV_DIR_OPPOSITE(direction);
12996     int hitting_side = direction;
12997     int touched_side = opposite_direction;
12998     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12999                           MovDir[hitx][hity] != direction ||
13000                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13001
13002     object_hit = TRUE;
13003
13004     if (object_hit)
13005     {
13006       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13007                                CE_HITTING_X, touched_side);
13008
13009       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13010                                CE_HIT_BY_X, hitting_side);
13011
13012       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13013                                CE_HIT_BY_SOMETHING, opposite_direction);
13014
13015       if (IS_PLAYER(hitx, hity))
13016       {
13017         /* use player element that is initially defined in the level playfield,
13018            not the player element that corresponds to the runtime player number
13019            (example: a level that contains EL_PLAYER_3 as the only player would
13020            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13021         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13022
13023         CheckElementChangeBySide(x, y, hitting_element, player_element,
13024                                  CE_HITTING_X, touched_side);
13025       }
13026     }
13027   }
13028
13029   /* "hitting something" is also true when hitting the playfield border */
13030   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13031                            CE_HITTING_SOMETHING, direction);
13032 }
13033
13034 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13035 {
13036   int i, kill_x = -1, kill_y = -1;
13037
13038   int bad_element = -1;
13039   static int test_xy[4][2] =
13040   {
13041     { 0, -1 },
13042     { -1, 0 },
13043     { +1, 0 },
13044     { 0, +1 }
13045   };
13046   static int test_dir[4] =
13047   {
13048     MV_UP,
13049     MV_LEFT,
13050     MV_RIGHT,
13051     MV_DOWN
13052   };
13053
13054   for (i = 0; i < NUM_DIRECTIONS; i++)
13055   {
13056     int test_x, test_y, test_move_dir, test_element;
13057
13058     test_x = good_x + test_xy[i][0];
13059     test_y = good_y + test_xy[i][1];
13060
13061     if (!IN_LEV_FIELD(test_x, test_y))
13062       continue;
13063
13064     test_move_dir =
13065       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13066
13067     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13068
13069     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13070        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13071     */
13072     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13073         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13074     {
13075       kill_x = test_x;
13076       kill_y = test_y;
13077       bad_element = test_element;
13078
13079       break;
13080     }
13081   }
13082
13083   if (kill_x != -1 || kill_y != -1)
13084   {
13085     if (IS_PLAYER(good_x, good_y))
13086     {
13087       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13088
13089       if (player->shield_deadly_time_left > 0 &&
13090           !IS_INDESTRUCTIBLE(bad_element))
13091         Bang(kill_x, kill_y);
13092       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13093         KillPlayer(player);
13094     }
13095     else
13096       Bang(good_x, good_y);
13097   }
13098 }
13099
13100 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13101 {
13102   int i, kill_x = -1, kill_y = -1;
13103   int bad_element = Feld[bad_x][bad_y];
13104   static int test_xy[4][2] =
13105   {
13106     { 0, -1 },
13107     { -1, 0 },
13108     { +1, 0 },
13109     { 0, +1 }
13110   };
13111   static int touch_dir[4] =
13112   {
13113     MV_LEFT | MV_RIGHT,
13114     MV_UP   | MV_DOWN,
13115     MV_UP   | MV_DOWN,
13116     MV_LEFT | MV_RIGHT
13117   };
13118   static int test_dir[4] =
13119   {
13120     MV_UP,
13121     MV_LEFT,
13122     MV_RIGHT,
13123     MV_DOWN
13124   };
13125
13126   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13127     return;
13128
13129   for (i = 0; i < NUM_DIRECTIONS; i++)
13130   {
13131     int test_x, test_y, test_move_dir, test_element;
13132
13133     test_x = bad_x + test_xy[i][0];
13134     test_y = bad_y + test_xy[i][1];
13135
13136     if (!IN_LEV_FIELD(test_x, test_y))
13137       continue;
13138
13139     test_move_dir =
13140       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13141
13142     test_element = Feld[test_x][test_y];
13143
13144     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13145        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13146     */
13147     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13148         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13149     {
13150       /* good thing is player or penguin that does not move away */
13151       if (IS_PLAYER(test_x, test_y))
13152       {
13153         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13154
13155         if (bad_element == EL_ROBOT && player->is_moving)
13156           continue;     /* robot does not kill player if he is moving */
13157
13158         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13159         {
13160           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13161             continue;           /* center and border element do not touch */
13162         }
13163
13164         kill_x = test_x;
13165         kill_y = test_y;
13166
13167         break;
13168       }
13169       else if (test_element == EL_PENGUIN)
13170       {
13171         kill_x = test_x;
13172         kill_y = test_y;
13173
13174         break;
13175       }
13176     }
13177   }
13178
13179   if (kill_x != -1 || kill_y != -1)
13180   {
13181     if (IS_PLAYER(kill_x, kill_y))
13182     {
13183       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13184
13185       if (player->shield_deadly_time_left > 0 &&
13186           !IS_INDESTRUCTIBLE(bad_element))
13187         Bang(bad_x, bad_y);
13188       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13189         KillPlayer(player);
13190     }
13191     else
13192       Bang(kill_x, kill_y);
13193   }
13194 }
13195
13196 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13197 {
13198   int bad_element = Feld[bad_x][bad_y];
13199   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13200   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13201   int test_x = bad_x + dx, test_y = bad_y + dy;
13202   int test_move_dir, test_element;
13203   int kill_x = -1, kill_y = -1;
13204
13205   if (!IN_LEV_FIELD(test_x, test_y))
13206     return;
13207
13208   test_move_dir =
13209     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13210
13211   test_element = Feld[test_x][test_y];
13212
13213   if (test_move_dir != bad_move_dir)
13214   {
13215     /* good thing can be player or penguin that does not move away */
13216     if (IS_PLAYER(test_x, test_y))
13217     {
13218       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13219
13220       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13221          player as being hit when he is moving towards the bad thing, because
13222          the "get hit by" condition would be lost after the player stops) */
13223       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13224         return;         /* player moves away from bad thing */
13225
13226       kill_x = test_x;
13227       kill_y = test_y;
13228     }
13229     else if (test_element == EL_PENGUIN)
13230     {
13231       kill_x = test_x;
13232       kill_y = test_y;
13233     }
13234   }
13235
13236   if (kill_x != -1 || kill_y != -1)
13237   {
13238     if (IS_PLAYER(kill_x, kill_y))
13239     {
13240       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13241
13242       if (player->shield_deadly_time_left > 0 &&
13243           !IS_INDESTRUCTIBLE(bad_element))
13244         Bang(bad_x, bad_y);
13245       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13246         KillPlayer(player);
13247     }
13248     else
13249       Bang(kill_x, kill_y);
13250   }
13251 }
13252
13253 void TestIfPlayerTouchesBadThing(int x, int y)
13254 {
13255   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13256 }
13257
13258 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13259 {
13260   TestIfGoodThingHitsBadThing(x, y, move_dir);
13261 }
13262
13263 void TestIfBadThingTouchesPlayer(int x, int y)
13264 {
13265   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13266 }
13267
13268 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13269 {
13270   TestIfBadThingHitsGoodThing(x, y, move_dir);
13271 }
13272
13273 void TestIfFriendTouchesBadThing(int x, int y)
13274 {
13275   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13276 }
13277
13278 void TestIfBadThingTouchesFriend(int x, int y)
13279 {
13280   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13281 }
13282
13283 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13284 {
13285   int i, kill_x = bad_x, kill_y = bad_y;
13286   static int xy[4][2] =
13287   {
13288     { 0, -1 },
13289     { -1, 0 },
13290     { +1, 0 },
13291     { 0, +1 }
13292   };
13293
13294   for (i = 0; i < NUM_DIRECTIONS; i++)
13295   {
13296     int x, y, element;
13297
13298     x = bad_x + xy[i][0];
13299     y = bad_y + xy[i][1];
13300     if (!IN_LEV_FIELD(x, y))
13301       continue;
13302
13303     element = Feld[x][y];
13304     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13305         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13306     {
13307       kill_x = x;
13308       kill_y = y;
13309       break;
13310     }
13311   }
13312
13313   if (kill_x != bad_x || kill_y != bad_y)
13314     Bang(bad_x, bad_y);
13315 }
13316
13317 void KillPlayer(struct PlayerInfo *player)
13318 {
13319   int jx = player->jx, jy = player->jy;
13320
13321   if (!player->active)
13322     return;
13323
13324 #if 0
13325   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13326          player->killed, player->active, player->reanimated);
13327 #endif
13328
13329   /* the following code was introduced to prevent an infinite loop when calling
13330      -> Bang()
13331      -> CheckTriggeredElementChangeExt()
13332      -> ExecuteCustomElementAction()
13333      -> KillPlayer()
13334      -> (infinitely repeating the above sequence of function calls)
13335      which occurs when killing the player while having a CE with the setting
13336      "kill player X when explosion of <player X>"; the solution using a new
13337      field "player->killed" was chosen for backwards compatibility, although
13338      clever use of the fields "player->active" etc. would probably also work */
13339 #if 1
13340   if (player->killed)
13341     return;
13342 #endif
13343
13344   player->killed = TRUE;
13345
13346   /* remove accessible field at the player's position */
13347   Feld[jx][jy] = EL_EMPTY;
13348
13349   /* deactivate shield (else Bang()/Explode() would not work right) */
13350   player->shield_normal_time_left = 0;
13351   player->shield_deadly_time_left = 0;
13352
13353 #if 0
13354   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13355          player->killed, player->active, player->reanimated);
13356 #endif
13357
13358   Bang(jx, jy);
13359
13360 #if 0
13361   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13362          player->killed, player->active, player->reanimated);
13363 #endif
13364
13365   if (player->reanimated)       /* killed player may have been reanimated */
13366     player->killed = player->reanimated = FALSE;
13367   else
13368     BuryPlayer(player);
13369 }
13370
13371 static void KillPlayerUnlessEnemyProtected(int x, int y)
13372 {
13373   if (!PLAYER_ENEMY_PROTECTED(x, y))
13374     KillPlayer(PLAYERINFO(x, y));
13375 }
13376
13377 static void KillPlayerUnlessExplosionProtected(int x, int y)
13378 {
13379   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13380     KillPlayer(PLAYERINFO(x, y));
13381 }
13382
13383 void BuryPlayer(struct PlayerInfo *player)
13384 {
13385   int jx = player->jx, jy = player->jy;
13386
13387   if (!player->active)
13388     return;
13389
13390   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13391   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13392
13393   player->GameOver = TRUE;
13394   RemovePlayer(player);
13395 }
13396
13397 void RemovePlayer(struct PlayerInfo *player)
13398 {
13399   int jx = player->jx, jy = player->jy;
13400   int i, found = FALSE;
13401
13402   player->present = FALSE;
13403   player->active = FALSE;
13404
13405   if (!ExplodeField[jx][jy])
13406     StorePlayer[jx][jy] = 0;
13407
13408   if (player->is_moving)
13409     TEST_DrawLevelField(player->last_jx, player->last_jy);
13410
13411   for (i = 0; i < MAX_PLAYERS; i++)
13412     if (stored_player[i].active)
13413       found = TRUE;
13414
13415   if (!found)
13416     AllPlayersGone = TRUE;
13417
13418   ExitX = ZX = jx;
13419   ExitY = ZY = jy;
13420 }
13421
13422 void ExitPlayer(struct PlayerInfo *player)
13423 {
13424   DrawPlayer(player);   /* needed here only to cleanup last field */
13425   RemovePlayer(player);
13426
13427   local_player->players_still_needed--;
13428 }
13429
13430 static void setFieldForSnapping(int x, int y, int element, int direction)
13431 {
13432   struct ElementInfo *ei = &element_info[element];
13433   int direction_bit = MV_DIR_TO_BIT(direction);
13434   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13435   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13436                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13437
13438   Feld[x][y] = EL_ELEMENT_SNAPPING;
13439   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13440
13441   ResetGfxAnimation(x, y);
13442
13443   GfxElement[x][y] = element;
13444   GfxAction[x][y] = action;
13445   GfxDir[x][y] = direction;
13446   GfxFrame[x][y] = -1;
13447 }
13448
13449 /*
13450   =============================================================================
13451   checkDiagonalPushing()
13452   -----------------------------------------------------------------------------
13453   check if diagonal input device direction results in pushing of object
13454   (by checking if the alternative direction is walkable, diggable, ...)
13455   =============================================================================
13456 */
13457
13458 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13459                                     int x, int y, int real_dx, int real_dy)
13460 {
13461   int jx, jy, dx, dy, xx, yy;
13462
13463   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13464     return TRUE;
13465
13466   /* diagonal direction: check alternative direction */
13467   jx = player->jx;
13468   jy = player->jy;
13469   dx = x - jx;
13470   dy = y - jy;
13471   xx = jx + (dx == 0 ? real_dx : 0);
13472   yy = jy + (dy == 0 ? real_dy : 0);
13473
13474   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13475 }
13476
13477 /*
13478   =============================================================================
13479   DigField()
13480   -----------------------------------------------------------------------------
13481   x, y:                 field next to player (non-diagonal) to try to dig to
13482   real_dx, real_dy:     direction as read from input device (can be diagonal)
13483   =============================================================================
13484 */
13485
13486 static int DigField(struct PlayerInfo *player,
13487                     int oldx, int oldy, int x, int y,
13488                     int real_dx, int real_dy, int mode)
13489 {
13490   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13491   boolean player_was_pushing = player->is_pushing;
13492   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13493   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13494   int jx = oldx, jy = oldy;
13495   int dx = x - jx, dy = y - jy;
13496   int nextx = x + dx, nexty = y + dy;
13497   int move_direction = (dx == -1 ? MV_LEFT  :
13498                         dx == +1 ? MV_RIGHT :
13499                         dy == -1 ? MV_UP    :
13500                         dy == +1 ? MV_DOWN  : MV_NONE);
13501   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13502   int dig_side = MV_DIR_OPPOSITE(move_direction);
13503   int old_element = Feld[jx][jy];
13504   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13505   int collect_count;
13506
13507   if (is_player)                /* function can also be called by EL_PENGUIN */
13508   {
13509     if (player->MovPos == 0)
13510     {
13511       player->is_digging = FALSE;
13512       player->is_collecting = FALSE;
13513     }
13514
13515     if (player->MovPos == 0)    /* last pushing move finished */
13516       player->is_pushing = FALSE;
13517
13518     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13519     {
13520       player->is_switching = FALSE;
13521       player->push_delay = -1;
13522
13523       return MP_NO_ACTION;
13524     }
13525   }
13526
13527   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13528     old_element = Back[jx][jy];
13529
13530   /* in case of element dropped at player position, check background */
13531   else if (Back[jx][jy] != EL_EMPTY &&
13532            game.engine_version >= VERSION_IDENT(2,2,0,0))
13533     old_element = Back[jx][jy];
13534
13535   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13536     return MP_NO_ACTION;        /* field has no opening in this direction */
13537
13538   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13539     return MP_NO_ACTION;        /* field has no opening in this direction */
13540
13541   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13542   {
13543     SplashAcid(x, y);
13544
13545     Feld[jx][jy] = player->artwork_element;
13546     InitMovingField(jx, jy, MV_DOWN);
13547     Store[jx][jy] = EL_ACID;
13548     ContinueMoving(jx, jy);
13549     BuryPlayer(player);
13550
13551     return MP_DONT_RUN_INTO;
13552   }
13553
13554   if (player_can_move && DONT_RUN_INTO(element))
13555   {
13556     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13557
13558     return MP_DONT_RUN_INTO;
13559   }
13560
13561   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13562     return MP_NO_ACTION;
13563
13564   collect_count = element_info[element].collect_count_initial;
13565
13566   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13567     return MP_NO_ACTION;
13568
13569   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13570     player_can_move = player_can_move_or_snap;
13571
13572   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13573       game.engine_version >= VERSION_IDENT(2,2,0,0))
13574   {
13575     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13576                                player->index_bit, dig_side);
13577     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13578                                         player->index_bit, dig_side);
13579
13580     if (element == EL_DC_LANDMINE)
13581       Bang(x, y);
13582
13583     if (Feld[x][y] != element)          /* field changed by snapping */
13584       return MP_ACTION;
13585
13586     return MP_NO_ACTION;
13587   }
13588
13589   if (player->gravity && is_player && !player->is_auto_moving &&
13590       canFallDown(player) && move_direction != MV_DOWN &&
13591       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13592     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13593
13594   if (player_can_move &&
13595       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13596   {
13597     int sound_element = SND_ELEMENT(element);
13598     int sound_action = ACTION_WALKING;
13599
13600     if (IS_RND_GATE(element))
13601     {
13602       if (!player->key[RND_GATE_NR(element)])
13603         return MP_NO_ACTION;
13604     }
13605     else if (IS_RND_GATE_GRAY(element))
13606     {
13607       if (!player->key[RND_GATE_GRAY_NR(element)])
13608         return MP_NO_ACTION;
13609     }
13610     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13611     {
13612       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13613         return MP_NO_ACTION;
13614     }
13615     else if (element == EL_EXIT_OPEN ||
13616              element == EL_EM_EXIT_OPEN ||
13617              element == EL_EM_EXIT_OPENING ||
13618              element == EL_STEEL_EXIT_OPEN ||
13619              element == EL_EM_STEEL_EXIT_OPEN ||
13620              element == EL_EM_STEEL_EXIT_OPENING ||
13621              element == EL_SP_EXIT_OPEN ||
13622              element == EL_SP_EXIT_OPENING)
13623     {
13624       sound_action = ACTION_PASSING;    /* player is passing exit */
13625     }
13626     else if (element == EL_EMPTY)
13627     {
13628       sound_action = ACTION_MOVING;             /* nothing to walk on */
13629     }
13630
13631     /* play sound from background or player, whatever is available */
13632     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13633       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13634     else
13635       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13636   }
13637   else if (player_can_move &&
13638            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13639   {
13640     if (!ACCESS_FROM(element, opposite_direction))
13641       return MP_NO_ACTION;      /* field not accessible from this direction */
13642
13643     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13644       return MP_NO_ACTION;
13645
13646     if (IS_EM_GATE(element))
13647     {
13648       if (!player->key[EM_GATE_NR(element)])
13649         return MP_NO_ACTION;
13650     }
13651     else if (IS_EM_GATE_GRAY(element))
13652     {
13653       if (!player->key[EM_GATE_GRAY_NR(element)])
13654         return MP_NO_ACTION;
13655     }
13656     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13657     {
13658       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13659         return MP_NO_ACTION;
13660     }
13661     else if (IS_EMC_GATE(element))
13662     {
13663       if (!player->key[EMC_GATE_NR(element)])
13664         return MP_NO_ACTION;
13665     }
13666     else if (IS_EMC_GATE_GRAY(element))
13667     {
13668       if (!player->key[EMC_GATE_GRAY_NR(element)])
13669         return MP_NO_ACTION;
13670     }
13671     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13672     {
13673       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13674         return MP_NO_ACTION;
13675     }
13676     else if (element == EL_DC_GATE_WHITE ||
13677              element == EL_DC_GATE_WHITE_GRAY ||
13678              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13679     {
13680       if (player->num_white_keys == 0)
13681         return MP_NO_ACTION;
13682
13683       player->num_white_keys--;
13684     }
13685     else if (IS_SP_PORT(element))
13686     {
13687       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13688           element == EL_SP_GRAVITY_PORT_RIGHT ||
13689           element == EL_SP_GRAVITY_PORT_UP ||
13690           element == EL_SP_GRAVITY_PORT_DOWN)
13691         player->gravity = !player->gravity;
13692       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13693                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13694                element == EL_SP_GRAVITY_ON_PORT_UP ||
13695                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13696         player->gravity = TRUE;
13697       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13698                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13699                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13700                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13701         player->gravity = FALSE;
13702     }
13703
13704     /* automatically move to the next field with double speed */
13705     player->programmed_action = move_direction;
13706
13707     if (player->move_delay_reset_counter == 0)
13708     {
13709       player->move_delay_reset_counter = 2;     /* two double speed steps */
13710
13711       DOUBLE_PLAYER_SPEED(player);
13712     }
13713
13714     PlayLevelSoundAction(x, y, ACTION_PASSING);
13715   }
13716   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13717   {
13718     RemoveField(x, y);
13719
13720     if (mode != DF_SNAP)
13721     {
13722       GfxElement[x][y] = GFX_ELEMENT(element);
13723       player->is_digging = TRUE;
13724     }
13725
13726     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13727
13728     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13729                                         player->index_bit, dig_side);
13730
13731     if (mode == DF_SNAP)
13732     {
13733       if (level.block_snap_field)
13734         setFieldForSnapping(x, y, element, move_direction);
13735       else
13736         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13737
13738       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13739                                           player->index_bit, dig_side);
13740     }
13741   }
13742   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13743   {
13744     RemoveField(x, y);
13745
13746     if (is_player && mode != DF_SNAP)
13747     {
13748       GfxElement[x][y] = element;
13749       player->is_collecting = TRUE;
13750     }
13751
13752     if (element == EL_SPEED_PILL)
13753     {
13754       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13755     }
13756     else if (element == EL_EXTRA_TIME && level.time > 0)
13757     {
13758       TimeLeft += level.extra_time;
13759
13760       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13761
13762       DisplayGameControlValues();
13763     }
13764     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13765     {
13766       player->shield_normal_time_left += level.shield_normal_time;
13767       if (element == EL_SHIELD_DEADLY)
13768         player->shield_deadly_time_left += level.shield_deadly_time;
13769     }
13770     else if (element == EL_DYNAMITE ||
13771              element == EL_EM_DYNAMITE ||
13772              element == EL_SP_DISK_RED)
13773     {
13774       if (player->inventory_size < MAX_INVENTORY_SIZE)
13775         player->inventory_element[player->inventory_size++] = element;
13776
13777       DrawGameDoorValues();
13778     }
13779     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13780     {
13781       player->dynabomb_count++;
13782       player->dynabombs_left++;
13783     }
13784     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13785     {
13786       player->dynabomb_size++;
13787     }
13788     else if (element == EL_DYNABOMB_INCREASE_POWER)
13789     {
13790       player->dynabomb_xl = TRUE;
13791     }
13792     else if (IS_KEY(element))
13793     {
13794       player->key[KEY_NR(element)] = TRUE;
13795
13796       DrawGameDoorValues();
13797     }
13798     else if (element == EL_DC_KEY_WHITE)
13799     {
13800       player->num_white_keys++;
13801
13802       /* display white keys? */
13803       /* DrawGameDoorValues(); */
13804     }
13805     else if (IS_ENVELOPE(element))
13806     {
13807       player->show_envelope = element;
13808     }
13809     else if (element == EL_EMC_LENSES)
13810     {
13811       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13812
13813       RedrawAllInvisibleElementsForLenses();
13814     }
13815     else if (element == EL_EMC_MAGNIFIER)
13816     {
13817       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13818
13819       RedrawAllInvisibleElementsForMagnifier();
13820     }
13821     else if (IS_DROPPABLE(element) ||
13822              IS_THROWABLE(element))     /* can be collected and dropped */
13823     {
13824       int i;
13825
13826       if (collect_count == 0)
13827         player->inventory_infinite_element = element;
13828       else
13829         for (i = 0; i < collect_count; i++)
13830           if (player->inventory_size < MAX_INVENTORY_SIZE)
13831             player->inventory_element[player->inventory_size++] = element;
13832
13833       DrawGameDoorValues();
13834     }
13835     else if (collect_count > 0)
13836     {
13837       local_player->gems_still_needed -= collect_count;
13838       if (local_player->gems_still_needed < 0)
13839         local_player->gems_still_needed = 0;
13840
13841       game.snapshot.collected_item = TRUE;
13842
13843       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13844
13845       DisplayGameControlValues();
13846     }
13847
13848     RaiseScoreElement(element);
13849     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13850
13851     if (is_player)
13852       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13853                                           player->index_bit, dig_side);
13854
13855     if (mode == DF_SNAP)
13856     {
13857       if (level.block_snap_field)
13858         setFieldForSnapping(x, y, element, move_direction);
13859       else
13860         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13861
13862       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13863                                           player->index_bit, dig_side);
13864     }
13865   }
13866   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13867   {
13868     if (mode == DF_SNAP && element != EL_BD_ROCK)
13869       return MP_NO_ACTION;
13870
13871     if (CAN_FALL(element) && dy)
13872       return MP_NO_ACTION;
13873
13874     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13875         !(element == EL_SPRING && level.use_spring_bug))
13876       return MP_NO_ACTION;
13877
13878     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13879         ((move_direction & MV_VERTICAL &&
13880           ((element_info[element].move_pattern & MV_LEFT &&
13881             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13882            (element_info[element].move_pattern & MV_RIGHT &&
13883             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13884          (move_direction & MV_HORIZONTAL &&
13885           ((element_info[element].move_pattern & MV_UP &&
13886             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13887            (element_info[element].move_pattern & MV_DOWN &&
13888             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13889       return MP_NO_ACTION;
13890
13891     /* do not push elements already moving away faster than player */
13892     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13893         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13894       return MP_NO_ACTION;
13895
13896     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13897     {
13898       if (player->push_delay_value == -1 || !player_was_pushing)
13899         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13900     }
13901     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13902     {
13903       if (player->push_delay_value == -1)
13904         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13905     }
13906     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13907     {
13908       if (!player->is_pushing)
13909         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13910     }
13911
13912     player->is_pushing = TRUE;
13913     player->is_active = TRUE;
13914
13915     if (!(IN_LEV_FIELD(nextx, nexty) &&
13916           (IS_FREE(nextx, nexty) ||
13917            (IS_SB_ELEMENT(element) &&
13918             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13919            (IS_CUSTOM_ELEMENT(element) &&
13920             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13921       return MP_NO_ACTION;
13922
13923     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13924       return MP_NO_ACTION;
13925
13926     if (player->push_delay == -1)       /* new pushing; restart delay */
13927       player->push_delay = 0;
13928
13929     if (player->push_delay < player->push_delay_value &&
13930         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13931         element != EL_SPRING && element != EL_BALLOON)
13932     {
13933       /* make sure that there is no move delay before next try to push */
13934       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13935         player->move_delay = 0;
13936
13937       return MP_NO_ACTION;
13938     }
13939
13940     if (IS_CUSTOM_ELEMENT(element) &&
13941         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13942     {
13943       if (!DigFieldByCE(nextx, nexty, element))
13944         return MP_NO_ACTION;
13945     }
13946
13947     if (IS_SB_ELEMENT(element))
13948     {
13949       if (element == EL_SOKOBAN_FIELD_FULL)
13950       {
13951         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13952         local_player->sokobanfields_still_needed++;
13953       }
13954
13955       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13956       {
13957         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13958         local_player->sokobanfields_still_needed--;
13959       }
13960
13961       Feld[x][y] = EL_SOKOBAN_OBJECT;
13962
13963       if (Back[x][y] == Back[nextx][nexty])
13964         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13965       else if (Back[x][y] != 0)
13966         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13967                                     ACTION_EMPTYING);
13968       else
13969         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13970                                     ACTION_FILLING);
13971
13972       if (local_player->sokobanfields_still_needed == 0 &&
13973           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13974       {
13975         local_player->players_still_needed = 0;
13976
13977         PlayerWins(player);
13978
13979         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13980       }
13981     }
13982     else
13983       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13984
13985     InitMovingField(x, y, move_direction);
13986     GfxAction[x][y] = ACTION_PUSHING;
13987
13988     if (mode == DF_SNAP)
13989       ContinueMoving(x, y);
13990     else
13991       MovPos[x][y] = (dx != 0 ? dx : dy);
13992
13993     Pushed[x][y] = TRUE;
13994     Pushed[nextx][nexty] = TRUE;
13995
13996     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13997       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13998     else
13999       player->push_delay_value = -1;    /* get new value later */
14000
14001     /* check for element change _after_ element has been pushed */
14002     if (game.use_change_when_pushing_bug)
14003     {
14004       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14005                                  player->index_bit, dig_side);
14006       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14007                                           player->index_bit, dig_side);
14008     }
14009   }
14010   else if (IS_SWITCHABLE(element))
14011   {
14012     if (PLAYER_SWITCHING(player, x, y))
14013     {
14014       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14015                                           player->index_bit, dig_side);
14016
14017       return MP_ACTION;
14018     }
14019
14020     player->is_switching = TRUE;
14021     player->switch_x = x;
14022     player->switch_y = y;
14023
14024     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14025
14026     if (element == EL_ROBOT_WHEEL)
14027     {
14028       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14029       ZX = x;
14030       ZY = y;
14031
14032       game.robot_wheel_active = TRUE;
14033
14034       TEST_DrawLevelField(x, y);
14035     }
14036     else if (element == EL_SP_TERMINAL)
14037     {
14038       int xx, yy;
14039
14040       SCAN_PLAYFIELD(xx, yy)
14041       {
14042         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14043         {
14044           Bang(xx, yy);
14045         }
14046         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14047         {
14048           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14049
14050           ResetGfxAnimation(xx, yy);
14051           TEST_DrawLevelField(xx, yy);
14052         }
14053       }
14054     }
14055     else if (IS_BELT_SWITCH(element))
14056     {
14057       ToggleBeltSwitch(x, y);
14058     }
14059     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14060              element == EL_SWITCHGATE_SWITCH_DOWN ||
14061              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14062              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14063     {
14064       ToggleSwitchgateSwitch(x, y);
14065     }
14066     else if (element == EL_LIGHT_SWITCH ||
14067              element == EL_LIGHT_SWITCH_ACTIVE)
14068     {
14069       ToggleLightSwitch(x, y);
14070     }
14071     else if (element == EL_TIMEGATE_SWITCH ||
14072              element == EL_DC_TIMEGATE_SWITCH)
14073     {
14074       ActivateTimegateSwitch(x, y);
14075     }
14076     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14077              element == EL_BALLOON_SWITCH_RIGHT ||
14078              element == EL_BALLOON_SWITCH_UP    ||
14079              element == EL_BALLOON_SWITCH_DOWN  ||
14080              element == EL_BALLOON_SWITCH_NONE  ||
14081              element == EL_BALLOON_SWITCH_ANY)
14082     {
14083       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14084                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14085                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14086                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14087                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14088                              move_direction);
14089     }
14090     else if (element == EL_LAMP)
14091     {
14092       Feld[x][y] = EL_LAMP_ACTIVE;
14093       local_player->lights_still_needed--;
14094
14095       ResetGfxAnimation(x, y);
14096       TEST_DrawLevelField(x, y);
14097     }
14098     else if (element == EL_TIME_ORB_FULL)
14099     {
14100       Feld[x][y] = EL_TIME_ORB_EMPTY;
14101
14102       if (level.time > 0 || level.use_time_orb_bug)
14103       {
14104         TimeLeft += level.time_orb_time;
14105         game.no_time_limit = FALSE;
14106
14107         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14108
14109         DisplayGameControlValues();
14110       }
14111
14112       ResetGfxAnimation(x, y);
14113       TEST_DrawLevelField(x, y);
14114     }
14115     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14116              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14117     {
14118       int xx, yy;
14119
14120       game.ball_state = !game.ball_state;
14121
14122       SCAN_PLAYFIELD(xx, yy)
14123       {
14124         int e = Feld[xx][yy];
14125
14126         if (game.ball_state)
14127         {
14128           if (e == EL_EMC_MAGIC_BALL)
14129             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14130           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14131             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14132         }
14133         else
14134         {
14135           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14136             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14137           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14138             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14139         }
14140       }
14141     }
14142
14143     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14144                                         player->index_bit, dig_side);
14145
14146     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14147                                         player->index_bit, dig_side);
14148
14149     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14150                                         player->index_bit, dig_side);
14151
14152     return MP_ACTION;
14153   }
14154   else
14155   {
14156     if (!PLAYER_SWITCHING(player, x, y))
14157     {
14158       player->is_switching = TRUE;
14159       player->switch_x = x;
14160       player->switch_y = y;
14161
14162       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14163                                  player->index_bit, dig_side);
14164       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14165                                           player->index_bit, dig_side);
14166
14167       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14168                                  player->index_bit, dig_side);
14169       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14170                                           player->index_bit, dig_side);
14171     }
14172
14173     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14174                                player->index_bit, dig_side);
14175     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14176                                         player->index_bit, dig_side);
14177
14178     return MP_NO_ACTION;
14179   }
14180
14181   player->push_delay = -1;
14182
14183   if (is_player)                /* function can also be called by EL_PENGUIN */
14184   {
14185     if (Feld[x][y] != element)          /* really digged/collected something */
14186     {
14187       player->is_collecting = !player->is_digging;
14188       player->is_active = TRUE;
14189     }
14190   }
14191
14192   return MP_MOVING;
14193 }
14194
14195 static boolean DigFieldByCE(int x, int y, int digging_element)
14196 {
14197   int element = Feld[x][y];
14198
14199   if (!IS_FREE(x, y))
14200   {
14201     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14202                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14203                   ACTION_BREAKING);
14204
14205     /* no element can dig solid indestructible elements */
14206     if (IS_INDESTRUCTIBLE(element) &&
14207         !IS_DIGGABLE(element) &&
14208         !IS_COLLECTIBLE(element))
14209       return FALSE;
14210
14211     if (AmoebaNr[x][y] &&
14212         (element == EL_AMOEBA_FULL ||
14213          element == EL_BD_AMOEBA ||
14214          element == EL_AMOEBA_GROWING))
14215     {
14216       AmoebaCnt[AmoebaNr[x][y]]--;
14217       AmoebaCnt2[AmoebaNr[x][y]]--;
14218     }
14219
14220     if (IS_MOVING(x, y))
14221       RemoveMovingField(x, y);
14222     else
14223     {
14224       RemoveField(x, y);
14225       TEST_DrawLevelField(x, y);
14226     }
14227
14228     /* if digged element was about to explode, prevent the explosion */
14229     ExplodeField[x][y] = EX_TYPE_NONE;
14230
14231     PlayLevelSoundAction(x, y, action);
14232   }
14233
14234   Store[x][y] = EL_EMPTY;
14235
14236   /* this makes it possible to leave the removed element again */
14237   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14238     Store[x][y] = element;
14239
14240   return TRUE;
14241 }
14242
14243 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14244 {
14245   int jx = player->jx, jy = player->jy;
14246   int x = jx + dx, y = jy + dy;
14247   int snap_direction = (dx == -1 ? MV_LEFT  :
14248                         dx == +1 ? MV_RIGHT :
14249                         dy == -1 ? MV_UP    :
14250                         dy == +1 ? MV_DOWN  : MV_NONE);
14251   boolean can_continue_snapping = (level.continuous_snapping &&
14252                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14253
14254   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14255     return FALSE;
14256
14257   if (!player->active || !IN_LEV_FIELD(x, y))
14258     return FALSE;
14259
14260   if (dx && dy)
14261     return FALSE;
14262
14263   if (!dx && !dy)
14264   {
14265     if (player->MovPos == 0)
14266       player->is_pushing = FALSE;
14267
14268     player->is_snapping = FALSE;
14269
14270     if (player->MovPos == 0)
14271     {
14272       player->is_moving = FALSE;
14273       player->is_digging = FALSE;
14274       player->is_collecting = FALSE;
14275     }
14276
14277     return FALSE;
14278   }
14279
14280   /* prevent snapping with already pressed snap key when not allowed */
14281   if (player->is_snapping && !can_continue_snapping)
14282     return FALSE;
14283
14284   player->MovDir = snap_direction;
14285
14286   if (player->MovPos == 0)
14287   {
14288     player->is_moving = FALSE;
14289     player->is_digging = FALSE;
14290     player->is_collecting = FALSE;
14291   }
14292
14293   player->is_dropping = FALSE;
14294   player->is_dropping_pressed = FALSE;
14295   player->drop_pressed_delay = 0;
14296
14297   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14298     return FALSE;
14299
14300   player->is_snapping = TRUE;
14301   player->is_active = TRUE;
14302
14303   if (player->MovPos == 0)
14304   {
14305     player->is_moving = FALSE;
14306     player->is_digging = FALSE;
14307     player->is_collecting = FALSE;
14308   }
14309
14310   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14311     TEST_DrawLevelField(player->last_jx, player->last_jy);
14312
14313   TEST_DrawLevelField(x, y);
14314
14315   return TRUE;
14316 }
14317
14318 static boolean DropElement(struct PlayerInfo *player)
14319 {
14320   int old_element, new_element;
14321   int dropx = player->jx, dropy = player->jy;
14322   int drop_direction = player->MovDir;
14323   int drop_side = drop_direction;
14324   int drop_element = get_next_dropped_element(player);
14325
14326   /* do not drop an element on top of another element; when holding drop key
14327      pressed without moving, dropped element must move away before the next
14328      element can be dropped (this is especially important if the next element
14329      is dynamite, which can be placed on background for historical reasons) */
14330   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14331     return MP_ACTION;
14332
14333   if (IS_THROWABLE(drop_element))
14334   {
14335     dropx += GET_DX_FROM_DIR(drop_direction);
14336     dropy += GET_DY_FROM_DIR(drop_direction);
14337
14338     if (!IN_LEV_FIELD(dropx, dropy))
14339       return FALSE;
14340   }
14341
14342   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14343   new_element = drop_element;           /* default: no change when dropping */
14344
14345   /* check if player is active, not moving and ready to drop */
14346   if (!player->active || player->MovPos || player->drop_delay > 0)
14347     return FALSE;
14348
14349   /* check if player has anything that can be dropped */
14350   if (new_element == EL_UNDEFINED)
14351     return FALSE;
14352
14353   /* only set if player has anything that can be dropped */
14354   player->is_dropping_pressed = TRUE;
14355
14356   /* check if drop key was pressed long enough for EM style dynamite */
14357   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14358     return FALSE;
14359
14360   /* check if anything can be dropped at the current position */
14361   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14362     return FALSE;
14363
14364   /* collected custom elements can only be dropped on empty fields */
14365   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14366     return FALSE;
14367
14368   if (old_element != EL_EMPTY)
14369     Back[dropx][dropy] = old_element;   /* store old element on this field */
14370
14371   ResetGfxAnimation(dropx, dropy);
14372   ResetRandomAnimationValue(dropx, dropy);
14373
14374   if (player->inventory_size > 0 ||
14375       player->inventory_infinite_element != EL_UNDEFINED)
14376   {
14377     if (player->inventory_size > 0)
14378     {
14379       player->inventory_size--;
14380
14381       DrawGameDoorValues();
14382
14383       if (new_element == EL_DYNAMITE)
14384         new_element = EL_DYNAMITE_ACTIVE;
14385       else if (new_element == EL_EM_DYNAMITE)
14386         new_element = EL_EM_DYNAMITE_ACTIVE;
14387       else if (new_element == EL_SP_DISK_RED)
14388         new_element = EL_SP_DISK_RED_ACTIVE;
14389     }
14390
14391     Feld[dropx][dropy] = new_element;
14392
14393     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14394       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14395                           el2img(Feld[dropx][dropy]), 0);
14396
14397     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14398
14399     /* needed if previous element just changed to "empty" in the last frame */
14400     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14401
14402     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14403                                player->index_bit, drop_side);
14404     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14405                                         CE_PLAYER_DROPS_X,
14406                                         player->index_bit, drop_side);
14407
14408     TestIfElementTouchesCustomElement(dropx, dropy);
14409   }
14410   else          /* player is dropping a dyna bomb */
14411   {
14412     player->dynabombs_left--;
14413
14414     Feld[dropx][dropy] = new_element;
14415
14416     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14417       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14418                           el2img(Feld[dropx][dropy]), 0);
14419
14420     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14421   }
14422
14423   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14424     InitField_WithBug1(dropx, dropy, FALSE);
14425
14426   new_element = Feld[dropx][dropy];     /* element might have changed */
14427
14428   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14429       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14430   {
14431     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14432       MovDir[dropx][dropy] = drop_direction;
14433
14434     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14435
14436     /* do not cause impact style collision by dropping elements that can fall */
14437     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14438   }
14439
14440   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14441   player->is_dropping = TRUE;
14442
14443   player->drop_pressed_delay = 0;
14444   player->is_dropping_pressed = FALSE;
14445
14446   player->drop_x = dropx;
14447   player->drop_y = dropy;
14448
14449   return TRUE;
14450 }
14451
14452 /* ------------------------------------------------------------------------- */
14453 /* game sound playing functions                                              */
14454 /* ------------------------------------------------------------------------- */
14455
14456 static int *loop_sound_frame = NULL;
14457 static int *loop_sound_volume = NULL;
14458
14459 void InitPlayLevelSound()
14460 {
14461   int num_sounds = getSoundListSize();
14462
14463   checked_free(loop_sound_frame);
14464   checked_free(loop_sound_volume);
14465
14466   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14467   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14468 }
14469
14470 static void PlayLevelSound(int x, int y, int nr)
14471 {
14472   int sx = SCREENX(x), sy = SCREENY(y);
14473   int volume, stereo_position;
14474   int max_distance = 8;
14475   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14476
14477   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14478       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14479     return;
14480
14481   if (!IN_LEV_FIELD(x, y) ||
14482       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14483       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14484     return;
14485
14486   volume = SOUND_MAX_VOLUME;
14487
14488   if (!IN_SCR_FIELD(sx, sy))
14489   {
14490     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14491     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14492
14493     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14494   }
14495
14496   stereo_position = (SOUND_MAX_LEFT +
14497                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14498                      (SCR_FIELDX + 2 * max_distance));
14499
14500   if (IS_LOOP_SOUND(nr))
14501   {
14502     /* This assures that quieter loop sounds do not overwrite louder ones,
14503        while restarting sound volume comparison with each new game frame. */
14504
14505     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14506       return;
14507
14508     loop_sound_volume[nr] = volume;
14509     loop_sound_frame[nr] = FrameCounter;
14510   }
14511
14512   PlaySoundExt(nr, volume, stereo_position, type);
14513 }
14514
14515 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14516 {
14517   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14518                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14519                  y < LEVELY(BY1) ? LEVELY(BY1) :
14520                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14521                  sound_action);
14522 }
14523
14524 static void PlayLevelSoundAction(int x, int y, int action)
14525 {
14526   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14527 }
14528
14529 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14530 {
14531   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14532
14533   if (sound_effect != SND_UNDEFINED)
14534     PlayLevelSound(x, y, sound_effect);
14535 }
14536
14537 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14538                                               int action)
14539 {
14540   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14541
14542   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14543     PlayLevelSound(x, y, sound_effect);
14544 }
14545
14546 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14547 {
14548   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14549
14550   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14551     PlayLevelSound(x, y, sound_effect);
14552 }
14553
14554 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14555 {
14556   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14557
14558   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14559     StopSound(sound_effect);
14560 }
14561
14562 static int getLevelMusicNr()
14563 {
14564   if (levelset.music[level_nr] != MUS_UNDEFINED)
14565     return levelset.music[level_nr];            /* from config file */
14566   else
14567     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14568 }
14569
14570 static void FadeLevelSounds()
14571 {
14572   FadeSounds();
14573 }
14574
14575 static void FadeLevelMusic()
14576 {
14577   int music_nr = getLevelMusicNr();
14578   char *curr_music = getCurrentlyPlayingMusicFilename();
14579   char *next_music = getMusicInfoEntryFilename(music_nr);
14580
14581   if (!strEqual(curr_music, next_music))
14582     FadeMusic();
14583 }
14584
14585 void FadeLevelSoundsAndMusic()
14586 {
14587   FadeLevelSounds();
14588   FadeLevelMusic();
14589 }
14590
14591 static void PlayLevelMusic()
14592 {
14593   int music_nr = getLevelMusicNr();
14594   char *curr_music = getCurrentlyPlayingMusicFilename();
14595   char *next_music = getMusicInfoEntryFilename(music_nr);
14596
14597   if (!strEqual(curr_music, next_music))
14598     PlayMusic(music_nr);
14599 }
14600
14601 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14602 {
14603   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14604   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14605   int x = xx - 1 - offset;
14606   int y = yy - 1 - offset;
14607
14608   switch (sample)
14609   {
14610     case SAMPLE_blank:
14611       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14612       break;
14613
14614     case SAMPLE_roll:
14615       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14616       break;
14617
14618     case SAMPLE_stone:
14619       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14620       break;
14621
14622     case SAMPLE_nut:
14623       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14624       break;
14625
14626     case SAMPLE_crack:
14627       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14628       break;
14629
14630     case SAMPLE_bug:
14631       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14632       break;
14633
14634     case SAMPLE_tank:
14635       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14636       break;
14637
14638     case SAMPLE_android_clone:
14639       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14640       break;
14641
14642     case SAMPLE_android_move:
14643       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14644       break;
14645
14646     case SAMPLE_spring:
14647       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14648       break;
14649
14650     case SAMPLE_slurp:
14651       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14652       break;
14653
14654     case SAMPLE_eater:
14655       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14656       break;
14657
14658     case SAMPLE_eater_eat:
14659       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14660       break;
14661
14662     case SAMPLE_alien:
14663       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14664       break;
14665
14666     case SAMPLE_collect:
14667       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14668       break;
14669
14670     case SAMPLE_diamond:
14671       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14672       break;
14673
14674     case SAMPLE_squash:
14675       /* !!! CHECK THIS !!! */
14676 #if 1
14677       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14678 #else
14679       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14680 #endif
14681       break;
14682
14683     case SAMPLE_wonderfall:
14684       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14685       break;
14686
14687     case SAMPLE_drip:
14688       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14689       break;
14690
14691     case SAMPLE_push:
14692       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14693       break;
14694
14695     case SAMPLE_dirt:
14696       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14697       break;
14698
14699     case SAMPLE_acid:
14700       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14701       break;
14702
14703     case SAMPLE_ball:
14704       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14705       break;
14706
14707     case SAMPLE_grow:
14708       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14709       break;
14710
14711     case SAMPLE_wonder:
14712       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14713       break;
14714
14715     case SAMPLE_door:
14716       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14717       break;
14718
14719     case SAMPLE_exit_open:
14720       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14721       break;
14722
14723     case SAMPLE_exit_leave:
14724       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14725       break;
14726
14727     case SAMPLE_dynamite:
14728       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14729       break;
14730
14731     case SAMPLE_tick:
14732       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14733       break;
14734
14735     case SAMPLE_press:
14736       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14737       break;
14738
14739     case SAMPLE_wheel:
14740       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14741       break;
14742
14743     case SAMPLE_boom:
14744       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14745       break;
14746
14747     case SAMPLE_die:
14748       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14749       break;
14750
14751     case SAMPLE_time:
14752       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14753       break;
14754
14755     default:
14756       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14757       break;
14758   }
14759 }
14760
14761 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14762 {
14763   int element = map_element_SP_to_RND(element_sp);
14764   int action = map_action_SP_to_RND(action_sp);
14765   int offset = (setup.sp_show_border_elements ? 0 : 1);
14766   int x = xx - offset;
14767   int y = yy - offset;
14768
14769   PlayLevelSoundElementAction(x, y, element, action);
14770 }
14771
14772 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14773 {
14774   int element = map_element_MM_to_RND(element_mm);
14775   int action = map_action_MM_to_RND(action_mm);
14776   int offset = 0;
14777   int x = xx - offset;
14778   int y = yy - offset;
14779
14780   if (!IS_MM_ELEMENT(element))
14781     element = EL_MM_DEFAULT;
14782
14783   PlayLevelSoundElementAction(x, y, element, action);
14784 }
14785
14786 void PlaySound_MM(int sound_mm)
14787 {
14788   int sound = map_sound_MM_to_RND(sound_mm);
14789
14790   if (sound == SND_UNDEFINED)
14791     return;
14792
14793   PlaySound(sound);
14794 }
14795
14796 void PlaySoundLoop_MM(int sound_mm)
14797 {
14798   int sound = map_sound_MM_to_RND(sound_mm);
14799
14800   if (sound == SND_UNDEFINED)
14801     return;
14802
14803   PlaySoundLoop(sound);
14804 }
14805
14806 void StopSound_MM(int sound_mm)
14807 {
14808   int sound = map_sound_MM_to_RND(sound_mm);
14809
14810   if (sound == SND_UNDEFINED)
14811     return;
14812
14813   StopSound(sound);
14814 }
14815
14816 void RaiseScore(int value)
14817 {
14818   local_player->score += value;
14819
14820   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14821
14822   DisplayGameControlValues();
14823 }
14824
14825 void RaiseScoreElement(int element)
14826 {
14827   switch (element)
14828   {
14829     case EL_EMERALD:
14830     case EL_BD_DIAMOND:
14831     case EL_EMERALD_YELLOW:
14832     case EL_EMERALD_RED:
14833     case EL_EMERALD_PURPLE:
14834     case EL_SP_INFOTRON:
14835       RaiseScore(level.score[SC_EMERALD]);
14836       break;
14837     case EL_DIAMOND:
14838       RaiseScore(level.score[SC_DIAMOND]);
14839       break;
14840     case EL_CRYSTAL:
14841       RaiseScore(level.score[SC_CRYSTAL]);
14842       break;
14843     case EL_PEARL:
14844       RaiseScore(level.score[SC_PEARL]);
14845       break;
14846     case EL_BUG:
14847     case EL_BD_BUTTERFLY:
14848     case EL_SP_ELECTRON:
14849       RaiseScore(level.score[SC_BUG]);
14850       break;
14851     case EL_SPACESHIP:
14852     case EL_BD_FIREFLY:
14853     case EL_SP_SNIKSNAK:
14854       RaiseScore(level.score[SC_SPACESHIP]);
14855       break;
14856     case EL_YAMYAM:
14857     case EL_DARK_YAMYAM:
14858       RaiseScore(level.score[SC_YAMYAM]);
14859       break;
14860     case EL_ROBOT:
14861       RaiseScore(level.score[SC_ROBOT]);
14862       break;
14863     case EL_PACMAN:
14864       RaiseScore(level.score[SC_PACMAN]);
14865       break;
14866     case EL_NUT:
14867       RaiseScore(level.score[SC_NUT]);
14868       break;
14869     case EL_DYNAMITE:
14870     case EL_EM_DYNAMITE:
14871     case EL_SP_DISK_RED:
14872     case EL_DYNABOMB_INCREASE_NUMBER:
14873     case EL_DYNABOMB_INCREASE_SIZE:
14874     case EL_DYNABOMB_INCREASE_POWER:
14875       RaiseScore(level.score[SC_DYNAMITE]);
14876       break;
14877     case EL_SHIELD_NORMAL:
14878     case EL_SHIELD_DEADLY:
14879       RaiseScore(level.score[SC_SHIELD]);
14880       break;
14881     case EL_EXTRA_TIME:
14882       RaiseScore(level.extra_time_score);
14883       break;
14884     case EL_KEY_1:
14885     case EL_KEY_2:
14886     case EL_KEY_3:
14887     case EL_KEY_4:
14888     case EL_EM_KEY_1:
14889     case EL_EM_KEY_2:
14890     case EL_EM_KEY_3:
14891     case EL_EM_KEY_4:
14892     case EL_EMC_KEY_5:
14893     case EL_EMC_KEY_6:
14894     case EL_EMC_KEY_7:
14895     case EL_EMC_KEY_8:
14896     case EL_DC_KEY_WHITE:
14897       RaiseScore(level.score[SC_KEY]);
14898       break;
14899     default:
14900       RaiseScore(element_info[element].collect_score);
14901       break;
14902   }
14903 }
14904
14905 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14906 {
14907   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14908   {
14909     /* closing door required in case of envelope style request dialogs */
14910     if (!skip_request)
14911       CloseDoor(DOOR_CLOSE_1);
14912
14913     if (network.enabled)
14914       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14915     else
14916     {
14917       if (quick_quit)
14918         FadeSkipNextFadeIn();
14919
14920       SetGameStatus(GAME_MODE_MAIN);
14921
14922       DrawMainMenu();
14923     }
14924   }
14925   else          /* continue playing the game */
14926   {
14927     if (tape.playing && tape.deactivate_display)
14928       TapeDeactivateDisplayOff(TRUE);
14929
14930     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14931
14932     if (tape.playing && tape.deactivate_display)
14933       TapeDeactivateDisplayOn();
14934   }
14935 }
14936
14937 void RequestQuitGame(boolean ask_if_really_quit)
14938 {
14939   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14940   boolean skip_request = AllPlayersGone || quick_quit;
14941
14942   RequestQuitGameExt(skip_request, quick_quit,
14943                      "Do you really want to quit the game?");
14944 }
14945
14946 void RequestRestartGame(char *message)
14947 {
14948   game.restart_game_message = NULL;
14949
14950   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14951   {
14952     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14953   }
14954   else
14955   {
14956     SetGameStatus(GAME_MODE_MAIN);
14957
14958     DrawMainMenu();
14959   }
14960 }
14961
14962
14963 /* ------------------------------------------------------------------------- */
14964 /* random generator functions                                                */
14965 /* ------------------------------------------------------------------------- */
14966
14967 unsigned int InitEngineRandom_RND(int seed)
14968 {
14969   game.num_random_calls = 0;
14970
14971   return InitEngineRandom(seed);
14972 }
14973
14974 unsigned int RND(int max)
14975 {
14976   if (max > 0)
14977   {
14978     game.num_random_calls++;
14979
14980     return GetEngineRandom(max);
14981   }
14982
14983   return 0;
14984 }
14985
14986
14987 /* ------------------------------------------------------------------------- */
14988 /* game engine snapshot handling functions                                   */
14989 /* ------------------------------------------------------------------------- */
14990
14991 struct EngineSnapshotInfo
14992 {
14993   /* runtime values for custom element collect score */
14994   int collect_score[NUM_CUSTOM_ELEMENTS];
14995
14996   /* runtime values for group element choice position */
14997   int choice_pos[NUM_GROUP_ELEMENTS];
14998
14999   /* runtime values for belt position animations */
15000   int belt_graphic[4][NUM_BELT_PARTS];
15001   int belt_anim_mode[4][NUM_BELT_PARTS];
15002 };
15003
15004 static struct EngineSnapshotInfo engine_snapshot_rnd;
15005 static char *snapshot_level_identifier = NULL;
15006 static int snapshot_level_nr = -1;
15007
15008 static void SaveEngineSnapshotValues_RND()
15009 {
15010   static int belt_base_active_element[4] =
15011   {
15012     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15013     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15014     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15015     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15016   };
15017   int i, j;
15018
15019   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15020   {
15021     int element = EL_CUSTOM_START + i;
15022
15023     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15024   }
15025
15026   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15027   {
15028     int element = EL_GROUP_START + i;
15029
15030     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15031   }
15032
15033   for (i = 0; i < 4; i++)
15034   {
15035     for (j = 0; j < NUM_BELT_PARTS; j++)
15036     {
15037       int element = belt_base_active_element[i] + j;
15038       int graphic = el2img(element);
15039       int anim_mode = graphic_info[graphic].anim_mode;
15040
15041       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15042       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15043     }
15044   }
15045 }
15046
15047 static void LoadEngineSnapshotValues_RND()
15048 {
15049   unsigned int num_random_calls = game.num_random_calls;
15050   int i, j;
15051
15052   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15053   {
15054     int element = EL_CUSTOM_START + i;
15055
15056     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15057   }
15058
15059   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15060   {
15061     int element = EL_GROUP_START + i;
15062
15063     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15064   }
15065
15066   for (i = 0; i < 4; i++)
15067   {
15068     for (j = 0; j < NUM_BELT_PARTS; j++)
15069     {
15070       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15071       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15072
15073       graphic_info[graphic].anim_mode = anim_mode;
15074     }
15075   }
15076
15077   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15078   {
15079     InitRND(tape.random_seed);
15080     for (i = 0; i < num_random_calls; i++)
15081       RND(1);
15082   }
15083
15084   if (game.num_random_calls != num_random_calls)
15085   {
15086     Error(ERR_INFO, "number of random calls out of sync");
15087     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15088     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15089     Error(ERR_EXIT, "this should not happen -- please debug");
15090   }
15091 }
15092
15093 void FreeEngineSnapshotSingle()
15094 {
15095   FreeSnapshotSingle();
15096
15097   setString(&snapshot_level_identifier, NULL);
15098   snapshot_level_nr = -1;
15099 }
15100
15101 void FreeEngineSnapshotList()
15102 {
15103   FreeSnapshotList();
15104 }
15105
15106 ListNode *SaveEngineSnapshotBuffers()
15107 {
15108   ListNode *buffers = NULL;
15109
15110   /* copy some special values to a structure better suited for the snapshot */
15111
15112   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15113     SaveEngineSnapshotValues_RND();
15114   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15115     SaveEngineSnapshotValues_EM();
15116   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15117     SaveEngineSnapshotValues_SP(&buffers);
15118   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15119     SaveEngineSnapshotValues_MM(&buffers);
15120
15121   /* save values stored in special snapshot structure */
15122
15123   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15124     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15125   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15126     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15127   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15128     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15129   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15130     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15131
15132   /* save further RND engine values */
15133
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15137
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15142
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15148
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15152
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15154
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15156
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15159
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15178
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15181
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15185
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15188
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15194
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15197
15198 #if 0
15199   ListNode *node = engine_snapshot_list_rnd;
15200   int num_bytes = 0;
15201
15202   while (node != NULL)
15203   {
15204     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15205
15206     node = node->next;
15207   }
15208
15209   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15210 #endif
15211
15212   return buffers;
15213 }
15214
15215 void SaveEngineSnapshotSingle()
15216 {
15217   ListNode *buffers = SaveEngineSnapshotBuffers();
15218
15219   /* finally save all snapshot buffers to single snapshot */
15220   SaveSnapshotSingle(buffers);
15221
15222   /* save level identification information */
15223   setString(&snapshot_level_identifier, leveldir_current->identifier);
15224   snapshot_level_nr = level_nr;
15225 }
15226
15227 boolean CheckSaveEngineSnapshotToList()
15228 {
15229   boolean save_snapshot =
15230     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15231      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15232       game.snapshot.changed_action) ||
15233      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15234       game.snapshot.collected_item));
15235
15236   game.snapshot.changed_action = FALSE;
15237   game.snapshot.collected_item = FALSE;
15238   game.snapshot.save_snapshot = save_snapshot;
15239
15240   return save_snapshot;
15241 }
15242
15243 void SaveEngineSnapshotToList()
15244 {
15245   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15246       tape.quick_resume)
15247     return;
15248
15249   ListNode *buffers = SaveEngineSnapshotBuffers();
15250
15251   /* finally save all snapshot buffers to snapshot list */
15252   SaveSnapshotToList(buffers);
15253 }
15254
15255 void SaveEngineSnapshotToListInitial()
15256 {
15257   FreeEngineSnapshotList();
15258
15259   SaveEngineSnapshotToList();
15260 }
15261
15262 void LoadEngineSnapshotValues()
15263 {
15264   /* restore special values from snapshot structure */
15265
15266   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15267     LoadEngineSnapshotValues_RND();
15268   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15269     LoadEngineSnapshotValues_EM();
15270   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15271     LoadEngineSnapshotValues_SP();
15272   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15273     LoadEngineSnapshotValues_MM();
15274 }
15275
15276 void LoadEngineSnapshotSingle()
15277 {
15278   LoadSnapshotSingle();
15279
15280   LoadEngineSnapshotValues();
15281 }
15282
15283 void LoadEngineSnapshot_Undo(int steps)
15284 {
15285   LoadSnapshotFromList_Older(steps);
15286
15287   LoadEngineSnapshotValues();
15288 }
15289
15290 void LoadEngineSnapshot_Redo(int steps)
15291 {
15292   LoadSnapshotFromList_Newer(steps);
15293
15294   LoadEngineSnapshotValues();
15295 }
15296
15297 boolean CheckEngineSnapshotSingle()
15298 {
15299   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15300           snapshot_level_nr == level_nr);
15301 }
15302
15303 boolean CheckEngineSnapshotList()
15304 {
15305   return CheckSnapshotList();
15306 }
15307
15308
15309 /* ---------- new game button stuff ---------------------------------------- */
15310
15311 static struct
15312 {
15313   int graphic;
15314   struct XY *pos;
15315   int gadget_id;
15316   boolean *setup_value;
15317   boolean allowed_on_tape;
15318   char *infotext;
15319 } gamebutton_info[NUM_GAME_BUTTONS] =
15320 {
15321   {
15322     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15323     GAME_CTRL_ID_STOP,                          NULL,
15324     TRUE,                                       "stop game"
15325   },
15326   {
15327     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15328     GAME_CTRL_ID_PAUSE,                         NULL,
15329     TRUE,                                       "pause game"
15330   },
15331   {
15332     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15333     GAME_CTRL_ID_PLAY,                          NULL,
15334     TRUE,                                       "play game"
15335   },
15336   {
15337     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15338     GAME_CTRL_ID_UNDO,                          NULL,
15339     TRUE,                                       "undo step"
15340   },
15341   {
15342     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15343     GAME_CTRL_ID_REDO,                          NULL,
15344     TRUE,                                       "redo step"
15345   },
15346   {
15347     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15348     GAME_CTRL_ID_SAVE,                          NULL,
15349     TRUE,                                       "save game"
15350   },
15351   {
15352     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15353     GAME_CTRL_ID_PAUSE2,                        NULL,
15354     TRUE,                                       "pause game"
15355   },
15356   {
15357     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15358     GAME_CTRL_ID_LOAD,                          NULL,
15359     TRUE,                                       "load game"
15360   },
15361   {
15362     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15363     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15364     FALSE,                                      "stop game"
15365   },
15366   {
15367     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15368     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15369     FALSE,                                      "pause game"
15370   },
15371   {
15372     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15373     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15374     FALSE,                                      "play game"
15375   },
15376   {
15377     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15378     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15379     TRUE,                                       "background music on/off"
15380   },
15381   {
15382     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15383     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15384     TRUE,                                       "sound loops on/off"
15385   },
15386   {
15387     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15388     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15389     TRUE,                                       "normal sounds on/off"
15390   },
15391   {
15392     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15393     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15394     FALSE,                                      "background music on/off"
15395   },
15396   {
15397     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15398     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15399     FALSE,                                      "sound loops on/off"
15400   },
15401   {
15402     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15403     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15404     FALSE,                                      "normal sounds on/off"
15405   }
15406 };
15407
15408 void CreateGameButtons()
15409 {
15410   int i;
15411
15412   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15413   {
15414     int graphic = gamebutton_info[i].graphic;
15415     struct GraphicInfo *gfx = &graphic_info[graphic];
15416     struct XY *pos = gamebutton_info[i].pos;
15417     struct GadgetInfo *gi;
15418     int button_type;
15419     boolean checked;
15420     unsigned int event_mask;
15421     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15422     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15423     int base_x = (on_tape ? VX : DX);
15424     int base_y = (on_tape ? VY : DY);
15425     int gd_x   = gfx->src_x;
15426     int gd_y   = gfx->src_y;
15427     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15428     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15429     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15430     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15431     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15432     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15433     int id = i;
15434
15435     if (gfx->bitmap == NULL)
15436     {
15437       game_gadget[id] = NULL;
15438
15439       continue;
15440     }
15441
15442     if (id == GAME_CTRL_ID_STOP ||
15443         id == GAME_CTRL_ID_PANEL_STOP ||
15444         id == GAME_CTRL_ID_PLAY ||
15445         id == GAME_CTRL_ID_PANEL_PLAY ||
15446         id == GAME_CTRL_ID_SAVE ||
15447         id == GAME_CTRL_ID_LOAD)
15448     {
15449       button_type = GD_TYPE_NORMAL_BUTTON;
15450       checked = FALSE;
15451       event_mask = GD_EVENT_RELEASED;
15452     }
15453     else if (id == GAME_CTRL_ID_UNDO ||
15454              id == GAME_CTRL_ID_REDO)
15455     {
15456       button_type = GD_TYPE_NORMAL_BUTTON;
15457       checked = FALSE;
15458       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15459     }
15460     else
15461     {
15462       button_type = GD_TYPE_CHECK_BUTTON;
15463       checked = (gamebutton_info[i].setup_value != NULL ?
15464                  *gamebutton_info[i].setup_value : FALSE);
15465       event_mask = GD_EVENT_PRESSED;
15466     }
15467
15468     gi = CreateGadget(GDI_CUSTOM_ID, id,
15469                       GDI_IMAGE_ID, graphic,
15470                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15471                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15472                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15473                       GDI_WIDTH, gfx->width,
15474                       GDI_HEIGHT, gfx->height,
15475                       GDI_TYPE, button_type,
15476                       GDI_STATE, GD_BUTTON_UNPRESSED,
15477                       GDI_CHECKED, checked,
15478                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15479                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15480                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15481                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15482                       GDI_DIRECT_DRAW, FALSE,
15483                       GDI_EVENT_MASK, event_mask,
15484                       GDI_CALLBACK_ACTION, HandleGameButtons,
15485                       GDI_END);
15486
15487     if (gi == NULL)
15488       Error(ERR_EXIT, "cannot create gadget");
15489
15490     game_gadget[id] = gi;
15491   }
15492 }
15493
15494 void FreeGameButtons()
15495 {
15496   int i;
15497
15498   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15499     FreeGadget(game_gadget[i]);
15500 }
15501
15502 static void UnmapGameButtonsAtSamePosition(int id)
15503 {
15504   int i;
15505
15506   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15507     if (i != id &&
15508         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15509         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15510       UnmapGadget(game_gadget[i]);
15511 }
15512
15513 static void UnmapGameButtonsAtSamePosition_All()
15514 {
15515   if (setup.show_snapshot_buttons)
15516   {
15517     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15518     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15519     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15520   }
15521   else
15522   {
15523     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15524     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15525     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15526
15527     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15528     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15529     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15530   }
15531 }
15532
15533 static void MapGameButtonsAtSamePosition(int id)
15534 {
15535   int i;
15536
15537   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15538     if (i != id &&
15539         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15540         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15541       MapGadget(game_gadget[i]);
15542
15543   UnmapGameButtonsAtSamePosition_All();
15544 }
15545
15546 void MapUndoRedoButtons()
15547 {
15548   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15549   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15550
15551   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15552   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15553
15554   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15555 }
15556
15557 void UnmapUndoRedoButtons()
15558 {
15559   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15560   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15561
15562   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15563   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15564
15565   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15566 }
15567
15568 void MapGameButtonsExt(boolean on_tape)
15569 {
15570   int i;
15571
15572   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15573     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15574         i != GAME_CTRL_ID_UNDO &&
15575         i != GAME_CTRL_ID_REDO)
15576       MapGadget(game_gadget[i]);
15577
15578   UnmapGameButtonsAtSamePosition_All();
15579
15580   RedrawGameButtons();
15581 }
15582
15583 void UnmapGameButtonsExt(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       UnmapGadget(game_gadget[i]);
15590 }
15591
15592 void RedrawGameButtonsExt(boolean on_tape)
15593 {
15594   int i;
15595
15596   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15597     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15598       RedrawGadget(game_gadget[i]);
15599
15600   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15601   redraw_mask &= ~REDRAW_ALL;
15602 }
15603
15604 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15605 {
15606   if (gi == NULL)
15607     return;
15608
15609   gi->checked = state;
15610 }
15611
15612 void RedrawSoundButtonGadget(int id)
15613 {
15614   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15615              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15616              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15617              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15618              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15619              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15620              id);
15621
15622   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15623   RedrawGadget(game_gadget[id2]);
15624 }
15625
15626 void MapGameButtons()
15627 {
15628   MapGameButtonsExt(FALSE);
15629 }
15630
15631 void UnmapGameButtons()
15632 {
15633   UnmapGameButtonsExt(FALSE);
15634 }
15635
15636 void RedrawGameButtons()
15637 {
15638   RedrawGameButtonsExt(FALSE);
15639 }
15640
15641 void MapGameButtonsOnTape()
15642 {
15643   MapGameButtonsExt(TRUE);
15644 }
15645
15646 void UnmapGameButtonsOnTape()
15647 {
15648   UnmapGameButtonsExt(TRUE);
15649 }
15650
15651 void RedrawGameButtonsOnTape()
15652 {
15653   RedrawGameButtonsExt(TRUE);
15654 }
15655
15656 void GameUndoRedoExt()
15657 {
15658   ClearPlayerAction();
15659
15660   tape.pausing = TRUE;
15661
15662   RedrawPlayfield();
15663   UpdateAndDisplayGameControlValues();
15664
15665   DrawCompleteVideoDisplay();
15666   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15667   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15668   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15669
15670   BackToFront();
15671 }
15672
15673 void GameUndo(int steps)
15674 {
15675   if (!CheckEngineSnapshotList())
15676     return;
15677
15678   LoadEngineSnapshot_Undo(steps);
15679
15680   GameUndoRedoExt();
15681 }
15682
15683 void GameRedo(int steps)
15684 {
15685   if (!CheckEngineSnapshotList())
15686     return;
15687
15688   LoadEngineSnapshot_Redo(steps);
15689
15690   GameUndoRedoExt();
15691 }
15692
15693 static void HandleGameButtonsExt(int id, int button)
15694 {
15695   static boolean game_undo_executed = FALSE;
15696   int steps = BUTTON_STEPSIZE(button);
15697   boolean handle_game_buttons =
15698     (game_status == GAME_MODE_PLAYING ||
15699      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15700
15701   if (!handle_game_buttons)
15702     return;
15703
15704   switch (id)
15705   {
15706     case GAME_CTRL_ID_STOP:
15707     case GAME_CTRL_ID_PANEL_STOP:
15708       if (game_status == GAME_MODE_MAIN)
15709         break;
15710
15711       if (tape.playing)
15712         TapeStop();
15713       else
15714         RequestQuitGame(TRUE);
15715
15716       break;
15717
15718     case GAME_CTRL_ID_PAUSE:
15719     case GAME_CTRL_ID_PAUSE2:
15720     case GAME_CTRL_ID_PANEL_PAUSE:
15721       if (network.enabled && game_status == GAME_MODE_PLAYING)
15722       {
15723         if (tape.pausing)
15724           SendToServer_ContinuePlaying();
15725         else
15726           SendToServer_PausePlaying();
15727       }
15728       else
15729         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15730
15731       game_undo_executed = FALSE;
15732
15733       break;
15734
15735     case GAME_CTRL_ID_PLAY:
15736     case GAME_CTRL_ID_PANEL_PLAY:
15737       if (game_status == GAME_MODE_MAIN)
15738       {
15739         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15740       }
15741       else if (tape.pausing)
15742       {
15743         if (network.enabled)
15744           SendToServer_ContinuePlaying();
15745         else
15746           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15747       }
15748       break;
15749
15750     case GAME_CTRL_ID_UNDO:
15751       // Important: When using "save snapshot when collecting an item" mode,
15752       // load last (current) snapshot for first "undo" after pressing "pause"
15753       // (else the last-but-one snapshot would be loaded, because the snapshot
15754       // pointer already points to the last snapshot when pressing "pause",
15755       // which is fine for "every step/move" mode, but not for "every collect")
15756       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15757           !game_undo_executed)
15758         steps--;
15759
15760       game_undo_executed = TRUE;
15761
15762       GameUndo(steps);
15763       break;
15764
15765     case GAME_CTRL_ID_REDO:
15766       GameRedo(steps);
15767       break;
15768
15769     case GAME_CTRL_ID_SAVE:
15770       TapeQuickSave();
15771       break;
15772
15773     case GAME_CTRL_ID_LOAD:
15774       TapeQuickLoad();
15775       break;
15776
15777     case SOUND_CTRL_ID_MUSIC:
15778     case SOUND_CTRL_ID_PANEL_MUSIC:
15779       if (setup.sound_music)
15780       { 
15781         setup.sound_music = FALSE;
15782
15783         FadeMusic();
15784       }
15785       else if (audio.music_available)
15786       { 
15787         setup.sound = setup.sound_music = TRUE;
15788
15789         SetAudioMode(setup.sound);
15790
15791         if (game_status == GAME_MODE_PLAYING)
15792           PlayLevelMusic();
15793       }
15794
15795       RedrawSoundButtonGadget(id);
15796
15797       break;
15798
15799     case SOUND_CTRL_ID_LOOPS:
15800     case SOUND_CTRL_ID_PANEL_LOOPS:
15801       if (setup.sound_loops)
15802         setup.sound_loops = FALSE;
15803       else if (audio.loops_available)
15804       {
15805         setup.sound = setup.sound_loops = TRUE;
15806
15807         SetAudioMode(setup.sound);
15808       }
15809
15810       RedrawSoundButtonGadget(id);
15811
15812       break;
15813
15814     case SOUND_CTRL_ID_SIMPLE:
15815     case SOUND_CTRL_ID_PANEL_SIMPLE:
15816       if (setup.sound_simple)
15817         setup.sound_simple = FALSE;
15818       else if (audio.sound_available)
15819       {
15820         setup.sound = setup.sound_simple = TRUE;
15821
15822         SetAudioMode(setup.sound);
15823       }
15824
15825       RedrawSoundButtonGadget(id);
15826
15827       break;
15828
15829     default:
15830       break;
15831   }
15832 }
15833
15834 static void HandleGameButtons(struct GadgetInfo *gi)
15835 {
15836   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15837 }
15838
15839 void HandleSoundButtonKeys(Key key)
15840 {
15841   if (key == setup.shortcut.sound_simple)
15842     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15843   else if (key == setup.shortcut.sound_loops)
15844     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15845   else if (key == setup.shortcut.sound_music)
15846     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15847 }