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