added using single button for snap and drop when using native EM engine
[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_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
832                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
833                                  (x) - MIDPOSX)
834 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
836                                  (y) - MIDPOSY)
837
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN       (1)
841 #define MOVE_STEPSIZE_MAX       (TILEX)
842
843 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
845
846 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
847
848 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
849                                  RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
851                                  RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
853                                  RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
855                                     (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
857                                  RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
860                                  RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
862                                  RND((c)->delay_random))
863
864
865 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
866          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
867
868 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
869         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
870          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
871          (be) + (e) - EL_SELF)
872
873 #define GET_PLAYER_FROM_BITS(p)                                         \
874         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
875
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
877         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
878          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
879          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
880          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
881          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
882          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
883          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
884          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
885          (e))
886
887 #define CAN_GROW_INTO(e)                                                \
888         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
892                                         (condition)))
893
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
895                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
896                                         (CAN_MOVE_INTO_ACID(e) &&       \
897                                          Feld[x][y] == EL_ACID) ||      \
898                                         (condition)))
899
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
901                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
902                                         (CAN_MOVE_INTO_ACID(e) &&       \
903                                          Feld[x][y] == EL_ACID) ||      \
904                                         (condition)))
905
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
907                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
908                                         (condition) ||                  \
909                                         (CAN_MOVE_INTO_ACID(e) &&       \
910                                          Feld[x][y] == EL_ACID) ||      \
911                                         (DONT_COLLIDE_WITH(e) &&        \
912                                          IS_PLAYER(x, y) &&             \
913                                          !PLAYER_ENEMY_PROTECTED(x, y))))
914
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
916         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
917
918 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
920
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
922         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
923
924 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
925         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
927
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
929         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
930
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
932         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
933
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
935         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
936
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
938         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
939
940 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
942
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
946                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948                                                  IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
951
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
954
955 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
956         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
959         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
960                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
961
962 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
963
964 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
965                 (!IS_PLAYER(x, y) &&                                    \
966                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
967
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
969         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
970
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
973
974 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
978
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP               0
981 #define GAME_CTRL_ID_PAUSE              1
982 #define GAME_CTRL_ID_PLAY               2
983 #define GAME_CTRL_ID_UNDO               3
984 #define GAME_CTRL_ID_REDO               4
985 #define GAME_CTRL_ID_SAVE               5
986 #define GAME_CTRL_ID_PAUSE2             6
987 #define GAME_CTRL_ID_LOAD               7
988 #define SOUND_CTRL_ID_MUSIC             8
989 #define SOUND_CTRL_ID_LOOPS             9
990 #define SOUND_CTRL_ID_SIMPLE            10
991
992 #define NUM_GAME_BUTTONS                11
993
994
995 /* forward declaration for internal use */
996
997 static void CreateField(int, int, int);
998
999 static void ResetGfxAnimation(int, int);
1000
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1003
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1008
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1013
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1020
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1024
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1028
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1031         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1033         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1035         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1037         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1038
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev)                             \
1041         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1043         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1045         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1046
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055
1056 static void HandleGameButtons(struct GadgetInfo *);
1057
1058 int AmoebeNachbarNr(int, int);
1059 void AmoebeUmwandeln(int, int);
1060 void ContinueMoving(int, int);
1061 void Bang(int, int);
1062 void InitMovDir(int, int);
1063 void InitAmoebaNr(int, int);
1064 int NewHiScore(void);
1065
1066 void TestIfGoodThingHitsBadThing(int, int, int);
1067 void TestIfBadThingHitsGoodThing(int, int, int);
1068 void TestIfPlayerTouchesBadThing(int, int);
1069 void TestIfPlayerRunsIntoBadThing(int, int, int);
1070 void TestIfBadThingTouchesPlayer(int, int);
1071 void TestIfBadThingRunsIntoPlayer(int, int, int);
1072 void TestIfFriendTouchesBadThing(int, int);
1073 void TestIfBadThingTouchesFriend(int, int);
1074 void TestIfBadThingTouchesOtherBadThing(int, int);
1075 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1076
1077 void KillPlayer(struct PlayerInfo *);
1078 void BuryPlayer(struct PlayerInfo *);
1079 void RemovePlayer(struct PlayerInfo *);
1080
1081 static int getInvisibleActiveFromInvisibleElement(int);
1082 static int getInvisibleFromInvisibleActiveElement(int);
1083
1084 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1085
1086 /* for detection of endless loops, caused by custom element programming */
1087 /* (using maximal playfield width x 10 is just a rough approximation) */
1088 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1089
1090 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1091 {                                                                       \
1092   if (recursion_loop_detected)                                          \
1093     return (rc);                                                        \
1094                                                                         \
1095   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1096   {                                                                     \
1097     recursion_loop_detected = TRUE;                                     \
1098     recursion_loop_element = (e);                                       \
1099   }                                                                     \
1100                                                                         \
1101   recursion_loop_depth++;                                               \
1102 }
1103
1104 #define RECURSION_LOOP_DETECTION_END()                                  \
1105 {                                                                       \
1106   recursion_loop_depth--;                                               \
1107 }
1108
1109 static int recursion_loop_depth;
1110 static boolean recursion_loop_detected;
1111 static boolean recursion_loop_element;
1112
1113 static int map_player_action[MAX_PLAYERS];
1114
1115
1116 /* ------------------------------------------------------------------------- */
1117 /* definition of elements that automatically change to other elements after  */
1118 /* a specified time, eventually calling a function when changing             */
1119 /* ------------------------------------------------------------------------- */
1120
1121 /* forward declaration for changer functions */
1122 static void InitBuggyBase(int, int);
1123 static void WarnBuggyBase(int, int);
1124
1125 static void InitTrap(int, int);
1126 static void ActivateTrap(int, int);
1127 static void ChangeActiveTrap(int, int);
1128
1129 static void InitRobotWheel(int, int);
1130 static void RunRobotWheel(int, int);
1131 static void StopRobotWheel(int, int);
1132
1133 static void InitTimegateWheel(int, int);
1134 static void RunTimegateWheel(int, int);
1135
1136 static void InitMagicBallDelay(int, int);
1137 static void ActivateMagicBall(int, int);
1138
1139 struct ChangingElementInfo
1140 {
1141   int element;
1142   int target_element;
1143   int change_delay;
1144   void (*pre_change_function)(int x, int y);
1145   void (*change_function)(int x, int y);
1146   void (*post_change_function)(int x, int y);
1147 };
1148
1149 static struct ChangingElementInfo change_delay_list[] =
1150 {
1151   {
1152     EL_NUT_BREAKING,
1153     EL_EMERALD,
1154     6,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_PEARL_BREAKING,
1161     EL_EMPTY,
1162     8,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_OPENING,
1169     EL_EXIT_OPEN,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_EXIT_CLOSING,
1177     EL_EXIT_CLOSED,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_OPENING,
1185     EL_STEEL_EXIT_OPEN,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_STEEL_EXIT_CLOSING,
1193     EL_STEEL_EXIT_CLOSED,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_OPENING,
1201     EL_EM_EXIT_OPEN,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_EXIT_CLOSING,
1209     EL_EMPTY,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_OPENING,
1217     EL_EM_STEEL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EM_STEEL_EXIT_CLOSING,
1225     EL_STEELWALL,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_OPENING,
1233     EL_SP_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SP_EXIT_CLOSING,
1241     EL_SP_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_OPENING,
1249     EL_SWITCHGATE_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_SWITCHGATE_CLOSING,
1257     EL_SWITCHGATE_CLOSED,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_OPENING,
1265     EL_TIMEGATE_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_TIMEGATE_CLOSING,
1273     EL_TIMEGATE_CLOSED,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279
1280   {
1281     EL_ACID_SPLASH_LEFT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_ACID_SPLASH_RIGHT,
1290     EL_EMPTY,
1291     8,
1292     NULL,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE,
1298     EL_SP_BUGGY_BASE_ACTIVATING,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVATING,
1306     EL_SP_BUGGY_BASE_ACTIVE,
1307     0,
1308     InitBuggyBase,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_BUGGY_BASE_ACTIVE,
1314     EL_SP_BUGGY_BASE,
1315     0,
1316     InitBuggyBase,
1317     WarnBuggyBase,
1318     NULL
1319   },
1320   {
1321     EL_TRAP,
1322     EL_TRAP_ACTIVE,
1323     0,
1324     InitTrap,
1325     NULL,
1326     ActivateTrap
1327   },
1328   {
1329     EL_TRAP_ACTIVE,
1330     EL_TRAP,
1331     31,
1332     NULL,
1333     ChangeActiveTrap,
1334     NULL
1335   },
1336   {
1337     EL_ROBOT_WHEEL_ACTIVE,
1338     EL_ROBOT_WHEEL,
1339     0,
1340     InitRobotWheel,
1341     RunRobotWheel,
1342     StopRobotWheel
1343   },
1344   {
1345     EL_TIMEGATE_SWITCH_ACTIVE,
1346     EL_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1354     EL_DC_TIMEGATE_SWITCH,
1355     0,
1356     InitTimegateWheel,
1357     RunTimegateWheel,
1358     NULL
1359   },
1360   {
1361     EL_EMC_MAGIC_BALL_ACTIVE,
1362     EL_EMC_MAGIC_BALL_ACTIVE,
1363     0,
1364     InitMagicBallDelay,
1365     NULL,
1366     ActivateMagicBall
1367   },
1368   {
1369     EL_EMC_SPRING_BUMPER_ACTIVE,
1370     EL_EMC_SPRING_BUMPER,
1371     8,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_SHRINKING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_DIAGONAL_GROWING,
1386     EL_UNDEFINED,
1387     0,
1388     NULL,
1389     NULL,
1390     NULL,
1391   },
1392
1393   {
1394     EL_UNDEFINED,
1395     EL_UNDEFINED,
1396     -1,
1397     NULL,
1398     NULL,
1399     NULL
1400   }
1401 };
1402
1403 struct
1404 {
1405   int element;
1406   int push_delay_fixed, push_delay_random;
1407 }
1408 push_delay_list[] =
1409 {
1410   { EL_SPRING,                  0, 0 },
1411   { EL_BALLOON,                 0, 0 },
1412
1413   { EL_SOKOBAN_OBJECT,          2, 0 },
1414   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1415   { EL_SATELLITE,               2, 0 },
1416   { EL_SP_DISK_YELLOW,          2, 0 },
1417
1418   { EL_UNDEFINED,               0, 0 },
1419 };
1420
1421 struct
1422 {
1423   int element;
1424   int move_stepsize;
1425 }
1426 move_stepsize_list[] =
1427 {
1428   { EL_AMOEBA_DROP,             2 },
1429   { EL_AMOEBA_DROPPING,         2 },
1430   { EL_QUICKSAND_FILLING,       1 },
1431   { EL_QUICKSAND_EMPTYING,      1 },
1432   { EL_QUICKSAND_FAST_FILLING,  2 },
1433   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1434   { EL_MAGIC_WALL_FILLING,      2 },
1435   { EL_MAGIC_WALL_EMPTYING,     2 },
1436   { EL_BD_MAGIC_WALL_FILLING,   2 },
1437   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1438   { EL_DC_MAGIC_WALL_FILLING,   2 },
1439   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1440
1441   { EL_UNDEFINED,               0 },
1442 };
1443
1444 struct
1445 {
1446   int element;
1447   int count;
1448 }
1449 collect_count_list[] =
1450 {
1451   { EL_EMERALD,                 1 },
1452   { EL_BD_DIAMOND,              1 },
1453   { EL_EMERALD_YELLOW,          1 },
1454   { EL_EMERALD_RED,             1 },
1455   { EL_EMERALD_PURPLE,          1 },
1456   { EL_DIAMOND,                 3 },
1457   { EL_SP_INFOTRON,             1 },
1458   { EL_PEARL,                   5 },
1459   { EL_CRYSTAL,                 8 },
1460
1461   { EL_UNDEFINED,               0 },
1462 };
1463
1464 struct
1465 {
1466   int element;
1467   int direction;
1468 }
1469 access_direction_list[] =
1470 {
1471   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1472   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1473   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1474   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1475   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1476   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1477   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1478   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1479   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1480   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1481   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1482
1483   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1484   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1485   { EL_SP_PORT_UP,                                                   MV_DOWN },
1486   { EL_SP_PORT_DOWN,                                         MV_UP           },
1487   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1488   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1489   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1490   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1491   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1492   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1493   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1494   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1495   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1496   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1497   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1498   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1499   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1500   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1501   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1502
1503   { EL_UNDEFINED,                       MV_NONE                              }
1504 };
1505
1506 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1507
1508 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1509 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1510 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1511                                  IS_JUST_CHANGING(x, y))
1512
1513 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1514
1515 /* static variables for playfield scan mode (scanning forward or backward) */
1516 static int playfield_scan_start_x = 0;
1517 static int playfield_scan_start_y = 0;
1518 static int playfield_scan_delta_x = 1;
1519 static int playfield_scan_delta_y = 1;
1520
1521 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1522                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1523                                      (y) += playfield_scan_delta_y)     \
1524                                 for ((x) = playfield_scan_start_x;      \
1525                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1526                                      (x) += playfield_scan_delta_x)
1527
1528 #ifdef DEBUG
1529 void DEBUG_SetMaximumDynamite()
1530 {
1531   int i;
1532
1533   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1534     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1535       local_player->inventory_element[local_player->inventory_size++] =
1536         EL_DYNAMITE;
1537 }
1538 #endif
1539
1540 static void InitPlayfieldScanModeVars()
1541 {
1542   if (game.use_reverse_scan_direction)
1543   {
1544     playfield_scan_start_x = lev_fieldx - 1;
1545     playfield_scan_start_y = lev_fieldy - 1;
1546
1547     playfield_scan_delta_x = -1;
1548     playfield_scan_delta_y = -1;
1549   }
1550   else
1551   {
1552     playfield_scan_start_x = 0;
1553     playfield_scan_start_y = 0;
1554
1555     playfield_scan_delta_x = 1;
1556     playfield_scan_delta_y = 1;
1557   }
1558 }
1559
1560 static void InitPlayfieldScanMode(int mode)
1561 {
1562   game.use_reverse_scan_direction =
1563     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1564
1565   InitPlayfieldScanModeVars();
1566 }
1567
1568 static int get_move_delay_from_stepsize(int move_stepsize)
1569 {
1570   move_stepsize =
1571     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1572
1573   /* make sure that stepsize value is always a power of 2 */
1574   move_stepsize = (1 << log_2(move_stepsize));
1575
1576   return TILEX / move_stepsize;
1577 }
1578
1579 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1580                                boolean init_game)
1581 {
1582   int player_nr = player->index_nr;
1583   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1584   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1585
1586   /* do no immediately change move delay -- the player might just be moving */
1587   player->move_delay_value_next = move_delay;
1588
1589   /* information if player can move must be set separately */
1590   player->cannot_move = cannot_move;
1591
1592   if (init_game)
1593   {
1594     player->move_delay       = game.initial_move_delay[player_nr];
1595     player->move_delay_value = game.initial_move_delay_value[player_nr];
1596
1597     player->move_delay_value_next = -1;
1598
1599     player->move_delay_reset_counter = 0;
1600   }
1601 }
1602
1603 void GetPlayerConfig()
1604 {
1605   GameFrameDelay = setup.game_frame_delay;
1606
1607   if (!audio.sound_available)
1608     setup.sound_simple = FALSE;
1609
1610   if (!audio.loops_available)
1611     setup.sound_loops = FALSE;
1612
1613   if (!audio.music_available)
1614     setup.sound_music = FALSE;
1615
1616   if (!video.fullscreen_available)
1617     setup.fullscreen = FALSE;
1618
1619   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1620
1621   SetAudioMode(setup.sound);
1622 }
1623
1624 int GetElementFromGroupElement(int element)
1625 {
1626   if (IS_GROUP_ELEMENT(element))
1627   {
1628     struct ElementGroupInfo *group = element_info[element].group;
1629     int last_anim_random_frame = gfx.anim_random_frame;
1630     int element_pos;
1631
1632     if (group->choice_mode == ANIM_RANDOM)
1633       gfx.anim_random_frame = RND(group->num_elements_resolved);
1634
1635     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1636                                     group->choice_mode, 0,
1637                                     group->choice_pos);
1638
1639     if (group->choice_mode == ANIM_RANDOM)
1640       gfx.anim_random_frame = last_anim_random_frame;
1641
1642     group->choice_pos++;
1643
1644     element = group->element_resolved[element_pos];
1645   }
1646
1647   return element;
1648 }
1649
1650 static void InitPlayerField(int x, int y, int element, boolean init_game)
1651 {
1652   if (element == EL_SP_MURPHY)
1653   {
1654     if (init_game)
1655     {
1656       if (stored_player[0].present)
1657       {
1658         Feld[x][y] = EL_SP_MURPHY_CLONE;
1659
1660         return;
1661       }
1662       else
1663       {
1664         stored_player[0].initial_element = element;
1665         stored_player[0].use_murphy = TRUE;
1666
1667         if (!level.use_artwork_element[0])
1668           stored_player[0].artwork_element = EL_SP_MURPHY;
1669       }
1670
1671       Feld[x][y] = EL_PLAYER_1;
1672     }
1673   }
1674
1675   if (init_game)
1676   {
1677     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1678     int jx = player->jx, jy = player->jy;
1679
1680     player->present = TRUE;
1681
1682     player->block_last_field = (element == EL_SP_MURPHY ?
1683                                 level.sp_block_last_field :
1684                                 level.block_last_field);
1685
1686     /* ---------- initialize player's last field block delay --------------- */
1687
1688     /* always start with reliable default value (no adjustment needed) */
1689     player->block_delay_adjustment = 0;
1690
1691     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1692     if (player->block_last_field && element == EL_SP_MURPHY)
1693       player->block_delay_adjustment = 1;
1694
1695     /* special case 2: in game engines before 3.1.1, blocking was different */
1696     if (game.use_block_last_field_bug)
1697       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1698
1699     if (!options.network || player->connected)
1700     {
1701       player->active = TRUE;
1702
1703       /* remove potentially duplicate players */
1704       if (StorePlayer[jx][jy] == Feld[x][y])
1705         StorePlayer[jx][jy] = 0;
1706
1707       StorePlayer[x][y] = Feld[x][y];
1708
1709 #if DEBUG_INIT_PLAYER
1710       if (options.debug)
1711       {
1712         printf("- player element %d activated", player->element_nr);
1713         printf(" (local player is %d and currently %s)\n",
1714                local_player->element_nr,
1715                local_player->active ? "active" : "not active");
1716       }
1717     }
1718 #endif
1719
1720     Feld[x][y] = EL_EMPTY;
1721
1722     player->jx = player->last_jx = x;
1723     player->jy = player->last_jy = y;
1724   }
1725
1726   if (!init_game)
1727   {
1728     int player_nr = GET_PLAYER_NR(element);
1729     struct PlayerInfo *player = &stored_player[player_nr];
1730
1731     if (player->active && player->killed)
1732       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1733   }
1734 }
1735
1736 static void InitField(int x, int y, boolean init_game)
1737 {
1738   int element = Feld[x][y];
1739
1740   switch (element)
1741   {
1742     case EL_SP_MURPHY:
1743     case EL_PLAYER_1:
1744     case EL_PLAYER_2:
1745     case EL_PLAYER_3:
1746     case EL_PLAYER_4:
1747       InitPlayerField(x, y, element, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_PLAYER:
1751       element = Feld[x][y] = EL_PLAYER_1;
1752       InitField(x, y, init_game);
1753
1754       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1755       InitField(x, y, init_game);
1756       break;
1757
1758     case EL_SOKOBAN_FIELD_EMPTY:
1759       local_player->sokobanfields_still_needed++;
1760       break;
1761
1762     case EL_STONEBLOCK:
1763       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1764         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1765       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1766         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1767       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1768         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1769       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1770         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1771       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1772         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1773       break;
1774
1775     case EL_BUG:
1776     case EL_BUG_RIGHT:
1777     case EL_BUG_UP:
1778     case EL_BUG_LEFT:
1779     case EL_BUG_DOWN:
1780     case EL_SPACESHIP:
1781     case EL_SPACESHIP_RIGHT:
1782     case EL_SPACESHIP_UP:
1783     case EL_SPACESHIP_LEFT:
1784     case EL_SPACESHIP_DOWN:
1785     case EL_BD_BUTTERFLY:
1786     case EL_BD_BUTTERFLY_RIGHT:
1787     case EL_BD_BUTTERFLY_UP:
1788     case EL_BD_BUTTERFLY_LEFT:
1789     case EL_BD_BUTTERFLY_DOWN:
1790     case EL_BD_FIREFLY:
1791     case EL_BD_FIREFLY_RIGHT:
1792     case EL_BD_FIREFLY_UP:
1793     case EL_BD_FIREFLY_LEFT:
1794     case EL_BD_FIREFLY_DOWN:
1795     case EL_PACMAN_RIGHT:
1796     case EL_PACMAN_UP:
1797     case EL_PACMAN_LEFT:
1798     case EL_PACMAN_DOWN:
1799     case EL_YAMYAM:
1800     case EL_YAMYAM_LEFT:
1801     case EL_YAMYAM_RIGHT:
1802     case EL_YAMYAM_UP:
1803     case EL_YAMYAM_DOWN:
1804     case EL_DARK_YAMYAM:
1805     case EL_ROBOT:
1806     case EL_PACMAN:
1807     case EL_SP_SNIKSNAK:
1808     case EL_SP_ELECTRON:
1809     case EL_MOLE:
1810     case EL_MOLE_LEFT:
1811     case EL_MOLE_RIGHT:
1812     case EL_MOLE_UP:
1813     case EL_MOLE_DOWN:
1814       InitMovDir(x, y);
1815       break;
1816
1817     case EL_AMOEBA_FULL:
1818     case EL_BD_AMOEBA:
1819       InitAmoebaNr(x, y);
1820       break;
1821
1822     case EL_AMOEBA_DROP:
1823       if (y == lev_fieldy - 1)
1824       {
1825         Feld[x][y] = EL_AMOEBA_GROWING;
1826         Store[x][y] = EL_AMOEBA_WET;
1827       }
1828       break;
1829
1830     case EL_DYNAMITE_ACTIVE:
1831     case EL_SP_DISK_RED_ACTIVE:
1832     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1833     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1834     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1835     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1836       MovDelay[x][y] = 96;
1837       break;
1838
1839     case EL_EM_DYNAMITE_ACTIVE:
1840       MovDelay[x][y] = 32;
1841       break;
1842
1843     case EL_LAMP:
1844       local_player->lights_still_needed++;
1845       break;
1846
1847     case EL_PENGUIN:
1848       local_player->friends_still_needed++;
1849       break;
1850
1851     case EL_PIG:
1852     case EL_DRAGON:
1853       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1854       break;
1855
1856     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1859     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1860     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1861     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1862     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1863     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1864     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1865     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1866     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1867     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1868       if (init_game)
1869       {
1870         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1871         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1872         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1873
1874         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1875         {
1876           game.belt_dir[belt_nr] = belt_dir;
1877           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1878         }
1879         else    /* more than one switch -- set it like the first switch */
1880         {
1881           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1882         }
1883       }
1884       break;
1885
1886     case EL_LIGHT_SWITCH_ACTIVE:
1887       if (init_game)
1888         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1889       break;
1890
1891     case EL_INVISIBLE_STEELWALL:
1892     case EL_INVISIBLE_WALL:
1893     case EL_INVISIBLE_SAND:
1894       if (game.light_time_left > 0 ||
1895           game.lenses_time_left > 0)
1896         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1897       break;
1898
1899     case EL_EMC_MAGIC_BALL:
1900       if (game.ball_state)
1901         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1902       break;
1903
1904     case EL_EMC_MAGIC_BALL_SWITCH:
1905       if (game.ball_state)
1906         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1907       break;
1908
1909     case EL_TRIGGER_PLAYER:
1910     case EL_TRIGGER_ELEMENT:
1911     case EL_TRIGGER_CE_VALUE:
1912     case EL_TRIGGER_CE_SCORE:
1913     case EL_SELF:
1914     case EL_ANY_ELEMENT:
1915     case EL_CURRENT_CE_VALUE:
1916     case EL_CURRENT_CE_SCORE:
1917     case EL_PREV_CE_1:
1918     case EL_PREV_CE_2:
1919     case EL_PREV_CE_3:
1920     case EL_PREV_CE_4:
1921     case EL_PREV_CE_5:
1922     case EL_PREV_CE_6:
1923     case EL_PREV_CE_7:
1924     case EL_PREV_CE_8:
1925     case EL_NEXT_CE_1:
1926     case EL_NEXT_CE_2:
1927     case EL_NEXT_CE_3:
1928     case EL_NEXT_CE_4:
1929     case EL_NEXT_CE_5:
1930     case EL_NEXT_CE_6:
1931     case EL_NEXT_CE_7:
1932     case EL_NEXT_CE_8:
1933       /* reference elements should not be used on the playfield */
1934       Feld[x][y] = EL_EMPTY;
1935       break;
1936
1937     default:
1938       if (IS_CUSTOM_ELEMENT(element))
1939       {
1940         if (CAN_MOVE(element))
1941           InitMovDir(x, y);
1942
1943         if (!element_info[element].use_last_ce_value || init_game)
1944           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1945       }
1946       else if (IS_GROUP_ELEMENT(element))
1947       {
1948         Feld[x][y] = GetElementFromGroupElement(element);
1949
1950         InitField(x, y, init_game);
1951       }
1952
1953       break;
1954   }
1955
1956   if (!init_game)
1957     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1958 }
1959
1960 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1961 {
1962   InitField(x, y, init_game);
1963
1964   /* not needed to call InitMovDir() -- already done by InitField()! */
1965   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966       CAN_MOVE(Feld[x][y]))
1967     InitMovDir(x, y);
1968 }
1969
1970 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1971 {
1972   int old_element = Feld[x][y];
1973
1974   InitField(x, y, init_game);
1975
1976   /* not needed to call InitMovDir() -- already done by InitField()! */
1977   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1978       CAN_MOVE(old_element) &&
1979       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1980     InitMovDir(x, y);
1981
1982   /* this case is in fact a combination of not less than three bugs:
1983      first, it calls InitMovDir() for elements that can move, although this is
1984      already done by InitField(); then, it checks the element that was at this
1985      field _before_ the call to InitField() (which can change it); lastly, it
1986      was not called for "mole with direction" elements, which were treated as
1987      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1988   */
1989 }
1990
1991 static int get_key_element_from_nr(int key_nr)
1992 {
1993   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1994                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1995                           EL_EM_KEY_1 : EL_KEY_1);
1996
1997   return key_base_element + key_nr;
1998 }
1999
2000 static int get_next_dropped_element(struct PlayerInfo *player)
2001 {
2002   return (player->inventory_size > 0 ?
2003           player->inventory_element[player->inventory_size - 1] :
2004           player->inventory_infinite_element != EL_UNDEFINED ?
2005           player->inventory_infinite_element :
2006           player->dynabombs_left > 0 ?
2007           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2008           EL_UNDEFINED);
2009 }
2010
2011 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2012 {
2013   /* pos >= 0: get element from bottom of the stack;
2014      pos <  0: get element from top of the stack */
2015
2016   if (pos < 0)
2017   {
2018     int min_inventory_size = -pos;
2019     int inventory_pos = player->inventory_size - min_inventory_size;
2020     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2021
2022     return (player->inventory_size >= min_inventory_size ?
2023             player->inventory_element[inventory_pos] :
2024             player->inventory_infinite_element != EL_UNDEFINED ?
2025             player->inventory_infinite_element :
2026             player->dynabombs_left >= min_dynabombs_left ?
2027             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2028             EL_UNDEFINED);
2029   }
2030   else
2031   {
2032     int min_dynabombs_left = pos + 1;
2033     int min_inventory_size = pos + 1 - player->dynabombs_left;
2034     int inventory_pos = pos - player->dynabombs_left;
2035
2036     return (player->inventory_infinite_element != EL_UNDEFINED ?
2037             player->inventory_infinite_element :
2038             player->dynabombs_left >= min_dynabombs_left ?
2039             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2040             player->inventory_size >= min_inventory_size ?
2041             player->inventory_element[inventory_pos] :
2042             EL_UNDEFINED);
2043   }
2044 }
2045
2046 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2047 {
2048   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2049   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2050   int compare_result;
2051
2052   if (gpo1->sort_priority != gpo2->sort_priority)
2053     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2054   else
2055     compare_result = gpo1->nr - gpo2->nr;
2056
2057   return compare_result;
2058 }
2059
2060 int getPlayerInventorySize(int player_nr)
2061 {
2062   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2063     return level.native_em_level->ply[player_nr]->dynamite;
2064   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2065     return level.native_sp_level->game_sp->red_disk_count;
2066   else
2067     return stored_player[player_nr].inventory_size;
2068 }
2069
2070 void InitGameControlValues()
2071 {
2072   int i;
2073
2074   for (i = 0; game_panel_controls[i].nr != -1; i++)
2075   {
2076     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2077     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2078     struct TextPosInfo *pos = gpc->pos;
2079     int nr = gpc->nr;
2080     int type = gpc->type;
2081
2082     if (nr != i)
2083     {
2084       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2085       Error(ERR_EXIT, "this should not happen -- please debug");
2086     }
2087
2088     /* force update of game controls after initialization */
2089     gpc->value = gpc->last_value = -1;
2090     gpc->frame = gpc->last_frame = -1;
2091     gpc->gfx_frame = -1;
2092
2093     /* determine panel value width for later calculation of alignment */
2094     if (type == TYPE_INTEGER || type == TYPE_STRING)
2095     {
2096       pos->width = pos->size * getFontWidth(pos->font);
2097       pos->height = getFontHeight(pos->font);
2098     }
2099     else if (type == TYPE_ELEMENT)
2100     {
2101       pos->width = pos->size;
2102       pos->height = pos->size;
2103     }
2104
2105     /* fill structure for game panel draw order */
2106     gpo->nr = gpc->nr;
2107     gpo->sort_priority = pos->sort_priority;
2108   }
2109
2110   /* sort game panel controls according to sort_priority and control number */
2111   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2112         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2113 }
2114
2115 void UpdatePlayfieldElementCount()
2116 {
2117   boolean use_element_count = FALSE;
2118   int i, j, x, y;
2119
2120   /* first check if it is needed at all to calculate playfield element count */
2121   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2122     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2123       use_element_count = TRUE;
2124
2125   if (!use_element_count)
2126     return;
2127
2128   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2129     element_info[i].element_count = 0;
2130
2131   SCAN_PLAYFIELD(x, y)
2132   {
2133     element_info[Feld[x][y]].element_count++;
2134   }
2135
2136   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2137     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2138       if (IS_IN_GROUP(j, i))
2139         element_info[EL_GROUP_START + i].element_count +=
2140           element_info[j].element_count;
2141 }
2142
2143 void UpdateGameControlValues()
2144 {
2145   int i, k;
2146   int time = (local_player->LevelSolved ?
2147               local_player->LevelSolved_CountingTime :
2148               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149               level.native_em_level->lev->time :
2150               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151               level.native_sp_level->game_sp->time_played :
2152               game.no_time_limit ? TimePlayed : TimeLeft);
2153   int score = (local_player->LevelSolved ?
2154                local_player->LevelSolved_CountingScore :
2155                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2156                level.native_em_level->lev->score :
2157                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2158                level.native_sp_level->game_sp->score :
2159                local_player->score);
2160   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2161               level.native_em_level->lev->required :
2162               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2163               level.native_sp_level->game_sp->infotrons_still_needed :
2164               local_player->gems_still_needed);
2165   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2166                      level.native_em_level->lev->required > 0 :
2167                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2168                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2169                      local_player->gems_still_needed > 0 ||
2170                      local_player->sokobanfields_still_needed > 0 ||
2171                      local_player->lights_still_needed > 0);
2172
2173   UpdatePlayfieldElementCount();
2174
2175   /* update game panel control values */
2176
2177   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2178   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2179
2180   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2181   for (i = 0; i < MAX_NUM_KEYS; i++)
2182     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2183   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2184   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2185
2186   if (game.centered_player_nr == -1)
2187   {
2188     for (i = 0; i < MAX_PLAYERS; i++)
2189     {
2190       /* only one player in Supaplex game engine */
2191       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2192         break;
2193
2194       for (k = 0; k < MAX_NUM_KEYS; k++)
2195       {
2196         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2197         {
2198           if (level.native_em_level->ply[i]->keys & (1 << k))
2199             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2200               get_key_element_from_nr(k);
2201         }
2202         else if (stored_player[i].key[k])
2203           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2204             get_key_element_from_nr(k);
2205       }
2206
2207       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2208         getPlayerInventorySize(i);
2209
2210       if (stored_player[i].num_white_keys > 0)
2211         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2212           EL_DC_KEY_WHITE;
2213
2214       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2215         stored_player[i].num_white_keys;
2216     }
2217   }
2218   else
2219   {
2220     int player_nr = game.centered_player_nr;
2221
2222     for (k = 0; k < MAX_NUM_KEYS; k++)
2223     {
2224       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       {
2226         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2227           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2228             get_key_element_from_nr(k);
2229       }
2230       else if (stored_player[player_nr].key[k])
2231         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2232           get_key_element_from_nr(k);
2233     }
2234
2235     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236       getPlayerInventorySize(player_nr);
2237
2238     if (stored_player[player_nr].num_white_keys > 0)
2239       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2240
2241     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242       stored_player[player_nr].num_white_keys;
2243   }
2244
2245   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2246   {
2247     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248       get_inventory_element_from_pos(local_player, i);
2249     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250       get_inventory_element_from_pos(local_player, -i - 1);
2251   }
2252
2253   game_panel_controls[GAME_PANEL_SCORE].value = score;
2254   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2255
2256   game_panel_controls[GAME_PANEL_TIME].value = time;
2257
2258   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2261
2262   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2263
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268     local_player->shield_normal_time_left;
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2271      EL_EMPTY);
2272   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273     local_player->shield_deadly_time_left;
2274
2275   game_panel_controls[GAME_PANEL_EXIT].value =
2276     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2277
2278   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282      EL_EMC_MAGIC_BALL_SWITCH);
2283
2284   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287     game.light_time_left;
2288
2289   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292     game.timegate_time_left;
2293
2294   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2296
2297   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300     game.lenses_time_left;
2301
2302   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305     game.magnify_time_left;
2306
2307   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2309      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2311      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2312      EL_BALLOON_SWITCH_NONE);
2313
2314   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315     local_player->dynabomb_count;
2316   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317     local_player->dynabomb_size;
2318   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2320
2321   game_panel_controls[GAME_PANEL_PENGUINS].value =
2322     local_player->friends_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325     local_player->sokobanfields_still_needed;
2326   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327     local_player->sokobanfields_still_needed;
2328
2329   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2331
2332   for (i = 0; i < NUM_BELTS; i++)
2333   {
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2339   }
2340
2341   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344     game.magic_wall_time_left;
2345
2346   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347     local_player->gravity;
2348
2349   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355        game.panel.element[i].id : EL_UNDEFINED);
2356
2357   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360        element_info[game.panel.element_count[i].id].element_count : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365        element_info[game.panel.ce_score[i].id].collect_score : 0);
2366
2367   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370        element_info[game.panel.ce_score_element[i].id].collect_score :
2371        EL_UNDEFINED);
2372
2373   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2376
2377   /* update game panel control frames */
2378
2379   for (i = 0; game_panel_controls[i].nr != -1; i++)
2380   {
2381     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2382
2383     if (gpc->type == TYPE_ELEMENT)
2384     {
2385       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2386       {
2387         int last_anim_random_frame = gfx.anim_random_frame;
2388         int element = gpc->value;
2389         int graphic = el2panelimg(element);
2390
2391         if (gpc->value != gpc->last_value)
2392         {
2393           gpc->gfx_frame = 0;
2394           gpc->gfx_random = INIT_GFX_RANDOM();
2395         }
2396         else
2397         {
2398           gpc->gfx_frame++;
2399
2400           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402             gpc->gfx_random = INIT_GFX_RANDOM();
2403         }
2404
2405         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406           gfx.anim_random_frame = gpc->gfx_random;
2407
2408         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409           gpc->gfx_frame = element_info[element].collect_score;
2410
2411         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2412                                               gpc->gfx_frame);
2413
2414         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415           gfx.anim_random_frame = last_anim_random_frame;
2416       }
2417     }
2418   }
2419 }
2420
2421 void DisplayGameControlValues()
2422 {
2423   boolean redraw_panel = FALSE;
2424   int i;
2425
2426   for (i = 0; game_panel_controls[i].nr != -1; i++)
2427   {
2428     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2429
2430     if (PANEL_DEACTIVATED(gpc->pos))
2431       continue;
2432
2433     if (gpc->value == gpc->last_value &&
2434         gpc->frame == gpc->last_frame)
2435       continue;
2436
2437     redraw_panel = TRUE;
2438   }
2439
2440   if (!redraw_panel)
2441     return;
2442
2443   /* copy default game door content to main double buffer */
2444
2445   /* !!! CHECK AGAIN !!! */
2446   SetPanelBackground();
2447   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2449
2450   /* redraw game control buttons */
2451   RedrawGameButtons();
2452
2453   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2454
2455   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2456   {
2457     int nr = game_panel_order[i].nr;
2458     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459     struct TextPosInfo *pos = gpc->pos;
2460     int type = gpc->type;
2461     int value = gpc->value;
2462     int frame = gpc->frame;
2463     int size = pos->size;
2464     int font = pos->font;
2465     boolean draw_masked = pos->draw_masked;
2466     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2467
2468     if (PANEL_DEACTIVATED(pos))
2469       continue;
2470
2471     gpc->last_value = value;
2472     gpc->last_frame = frame;
2473
2474     if (type == TYPE_INTEGER)
2475     {
2476       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477           nr == GAME_PANEL_TIME)
2478       {
2479         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2480
2481         if (use_dynamic_size)           /* use dynamic number of digits */
2482         {
2483           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485           int size2 = size1 + 1;
2486           int font1 = pos->font;
2487           int font2 = pos->font_alt;
2488
2489           size = (value < value_change ? size1 : size2);
2490           font = (value < value_change ? font1 : font2);
2491         }
2492       }
2493
2494       /* correct text size if "digits" is zero or less */
2495       if (size <= 0)
2496         size = strlen(int2str(value, size));
2497
2498       /* dynamically correct text alignment */
2499       pos->width = size * getFontWidth(font);
2500
2501       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502                   int2str(value, size), font, mask_mode);
2503     }
2504     else if (type == TYPE_ELEMENT)
2505     {
2506       int element, graphic;
2507       Bitmap *src_bitmap;
2508       int src_x, src_y;
2509       int width, height;
2510       int dst_x = PANEL_XPOS(pos);
2511       int dst_y = PANEL_YPOS(pos);
2512
2513       if (value != EL_UNDEFINED && value != EL_EMPTY)
2514       {
2515         element = value;
2516         graphic = el2panelimg(value);
2517
2518         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2519
2520         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2521           size = TILESIZE;
2522
2523         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2524                               &src_x, &src_y);
2525
2526         width  = graphic_info[graphic].width  * size / TILESIZE;
2527         height = graphic_info[graphic].height * size / TILESIZE;
2528
2529         if (draw_masked)
2530           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2531                            dst_x, dst_y);
2532         else
2533           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2534                      dst_x, dst_y);
2535       }
2536     }
2537     else if (type == TYPE_STRING)
2538     {
2539       boolean active = (value != 0);
2540       char *state_normal = "off";
2541       char *state_active = "on";
2542       char *state = (active ? state_active : state_normal);
2543       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2545                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2546                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2547
2548       if (nr == GAME_PANEL_GRAVITY_STATE)
2549       {
2550         int font1 = pos->font;          /* (used for normal state) */
2551         int font2 = pos->font_alt;      /* (used for active state) */
2552
2553         font = (active ? font2 : font1);
2554       }
2555
2556       if (s != NULL)
2557       {
2558         char *s_cut;
2559
2560         if (size <= 0)
2561         {
2562           /* don't truncate output if "chars" is zero or less */
2563           size = strlen(s);
2564
2565           /* dynamically correct text alignment */
2566           pos->width = size * getFontWidth(font);
2567         }
2568
2569         s_cut = getStringCopyN(s, size);
2570
2571         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572                     s_cut, font, mask_mode);
2573
2574         free(s_cut);
2575       }
2576     }
2577
2578     redraw_mask |= REDRAW_DOOR_1;
2579   }
2580
2581   SetGameStatus(GAME_MODE_PLAYING);
2582 }
2583
2584 void UpdateAndDisplayGameControlValues()
2585 {
2586   if (tape.deactivate_display)
2587     return;
2588
2589   UpdateGameControlValues();
2590   DisplayGameControlValues();
2591 }
2592
2593 void UpdateGameDoorValues()
2594 {
2595   UpdateGameControlValues();
2596 }
2597
2598 void DrawGameDoorValues()
2599 {
2600   DisplayGameControlValues();
2601 }
2602
2603
2604 /*
2605   =============================================================================
2606   InitGameEngine()
2607   -----------------------------------------------------------------------------
2608   initialize game engine due to level / tape version number
2609   =============================================================================
2610 */
2611
2612 static void InitGameEngine()
2613 {
2614   int i, j, k, l, x, y;
2615
2616   /* set game engine from tape file when re-playing, else from level file */
2617   game.engine_version = (tape.playing ? tape.engine_version :
2618                          level.game_version);
2619
2620   /* set single or multi-player game mode (needed for re-playing tapes) */
2621   game.team_mode = setup.team_mode;
2622
2623   if (tape.playing)
2624   {
2625     int num_players = 0;
2626
2627     for (i = 0; i < MAX_PLAYERS; i++)
2628       if (tape.player_participates[i])
2629         num_players++;
2630
2631     /* multi-player tapes contain input data for more than one player */
2632     game.team_mode = (num_players > 1);
2633   }
2634
2635   /* ---------------------------------------------------------------------- */
2636   /* set flags for bugs and changes according to active game engine version */
2637   /* ---------------------------------------------------------------------- */
2638
2639   /*
2640     Summary of bugfix/change:
2641     Fixed handling for custom elements that change when pushed by the player.
2642
2643     Fixed/changed in version:
2644     3.1.0
2645
2646     Description:
2647     Before 3.1.0, custom elements that "change when pushing" changed directly
2648     after the player started pushing them (until then handled in "DigField()").
2649     Since 3.1.0, these custom elements are not changed until the "pushing"
2650     move of the element is finished (now handled in "ContinueMoving()").
2651
2652     Affected levels/tapes:
2653     The first condition is generally needed for all levels/tapes before version
2654     3.1.0, which might use the old behaviour before it was changed; known tapes
2655     that are affected are some tapes from the level set "Walpurgis Gardens" by
2656     Jamie Cullen.
2657     The second condition is an exception from the above case and is needed for
2658     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659     above (including some development versions of 3.1.0), but before it was
2660     known that this change would break tapes like the above and was fixed in
2661     3.1.1, so that the changed behaviour was active although the engine version
2662     while recording maybe was before 3.1.0. There is at least one tape that is
2663     affected by this exception, which is the tape for the one-level set "Bug
2664     Machine" by Juergen Bonhagen.
2665   */
2666
2667   game.use_change_when_pushing_bug =
2668     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2669      !(tape.playing &&
2670        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2672
2673   /*
2674     Summary of bugfix/change:
2675     Fixed handling for blocking the field the player leaves when moving.
2676
2677     Fixed/changed in version:
2678     3.1.1
2679
2680     Description:
2681     Before 3.1.1, when "block last field when moving" was enabled, the field
2682     the player is leaving when moving was blocked for the time of the move,
2683     and was directly unblocked afterwards. This resulted in the last field
2684     being blocked for exactly one less than the number of frames of one player
2685     move. Additionally, even when blocking was disabled, the last field was
2686     blocked for exactly one frame.
2687     Since 3.1.1, due to changes in player movement handling, the last field
2688     is not blocked at all when blocking is disabled. When blocking is enabled,
2689     the last field is blocked for exactly the number of frames of one player
2690     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691     last field is blocked for exactly one more than the number of frames of
2692     one player move.
2693
2694     Affected levels/tapes:
2695     (!!! yet to be determined -- probably many !!!)
2696   */
2697
2698   game.use_block_last_field_bug =
2699     (game.engine_version < VERSION_IDENT(3,1,1,0));
2700
2701   game_em.use_single_button = game_em.use_single_button_initial =
2702     (game.engine_version > VERSION_IDENT(4,0,0,2));
2703
2704   /* ---------------------------------------------------------------------- */
2705
2706   /* set maximal allowed number of custom element changes per game frame */
2707   game.max_num_changes_per_frame = 1;
2708
2709   /* default scan direction: scan playfield from top/left to bottom/right */
2710   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2711
2712   /* dynamically adjust element properties according to game engine version */
2713   InitElementPropertiesEngine(game.engine_version);
2714
2715 #if 0
2716   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2717   printf("          tape version == %06d [%s] [file: %06d]\n",
2718          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2719          tape.file_version);
2720   printf("       => game.engine_version == %06d\n", game.engine_version);
2721 #endif
2722
2723   /* ---------- initialize player's initial move delay --------------------- */
2724
2725   /* dynamically adjust player properties according to level information */
2726   for (i = 0; i < MAX_PLAYERS; i++)
2727     game.initial_move_delay_value[i] =
2728       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   for (i = 0; i < MAX_PLAYERS; i++)
2732     game.initial_move_delay[i] =
2733       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2734        game.initial_move_delay_value[i] : 0);
2735
2736   /* ---------- initialize player's initial push delay --------------------- */
2737
2738   /* dynamically adjust player properties according to game engine version */
2739   game.initial_push_delay_value =
2740     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2741
2742   /* ---------- initialize changing elements ------------------------------- */
2743
2744   /* initialize changing elements information */
2745   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2746   {
2747     struct ElementInfo *ei = &element_info[i];
2748
2749     /* this pointer might have been changed in the level editor */
2750     ei->change = &ei->change_page[0];
2751
2752     if (!IS_CUSTOM_ELEMENT(i))
2753     {
2754       ei->change->target_element = EL_EMPTY_SPACE;
2755       ei->change->delay_fixed = 0;
2756       ei->change->delay_random = 0;
2757       ei->change->delay_frames = 1;
2758     }
2759
2760     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2761     {
2762       ei->has_change_event[j] = FALSE;
2763
2764       ei->event_page_nr[j] = 0;
2765       ei->event_page[j] = &ei->change_page[0];
2766     }
2767   }
2768
2769   /* add changing elements from pre-defined list */
2770   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2771   {
2772     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2773     struct ElementInfo *ei = &element_info[ch_delay->element];
2774
2775     ei->change->target_element       = ch_delay->target_element;
2776     ei->change->delay_fixed          = ch_delay->change_delay;
2777
2778     ei->change->pre_change_function  = ch_delay->pre_change_function;
2779     ei->change->change_function      = ch_delay->change_function;
2780     ei->change->post_change_function = ch_delay->post_change_function;
2781
2782     ei->change->can_change = TRUE;
2783     ei->change->can_change_or_has_action = TRUE;
2784
2785     ei->has_change_event[CE_DELAY] = TRUE;
2786
2787     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2788     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2789   }
2790
2791   /* ---------- initialize internal run-time variables --------------------- */
2792
2793   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2794   {
2795     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2796
2797     for (j = 0; j < ei->num_change_pages; j++)
2798     {
2799       ei->change_page[j].can_change_or_has_action =
2800         (ei->change_page[j].can_change |
2801          ei->change_page[j].has_action);
2802     }
2803   }
2804
2805   /* add change events from custom element configuration */
2806   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2807   {
2808     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2809
2810     for (j = 0; j < ei->num_change_pages; j++)
2811     {
2812       if (!ei->change_page[j].can_change_or_has_action)
2813         continue;
2814
2815       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2816       {
2817         /* only add event page for the first page found with this event */
2818         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2819         {
2820           ei->has_change_event[k] = TRUE;
2821
2822           ei->event_page_nr[k] = j;
2823           ei->event_page[k] = &ei->change_page[j];
2824         }
2825       }
2826     }
2827   }
2828
2829   /* ---------- initialize reference elements in change conditions --------- */
2830
2831   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2832   {
2833     int element = EL_CUSTOM_START + i;
2834     struct ElementInfo *ei = &element_info[element];
2835
2836     for (j = 0; j < ei->num_change_pages; j++)
2837     {
2838       int trigger_element = ei->change_page[j].initial_trigger_element;
2839
2840       if (trigger_element >= EL_PREV_CE_8 &&
2841           trigger_element <= EL_NEXT_CE_8)
2842         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2843
2844       ei->change_page[j].trigger_element = trigger_element;
2845     }
2846   }
2847
2848   /* ---------- initialize run-time trigger player and element ------------- */
2849
2850   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2851   {
2852     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2853
2854     for (j = 0; j < ei->num_change_pages; j++)
2855     {
2856       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2857       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2858       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2859       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2860       ei->change_page[j].actual_trigger_ce_value = 0;
2861       ei->change_page[j].actual_trigger_ce_score = 0;
2862     }
2863   }
2864
2865   /* ---------- initialize trigger events ---------------------------------- */
2866
2867   /* initialize trigger events information */
2868   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2870       trigger_events[i][j] = FALSE;
2871
2872   /* add trigger events from element change event properties */
2873   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2874   {
2875     struct ElementInfo *ei = &element_info[i];
2876
2877     for (j = 0; j < ei->num_change_pages; j++)
2878     {
2879       if (!ei->change_page[j].can_change_or_has_action)
2880         continue;
2881
2882       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2883       {
2884         int trigger_element = ei->change_page[j].trigger_element;
2885
2886         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2887         {
2888           if (ei->change_page[j].has_event[k])
2889           {
2890             if (IS_GROUP_ELEMENT(trigger_element))
2891             {
2892               struct ElementGroupInfo *group =
2893                 element_info[trigger_element].group;
2894
2895               for (l = 0; l < group->num_elements_resolved; l++)
2896                 trigger_events[group->element_resolved[l]][k] = TRUE;
2897             }
2898             else if (trigger_element == EL_ANY_ELEMENT)
2899               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2900                 trigger_events[l][k] = TRUE;
2901             else
2902               trigger_events[trigger_element][k] = TRUE;
2903           }
2904         }
2905       }
2906     }
2907   }
2908
2909   /* ---------- initialize push delay -------------------------------------- */
2910
2911   /* initialize push delay values to default */
2912   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2913   {
2914     if (!IS_CUSTOM_ELEMENT(i))
2915     {
2916       /* set default push delay values (corrected since version 3.0.7-1) */
2917       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2918       {
2919         element_info[i].push_delay_fixed = 2;
2920         element_info[i].push_delay_random = 8;
2921       }
2922       else
2923       {
2924         element_info[i].push_delay_fixed = 8;
2925         element_info[i].push_delay_random = 8;
2926       }
2927     }
2928   }
2929
2930   /* set push delay value for certain elements from pre-defined list */
2931   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2932   {
2933     int e = push_delay_list[i].element;
2934
2935     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2936     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2937   }
2938
2939   /* set push delay value for Supaplex elements for newer engine versions */
2940   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2941   {
2942     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2943     {
2944       if (IS_SP_ELEMENT(i))
2945       {
2946         /* set SP push delay to just enough to push under a falling zonk */
2947         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2948
2949         element_info[i].push_delay_fixed  = delay;
2950         element_info[i].push_delay_random = 0;
2951       }
2952     }
2953   }
2954
2955   /* ---------- initialize move stepsize ----------------------------------- */
2956
2957   /* initialize move stepsize values to default */
2958   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2959     if (!IS_CUSTOM_ELEMENT(i))
2960       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2961
2962   /* set move stepsize value for certain elements from pre-defined list */
2963   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2964   {
2965     int e = move_stepsize_list[i].element;
2966
2967     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2968   }
2969
2970   /* ---------- initialize collect score ----------------------------------- */
2971
2972   /* initialize collect score values for custom elements from initial value */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_score = element_info[i].collect_score_initial;
2976
2977   /* ---------- initialize collect count ----------------------------------- */
2978
2979   /* initialize collect count values for non-custom elements */
2980   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2981     if (!IS_CUSTOM_ELEMENT(i))
2982       element_info[i].collect_count_initial = 0;
2983
2984   /* add collect count values for all elements from pre-defined list */
2985   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2986     element_info[collect_count_list[i].element].collect_count_initial =
2987       collect_count_list[i].count;
2988
2989   /* ---------- initialize access direction -------------------------------- */
2990
2991   /* initialize access direction values to default (access from every side) */
2992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993     if (!IS_CUSTOM_ELEMENT(i))
2994       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2995
2996   /* set access direction value for certain elements from pre-defined list */
2997   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2998     element_info[access_direction_list[i].element].access_direction =
2999       access_direction_list[i].direction;
3000
3001   /* ---------- initialize explosion content ------------------------------- */
3002   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3003   {
3004     if (IS_CUSTOM_ELEMENT(i))
3005       continue;
3006
3007     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3008     {
3009       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3010
3011       element_info[i].content.e[x][y] =
3012         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3013          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3014          i == EL_PLAYER_3 ? EL_EMERALD :
3015          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3016          i == EL_MOLE ? EL_EMERALD_RED :
3017          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3018          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3019          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3020          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3021          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3022          i == EL_WALL_EMERALD ? EL_EMERALD :
3023          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3024          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3025          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3026          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3027          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3028          i == EL_WALL_PEARL ? EL_PEARL :
3029          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3030          EL_EMPTY);
3031     }
3032   }
3033
3034   /* ---------- initialize recursion detection ------------------------------ */
3035   recursion_loop_depth = 0;
3036   recursion_loop_detected = FALSE;
3037   recursion_loop_element = EL_UNDEFINED;
3038
3039   /* ---------- initialize graphics engine ---------------------------------- */
3040   game.scroll_delay_value =
3041     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3042      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3043   game.scroll_delay_value =
3044     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3045
3046   /* ---------- initialize game engine snapshots ---------------------------- */
3047   for (i = 0; i < MAX_PLAYERS; i++)
3048     game.snapshot.last_action[i] = 0;
3049   game.snapshot.changed_action = FALSE;
3050   game.snapshot.collected_item = FALSE;
3051   game.snapshot.mode =
3052     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3053      SNAPSHOT_MODE_EVERY_STEP :
3054      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3055      SNAPSHOT_MODE_EVERY_MOVE :
3056      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3057      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3058   game.snapshot.save_snapshot = FALSE;
3059 }
3060
3061 int get_num_special_action(int element, int action_first, int action_last)
3062 {
3063   int num_special_action = 0;
3064   int i, j;
3065
3066   for (i = action_first; i <= action_last; i++)
3067   {
3068     boolean found = FALSE;
3069
3070     for (j = 0; j < NUM_DIRECTIONS; j++)
3071       if (el_act_dir2img(element, i, j) !=
3072           el_act_dir2img(element, ACTION_DEFAULT, j))
3073         found = TRUE;
3074
3075     if (found)
3076       num_special_action++;
3077     else
3078       break;
3079   }
3080
3081   return num_special_action;
3082 }
3083
3084
3085 /*
3086   =============================================================================
3087   InitGame()
3088   -----------------------------------------------------------------------------
3089   initialize and start new game
3090   =============================================================================
3091 */
3092
3093 void InitGame()
3094 {
3095   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3096   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3097   int fade_mask = REDRAW_FIELD;
3098
3099   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3100   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3101   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3102   int initial_move_dir = MV_DOWN;
3103   int i, j, x, y;
3104
3105   // required here to update video display before fading (FIX THIS)
3106   DrawMaskedBorder(REDRAW_DOOR_2);
3107
3108   if (!game.restart_level)
3109     CloseDoor(DOOR_CLOSE_1);
3110
3111   SetGameStatus(GAME_MODE_PLAYING);
3112
3113   if (level_editor_test_game)
3114     FadeSkipNextFadeIn();
3115   else
3116     FadeSetEnterScreen();
3117
3118   if (CheckIfGlobalBorderHasChanged())
3119     fade_mask = REDRAW_ALL;
3120
3121   FadeSoundsAndMusic();
3122
3123   ExpireSoundLoops(TRUE);
3124
3125   FadeOut(fade_mask);
3126
3127   /* needed if different viewport properties defined for playing */
3128   ChangeViewportPropertiesIfNeeded();
3129
3130   ClearField();
3131
3132   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3133
3134   DrawCompleteVideoDisplay();
3135
3136   InitGameEngine();
3137   InitGameControlValues();
3138
3139   /* don't play tapes over network */
3140   network_playing = (options.network && !tape.playing);
3141
3142   for (i = 0; i < MAX_PLAYERS; i++)
3143   {
3144     struct PlayerInfo *player = &stored_player[i];
3145
3146     player->index_nr = i;
3147     player->index_bit = (1 << i);
3148     player->element_nr = EL_PLAYER_1 + i;
3149
3150     player->present = FALSE;
3151     player->active = FALSE;
3152     player->mapped = FALSE;
3153
3154     player->killed = FALSE;
3155     player->reanimated = FALSE;
3156
3157     player->action = 0;
3158     player->effective_action = 0;
3159     player->programmed_action = 0;
3160
3161     player->score = 0;
3162     player->score_final = 0;
3163
3164     player->gems_still_needed = level.gems_needed;
3165     player->sokobanfields_still_needed = 0;
3166     player->lights_still_needed = 0;
3167     player->friends_still_needed = 0;
3168
3169     for (j = 0; j < MAX_NUM_KEYS; j++)
3170       player->key[j] = FALSE;
3171
3172     player->num_white_keys = 0;
3173
3174     player->dynabomb_count = 0;
3175     player->dynabomb_size = 1;
3176     player->dynabombs_left = 0;
3177     player->dynabomb_xl = FALSE;
3178
3179     player->MovDir = initial_move_dir;
3180     player->MovPos = 0;
3181     player->GfxPos = 0;
3182     player->GfxDir = initial_move_dir;
3183     player->GfxAction = ACTION_DEFAULT;
3184     player->Frame = 0;
3185     player->StepFrame = 0;
3186
3187     player->initial_element = player->element_nr;
3188     player->artwork_element =
3189       (level.use_artwork_element[i] ? level.artwork_element[i] :
3190        player->element_nr);
3191     player->use_murphy = FALSE;
3192
3193     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3194     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3195
3196     player->gravity = level.initial_player_gravity[i];
3197
3198     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3199
3200     player->actual_frame_counter = 0;
3201
3202     player->step_counter = 0;
3203
3204     player->last_move_dir = initial_move_dir;
3205
3206     player->is_active = FALSE;
3207
3208     player->is_waiting = FALSE;
3209     player->is_moving = FALSE;
3210     player->is_auto_moving = FALSE;
3211     player->is_digging = FALSE;
3212     player->is_snapping = FALSE;
3213     player->is_collecting = FALSE;
3214     player->is_pushing = FALSE;
3215     player->is_switching = FALSE;
3216     player->is_dropping = FALSE;
3217     player->is_dropping_pressed = FALSE;
3218
3219     player->is_bored = FALSE;
3220     player->is_sleeping = FALSE;
3221
3222     player->was_waiting = TRUE;
3223     player->was_moving = FALSE;
3224     player->was_snapping = FALSE;
3225     player->was_dropping = FALSE;
3226
3227     player->frame_counter_bored = -1;
3228     player->frame_counter_sleeping = -1;
3229
3230     player->anim_delay_counter = 0;
3231     player->post_delay_counter = 0;
3232
3233     player->dir_waiting = initial_move_dir;
3234     player->action_waiting = ACTION_DEFAULT;
3235     player->last_action_waiting = ACTION_DEFAULT;
3236     player->special_action_bored = ACTION_DEFAULT;
3237     player->special_action_sleeping = ACTION_DEFAULT;
3238
3239     player->switch_x = -1;
3240     player->switch_y = -1;
3241
3242     player->drop_x = -1;
3243     player->drop_y = -1;
3244
3245     player->show_envelope = 0;
3246
3247     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3248
3249     player->push_delay       = -1;      /* initialized when pushing starts */
3250     player->push_delay_value = game.initial_push_delay_value;
3251
3252     player->drop_delay = 0;
3253     player->drop_pressed_delay = 0;
3254
3255     player->last_jx = -1;
3256     player->last_jy = -1;
3257     player->jx = -1;
3258     player->jy = -1;
3259
3260     player->shield_normal_time_left = 0;
3261     player->shield_deadly_time_left = 0;
3262
3263     player->inventory_infinite_element = EL_UNDEFINED;
3264     player->inventory_size = 0;
3265
3266     if (level.use_initial_inventory[i])
3267     {
3268       for (j = 0; j < level.initial_inventory_size[i]; j++)
3269       {
3270         int element = level.initial_inventory_content[i][j];
3271         int collect_count = element_info[element].collect_count_initial;
3272         int k;
3273
3274         if (!IS_CUSTOM_ELEMENT(element))
3275           collect_count = 1;
3276
3277         if (collect_count == 0)
3278           player->inventory_infinite_element = element;
3279         else
3280           for (k = 0; k < collect_count; k++)
3281             if (player->inventory_size < MAX_INVENTORY_SIZE)
3282               player->inventory_element[player->inventory_size++] = element;
3283       }
3284     }
3285
3286     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3287     SnapField(player, 0, 0);
3288
3289     player->LevelSolved = FALSE;
3290     player->GameOver = FALSE;
3291
3292     player->LevelSolved_GameWon = FALSE;
3293     player->LevelSolved_GameEnd = FALSE;
3294     player->LevelSolved_PanelOff = FALSE;
3295     player->LevelSolved_SaveTape = FALSE;
3296     player->LevelSolved_SaveScore = FALSE;
3297     player->LevelSolved_CountingTime = 0;
3298     player->LevelSolved_CountingScore = 0;
3299
3300     map_player_action[i] = i;
3301   }
3302
3303   network_player_action_received = FALSE;
3304
3305 #if defined(NETWORK_AVALIABLE)
3306   /* initial null action */
3307   if (network_playing)
3308     SendToServer_MovePlayer(MV_NONE);
3309 #endif
3310
3311   ZX = ZY = -1;
3312   ExitX = ExitY = -1;
3313
3314   FrameCounter = 0;
3315   TimeFrames = 0;
3316   TimePlayed = 0;
3317   TimeLeft = level.time;
3318   TapeTime = 0;
3319
3320   ScreenMovDir = MV_NONE;
3321   ScreenMovPos = 0;
3322   ScreenGfxPos = 0;
3323
3324   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3325
3326   AllPlayersGone = FALSE;
3327
3328   game.no_time_limit = (level.time == 0);
3329
3330   game.yamyam_content_nr = 0;
3331   game.robot_wheel_active = FALSE;
3332   game.magic_wall_active = FALSE;
3333   game.magic_wall_time_left = 0;
3334   game.light_time_left = 0;
3335   game.timegate_time_left = 0;
3336   game.switchgate_pos = 0;
3337   game.wind_direction = level.wind_direction_initial;
3338
3339   game.lenses_time_left = 0;
3340   game.magnify_time_left = 0;
3341
3342   game.ball_state = level.ball_state_initial;
3343   game.ball_content_nr = 0;
3344
3345   game.envelope_active = FALSE;
3346
3347   /* set focus to local player for network games, else to all players */
3348   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3349   game.centered_player_nr_next = game.centered_player_nr;
3350   game.set_centered_player = FALSE;
3351
3352   if (network_playing && tape.recording)
3353   {
3354     /* store client dependent player focus when recording network games */
3355     tape.centered_player_nr_next = game.centered_player_nr_next;
3356     tape.set_centered_player = TRUE;
3357   }
3358
3359   for (i = 0; i < NUM_BELTS; i++)
3360   {
3361     game.belt_dir[i] = MV_NONE;
3362     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3363   }
3364
3365   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3366     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3367
3368 #if DEBUG_INIT_PLAYER
3369   if (options.debug)
3370   {
3371     printf("Player status at level initialization:\n");
3372   }
3373 #endif
3374
3375   SCAN_PLAYFIELD(x, y)
3376   {
3377     Feld[x][y] = level.field[x][y];
3378     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3379     ChangeDelay[x][y] = 0;
3380     ChangePage[x][y] = -1;
3381     CustomValue[x][y] = 0;              /* initialized in InitField() */
3382     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3383     AmoebaNr[x][y] = 0;
3384     WasJustMoving[x][y] = 0;
3385     WasJustFalling[x][y] = 0;
3386     CheckCollision[x][y] = 0;
3387     CheckImpact[x][y] = 0;
3388     Stop[x][y] = FALSE;
3389     Pushed[x][y] = FALSE;
3390
3391     ChangeCount[x][y] = 0;
3392     ChangeEvent[x][y] = -1;
3393
3394     ExplodePhase[x][y] = 0;
3395     ExplodeDelay[x][y] = 0;
3396     ExplodeField[x][y] = EX_TYPE_NONE;
3397
3398     RunnerVisit[x][y] = 0;
3399     PlayerVisit[x][y] = 0;
3400
3401     GfxFrame[x][y] = 0;
3402     GfxRandom[x][y] = INIT_GFX_RANDOM();
3403     GfxElement[x][y] = EL_UNDEFINED;
3404     GfxAction[x][y] = ACTION_DEFAULT;
3405     GfxDir[x][y] = MV_NONE;
3406     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3407   }
3408
3409   SCAN_PLAYFIELD(x, y)
3410   {
3411     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3412       emulate_bd = FALSE;
3413     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3414       emulate_sb = FALSE;
3415     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3416       emulate_sp = FALSE;
3417
3418     InitField(x, y, TRUE);
3419
3420     ResetGfxAnimation(x, y);
3421   }
3422
3423   InitBeltMovement();
3424
3425   for (i = 0; i < MAX_PLAYERS; i++)
3426   {
3427     struct PlayerInfo *player = &stored_player[i];
3428
3429     /* set number of special actions for bored and sleeping animation */
3430     player->num_special_action_bored =
3431       get_num_special_action(player->artwork_element,
3432                              ACTION_BORING_1, ACTION_BORING_LAST);
3433     player->num_special_action_sleeping =
3434       get_num_special_action(player->artwork_element,
3435                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3436   }
3437
3438   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3439                     emulate_sb ? EMU_SOKOBAN :
3440                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3441
3442   /* initialize type of slippery elements */
3443   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3444   {
3445     if (!IS_CUSTOM_ELEMENT(i))
3446     {
3447       /* default: elements slip down either to the left or right randomly */
3448       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3449
3450       /* SP style elements prefer to slip down on the left side */
3451       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3452         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3453
3454       /* BD style elements prefer to slip down on the left side */
3455       if (game.emulation == EMU_BOULDERDASH)
3456         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3457     }
3458   }
3459
3460   /* initialize explosion and ignition delay */
3461   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3462   {
3463     if (!IS_CUSTOM_ELEMENT(i))
3464     {
3465       int num_phase = 8;
3466       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3467                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3468                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3469       int last_phase = (num_phase + 1) * delay;
3470       int half_phase = (num_phase / 2) * delay;
3471
3472       element_info[i].explosion_delay = last_phase - 1;
3473       element_info[i].ignition_delay = half_phase;
3474
3475       if (i == EL_BLACK_ORB)
3476         element_info[i].ignition_delay = 1;
3477     }
3478   }
3479
3480   /* correct non-moving belts to start moving left */
3481   for (i = 0; i < NUM_BELTS; i++)
3482     if (game.belt_dir[i] == MV_NONE)
3483       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3484
3485 #if USE_NEW_PLAYER_ASSIGNMENTS
3486   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3487   /* choose default local player */
3488   local_player = &stored_player[0];
3489
3490   for (i = 0; i < MAX_PLAYERS; i++)
3491     stored_player[i].connected = FALSE;
3492
3493   local_player->connected = TRUE;
3494   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3495
3496   if (tape.playing)
3497   {
3498     for (i = 0; i < MAX_PLAYERS; i++)
3499       stored_player[i].connected = tape.player_participates[i];
3500   }
3501   else if (game.team_mode && !options.network)
3502   {
3503     /* try to guess locally connected team mode players (needed for correct
3504        assignment of player figures from level to locally playing players) */
3505
3506     for (i = 0; i < MAX_PLAYERS; i++)
3507       if (setup.input[i].use_joystick ||
3508           setup.input[i].key.left != KSYM_UNDEFINED)
3509         stored_player[i].connected = TRUE;
3510   }
3511
3512 #if DEBUG_INIT_PLAYER
3513   if (options.debug)
3514   {
3515     printf("Player status after level initialization:\n");
3516
3517     for (i = 0; i < MAX_PLAYERS; i++)
3518     {
3519       struct PlayerInfo *player = &stored_player[i];
3520
3521       printf("- player %d: present == %d, connected == %d, active == %d",
3522              i + 1,
3523              player->present,
3524              player->connected,
3525              player->active);
3526
3527       if (local_player == player)
3528         printf(" (local player)");
3529
3530       printf("\n");
3531     }
3532   }
3533 #endif
3534
3535 #if DEBUG_INIT_PLAYER
3536   if (options.debug)
3537     printf("Reassigning players ...\n");
3538 #endif
3539
3540   /* check if any connected player was not found in playfield */
3541   for (i = 0; i < MAX_PLAYERS; i++)
3542   {
3543     struct PlayerInfo *player = &stored_player[i];
3544
3545     if (player->connected && !player->present)
3546     {
3547       struct PlayerInfo *field_player = NULL;
3548
3549 #if DEBUG_INIT_PLAYER
3550       if (options.debug)
3551         printf("- looking for field player for player %d ...\n", i + 1);
3552 #endif
3553
3554       /* assign first free player found that is present in the playfield */
3555
3556       /* first try: look for unmapped playfield player that is not connected */
3557       for (j = 0; j < MAX_PLAYERS; j++)
3558         if (field_player == NULL &&
3559             stored_player[j].present &&
3560             !stored_player[j].mapped &&
3561             !stored_player[j].connected)
3562           field_player = &stored_player[j];
3563
3564       /* second try: look for *any* unmapped playfield player */
3565       for (j = 0; j < MAX_PLAYERS; j++)
3566         if (field_player == NULL &&
3567             stored_player[j].present &&
3568             !stored_player[j].mapped)
3569           field_player = &stored_player[j];
3570
3571       if (field_player != NULL)
3572       {
3573         int jx = field_player->jx, jy = field_player->jy;
3574
3575 #if DEBUG_INIT_PLAYER
3576         if (options.debug)
3577           printf("- found player %d\n", field_player->index_nr + 1);
3578 #endif
3579
3580         player->present = FALSE;
3581         player->active = FALSE;
3582
3583         field_player->present = TRUE;
3584         field_player->active = TRUE;
3585
3586         /*
3587         player->initial_element = field_player->initial_element;
3588         player->artwork_element = field_player->artwork_element;
3589
3590         player->block_last_field       = field_player->block_last_field;
3591         player->block_delay_adjustment = field_player->block_delay_adjustment;
3592         */
3593
3594         StorePlayer[jx][jy] = field_player->element_nr;
3595
3596         field_player->jx = field_player->last_jx = jx;
3597         field_player->jy = field_player->last_jy = jy;
3598
3599         if (local_player == player)
3600           local_player = field_player;
3601
3602         map_player_action[field_player->index_nr] = i;
3603
3604         field_player->mapped = TRUE;
3605
3606 #if DEBUG_INIT_PLAYER
3607         if (options.debug)
3608           printf("- map_player_action[%d] == %d\n",
3609                  field_player->index_nr + 1, i + 1);
3610 #endif
3611       }
3612     }
3613
3614     if (player->connected && player->present)
3615       player->mapped = TRUE;
3616   }
3617
3618 #if DEBUG_INIT_PLAYER
3619   if (options.debug)
3620   {
3621     printf("Player status after player assignment (first stage):\n");
3622
3623     for (i = 0; i < MAX_PLAYERS; i++)
3624     {
3625       struct PlayerInfo *player = &stored_player[i];
3626
3627       printf("- player %d: present == %d, connected == %d, active == %d",
3628              i + 1,
3629              player->present,
3630              player->connected,
3631              player->active);
3632
3633       if (local_player == player)
3634         printf(" (local player)");
3635
3636       printf("\n");
3637     }
3638   }
3639 #endif
3640
3641 #else
3642
3643   /* check if any connected player was not found in playfield */
3644   for (i = 0; i < MAX_PLAYERS; i++)
3645   {
3646     struct PlayerInfo *player = &stored_player[i];
3647
3648     if (player->connected && !player->present)
3649     {
3650       for (j = 0; j < MAX_PLAYERS; j++)
3651       {
3652         struct PlayerInfo *field_player = &stored_player[j];
3653         int jx = field_player->jx, jy = field_player->jy;
3654
3655         /* assign first free player found that is present in the playfield */
3656         if (field_player->present && !field_player->connected)
3657         {
3658           player->present = TRUE;
3659           player->active = TRUE;
3660
3661           field_player->present = FALSE;
3662           field_player->active = FALSE;
3663
3664           player->initial_element = field_player->initial_element;
3665           player->artwork_element = field_player->artwork_element;
3666
3667           player->block_last_field       = field_player->block_last_field;
3668           player->block_delay_adjustment = field_player->block_delay_adjustment;
3669
3670           StorePlayer[jx][jy] = player->element_nr;
3671
3672           player->jx = player->last_jx = jx;
3673           player->jy = player->last_jy = jy;
3674
3675           break;
3676         }
3677       }
3678     }
3679   }
3680 #endif
3681
3682 #if 0
3683   printf("::: local_player->present == %d\n", local_player->present);
3684 #endif
3685
3686   if (tape.playing)
3687   {
3688     /* when playing a tape, eliminate all players who do not participate */
3689
3690 #if USE_NEW_PLAYER_ASSIGNMENTS
3691
3692     if (!game.team_mode)
3693     {
3694       for (i = 0; i < MAX_PLAYERS; i++)
3695       {
3696         if (stored_player[i].active &&
3697             !tape.player_participates[map_player_action[i]])
3698         {
3699           struct PlayerInfo *player = &stored_player[i];
3700           int jx = player->jx, jy = player->jy;
3701
3702 #if DEBUG_INIT_PLAYER
3703           if (options.debug)
3704             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3705 #endif
3706
3707           player->active = FALSE;
3708           StorePlayer[jx][jy] = 0;
3709           Feld[jx][jy] = EL_EMPTY;
3710         }
3711       }
3712     }
3713
3714 #else
3715
3716     for (i = 0; i < MAX_PLAYERS; i++)
3717     {
3718       if (stored_player[i].active &&
3719           !tape.player_participates[i])
3720       {
3721         struct PlayerInfo *player = &stored_player[i];
3722         int jx = player->jx, jy = player->jy;
3723
3724         player->active = FALSE;
3725         StorePlayer[jx][jy] = 0;
3726         Feld[jx][jy] = EL_EMPTY;
3727       }
3728     }
3729 #endif
3730   }
3731   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3732   {
3733     /* when in single player mode, eliminate all but the first active player */
3734
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736     {
3737       if (stored_player[i].active)
3738       {
3739         for (j = i + 1; j < MAX_PLAYERS; j++)
3740         {
3741           if (stored_player[j].active)
3742           {
3743             struct PlayerInfo *player = &stored_player[j];
3744             int jx = player->jx, jy = player->jy;
3745
3746             player->active = FALSE;
3747             player->present = FALSE;
3748
3749             StorePlayer[jx][jy] = 0;
3750             Feld[jx][jy] = EL_EMPTY;
3751           }
3752         }
3753       }
3754     }
3755   }
3756
3757   /* when recording the game, store which players take part in the game */
3758   if (tape.recording)
3759   {
3760 #if USE_NEW_PLAYER_ASSIGNMENTS
3761     for (i = 0; i < MAX_PLAYERS; i++)
3762       if (stored_player[i].connected)
3763         tape.player_participates[i] = TRUE;
3764 #else
3765     for (i = 0; i < MAX_PLAYERS; i++)
3766       if (stored_player[i].active)
3767         tape.player_participates[i] = TRUE;
3768 #endif
3769   }
3770
3771 #if DEBUG_INIT_PLAYER
3772   if (options.debug)
3773   {
3774     printf("Player status after player assignment (final stage):\n");
3775
3776     for (i = 0; i < MAX_PLAYERS; i++)
3777     {
3778       struct PlayerInfo *player = &stored_player[i];
3779
3780       printf("- player %d: present == %d, connected == %d, active == %d",
3781              i + 1,
3782              player->present,
3783              player->connected,
3784              player->active);
3785
3786       if (local_player == player)
3787         printf(" (local player)");
3788
3789       printf("\n");
3790     }
3791   }
3792 #endif
3793
3794   if (BorderElement == EL_EMPTY)
3795   {
3796     SBX_Left = 0;
3797     SBX_Right = lev_fieldx - SCR_FIELDX;
3798     SBY_Upper = 0;
3799     SBY_Lower = lev_fieldy - SCR_FIELDY;
3800   }
3801   else
3802   {
3803     SBX_Left = -1;
3804     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3805     SBY_Upper = -1;
3806     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3807   }
3808
3809   if (full_lev_fieldx <= SCR_FIELDX)
3810     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3811   if (full_lev_fieldy <= SCR_FIELDY)
3812     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3813
3814   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3815     SBX_Left--;
3816   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3817     SBY_Upper--;
3818
3819   /* if local player not found, look for custom element that might create
3820      the player (make some assumptions about the right custom element) */
3821   if (!local_player->present)
3822   {
3823     int start_x = 0, start_y = 0;
3824     int found_rating = 0;
3825     int found_element = EL_UNDEFINED;
3826     int player_nr = local_player->index_nr;
3827
3828     SCAN_PLAYFIELD(x, y)
3829     {
3830       int element = Feld[x][y];
3831       int content;
3832       int xx, yy;
3833       boolean is_player;
3834
3835       if (level.use_start_element[player_nr] &&
3836           level.start_element[player_nr] == element &&
3837           found_rating < 4)
3838       {
3839         start_x = x;
3840         start_y = y;
3841
3842         found_rating = 4;
3843         found_element = element;
3844       }
3845
3846       if (!IS_CUSTOM_ELEMENT(element))
3847         continue;
3848
3849       if (CAN_CHANGE(element))
3850       {
3851         for (i = 0; i < element_info[element].num_change_pages; i++)
3852         {
3853           /* check for player created from custom element as single target */
3854           content = element_info[element].change_page[i].target_element;
3855           is_player = ELEM_IS_PLAYER(content);
3856
3857           if (is_player && (found_rating < 3 ||
3858                             (found_rating == 3 && element < found_element)))
3859           {
3860             start_x = x;
3861             start_y = y;
3862
3863             found_rating = 3;
3864             found_element = element;
3865           }
3866         }
3867       }
3868
3869       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3870       {
3871         /* check for player created from custom element as explosion content */
3872         content = element_info[element].content.e[xx][yy];
3873         is_player = ELEM_IS_PLAYER(content);
3874
3875         if (is_player && (found_rating < 2 ||
3876                           (found_rating == 2 && element < found_element)))
3877         {
3878           start_x = x + xx - 1;
3879           start_y = y + yy - 1;
3880
3881           found_rating = 2;
3882           found_element = element;
3883         }
3884
3885         if (!CAN_CHANGE(element))
3886           continue;
3887
3888         for (i = 0; i < element_info[element].num_change_pages; i++)
3889         {
3890           /* check for player created from custom element as extended target */
3891           content =
3892             element_info[element].change_page[i].target_content.e[xx][yy];
3893
3894           is_player = ELEM_IS_PLAYER(content);
3895
3896           if (is_player && (found_rating < 1 ||
3897                             (found_rating == 1 && element < found_element)))
3898           {
3899             start_x = x + xx - 1;
3900             start_y = y + yy - 1;
3901
3902             found_rating = 1;
3903             found_element = element;
3904           }
3905         }
3906       }
3907     }
3908
3909     scroll_x = SCROLL_POSITION_X(start_x);
3910     scroll_y = SCROLL_POSITION_Y(start_y);
3911   }
3912   else
3913   {
3914     scroll_x = SCROLL_POSITION_X(local_player->jx);
3915     scroll_y = SCROLL_POSITION_Y(local_player->jy);
3916   }
3917
3918   /* !!! FIX THIS (START) !!! */
3919   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3920   {
3921     InitGameEngine_EM();
3922   }
3923   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3924   {
3925     InitGameEngine_SP();
3926   }
3927   else
3928   {
3929     DrawLevel(REDRAW_FIELD);
3930     DrawAllPlayers();
3931
3932     /* after drawing the level, correct some elements */
3933     if (game.timegate_time_left == 0)
3934       CloseAllOpenTimegates();
3935   }
3936
3937   /* blit playfield from scroll buffer to normal back buffer for fading in */
3938   BlitScreenToBitmap(backbuffer);
3939   /* !!! FIX THIS (END) !!! */
3940
3941   DrawMaskedBorder(fade_mask);
3942
3943   FadeIn(fade_mask);
3944
3945 #if 1
3946   // full screen redraw is required at this point in the following cases:
3947   // - special editor door undrawn when game was started from level editor
3948   // - drawing area (playfield) was changed and has to be removed completely
3949   redraw_mask = REDRAW_ALL;
3950   BackToFront();
3951 #endif
3952
3953   if (!game.restart_level)
3954   {
3955     /* copy default game door content to main double buffer */
3956
3957     /* !!! CHECK AGAIN !!! */
3958     SetPanelBackground();
3959     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3960     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3961   }
3962
3963   SetPanelBackground();
3964   SetDrawBackgroundMask(REDRAW_DOOR_1);
3965
3966   UpdateAndDisplayGameControlValues();
3967
3968   if (!game.restart_level)
3969   {
3970     UnmapGameButtons();
3971     UnmapTapeButtons();
3972
3973     FreeGameButtons();
3974     CreateGameButtons();
3975
3976     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3977     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3978     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3979
3980     MapGameButtons();
3981     MapTapeButtons();
3982
3983     /* copy actual game door content to door double buffer for OpenDoor() */
3984     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3985
3986     OpenDoor(DOOR_OPEN_ALL);
3987
3988     PlaySound(SND_GAME_STARTING);
3989
3990     if (setup.sound_music)
3991       PlayLevelMusic();
3992
3993     KeyboardAutoRepeatOffUnlessAutoplay();
3994
3995 #if DEBUG_INIT_PLAYER
3996     if (options.debug)
3997     {
3998       printf("Player status (final):\n");
3999
4000       for (i = 0; i < MAX_PLAYERS; i++)
4001       {
4002         struct PlayerInfo *player = &stored_player[i];
4003
4004         printf("- player %d: present == %d, connected == %d, active == %d",
4005                i + 1,
4006                player->present,
4007                player->connected,
4008                player->active);
4009
4010         if (local_player == player)
4011           printf(" (local player)");
4012
4013         printf("\n");
4014       }
4015     }
4016 #endif
4017   }
4018
4019   UnmapAllGadgets();
4020
4021   MapGameButtons();
4022   MapTapeButtons();
4023
4024   if (!game.restart_level && !tape.playing)
4025   {
4026     LevelStats_incPlayed(level_nr);
4027
4028     SaveLevelSetup_SeriesInfo();
4029   }
4030
4031   game.restart_level = FALSE;
4032
4033   SaveEngineSnapshotToListInitial();
4034 }
4035
4036 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4037                         int actual_player_x, int actual_player_y)
4038 {
4039   /* this is used for non-R'n'D game engines to update certain engine values */
4040
4041   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4042   {
4043     actual_player_x = correctLevelPosX_EM(actual_player_x);
4044     actual_player_y = correctLevelPosY_EM(actual_player_y);
4045   }
4046
4047   /* needed to determine if sounds are played within the visible screen area */
4048   scroll_x = actual_scroll_x;
4049   scroll_y = actual_scroll_y;
4050
4051   /* needed to get player position for "follow finger" playing input method */
4052   local_player->jx = actual_player_x;
4053   local_player->jy = actual_player_y;
4054 }
4055
4056 void InitMovDir(int x, int y)
4057 {
4058   int i, element = Feld[x][y];
4059   static int xy[4][2] =
4060   {
4061     {  0, +1 },
4062     { +1,  0 },
4063     {  0, -1 },
4064     { -1,  0 }
4065   };
4066   static int direction[3][4] =
4067   {
4068     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4069     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4070     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4071   };
4072
4073   switch (element)
4074   {
4075     case EL_BUG_RIGHT:
4076     case EL_BUG_UP:
4077     case EL_BUG_LEFT:
4078     case EL_BUG_DOWN:
4079       Feld[x][y] = EL_BUG;
4080       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4081       break;
4082
4083     case EL_SPACESHIP_RIGHT:
4084     case EL_SPACESHIP_UP:
4085     case EL_SPACESHIP_LEFT:
4086     case EL_SPACESHIP_DOWN:
4087       Feld[x][y] = EL_SPACESHIP;
4088       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4089       break;
4090
4091     case EL_BD_BUTTERFLY_RIGHT:
4092     case EL_BD_BUTTERFLY_UP:
4093     case EL_BD_BUTTERFLY_LEFT:
4094     case EL_BD_BUTTERFLY_DOWN:
4095       Feld[x][y] = EL_BD_BUTTERFLY;
4096       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4097       break;
4098
4099     case EL_BD_FIREFLY_RIGHT:
4100     case EL_BD_FIREFLY_UP:
4101     case EL_BD_FIREFLY_LEFT:
4102     case EL_BD_FIREFLY_DOWN:
4103       Feld[x][y] = EL_BD_FIREFLY;
4104       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4105       break;
4106
4107     case EL_PACMAN_RIGHT:
4108     case EL_PACMAN_UP:
4109     case EL_PACMAN_LEFT:
4110     case EL_PACMAN_DOWN:
4111       Feld[x][y] = EL_PACMAN;
4112       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4113       break;
4114
4115     case EL_YAMYAM_LEFT:
4116     case EL_YAMYAM_RIGHT:
4117     case EL_YAMYAM_UP:
4118     case EL_YAMYAM_DOWN:
4119       Feld[x][y] = EL_YAMYAM;
4120       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4121       break;
4122
4123     case EL_SP_SNIKSNAK:
4124       MovDir[x][y] = MV_UP;
4125       break;
4126
4127     case EL_SP_ELECTRON:
4128       MovDir[x][y] = MV_LEFT;
4129       break;
4130
4131     case EL_MOLE_LEFT:
4132     case EL_MOLE_RIGHT:
4133     case EL_MOLE_UP:
4134     case EL_MOLE_DOWN:
4135       Feld[x][y] = EL_MOLE;
4136       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4137       break;
4138
4139     default:
4140       if (IS_CUSTOM_ELEMENT(element))
4141       {
4142         struct ElementInfo *ei = &element_info[element];
4143         int move_direction_initial = ei->move_direction_initial;
4144         int move_pattern = ei->move_pattern;
4145
4146         if (move_direction_initial == MV_START_PREVIOUS)
4147         {
4148           if (MovDir[x][y] != MV_NONE)
4149             return;
4150
4151           move_direction_initial = MV_START_AUTOMATIC;
4152         }
4153
4154         if (move_direction_initial == MV_START_RANDOM)
4155           MovDir[x][y] = 1 << RND(4);
4156         else if (move_direction_initial & MV_ANY_DIRECTION)
4157           MovDir[x][y] = move_direction_initial;
4158         else if (move_pattern == MV_ALL_DIRECTIONS ||
4159                  move_pattern == MV_TURNING_LEFT ||
4160                  move_pattern == MV_TURNING_RIGHT ||
4161                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4162                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4163                  move_pattern == MV_TURNING_RANDOM)
4164           MovDir[x][y] = 1 << RND(4);
4165         else if (move_pattern == MV_HORIZONTAL)
4166           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4167         else if (move_pattern == MV_VERTICAL)
4168           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4169         else if (move_pattern & MV_ANY_DIRECTION)
4170           MovDir[x][y] = element_info[element].move_pattern;
4171         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4172                  move_pattern == MV_ALONG_RIGHT_SIDE)
4173         {
4174           /* use random direction as default start direction */
4175           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4176             MovDir[x][y] = 1 << RND(4);
4177
4178           for (i = 0; i < NUM_DIRECTIONS; i++)
4179           {
4180             int x1 = x + xy[i][0];
4181             int y1 = y + xy[i][1];
4182
4183             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4184             {
4185               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4186                 MovDir[x][y] = direction[0][i];
4187               else
4188                 MovDir[x][y] = direction[1][i];
4189
4190               break;
4191             }
4192           }
4193         }                
4194       }
4195       else
4196       {
4197         MovDir[x][y] = 1 << RND(4);
4198
4199         if (element != EL_BUG &&
4200             element != EL_SPACESHIP &&
4201             element != EL_BD_BUTTERFLY &&
4202             element != EL_BD_FIREFLY)
4203           break;
4204
4205         for (i = 0; i < NUM_DIRECTIONS; i++)
4206         {
4207           int x1 = x + xy[i][0];
4208           int y1 = y + xy[i][1];
4209
4210           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4211           {
4212             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4213             {
4214               MovDir[x][y] = direction[0][i];
4215               break;
4216             }
4217             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4218                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4219             {
4220               MovDir[x][y] = direction[1][i];
4221               break;
4222             }
4223           }
4224         }
4225       }
4226       break;
4227   }
4228
4229   GfxDir[x][y] = MovDir[x][y];
4230 }
4231
4232 void InitAmoebaNr(int x, int y)
4233 {
4234   int i;
4235   int group_nr = AmoebeNachbarNr(x, y);
4236
4237   if (group_nr == 0)
4238   {
4239     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4240     {
4241       if (AmoebaCnt[i] == 0)
4242       {
4243         group_nr = i;
4244         break;
4245       }
4246     }
4247   }
4248
4249   AmoebaNr[x][y] = group_nr;
4250   AmoebaCnt[group_nr]++;
4251   AmoebaCnt2[group_nr]++;
4252 }
4253
4254 static void PlayerWins(struct PlayerInfo *player)
4255 {
4256   player->LevelSolved = TRUE;
4257   player->GameOver = TRUE;
4258
4259   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4260                          level.native_em_level->lev->score : player->score);
4261
4262   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4263                                       TimeLeft);
4264   player->LevelSolved_CountingScore = player->score_final;
4265 }
4266
4267 void GameWon()
4268 {
4269   static int time, time_final;
4270   static int score, score_final;
4271   static int game_over_delay_1 = 0;
4272   static int game_over_delay_2 = 0;
4273   int game_over_delay_value_1 = 50;
4274   int game_over_delay_value_2 = 50;
4275
4276   if (!local_player->LevelSolved_GameWon)
4277   {
4278     int i;
4279
4280     /* do not start end game actions before the player stops moving (to exit) */
4281     if (local_player->MovPos)
4282       return;
4283
4284     local_player->LevelSolved_GameWon = TRUE;
4285     local_player->LevelSolved_SaveTape = tape.recording;
4286     local_player->LevelSolved_SaveScore = !tape.playing;
4287
4288     if (!tape.playing)
4289     {
4290       LevelStats_incSolved(level_nr);
4291
4292       SaveLevelSetup_SeriesInfo();
4293     }
4294
4295     if (tape.auto_play)         /* tape might already be stopped here */
4296       tape.auto_play_level_solved = TRUE;
4297
4298     TapeStop();
4299
4300     game_over_delay_1 = game_over_delay_value_1;
4301     game_over_delay_2 = game_over_delay_value_2;
4302
4303     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4304     score = score_final = local_player->score_final;
4305
4306     if (TimeLeft > 0)
4307     {
4308       time_final = 0;
4309       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4310     }
4311     else if (game.no_time_limit && TimePlayed < 999)
4312     {
4313       time_final = 999;
4314       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4315     }
4316
4317     local_player->score_final = score_final;
4318
4319     if (level_editor_test_game)
4320     {
4321       time = time_final;
4322       score = score_final;
4323
4324       local_player->LevelSolved_CountingTime = time;
4325       local_player->LevelSolved_CountingScore = score;
4326
4327       game_panel_controls[GAME_PANEL_TIME].value = time;
4328       game_panel_controls[GAME_PANEL_SCORE].value = score;
4329
4330       DisplayGameControlValues();
4331     }
4332
4333     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4334     {
4335       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4336       {
4337         /* close exit door after last player */
4338         if ((AllPlayersGone &&
4339              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4340               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4341               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4342             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4343             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4344         {
4345           int element = Feld[ExitX][ExitY];
4346
4347           Feld[ExitX][ExitY] =
4348             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4349              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4350              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4351              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4352              EL_EM_STEEL_EXIT_CLOSING);
4353
4354           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4355         }
4356
4357         /* player disappears */
4358         DrawLevelField(ExitX, ExitY);
4359       }
4360
4361       for (i = 0; i < MAX_PLAYERS; i++)
4362       {
4363         struct PlayerInfo *player = &stored_player[i];
4364
4365         if (player->present)
4366         {
4367           RemovePlayer(player);
4368
4369           /* player disappears */
4370           DrawLevelField(player->jx, player->jy);
4371         }
4372       }
4373     }
4374
4375     PlaySound(SND_GAME_WINNING);
4376   }
4377
4378   if (game_over_delay_1 > 0)
4379   {
4380     game_over_delay_1--;
4381
4382     return;
4383   }
4384
4385   if (time != time_final)
4386   {
4387     int time_to_go = ABS(time_final - time);
4388     int time_count_dir = (time < time_final ? +1 : -1);
4389     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4390
4391     time  += time_count_steps * time_count_dir;
4392     score += time_count_steps * level.score[SC_TIME_BONUS];
4393
4394     local_player->LevelSolved_CountingTime = time;
4395     local_player->LevelSolved_CountingScore = score;
4396
4397     game_panel_controls[GAME_PANEL_TIME].value = time;
4398     game_panel_controls[GAME_PANEL_SCORE].value = score;
4399
4400     DisplayGameControlValues();
4401
4402     if (time == time_final)
4403       StopSound(SND_GAME_LEVELTIME_BONUS);
4404     else if (setup.sound_loops)
4405       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4406     else
4407       PlaySound(SND_GAME_LEVELTIME_BONUS);
4408
4409     return;
4410   }
4411
4412   local_player->LevelSolved_PanelOff = TRUE;
4413
4414   if (game_over_delay_2 > 0)
4415   {
4416     game_over_delay_2--;
4417
4418     return;
4419   }
4420
4421   GameEnd();
4422 }
4423
4424 void GameEnd()
4425 {
4426   int hi_pos;
4427   boolean raise_level = FALSE;
4428
4429   local_player->LevelSolved_GameEnd = TRUE;
4430
4431   if (!global.use_envelope_request)
4432     CloseDoor(DOOR_CLOSE_1);
4433
4434   if (local_player->LevelSolved_SaveTape)
4435   {
4436     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4437   }
4438
4439   CloseDoor(DOOR_CLOSE_ALL);
4440
4441   if (level_editor_test_game)
4442   {
4443     SetGameStatus(GAME_MODE_MAIN);
4444
4445     DrawMainMenu();
4446
4447     return;
4448   }
4449
4450   if (!local_player->LevelSolved_SaveScore)
4451   {
4452     SetGameStatus(GAME_MODE_MAIN);
4453
4454     DrawMainMenu();
4455
4456     return;
4457   }
4458
4459   if (level_nr == leveldir_current->handicap_level)
4460   {
4461     leveldir_current->handicap_level++;
4462
4463     SaveLevelSetup_SeriesInfo();
4464   }
4465
4466   if (setup.increment_levels &&
4467       level_nr < leveldir_current->last_level)
4468     raise_level = TRUE;                 /* advance to next level */
4469
4470   if ((hi_pos = NewHiScore()) >= 0) 
4471   {
4472     SetGameStatus(GAME_MODE_SCORES);
4473
4474     DrawHallOfFame(hi_pos);
4475
4476     if (raise_level)
4477     {
4478       level_nr++;
4479       TapeErase();
4480     }
4481   }
4482   else
4483   {
4484     SetGameStatus(GAME_MODE_MAIN);
4485
4486     if (raise_level)
4487     {
4488       level_nr++;
4489       TapeErase();
4490     }
4491
4492     DrawMainMenu();
4493   }
4494 }
4495
4496 int NewHiScore()
4497 {
4498   int k, l;
4499   int position = -1;
4500   boolean one_score_entry_per_name = !program.many_scores_per_name;
4501
4502   LoadScore(level_nr);
4503
4504   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4505       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4506     return -1;
4507
4508   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4509   {
4510     if (local_player->score_final > highscore[k].Score)
4511     {
4512       /* player has made it to the hall of fame */
4513
4514       if (k < MAX_SCORE_ENTRIES - 1)
4515       {
4516         int m = MAX_SCORE_ENTRIES - 1;
4517
4518         if (one_score_entry_per_name)
4519         {
4520           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4521             if (strEqual(setup.player_name, highscore[l].Name))
4522               m = l;
4523
4524           if (m == k)   /* player's new highscore overwrites his old one */
4525             goto put_into_list;
4526         }
4527
4528         for (l = m; l > k; l--)
4529         {
4530           strcpy(highscore[l].Name, highscore[l - 1].Name);
4531           highscore[l].Score = highscore[l - 1].Score;
4532         }
4533       }
4534
4535       put_into_list:
4536
4537       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4538       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4539       highscore[k].Score = local_player->score_final; 
4540       position = k;
4541
4542       break;
4543     }
4544     else if (one_score_entry_per_name &&
4545              !strncmp(setup.player_name, highscore[k].Name,
4546                       MAX_PLAYER_NAME_LEN))
4547       break;    /* player already there with a higher score */
4548   }
4549
4550   if (position >= 0) 
4551     SaveScore(level_nr);
4552
4553   return position;
4554 }
4555
4556 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4557 {
4558   int element = Feld[x][y];
4559   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4560   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4561   int horiz_move = (dx != 0);
4562   int sign = (horiz_move ? dx : dy);
4563   int step = sign * element_info[element].move_stepsize;
4564
4565   /* special values for move stepsize for spring and things on conveyor belt */
4566   if (horiz_move)
4567   {
4568     if (CAN_FALL(element) &&
4569         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4570       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4571     else if (element == EL_SPRING)
4572       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4573   }
4574
4575   return step;
4576 }
4577
4578 inline static int getElementMoveStepsize(int x, int y)
4579 {
4580   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4581 }
4582
4583 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4584 {
4585   if (player->GfxAction != action || player->GfxDir != dir)
4586   {
4587     player->GfxAction = action;
4588     player->GfxDir = dir;
4589     player->Frame = 0;
4590     player->StepFrame = 0;
4591   }
4592 }
4593
4594 static void ResetGfxFrame(int x, int y)
4595 {
4596   int element = Feld[x][y];
4597   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4598
4599   if (graphic_info[graphic].anim_global_sync)
4600     GfxFrame[x][y] = FrameCounter;
4601   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4602     GfxFrame[x][y] = CustomValue[x][y];
4603   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4604     GfxFrame[x][y] = element_info[element].collect_score;
4605   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4606     GfxFrame[x][y] = ChangeDelay[x][y];
4607 }
4608
4609 static void ResetGfxAnimation(int x, int y)
4610 {
4611   GfxAction[x][y] = ACTION_DEFAULT;
4612   GfxDir[x][y] = MovDir[x][y];
4613   GfxFrame[x][y] = 0;
4614
4615   ResetGfxFrame(x, y);
4616 }
4617
4618 static void ResetRandomAnimationValue(int x, int y)
4619 {
4620   GfxRandom[x][y] = INIT_GFX_RANDOM();
4621 }
4622
4623 void InitMovingField(int x, int y, int direction)
4624 {
4625   int element = Feld[x][y];
4626   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4627   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4628   int newx = x + dx;
4629   int newy = y + dy;
4630   boolean is_moving_before, is_moving_after;
4631
4632   /* check if element was/is moving or being moved before/after mode change */
4633   is_moving_before = (WasJustMoving[x][y] != 0);
4634   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4635
4636   /* reset animation only for moving elements which change direction of moving
4637      or which just started or stopped moving
4638      (else CEs with property "can move" / "not moving" are reset each frame) */
4639   if (is_moving_before != is_moving_after ||
4640       direction != MovDir[x][y])
4641     ResetGfxAnimation(x, y);
4642
4643   MovDir[x][y] = direction;
4644   GfxDir[x][y] = direction;
4645
4646   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4647                      direction == MV_DOWN && CAN_FALL(element) ?
4648                      ACTION_FALLING : ACTION_MOVING);
4649
4650   /* this is needed for CEs with property "can move" / "not moving" */
4651
4652   if (is_moving_after)
4653   {
4654     if (Feld[newx][newy] == EL_EMPTY)
4655       Feld[newx][newy] = EL_BLOCKED;
4656
4657     MovDir[newx][newy] = MovDir[x][y];
4658
4659     CustomValue[newx][newy] = CustomValue[x][y];
4660
4661     GfxFrame[newx][newy] = GfxFrame[x][y];
4662     GfxRandom[newx][newy] = GfxRandom[x][y];
4663     GfxAction[newx][newy] = GfxAction[x][y];
4664     GfxDir[newx][newy] = GfxDir[x][y];
4665   }
4666 }
4667
4668 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4669 {
4670   int direction = MovDir[x][y];
4671   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4672   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4673
4674   *goes_to_x = newx;
4675   *goes_to_y = newy;
4676 }
4677
4678 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4679 {
4680   int oldx = x, oldy = y;
4681   int direction = MovDir[x][y];
4682
4683   if (direction == MV_LEFT)
4684     oldx++;
4685   else if (direction == MV_RIGHT)
4686     oldx--;
4687   else if (direction == MV_UP)
4688     oldy++;
4689   else if (direction == MV_DOWN)
4690     oldy--;
4691
4692   *comes_from_x = oldx;
4693   *comes_from_y = oldy;
4694 }
4695
4696 int MovingOrBlocked2Element(int x, int y)
4697 {
4698   int element = Feld[x][y];
4699
4700   if (element == EL_BLOCKED)
4701   {
4702     int oldx, oldy;
4703
4704     Blocked2Moving(x, y, &oldx, &oldy);
4705     return Feld[oldx][oldy];
4706   }
4707   else
4708     return element;
4709 }
4710
4711 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4712 {
4713   /* like MovingOrBlocked2Element(), but if element is moving
4714      and (x,y) is the field the moving element is just leaving,
4715      return EL_BLOCKED instead of the element value */
4716   int element = Feld[x][y];
4717
4718   if (IS_MOVING(x, y))
4719   {
4720     if (element == EL_BLOCKED)
4721     {
4722       int oldx, oldy;
4723
4724       Blocked2Moving(x, y, &oldx, &oldy);
4725       return Feld[oldx][oldy];
4726     }
4727     else
4728       return EL_BLOCKED;
4729   }
4730   else
4731     return element;
4732 }
4733
4734 static void RemoveField(int x, int y)
4735 {
4736   Feld[x][y] = EL_EMPTY;
4737
4738   MovPos[x][y] = 0;
4739   MovDir[x][y] = 0;
4740   MovDelay[x][y] = 0;
4741
4742   CustomValue[x][y] = 0;
4743
4744   AmoebaNr[x][y] = 0;
4745   ChangeDelay[x][y] = 0;
4746   ChangePage[x][y] = -1;
4747   Pushed[x][y] = FALSE;
4748
4749   GfxElement[x][y] = EL_UNDEFINED;
4750   GfxAction[x][y] = ACTION_DEFAULT;
4751   GfxDir[x][y] = MV_NONE;
4752 }
4753
4754 void RemoveMovingField(int x, int y)
4755 {
4756   int oldx = x, oldy = y, newx = x, newy = y;
4757   int element = Feld[x][y];
4758   int next_element = EL_UNDEFINED;
4759
4760   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4761     return;
4762
4763   if (IS_MOVING(x, y))
4764   {
4765     Moving2Blocked(x, y, &newx, &newy);
4766
4767     if (Feld[newx][newy] != EL_BLOCKED)
4768     {
4769       /* element is moving, but target field is not free (blocked), but
4770          already occupied by something different (example: acid pool);
4771          in this case, only remove the moving field, but not the target */
4772
4773       RemoveField(oldx, oldy);
4774
4775       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4776
4777       TEST_DrawLevelField(oldx, oldy);
4778
4779       return;
4780     }
4781   }
4782   else if (element == EL_BLOCKED)
4783   {
4784     Blocked2Moving(x, y, &oldx, &oldy);
4785     if (!IS_MOVING(oldx, oldy))
4786       return;
4787   }
4788
4789   if (element == EL_BLOCKED &&
4790       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4791        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4792        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4793        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4794        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4795        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4796     next_element = get_next_element(Feld[oldx][oldy]);
4797
4798   RemoveField(oldx, oldy);
4799   RemoveField(newx, newy);
4800
4801   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4802
4803   if (next_element != EL_UNDEFINED)
4804     Feld[oldx][oldy] = next_element;
4805
4806   TEST_DrawLevelField(oldx, oldy);
4807   TEST_DrawLevelField(newx, newy);
4808 }
4809
4810 void DrawDynamite(int x, int y)
4811 {
4812   int sx = SCREENX(x), sy = SCREENY(y);
4813   int graphic = el2img(Feld[x][y]);
4814   int frame;
4815
4816   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4817     return;
4818
4819   if (IS_WALKABLE_INSIDE(Back[x][y]))
4820     return;
4821
4822   if (Back[x][y])
4823     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4824   else if (Store[x][y])
4825     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4826
4827   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4828
4829   if (Back[x][y] || Store[x][y])
4830     DrawGraphicThruMask(sx, sy, graphic, frame);
4831   else
4832     DrawGraphic(sx, sy, graphic, frame);
4833 }
4834
4835 void CheckDynamite(int x, int y)
4836 {
4837   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4838   {
4839     MovDelay[x][y]--;
4840
4841     if (MovDelay[x][y] != 0)
4842     {
4843       DrawDynamite(x, y);
4844       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4845
4846       return;
4847     }
4848   }
4849
4850   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4851
4852   Bang(x, y);
4853 }
4854
4855 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4856 {
4857   boolean num_checked_players = 0;
4858   int i;
4859
4860   for (i = 0; i < MAX_PLAYERS; i++)
4861   {
4862     if (stored_player[i].active)
4863     {
4864       int sx = stored_player[i].jx;
4865       int sy = stored_player[i].jy;
4866
4867       if (num_checked_players == 0)
4868       {
4869         *sx1 = *sx2 = sx;
4870         *sy1 = *sy2 = sy;
4871       }
4872       else
4873       {
4874         *sx1 = MIN(*sx1, sx);
4875         *sy1 = MIN(*sy1, sy);
4876         *sx2 = MAX(*sx2, sx);
4877         *sy2 = MAX(*sy2, sy);
4878       }
4879
4880       num_checked_players++;
4881     }
4882   }
4883 }
4884
4885 static boolean checkIfAllPlayersFitToScreen_RND()
4886 {
4887   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4888
4889   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4890
4891   return (sx2 - sx1 < SCR_FIELDX &&
4892           sy2 - sy1 < SCR_FIELDY);
4893 }
4894
4895 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4896 {
4897   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4898
4899   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4900
4901   *sx = (sx1 + sx2) / 2;
4902   *sy = (sy1 + sy2) / 2;
4903 }
4904
4905 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4906                         boolean center_screen, boolean quick_relocation)
4907 {
4908   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4909   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4910   boolean no_delay = (tape.warp_forward);
4911   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4912   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4913   int new_scroll_x, new_scroll_y;
4914
4915   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4916   {
4917     /* case 1: quick relocation inside visible screen (without scrolling) */
4918
4919     RedrawPlayfield();
4920
4921     return;
4922   }
4923
4924   if (!level.shifted_relocation || center_screen)
4925   {
4926     /* relocation _with_ centering of screen */
4927
4928     new_scroll_x = SCROLL_POSITION_X(x);
4929     new_scroll_y = SCROLL_POSITION_Y(y);
4930   }
4931   else
4932   {
4933     /* relocation _without_ centering of screen */
4934
4935     int center_scroll_x = SCROLL_POSITION_X(old_x);
4936     int center_scroll_y = SCROLL_POSITION_Y(old_y);
4937     int offset_x = x + (scroll_x - center_scroll_x);
4938     int offset_y = y + (scroll_y - center_scroll_y);
4939
4940     /* for new screen position, apply previous offset to center position */
4941     new_scroll_x = SCROLL_POSITION_X(offset_x);
4942     new_scroll_y = SCROLL_POSITION_Y(offset_y);
4943   }
4944
4945   if (quick_relocation)
4946   {
4947     /* case 2: quick relocation (redraw without visible scrolling) */
4948
4949     scroll_x = new_scroll_x;
4950     scroll_y = new_scroll_y;
4951
4952     RedrawPlayfield();
4953
4954     return;
4955   }
4956
4957   /* case 3: visible relocation (with scrolling to new position) */
4958
4959   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4960
4961   SetVideoFrameDelay(wait_delay_value);
4962
4963   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4964   {
4965     int dx = 0, dy = 0;
4966     int fx = FX, fy = FY;
4967
4968     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4969     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4970
4971     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4972       break;
4973
4974     scroll_x -= dx;
4975     scroll_y -= dy;
4976
4977     fx += dx * TILEX / 2;
4978     fy += dy * TILEY / 2;
4979
4980     ScrollLevel(dx, dy);
4981     DrawAllPlayers();
4982
4983     /* scroll in two steps of half tile size to make things smoother */
4984     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4985
4986     /* scroll second step to align at full tile size */
4987     BlitScreenToBitmap(window);
4988   }
4989
4990   DrawAllPlayers();
4991   BackToFront();
4992
4993   SetVideoFrameDelay(frame_delay_value_old);
4994 }
4995
4996 void RelocatePlayer(int jx, int jy, int el_player_raw)
4997 {
4998   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4999   int player_nr = GET_PLAYER_NR(el_player);
5000   struct PlayerInfo *player = &stored_player[player_nr];
5001   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5002   boolean no_delay = (tape.warp_forward);
5003   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5004   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5005   int old_jx = player->jx;
5006   int old_jy = player->jy;
5007   int old_element = Feld[old_jx][old_jy];
5008   int element = Feld[jx][jy];
5009   boolean player_relocated = (old_jx != jx || old_jy != jy);
5010
5011   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5012   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5013   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5014   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5015   int leave_side_horiz = move_dir_horiz;
5016   int leave_side_vert  = move_dir_vert;
5017   int enter_side = enter_side_horiz | enter_side_vert;
5018   int leave_side = leave_side_horiz | leave_side_vert;
5019
5020   if (player->GameOver)         /* do not reanimate dead player */
5021     return;
5022
5023   if (!player_relocated)        /* no need to relocate the player */
5024     return;
5025
5026   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5027   {
5028     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5029     DrawLevelField(jx, jy);
5030   }
5031
5032   if (player->present)
5033   {
5034     while (player->MovPos)
5035     {
5036       ScrollPlayer(player, SCROLL_GO_ON);
5037       ScrollScreen(NULL, SCROLL_GO_ON);
5038
5039       AdvanceFrameAndPlayerCounters(player->index_nr);
5040
5041       DrawPlayer(player);
5042
5043       BackToFront_WithFrameDelay(wait_delay_value);
5044     }
5045
5046     DrawPlayer(player);         /* needed here only to cleanup last field */
5047     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5048
5049     player->is_moving = FALSE;
5050   }
5051
5052   if (IS_CUSTOM_ELEMENT(old_element))
5053     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5054                                CE_LEFT_BY_PLAYER,
5055                                player->index_bit, leave_side);
5056
5057   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5058                                       CE_PLAYER_LEAVES_X,
5059                                       player->index_bit, leave_side);
5060
5061   Feld[jx][jy] = el_player;
5062   InitPlayerField(jx, jy, el_player, TRUE);
5063
5064   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5065      possible that the relocation target field did not contain a player element,
5066      but a walkable element, to which the new player was relocated -- in this
5067      case, restore that (already initialized!) element on the player field */
5068   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5069   {
5070     Feld[jx][jy] = element;     /* restore previously existing element */
5071   }
5072
5073   /* only visually relocate centered player */
5074   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5075                      FALSE, level.instant_relocation);
5076
5077   TestIfPlayerTouchesBadThing(jx, jy);
5078   TestIfPlayerTouchesCustomElement(jx, jy);
5079
5080   if (IS_CUSTOM_ELEMENT(element))
5081     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5082                                player->index_bit, enter_side);
5083
5084   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5085                                       player->index_bit, enter_side);
5086
5087   if (player->is_switching)
5088   {
5089     /* ensure that relocation while still switching an element does not cause
5090        a new element to be treated as also switched directly after relocation
5091        (this is important for teleporter switches that teleport the player to
5092        a place where another teleporter switch is in the same direction, which
5093        would then incorrectly be treated as immediately switched before the
5094        direction key that caused the switch was released) */
5095
5096     player->switch_x += jx - old_jx;
5097     player->switch_y += jy - old_jy;
5098   }
5099 }
5100
5101 void Explode(int ex, int ey, int phase, int mode)
5102 {
5103   int x, y;
5104   int last_phase;
5105   int border_element;
5106
5107   /* !!! eliminate this variable !!! */
5108   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5109
5110   if (game.explosions_delayed)
5111   {
5112     ExplodeField[ex][ey] = mode;
5113     return;
5114   }
5115
5116   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5117   {
5118     int center_element = Feld[ex][ey];
5119     int artwork_element, explosion_element;     /* set these values later */
5120
5121     /* remove things displayed in background while burning dynamite */
5122     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5123       Back[ex][ey] = 0;
5124
5125     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5126     {
5127       /* put moving element to center field (and let it explode there) */
5128       center_element = MovingOrBlocked2Element(ex, ey);
5129       RemoveMovingField(ex, ey);
5130       Feld[ex][ey] = center_element;
5131     }
5132
5133     /* now "center_element" is finally determined -- set related values now */
5134     artwork_element = center_element;           /* for custom player artwork */
5135     explosion_element = center_element;         /* for custom player artwork */
5136
5137     if (IS_PLAYER(ex, ey))
5138     {
5139       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5140
5141       artwork_element = stored_player[player_nr].artwork_element;
5142
5143       if (level.use_explosion_element[player_nr])
5144       {
5145         explosion_element = level.explosion_element[player_nr];
5146         artwork_element = explosion_element;
5147       }
5148     }
5149
5150     if (mode == EX_TYPE_NORMAL ||
5151         mode == EX_TYPE_CENTER ||
5152         mode == EX_TYPE_CROSS)
5153       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5154
5155     last_phase = element_info[explosion_element].explosion_delay + 1;
5156
5157     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5158     {
5159       int xx = x - ex + 1;
5160       int yy = y - ey + 1;
5161       int element;
5162
5163       if (!IN_LEV_FIELD(x, y) ||
5164           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5165           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5166         continue;
5167
5168       element = Feld[x][y];
5169
5170       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5171       {
5172         element = MovingOrBlocked2Element(x, y);
5173
5174         if (!IS_EXPLOSION_PROOF(element))
5175           RemoveMovingField(x, y);
5176       }
5177
5178       /* indestructible elements can only explode in center (but not flames) */
5179       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5180                                            mode == EX_TYPE_BORDER)) ||
5181           element == EL_FLAMES)
5182         continue;
5183
5184       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5185          behaviour, for example when touching a yamyam that explodes to rocks
5186          with active deadly shield, a rock is created under the player !!! */
5187       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5188 #if 0
5189       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5190           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5191            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5192 #else
5193       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5194 #endif
5195       {
5196         if (IS_ACTIVE_BOMB(element))
5197         {
5198           /* re-activate things under the bomb like gate or penguin */
5199           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5200           Back[x][y] = 0;
5201         }
5202
5203         continue;
5204       }
5205
5206       /* save walkable background elements while explosion on same tile */
5207       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5208           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5209         Back[x][y] = element;
5210
5211       /* ignite explodable elements reached by other explosion */
5212       if (element == EL_EXPLOSION)
5213         element = Store2[x][y];
5214
5215       if (AmoebaNr[x][y] &&
5216           (element == EL_AMOEBA_FULL ||
5217            element == EL_BD_AMOEBA ||
5218            element == EL_AMOEBA_GROWING))
5219       {
5220         AmoebaCnt[AmoebaNr[x][y]]--;
5221         AmoebaCnt2[AmoebaNr[x][y]]--;
5222       }
5223
5224       RemoveField(x, y);
5225
5226       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5227       {
5228         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5229
5230         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5231
5232         if (PLAYERINFO(ex, ey)->use_murphy)
5233           Store[x][y] = EL_EMPTY;
5234       }
5235
5236       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5237          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5238       else if (ELEM_IS_PLAYER(center_element))
5239         Store[x][y] = EL_EMPTY;
5240       else if (center_element == EL_YAMYAM)
5241         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5242       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5243         Store[x][y] = element_info[center_element].content.e[xx][yy];
5244 #if 1
5245       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5246          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5247          otherwise) -- FIX THIS !!! */
5248       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5249         Store[x][y] = element_info[element].content.e[1][1];
5250 #else
5251       else if (!CAN_EXPLODE(element))
5252         Store[x][y] = element_info[element].content.e[1][1];
5253 #endif
5254       else
5255         Store[x][y] = EL_EMPTY;
5256
5257       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5258           center_element == EL_AMOEBA_TO_DIAMOND)
5259         Store2[x][y] = element;
5260
5261       Feld[x][y] = EL_EXPLOSION;
5262       GfxElement[x][y] = artwork_element;
5263
5264       ExplodePhase[x][y] = 1;
5265       ExplodeDelay[x][y] = last_phase;
5266
5267       Stop[x][y] = TRUE;
5268     }
5269
5270     if (center_element == EL_YAMYAM)
5271       game.yamyam_content_nr =
5272         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5273
5274     return;
5275   }
5276
5277   if (Stop[ex][ey])
5278     return;
5279
5280   x = ex;
5281   y = ey;
5282
5283   if (phase == 1)
5284     GfxFrame[x][y] = 0;         /* restart explosion animation */
5285
5286   last_phase = ExplodeDelay[x][y];
5287
5288   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5289
5290   /* this can happen if the player leaves an explosion just in time */
5291   if (GfxElement[x][y] == EL_UNDEFINED)
5292     GfxElement[x][y] = EL_EMPTY;
5293
5294   border_element = Store2[x][y];
5295   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5296     border_element = StorePlayer[x][y];
5297
5298   if (phase == element_info[border_element].ignition_delay ||
5299       phase == last_phase)
5300   {
5301     boolean border_explosion = FALSE;
5302
5303     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5304         !PLAYER_EXPLOSION_PROTECTED(x, y))
5305     {
5306       KillPlayerUnlessExplosionProtected(x, y);
5307       border_explosion = TRUE;
5308     }
5309     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5310     {
5311       Feld[x][y] = Store2[x][y];
5312       Store2[x][y] = 0;
5313       Bang(x, y);
5314       border_explosion = TRUE;
5315     }
5316     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5317     {
5318       AmoebeUmwandeln(x, y);
5319       Store2[x][y] = 0;
5320       border_explosion = TRUE;
5321     }
5322
5323     /* if an element just explodes due to another explosion (chain-reaction),
5324        do not immediately end the new explosion when it was the last frame of
5325        the explosion (as it would be done in the following "if"-statement!) */
5326     if (border_explosion && phase == last_phase)
5327       return;
5328   }
5329
5330   if (phase == last_phase)
5331   {
5332     int element;
5333
5334     element = Feld[x][y] = Store[x][y];
5335     Store[x][y] = Store2[x][y] = 0;
5336     GfxElement[x][y] = EL_UNDEFINED;
5337
5338     /* player can escape from explosions and might therefore be still alive */
5339     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5340         element <= EL_PLAYER_IS_EXPLODING_4)
5341     {
5342       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5343       int explosion_element = EL_PLAYER_1 + player_nr;
5344       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5345       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5346
5347       if (level.use_explosion_element[player_nr])
5348         explosion_element = level.explosion_element[player_nr];
5349
5350       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5351                     element_info[explosion_element].content.e[xx][yy]);
5352     }
5353
5354     /* restore probably existing indestructible background element */
5355     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5356       element = Feld[x][y] = Back[x][y];
5357     Back[x][y] = 0;
5358
5359     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5360     GfxDir[x][y] = MV_NONE;
5361     ChangeDelay[x][y] = 0;
5362     ChangePage[x][y] = -1;
5363
5364     CustomValue[x][y] = 0;
5365
5366     InitField_WithBug2(x, y, FALSE);
5367
5368     TEST_DrawLevelField(x, y);
5369
5370     TestIfElementTouchesCustomElement(x, y);
5371
5372     if (GFX_CRUMBLED(element))
5373       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5374
5375     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5376       StorePlayer[x][y] = 0;
5377
5378     if (ELEM_IS_PLAYER(element))
5379       RelocatePlayer(x, y, element);
5380   }
5381   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5382   {
5383     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5384     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5385
5386     if (phase == delay)
5387       TEST_DrawLevelFieldCrumbled(x, y);
5388
5389     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5390     {
5391       DrawLevelElement(x, y, Back[x][y]);
5392       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5393     }
5394     else if (IS_WALKABLE_UNDER(Back[x][y]))
5395     {
5396       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5397       DrawLevelElementThruMask(x, y, Back[x][y]);
5398     }
5399     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5400       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5401   }
5402 }
5403
5404 void DynaExplode(int ex, int ey)
5405 {
5406   int i, j;
5407   int dynabomb_element = Feld[ex][ey];
5408   int dynabomb_size = 1;
5409   boolean dynabomb_xl = FALSE;
5410   struct PlayerInfo *player;
5411   static int xy[4][2] =
5412   {
5413     { 0, -1 },
5414     { -1, 0 },
5415     { +1, 0 },
5416     { 0, +1 }
5417   };
5418
5419   if (IS_ACTIVE_BOMB(dynabomb_element))
5420   {
5421     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5422     dynabomb_size = player->dynabomb_size;
5423     dynabomb_xl = player->dynabomb_xl;
5424     player->dynabombs_left++;
5425   }
5426
5427   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5428
5429   for (i = 0; i < NUM_DIRECTIONS; i++)
5430   {
5431     for (j = 1; j <= dynabomb_size; j++)
5432     {
5433       int x = ex + j * xy[i][0];
5434       int y = ey + j * xy[i][1];
5435       int element;
5436
5437       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5438         break;
5439
5440       element = Feld[x][y];
5441
5442       /* do not restart explosions of fields with active bombs */
5443       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5444         continue;
5445
5446       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5447
5448       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5449           !IS_DIGGABLE(element) && !dynabomb_xl)
5450         break;
5451     }
5452   }
5453 }
5454
5455 void Bang(int x, int y)
5456 {
5457   int element = MovingOrBlocked2Element(x, y);
5458   int explosion_type = EX_TYPE_NORMAL;
5459
5460   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5461   {
5462     struct PlayerInfo *player = PLAYERINFO(x, y);
5463
5464     element = Feld[x][y] = player->initial_element;
5465
5466     if (level.use_explosion_element[player->index_nr])
5467     {
5468       int explosion_element = level.explosion_element[player->index_nr];
5469
5470       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5471         explosion_type = EX_TYPE_CROSS;
5472       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5473         explosion_type = EX_TYPE_CENTER;
5474     }
5475   }
5476
5477   switch (element)
5478   {
5479     case EL_BUG:
5480     case EL_SPACESHIP:
5481     case EL_BD_BUTTERFLY:
5482     case EL_BD_FIREFLY:
5483     case EL_YAMYAM:
5484     case EL_DARK_YAMYAM:
5485     case EL_ROBOT:
5486     case EL_PACMAN:
5487     case EL_MOLE:
5488       RaiseScoreElement(element);
5489       break;
5490
5491     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5492     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5493     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5494     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5495     case EL_DYNABOMB_INCREASE_NUMBER:
5496     case EL_DYNABOMB_INCREASE_SIZE:
5497     case EL_DYNABOMB_INCREASE_POWER:
5498       explosion_type = EX_TYPE_DYNA;
5499       break;
5500
5501     case EL_DC_LANDMINE:
5502       explosion_type = EX_TYPE_CENTER;
5503       break;
5504
5505     case EL_PENGUIN:
5506     case EL_LAMP:
5507     case EL_LAMP_ACTIVE:
5508     case EL_AMOEBA_TO_DIAMOND:
5509       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5510         explosion_type = EX_TYPE_CENTER;
5511       break;
5512
5513     default:
5514       if (element_info[element].explosion_type == EXPLODES_CROSS)
5515         explosion_type = EX_TYPE_CROSS;
5516       else if (element_info[element].explosion_type == EXPLODES_1X1)
5517         explosion_type = EX_TYPE_CENTER;
5518       break;
5519   }
5520
5521   if (explosion_type == EX_TYPE_DYNA)
5522     DynaExplode(x, y);
5523   else
5524     Explode(x, y, EX_PHASE_START, explosion_type);
5525
5526   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5527 }
5528
5529 void SplashAcid(int x, int y)
5530 {
5531   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5532       (!IN_LEV_FIELD(x - 1, y - 2) ||
5533        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5534     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5535
5536   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5537       (!IN_LEV_FIELD(x + 1, y - 2) ||
5538        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5539     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5540
5541   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5542 }
5543
5544 static void InitBeltMovement()
5545 {
5546   static int belt_base_element[4] =
5547   {
5548     EL_CONVEYOR_BELT_1_LEFT,
5549     EL_CONVEYOR_BELT_2_LEFT,
5550     EL_CONVEYOR_BELT_3_LEFT,
5551     EL_CONVEYOR_BELT_4_LEFT
5552   };
5553   static int belt_base_active_element[4] =
5554   {
5555     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5556     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5557     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5558     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5559   };
5560
5561   int x, y, i, j;
5562
5563   /* set frame order for belt animation graphic according to belt direction */
5564   for (i = 0; i < NUM_BELTS; i++)
5565   {
5566     int belt_nr = i;
5567
5568     for (j = 0; j < NUM_BELT_PARTS; j++)
5569     {
5570       int element = belt_base_active_element[belt_nr] + j;
5571       int graphic_1 = el2img(element);
5572       int graphic_2 = el2panelimg(element);
5573
5574       if (game.belt_dir[i] == MV_LEFT)
5575       {
5576         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5577         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5578       }
5579       else
5580       {
5581         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5582         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5583       }
5584     }
5585   }
5586
5587   SCAN_PLAYFIELD(x, y)
5588   {
5589     int element = Feld[x][y];
5590
5591     for (i = 0; i < NUM_BELTS; i++)
5592     {
5593       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5594       {
5595         int e_belt_nr = getBeltNrFromBeltElement(element);
5596         int belt_nr = i;
5597
5598         if (e_belt_nr == belt_nr)
5599         {
5600           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5601
5602           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5603         }
5604       }
5605     }
5606   }
5607 }
5608
5609 static void ToggleBeltSwitch(int x, int y)
5610 {
5611   static int belt_base_element[4] =
5612   {
5613     EL_CONVEYOR_BELT_1_LEFT,
5614     EL_CONVEYOR_BELT_2_LEFT,
5615     EL_CONVEYOR_BELT_3_LEFT,
5616     EL_CONVEYOR_BELT_4_LEFT
5617   };
5618   static int belt_base_active_element[4] =
5619   {
5620     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5621     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5622     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5623     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5624   };
5625   static int belt_base_switch_element[4] =
5626   {
5627     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5628     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5629     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5630     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5631   };
5632   static int belt_move_dir[4] =
5633   {
5634     MV_LEFT,
5635     MV_NONE,
5636     MV_RIGHT,
5637     MV_NONE,
5638   };
5639
5640   int element = Feld[x][y];
5641   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5642   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5643   int belt_dir = belt_move_dir[belt_dir_nr];
5644   int xx, yy, i;
5645
5646   if (!IS_BELT_SWITCH(element))
5647     return;
5648
5649   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5650   game.belt_dir[belt_nr] = belt_dir;
5651
5652   if (belt_dir_nr == 3)
5653     belt_dir_nr = 1;
5654
5655   /* set frame order for belt animation graphic according to belt direction */
5656   for (i = 0; i < NUM_BELT_PARTS; i++)
5657   {
5658     int element = belt_base_active_element[belt_nr] + i;
5659     int graphic_1 = el2img(element);
5660     int graphic_2 = el2panelimg(element);
5661
5662     if (belt_dir == MV_LEFT)
5663     {
5664       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5665       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5666     }
5667     else
5668     {
5669       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5670       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5671     }
5672   }
5673
5674   SCAN_PLAYFIELD(xx, yy)
5675   {
5676     int element = Feld[xx][yy];
5677
5678     if (IS_BELT_SWITCH(element))
5679     {
5680       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5681
5682       if (e_belt_nr == belt_nr)
5683       {
5684         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5685         TEST_DrawLevelField(xx, yy);
5686       }
5687     }
5688     else if (IS_BELT(element) && belt_dir != MV_NONE)
5689     {
5690       int e_belt_nr = getBeltNrFromBeltElement(element);
5691
5692       if (e_belt_nr == belt_nr)
5693       {
5694         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5695
5696         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5697         TEST_DrawLevelField(xx, yy);
5698       }
5699     }
5700     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5701     {
5702       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5703
5704       if (e_belt_nr == belt_nr)
5705       {
5706         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5707
5708         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5709         TEST_DrawLevelField(xx, yy);
5710       }
5711     }
5712   }
5713 }
5714
5715 static void ToggleSwitchgateSwitch(int x, int y)
5716 {
5717   int xx, yy;
5718
5719   game.switchgate_pos = !game.switchgate_pos;
5720
5721   SCAN_PLAYFIELD(xx, yy)
5722   {
5723     int element = Feld[xx][yy];
5724
5725     if (element == EL_SWITCHGATE_SWITCH_UP)
5726     {
5727       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5728       TEST_DrawLevelField(xx, yy);
5729     }
5730     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5731     {
5732       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5733       TEST_DrawLevelField(xx, yy);
5734     }
5735     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5736     {
5737       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5738       TEST_DrawLevelField(xx, yy);
5739     }
5740     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5741     {
5742       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5743       TEST_DrawLevelField(xx, yy);
5744     }
5745     else if (element == EL_SWITCHGATE_OPEN ||
5746              element == EL_SWITCHGATE_OPENING)
5747     {
5748       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5749
5750       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5751     }
5752     else if (element == EL_SWITCHGATE_CLOSED ||
5753              element == EL_SWITCHGATE_CLOSING)
5754     {
5755       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5756
5757       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5758     }
5759   }
5760 }
5761
5762 static int getInvisibleActiveFromInvisibleElement(int element)
5763 {
5764   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5765           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5766           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5767           element);
5768 }
5769
5770 static int getInvisibleFromInvisibleActiveElement(int element)
5771 {
5772   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5773           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5774           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5775           element);
5776 }
5777
5778 static void RedrawAllLightSwitchesAndInvisibleElements()
5779 {
5780   int x, y;
5781
5782   SCAN_PLAYFIELD(x, y)
5783   {
5784     int element = Feld[x][y];
5785
5786     if (element == EL_LIGHT_SWITCH &&
5787         game.light_time_left > 0)
5788     {
5789       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5790       TEST_DrawLevelField(x, y);
5791     }
5792     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5793              game.light_time_left == 0)
5794     {
5795       Feld[x][y] = EL_LIGHT_SWITCH;
5796       TEST_DrawLevelField(x, y);
5797     }
5798     else if (element == EL_EMC_DRIPPER &&
5799              game.light_time_left > 0)
5800     {
5801       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5802       TEST_DrawLevelField(x, y);
5803     }
5804     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5805              game.light_time_left == 0)
5806     {
5807       Feld[x][y] = EL_EMC_DRIPPER;
5808       TEST_DrawLevelField(x, y);
5809     }
5810     else if (element == EL_INVISIBLE_STEELWALL ||
5811              element == EL_INVISIBLE_WALL ||
5812              element == EL_INVISIBLE_SAND)
5813     {
5814       if (game.light_time_left > 0)
5815         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5816
5817       TEST_DrawLevelField(x, y);
5818
5819       /* uncrumble neighbour fields, if needed */
5820       if (element == EL_INVISIBLE_SAND)
5821         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5822     }
5823     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5824              element == EL_INVISIBLE_WALL_ACTIVE ||
5825              element == EL_INVISIBLE_SAND_ACTIVE)
5826     {
5827       if (game.light_time_left == 0)
5828         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5829
5830       TEST_DrawLevelField(x, y);
5831
5832       /* re-crumble neighbour fields, if needed */
5833       if (element == EL_INVISIBLE_SAND)
5834         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5835     }
5836   }
5837 }
5838
5839 static void RedrawAllInvisibleElementsForLenses()
5840 {
5841   int x, y;
5842
5843   SCAN_PLAYFIELD(x, y)
5844   {
5845     int element = Feld[x][y];
5846
5847     if (element == EL_EMC_DRIPPER &&
5848         game.lenses_time_left > 0)
5849     {
5850       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5851       TEST_DrawLevelField(x, y);
5852     }
5853     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5854              game.lenses_time_left == 0)
5855     {
5856       Feld[x][y] = EL_EMC_DRIPPER;
5857       TEST_DrawLevelField(x, y);
5858     }
5859     else if (element == EL_INVISIBLE_STEELWALL ||
5860              element == EL_INVISIBLE_WALL ||
5861              element == EL_INVISIBLE_SAND)
5862     {
5863       if (game.lenses_time_left > 0)
5864         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5865
5866       TEST_DrawLevelField(x, y);
5867
5868       /* uncrumble neighbour fields, if needed */
5869       if (element == EL_INVISIBLE_SAND)
5870         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5871     }
5872     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5873              element == EL_INVISIBLE_WALL_ACTIVE ||
5874              element == EL_INVISIBLE_SAND_ACTIVE)
5875     {
5876       if (game.lenses_time_left == 0)
5877         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5878
5879       TEST_DrawLevelField(x, y);
5880
5881       /* re-crumble neighbour fields, if needed */
5882       if (element == EL_INVISIBLE_SAND)
5883         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5884     }
5885   }
5886 }
5887
5888 static void RedrawAllInvisibleElementsForMagnifier()
5889 {
5890   int x, y;
5891
5892   SCAN_PLAYFIELD(x, y)
5893   {
5894     int element = Feld[x][y];
5895
5896     if (element == EL_EMC_FAKE_GRASS &&
5897         game.magnify_time_left > 0)
5898     {
5899       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5900       TEST_DrawLevelField(x, y);
5901     }
5902     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5903              game.magnify_time_left == 0)
5904     {
5905       Feld[x][y] = EL_EMC_FAKE_GRASS;
5906       TEST_DrawLevelField(x, y);
5907     }
5908     else if (IS_GATE_GRAY(element) &&
5909              game.magnify_time_left > 0)
5910     {
5911       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5912                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5913                     IS_EM_GATE_GRAY(element) ?
5914                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5915                     IS_EMC_GATE_GRAY(element) ?
5916                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5917                     IS_DC_GATE_GRAY(element) ?
5918                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5919                     element);
5920       TEST_DrawLevelField(x, y);
5921     }
5922     else if (IS_GATE_GRAY_ACTIVE(element) &&
5923              game.magnify_time_left == 0)
5924     {
5925       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5926                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5927                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5928                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5929                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5930                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5931                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5932                     EL_DC_GATE_WHITE_GRAY :
5933                     element);
5934       TEST_DrawLevelField(x, y);
5935     }
5936   }
5937 }
5938
5939 static void ToggleLightSwitch(int x, int y)
5940 {
5941   int element = Feld[x][y];
5942
5943   game.light_time_left =
5944     (element == EL_LIGHT_SWITCH ?
5945      level.time_light * FRAMES_PER_SECOND : 0);
5946
5947   RedrawAllLightSwitchesAndInvisibleElements();
5948 }
5949
5950 static void ActivateTimegateSwitch(int x, int y)
5951 {
5952   int xx, yy;
5953
5954   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5955
5956   SCAN_PLAYFIELD(xx, yy)
5957   {
5958     int element = Feld[xx][yy];
5959
5960     if (element == EL_TIMEGATE_CLOSED ||
5961         element == EL_TIMEGATE_CLOSING)
5962     {
5963       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5964       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5965     }
5966
5967     /*
5968     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5969     {
5970       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5971       TEST_DrawLevelField(xx, yy);
5972     }
5973     */
5974
5975   }
5976
5977   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5978                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5979 }
5980
5981 void Impact(int x, int y)
5982 {
5983   boolean last_line = (y == lev_fieldy - 1);
5984   boolean object_hit = FALSE;
5985   boolean impact = (last_line || object_hit);
5986   int element = Feld[x][y];
5987   int smashed = EL_STEELWALL;
5988
5989   if (!last_line)       /* check if element below was hit */
5990   {
5991     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5992       return;
5993
5994     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5995                                          MovDir[x][y + 1] != MV_DOWN ||
5996                                          MovPos[x][y + 1] <= TILEY / 2));
5997
5998     /* do not smash moving elements that left the smashed field in time */
5999     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6000         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6001       object_hit = FALSE;
6002
6003 #if USE_QUICKSAND_IMPACT_BUGFIX
6004     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6005     {
6006       RemoveMovingField(x, y + 1);
6007       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6008       Feld[x][y + 2] = EL_ROCK;
6009       TEST_DrawLevelField(x, y + 2);
6010
6011       object_hit = TRUE;
6012     }
6013
6014     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6015     {
6016       RemoveMovingField(x, y + 1);
6017       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6018       Feld[x][y + 2] = EL_ROCK;
6019       TEST_DrawLevelField(x, y + 2);
6020
6021       object_hit = TRUE;
6022     }
6023 #endif
6024
6025     if (object_hit)
6026       smashed = MovingOrBlocked2Element(x, y + 1);
6027
6028     impact = (last_line || object_hit);
6029   }
6030
6031   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6032   {
6033     SplashAcid(x, y + 1);
6034     return;
6035   }
6036
6037   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6038   /* only reset graphic animation if graphic really changes after impact */
6039   if (impact &&
6040       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6041   {
6042     ResetGfxAnimation(x, y);
6043     TEST_DrawLevelField(x, y);
6044   }
6045
6046   if (impact && CAN_EXPLODE_IMPACT(element))
6047   {
6048     Bang(x, y);
6049     return;
6050   }
6051   else if (impact && element == EL_PEARL &&
6052            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6053   {
6054     ResetGfxAnimation(x, y);
6055
6056     Feld[x][y] = EL_PEARL_BREAKING;
6057     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6058     return;
6059   }
6060   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6061   {
6062     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6063
6064     return;
6065   }
6066
6067   if (impact && element == EL_AMOEBA_DROP)
6068   {
6069     if (object_hit && IS_PLAYER(x, y + 1))
6070       KillPlayerUnlessEnemyProtected(x, y + 1);
6071     else if (object_hit && smashed == EL_PENGUIN)
6072       Bang(x, y + 1);
6073     else
6074     {
6075       Feld[x][y] = EL_AMOEBA_GROWING;
6076       Store[x][y] = EL_AMOEBA_WET;
6077
6078       ResetRandomAnimationValue(x, y);
6079     }
6080     return;
6081   }
6082
6083   if (object_hit)               /* check which object was hit */
6084   {
6085     if ((CAN_PASS_MAGIC_WALL(element) && 
6086          (smashed == EL_MAGIC_WALL ||
6087           smashed == EL_BD_MAGIC_WALL)) ||
6088         (CAN_PASS_DC_MAGIC_WALL(element) &&
6089          smashed == EL_DC_MAGIC_WALL))
6090     {
6091       int xx, yy;
6092       int activated_magic_wall =
6093         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6094          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6095          EL_DC_MAGIC_WALL_ACTIVE);
6096
6097       /* activate magic wall / mill */
6098       SCAN_PLAYFIELD(xx, yy)
6099       {
6100         if (Feld[xx][yy] == smashed)
6101           Feld[xx][yy] = activated_magic_wall;
6102       }
6103
6104       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6105       game.magic_wall_active = TRUE;
6106
6107       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6108                             SND_MAGIC_WALL_ACTIVATING :
6109                             smashed == EL_BD_MAGIC_WALL ?
6110                             SND_BD_MAGIC_WALL_ACTIVATING :
6111                             SND_DC_MAGIC_WALL_ACTIVATING));
6112     }
6113
6114     if (IS_PLAYER(x, y + 1))
6115     {
6116       if (CAN_SMASH_PLAYER(element))
6117       {
6118         KillPlayerUnlessEnemyProtected(x, y + 1);
6119         return;
6120       }
6121     }
6122     else if (smashed == EL_PENGUIN)
6123     {
6124       if (CAN_SMASH_PLAYER(element))
6125       {
6126         Bang(x, y + 1);
6127         return;
6128       }
6129     }
6130     else if (element == EL_BD_DIAMOND)
6131     {
6132       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6133       {
6134         Bang(x, y + 1);
6135         return;
6136       }
6137     }
6138     else if (((element == EL_SP_INFOTRON ||
6139                element == EL_SP_ZONK) &&
6140               (smashed == EL_SP_SNIKSNAK ||
6141                smashed == EL_SP_ELECTRON ||
6142                smashed == EL_SP_DISK_ORANGE)) ||
6143              (element == EL_SP_INFOTRON &&
6144               smashed == EL_SP_DISK_YELLOW))
6145     {
6146       Bang(x, y + 1);
6147       return;
6148     }
6149     else if (CAN_SMASH_EVERYTHING(element))
6150     {
6151       if (IS_CLASSIC_ENEMY(smashed) ||
6152           CAN_EXPLODE_SMASHED(smashed))
6153       {
6154         Bang(x, y + 1);
6155         return;
6156       }
6157       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6158       {
6159         if (smashed == EL_LAMP ||
6160             smashed == EL_LAMP_ACTIVE)
6161         {
6162           Bang(x, y + 1);
6163           return;
6164         }
6165         else if (smashed == EL_NUT)
6166         {
6167           Feld[x][y + 1] = EL_NUT_BREAKING;
6168           PlayLevelSound(x, y, SND_NUT_BREAKING);
6169           RaiseScoreElement(EL_NUT);
6170           return;
6171         }
6172         else if (smashed == EL_PEARL)
6173         {
6174           ResetGfxAnimation(x, y);
6175
6176           Feld[x][y + 1] = EL_PEARL_BREAKING;
6177           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6178           return;
6179         }
6180         else if (smashed == EL_DIAMOND)
6181         {
6182           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6183           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6184           return;
6185         }
6186         else if (IS_BELT_SWITCH(smashed))
6187         {
6188           ToggleBeltSwitch(x, y + 1);
6189         }
6190         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6191                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6192                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6193                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6194         {
6195           ToggleSwitchgateSwitch(x, y + 1);
6196         }
6197         else if (smashed == EL_LIGHT_SWITCH ||
6198                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6199         {
6200           ToggleLightSwitch(x, y + 1);
6201         }
6202         else
6203         {
6204           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6205
6206           CheckElementChangeBySide(x, y + 1, smashed, element,
6207                                    CE_SWITCHED, CH_SIDE_TOP);
6208           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6209                                             CH_SIDE_TOP);
6210         }
6211       }
6212       else
6213       {
6214         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6215       }
6216     }
6217   }
6218
6219   /* play sound of magic wall / mill */
6220   if (!last_line &&
6221       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6222        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6223        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6224   {
6225     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6226       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6227     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6228       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6229     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6230       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6231
6232     return;
6233   }
6234
6235   /* play sound of object that hits the ground */
6236   if (last_line || object_hit)
6237     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6238 }
6239
6240 inline static void TurnRoundExt(int x, int y)
6241 {
6242   static struct
6243   {
6244     int dx, dy;
6245   } move_xy[] =
6246   {
6247     {  0,  0 },
6248     { -1,  0 },
6249     { +1,  0 },
6250     {  0,  0 },
6251     {  0, -1 },
6252     {  0,  0 }, { 0, 0 }, { 0, 0 },
6253     {  0, +1 }
6254   };
6255   static struct
6256   {
6257     int left, right, back;
6258   } turn[] =
6259   {
6260     { 0,        0,              0        },
6261     { MV_DOWN,  MV_UP,          MV_RIGHT },
6262     { MV_UP,    MV_DOWN,        MV_LEFT  },
6263     { 0,        0,              0        },
6264     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6265     { 0,        0,              0        },
6266     { 0,        0,              0        },
6267     { 0,        0,              0        },
6268     { MV_RIGHT, MV_LEFT,        MV_UP    }
6269   };
6270
6271   int element = Feld[x][y];
6272   int move_pattern = element_info[element].move_pattern;
6273
6274   int old_move_dir = MovDir[x][y];
6275   int left_dir  = turn[old_move_dir].left;
6276   int right_dir = turn[old_move_dir].right;
6277   int back_dir  = turn[old_move_dir].back;
6278
6279   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6280   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6281   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6282   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6283
6284   int left_x  = x + left_dx,  left_y  = y + left_dy;
6285   int right_x = x + right_dx, right_y = y + right_dy;
6286   int move_x  = x + move_dx,  move_y  = y + move_dy;
6287
6288   int xx, yy;
6289
6290   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6291   {
6292     TestIfBadThingTouchesOtherBadThing(x, y);
6293
6294     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6295       MovDir[x][y] = right_dir;
6296     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6297       MovDir[x][y] = left_dir;
6298
6299     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6300       MovDelay[x][y] = 9;
6301     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6302       MovDelay[x][y] = 1;
6303   }
6304   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6305   {
6306     TestIfBadThingTouchesOtherBadThing(x, y);
6307
6308     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6309       MovDir[x][y] = left_dir;
6310     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6311       MovDir[x][y] = right_dir;
6312
6313     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6314       MovDelay[x][y] = 9;
6315     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6316       MovDelay[x][y] = 1;
6317   }
6318   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6319   {
6320     TestIfBadThingTouchesOtherBadThing(x, y);
6321
6322     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6323       MovDir[x][y] = left_dir;
6324     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6325       MovDir[x][y] = right_dir;
6326
6327     if (MovDir[x][y] != old_move_dir)
6328       MovDelay[x][y] = 9;
6329   }
6330   else if (element == EL_YAMYAM)
6331   {
6332     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6333     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6334
6335     if (can_turn_left && can_turn_right)
6336       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6337     else if (can_turn_left)
6338       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6339     else if (can_turn_right)
6340       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6341     else
6342       MovDir[x][y] = back_dir;
6343
6344     MovDelay[x][y] = 16 + 16 * RND(3);
6345   }
6346   else if (element == EL_DARK_YAMYAM)
6347   {
6348     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6349                                                          left_x, left_y);
6350     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6351                                                          right_x, right_y);
6352
6353     if (can_turn_left && can_turn_right)
6354       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6355     else if (can_turn_left)
6356       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6357     else if (can_turn_right)
6358       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6359     else
6360       MovDir[x][y] = back_dir;
6361
6362     MovDelay[x][y] = 16 + 16 * RND(3);
6363   }
6364   else if (element == EL_PACMAN)
6365   {
6366     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6367     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6368
6369     if (can_turn_left && can_turn_right)
6370       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6371     else if (can_turn_left)
6372       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6373     else if (can_turn_right)
6374       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6375     else
6376       MovDir[x][y] = back_dir;
6377
6378     MovDelay[x][y] = 6 + RND(40);
6379   }
6380   else if (element == EL_PIG)
6381   {
6382     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6383     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6384     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6385     boolean should_turn_left, should_turn_right, should_move_on;
6386     int rnd_value = 24;
6387     int rnd = RND(rnd_value);
6388
6389     should_turn_left = (can_turn_left &&
6390                         (!can_move_on ||
6391                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6392                                                    y + back_dy + left_dy)));
6393     should_turn_right = (can_turn_right &&
6394                          (!can_move_on ||
6395                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6396                                                     y + back_dy + right_dy)));
6397     should_move_on = (can_move_on &&
6398                       (!can_turn_left ||
6399                        !can_turn_right ||
6400                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6401                                                  y + move_dy + left_dy) ||
6402                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6403                                                  y + move_dy + right_dy)));
6404
6405     if (should_turn_left || should_turn_right || should_move_on)
6406     {
6407       if (should_turn_left && should_turn_right && should_move_on)
6408         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6409                         rnd < 2 * rnd_value / 3 ? right_dir :
6410                         old_move_dir);
6411       else if (should_turn_left && should_turn_right)
6412         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6413       else if (should_turn_left && should_move_on)
6414         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6415       else if (should_turn_right && should_move_on)
6416         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6417       else if (should_turn_left)
6418         MovDir[x][y] = left_dir;
6419       else if (should_turn_right)
6420         MovDir[x][y] = right_dir;
6421       else if (should_move_on)
6422         MovDir[x][y] = old_move_dir;
6423     }
6424     else if (can_move_on && rnd > rnd_value / 8)
6425       MovDir[x][y] = old_move_dir;
6426     else if (can_turn_left && can_turn_right)
6427       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6428     else if (can_turn_left && rnd > rnd_value / 8)
6429       MovDir[x][y] = left_dir;
6430     else if (can_turn_right && rnd > rnd_value/8)
6431       MovDir[x][y] = right_dir;
6432     else
6433       MovDir[x][y] = back_dir;
6434
6435     xx = x + move_xy[MovDir[x][y]].dx;
6436     yy = y + move_xy[MovDir[x][y]].dy;
6437
6438     if (!IN_LEV_FIELD(xx, yy) ||
6439         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6440       MovDir[x][y] = old_move_dir;
6441
6442     MovDelay[x][y] = 0;
6443   }
6444   else if (element == EL_DRAGON)
6445   {
6446     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6447     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6448     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6449     int rnd_value = 24;
6450     int rnd = RND(rnd_value);
6451
6452     if (can_move_on && rnd > rnd_value / 8)
6453       MovDir[x][y] = old_move_dir;
6454     else if (can_turn_left && can_turn_right)
6455       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6456     else if (can_turn_left && rnd > rnd_value / 8)
6457       MovDir[x][y] = left_dir;
6458     else if (can_turn_right && rnd > rnd_value / 8)
6459       MovDir[x][y] = right_dir;
6460     else
6461       MovDir[x][y] = back_dir;
6462
6463     xx = x + move_xy[MovDir[x][y]].dx;
6464     yy = y + move_xy[MovDir[x][y]].dy;
6465
6466     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6467       MovDir[x][y] = old_move_dir;
6468
6469     MovDelay[x][y] = 0;
6470   }
6471   else if (element == EL_MOLE)
6472   {
6473     boolean can_move_on =
6474       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6475                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6476                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6477     if (!can_move_on)
6478     {
6479       boolean can_turn_left =
6480         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6481                               IS_AMOEBOID(Feld[left_x][left_y])));
6482
6483       boolean can_turn_right =
6484         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6485                               IS_AMOEBOID(Feld[right_x][right_y])));
6486
6487       if (can_turn_left && can_turn_right)
6488         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6489       else if (can_turn_left)
6490         MovDir[x][y] = left_dir;
6491       else
6492         MovDir[x][y] = right_dir;
6493     }
6494
6495     if (MovDir[x][y] != old_move_dir)
6496       MovDelay[x][y] = 9;
6497   }
6498   else if (element == EL_BALLOON)
6499   {
6500     MovDir[x][y] = game.wind_direction;
6501     MovDelay[x][y] = 0;
6502   }
6503   else if (element == EL_SPRING)
6504   {
6505     if (MovDir[x][y] & MV_HORIZONTAL)
6506     {
6507       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6508           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6509       {
6510         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6511         ResetGfxAnimation(move_x, move_y);
6512         TEST_DrawLevelField(move_x, move_y);
6513
6514         MovDir[x][y] = back_dir;
6515       }
6516       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6517                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6518         MovDir[x][y] = MV_NONE;
6519     }
6520
6521     MovDelay[x][y] = 0;
6522   }
6523   else if (element == EL_ROBOT ||
6524            element == EL_SATELLITE ||
6525            element == EL_PENGUIN ||
6526            element == EL_EMC_ANDROID)
6527   {
6528     int attr_x = -1, attr_y = -1;
6529
6530     if (AllPlayersGone)
6531     {
6532       attr_x = ExitX;
6533       attr_y = ExitY;
6534     }
6535     else
6536     {
6537       int i;
6538
6539       for (i = 0; i < MAX_PLAYERS; i++)
6540       {
6541         struct PlayerInfo *player = &stored_player[i];
6542         int jx = player->jx, jy = player->jy;
6543
6544         if (!player->active)
6545           continue;
6546
6547         if (attr_x == -1 ||
6548             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6549         {
6550           attr_x = jx;
6551           attr_y = jy;
6552         }
6553       }
6554     }
6555
6556     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6557         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6558          game.engine_version < VERSION_IDENT(3,1,0,0)))
6559     {
6560       attr_x = ZX;
6561       attr_y = ZY;
6562     }
6563
6564     if (element == EL_PENGUIN)
6565     {
6566       int i;
6567       static int xy[4][2] =
6568       {
6569         { 0, -1 },
6570         { -1, 0 },
6571         { +1, 0 },
6572         { 0, +1 }
6573       };
6574
6575       for (i = 0; i < NUM_DIRECTIONS; i++)
6576       {
6577         int ex = x + xy[i][0];
6578         int ey = y + xy[i][1];
6579
6580         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6581                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6582                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6583                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6584         {
6585           attr_x = ex;
6586           attr_y = ey;
6587           break;
6588         }
6589       }
6590     }
6591
6592     MovDir[x][y] = MV_NONE;
6593     if (attr_x < x)
6594       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6595     else if (attr_x > x)
6596       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6597     if (attr_y < y)
6598       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6599     else if (attr_y > y)
6600       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6601
6602     if (element == EL_ROBOT)
6603     {
6604       int newx, newy;
6605
6606       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6607         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6608       Moving2Blocked(x, y, &newx, &newy);
6609
6610       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6611         MovDelay[x][y] = 8 + 8 * !RND(3);
6612       else
6613         MovDelay[x][y] = 16;
6614     }
6615     else if (element == EL_PENGUIN)
6616     {
6617       int newx, newy;
6618
6619       MovDelay[x][y] = 1;
6620
6621       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6622       {
6623         boolean first_horiz = RND(2);
6624         int new_move_dir = MovDir[x][y];
6625
6626         MovDir[x][y] =
6627           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6628         Moving2Blocked(x, y, &newx, &newy);
6629
6630         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6631           return;
6632
6633         MovDir[x][y] =
6634           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6635         Moving2Blocked(x, y, &newx, &newy);
6636
6637         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6638           return;
6639
6640         MovDir[x][y] = old_move_dir;
6641         return;
6642       }
6643     }
6644     else if (element == EL_SATELLITE)
6645     {
6646       int newx, newy;
6647
6648       MovDelay[x][y] = 1;
6649
6650       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6651       {
6652         boolean first_horiz = RND(2);
6653         int new_move_dir = MovDir[x][y];
6654
6655         MovDir[x][y] =
6656           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657         Moving2Blocked(x, y, &newx, &newy);
6658
6659         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6660           return;
6661
6662         MovDir[x][y] =
6663           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6664         Moving2Blocked(x, y, &newx, &newy);
6665
6666         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6667           return;
6668
6669         MovDir[x][y] = old_move_dir;
6670         return;
6671       }
6672     }
6673     else if (element == EL_EMC_ANDROID)
6674     {
6675       static int check_pos[16] =
6676       {
6677         -1,             /*  0 => (invalid)          */
6678         7,              /*  1 => MV_LEFT            */
6679         3,              /*  2 => MV_RIGHT           */
6680         -1,             /*  3 => (invalid)          */
6681         1,              /*  4 =>            MV_UP   */
6682         0,              /*  5 => MV_LEFT  | MV_UP   */
6683         2,              /*  6 => MV_RIGHT | MV_UP   */
6684         -1,             /*  7 => (invalid)          */
6685         5,              /*  8 =>            MV_DOWN */
6686         6,              /*  9 => MV_LEFT  | MV_DOWN */
6687         4,              /* 10 => MV_RIGHT | MV_DOWN */
6688         -1,             /* 11 => (invalid)          */
6689         -1,             /* 12 => (invalid)          */
6690         -1,             /* 13 => (invalid)          */
6691         -1,             /* 14 => (invalid)          */
6692         -1,             /* 15 => (invalid)          */
6693       };
6694       static struct
6695       {
6696         int dx, dy;
6697         int dir;
6698       } check_xy[8] =
6699       {
6700         { -1, -1,       MV_LEFT  | MV_UP   },
6701         {  0, -1,                  MV_UP   },
6702         { +1, -1,       MV_RIGHT | MV_UP   },
6703         { +1,  0,       MV_RIGHT           },
6704         { +1, +1,       MV_RIGHT | MV_DOWN },
6705         {  0, +1,                  MV_DOWN },
6706         { -1, +1,       MV_LEFT  | MV_DOWN },
6707         { -1,  0,       MV_LEFT            },
6708       };
6709       int start_pos, check_order;
6710       boolean can_clone = FALSE;
6711       int i;
6712
6713       /* check if there is any free field around current position */
6714       for (i = 0; i < 8; i++)
6715       {
6716         int newx = x + check_xy[i].dx;
6717         int newy = y + check_xy[i].dy;
6718
6719         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6720         {
6721           can_clone = TRUE;
6722
6723           break;
6724         }
6725       }
6726
6727       if (can_clone)            /* randomly find an element to clone */
6728       {
6729         can_clone = FALSE;
6730
6731         start_pos = check_pos[RND(8)];
6732         check_order = (RND(2) ? -1 : +1);
6733
6734         for (i = 0; i < 8; i++)
6735         {
6736           int pos_raw = start_pos + i * check_order;
6737           int pos = (pos_raw + 8) % 8;
6738           int newx = x + check_xy[pos].dx;
6739           int newy = y + check_xy[pos].dy;
6740
6741           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6742           {
6743             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6744             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6745
6746             Store[x][y] = Feld[newx][newy];
6747
6748             can_clone = TRUE;
6749
6750             break;
6751           }
6752         }
6753       }
6754
6755       if (can_clone)            /* randomly find a direction to move */
6756       {
6757         can_clone = FALSE;
6758
6759         start_pos = check_pos[RND(8)];
6760         check_order = (RND(2) ? -1 : +1);
6761
6762         for (i = 0; i < 8; i++)
6763         {
6764           int pos_raw = start_pos + i * check_order;
6765           int pos = (pos_raw + 8) % 8;
6766           int newx = x + check_xy[pos].dx;
6767           int newy = y + check_xy[pos].dy;
6768           int new_move_dir = check_xy[pos].dir;
6769
6770           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6771           {
6772             MovDir[x][y] = new_move_dir;
6773             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6774
6775             can_clone = TRUE;
6776
6777             break;
6778           }
6779         }
6780       }
6781
6782       if (can_clone)            /* cloning and moving successful */
6783         return;
6784
6785       /* cannot clone -- try to move towards player */
6786
6787       start_pos = check_pos[MovDir[x][y] & 0x0f];
6788       check_order = (RND(2) ? -1 : +1);
6789
6790       for (i = 0; i < 3; i++)
6791       {
6792         /* first check start_pos, then previous/next or (next/previous) pos */
6793         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6794         int pos = (pos_raw + 8) % 8;
6795         int newx = x + check_xy[pos].dx;
6796         int newy = y + check_xy[pos].dy;
6797         int new_move_dir = check_xy[pos].dir;
6798
6799         if (IS_PLAYER(newx, newy))
6800           break;
6801
6802         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6803         {
6804           MovDir[x][y] = new_move_dir;
6805           MovDelay[x][y] = level.android_move_time * 8 + 1;
6806
6807           break;
6808         }
6809       }
6810     }
6811   }
6812   else if (move_pattern == MV_TURNING_LEFT ||
6813            move_pattern == MV_TURNING_RIGHT ||
6814            move_pattern == MV_TURNING_LEFT_RIGHT ||
6815            move_pattern == MV_TURNING_RIGHT_LEFT ||
6816            move_pattern == MV_TURNING_RANDOM ||
6817            move_pattern == MV_ALL_DIRECTIONS)
6818   {
6819     boolean can_turn_left =
6820       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6821     boolean can_turn_right =
6822       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6823
6824     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6825       return;
6826
6827     if (move_pattern == MV_TURNING_LEFT)
6828       MovDir[x][y] = left_dir;
6829     else if (move_pattern == MV_TURNING_RIGHT)
6830       MovDir[x][y] = right_dir;
6831     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6832       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6833     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6834       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6835     else if (move_pattern == MV_TURNING_RANDOM)
6836       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6837                       can_turn_right && !can_turn_left ? right_dir :
6838                       RND(2) ? left_dir : right_dir);
6839     else if (can_turn_left && can_turn_right)
6840       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6841     else if (can_turn_left)
6842       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6843     else if (can_turn_right)
6844       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6845     else
6846       MovDir[x][y] = back_dir;
6847
6848     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6849   }
6850   else if (move_pattern == MV_HORIZONTAL ||
6851            move_pattern == MV_VERTICAL)
6852   {
6853     if (move_pattern & old_move_dir)
6854       MovDir[x][y] = back_dir;
6855     else if (move_pattern == MV_HORIZONTAL)
6856       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6857     else if (move_pattern == MV_VERTICAL)
6858       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6859
6860     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6861   }
6862   else if (move_pattern & MV_ANY_DIRECTION)
6863   {
6864     MovDir[x][y] = move_pattern;
6865     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6866   }
6867   else if (move_pattern & MV_WIND_DIRECTION)
6868   {
6869     MovDir[x][y] = game.wind_direction;
6870     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6871   }
6872   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6873   {
6874     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6875       MovDir[x][y] = left_dir;
6876     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6877       MovDir[x][y] = right_dir;
6878
6879     if (MovDir[x][y] != old_move_dir)
6880       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6881   }
6882   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6883   {
6884     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6885       MovDir[x][y] = right_dir;
6886     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6887       MovDir[x][y] = left_dir;
6888
6889     if (MovDir[x][y] != old_move_dir)
6890       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6891   }
6892   else if (move_pattern == MV_TOWARDS_PLAYER ||
6893            move_pattern == MV_AWAY_FROM_PLAYER)
6894   {
6895     int attr_x = -1, attr_y = -1;
6896     int newx, newy;
6897     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6898
6899     if (AllPlayersGone)
6900     {
6901       attr_x = ExitX;
6902       attr_y = ExitY;
6903     }
6904     else
6905     {
6906       int i;
6907
6908       for (i = 0; i < MAX_PLAYERS; i++)
6909       {
6910         struct PlayerInfo *player = &stored_player[i];
6911         int jx = player->jx, jy = player->jy;
6912
6913         if (!player->active)
6914           continue;
6915
6916         if (attr_x == -1 ||
6917             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6918         {
6919           attr_x = jx;
6920           attr_y = jy;
6921         }
6922       }
6923     }
6924
6925     MovDir[x][y] = MV_NONE;
6926     if (attr_x < x)
6927       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6928     else if (attr_x > x)
6929       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6930     if (attr_y < y)
6931       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6932     else if (attr_y > y)
6933       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6934
6935     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6936
6937     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6938     {
6939       boolean first_horiz = RND(2);
6940       int new_move_dir = MovDir[x][y];
6941
6942       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6943       {
6944         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6945         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6946
6947         return;
6948       }
6949
6950       MovDir[x][y] =
6951         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6952       Moving2Blocked(x, y, &newx, &newy);
6953
6954       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6955         return;
6956
6957       MovDir[x][y] =
6958         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6959       Moving2Blocked(x, y, &newx, &newy);
6960
6961       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6962         return;
6963
6964       MovDir[x][y] = old_move_dir;
6965     }
6966   }
6967   else if (move_pattern == MV_WHEN_PUSHED ||
6968            move_pattern == MV_WHEN_DROPPED)
6969   {
6970     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6971       MovDir[x][y] = MV_NONE;
6972
6973     MovDelay[x][y] = 0;
6974   }
6975   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6976   {
6977     static int test_xy[7][2] =
6978     {
6979       { 0, -1 },
6980       { -1, 0 },
6981       { +1, 0 },
6982       { 0, +1 },
6983       { 0, -1 },
6984       { -1, 0 },
6985       { +1, 0 },
6986     };
6987     static int test_dir[7] =
6988     {
6989       MV_UP,
6990       MV_LEFT,
6991       MV_RIGHT,
6992       MV_DOWN,
6993       MV_UP,
6994       MV_LEFT,
6995       MV_RIGHT,
6996     };
6997     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6998     int move_preference = -1000000;     /* start with very low preference */
6999     int new_move_dir = MV_NONE;
7000     int start_test = RND(4);
7001     int i;
7002
7003     for (i = 0; i < NUM_DIRECTIONS; i++)
7004     {
7005       int move_dir = test_dir[start_test + i];
7006       int move_dir_preference;
7007
7008       xx = x + test_xy[start_test + i][0];
7009       yy = y + test_xy[start_test + i][1];
7010
7011       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7012           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7013       {
7014         new_move_dir = move_dir;
7015
7016         break;
7017       }
7018
7019       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7020         continue;
7021
7022       move_dir_preference = -1 * RunnerVisit[xx][yy];
7023       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7024         move_dir_preference = PlayerVisit[xx][yy];
7025
7026       if (move_dir_preference > move_preference)
7027       {
7028         /* prefer field that has not been visited for the longest time */
7029         move_preference = move_dir_preference;
7030         new_move_dir = move_dir;
7031       }
7032       else if (move_dir_preference == move_preference &&
7033                move_dir == old_move_dir)
7034       {
7035         /* prefer last direction when all directions are preferred equally */
7036         move_preference = move_dir_preference;
7037         new_move_dir = move_dir;
7038       }
7039     }
7040
7041     MovDir[x][y] = new_move_dir;
7042     if (old_move_dir != new_move_dir)
7043       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7044   }
7045 }
7046
7047 static void TurnRound(int x, int y)
7048 {
7049   int direction = MovDir[x][y];
7050
7051   TurnRoundExt(x, y);
7052
7053   GfxDir[x][y] = MovDir[x][y];
7054
7055   if (direction != MovDir[x][y])
7056     GfxFrame[x][y] = 0;
7057
7058   if (MovDelay[x][y])
7059     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7060
7061   ResetGfxFrame(x, y);
7062 }
7063
7064 static boolean JustBeingPushed(int x, int y)
7065 {
7066   int i;
7067
7068   for (i = 0; i < MAX_PLAYERS; i++)
7069   {
7070     struct PlayerInfo *player = &stored_player[i];
7071
7072     if (player->active && player->is_pushing && player->MovPos)
7073     {
7074       int next_jx = player->jx + (player->jx - player->last_jx);
7075       int next_jy = player->jy + (player->jy - player->last_jy);
7076
7077       if (x == next_jx && y == next_jy)
7078         return TRUE;
7079     }
7080   }
7081
7082   return FALSE;
7083 }
7084
7085 void StartMoving(int x, int y)
7086 {
7087   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7088   int element = Feld[x][y];
7089
7090   if (Stop[x][y])
7091     return;
7092
7093   if (MovDelay[x][y] == 0)
7094     GfxAction[x][y] = ACTION_DEFAULT;
7095
7096   if (CAN_FALL(element) && y < lev_fieldy - 1)
7097   {
7098     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7099         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7100       if (JustBeingPushed(x, y))
7101         return;
7102
7103     if (element == EL_QUICKSAND_FULL)
7104     {
7105       if (IS_FREE(x, y + 1))
7106       {
7107         InitMovingField(x, y, MV_DOWN);
7108         started_moving = TRUE;
7109
7110         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7111 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7112         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7113           Store[x][y] = EL_ROCK;
7114 #else
7115         Store[x][y] = EL_ROCK;
7116 #endif
7117
7118         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7119       }
7120       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7121       {
7122         if (!MovDelay[x][y])
7123         {
7124           MovDelay[x][y] = TILEY + 1;
7125
7126           ResetGfxAnimation(x, y);
7127           ResetGfxAnimation(x, y + 1);
7128         }
7129
7130         if (MovDelay[x][y])
7131         {
7132           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7133           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7134
7135           MovDelay[x][y]--;
7136           if (MovDelay[x][y])
7137             return;
7138         }
7139
7140         Feld[x][y] = EL_QUICKSAND_EMPTY;
7141         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7142         Store[x][y + 1] = Store[x][y];
7143         Store[x][y] = 0;
7144
7145         PlayLevelSoundAction(x, y, ACTION_FILLING);
7146       }
7147       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7148       {
7149         if (!MovDelay[x][y])
7150         {
7151           MovDelay[x][y] = TILEY + 1;
7152
7153           ResetGfxAnimation(x, y);
7154           ResetGfxAnimation(x, y + 1);
7155         }
7156
7157         if (MovDelay[x][y])
7158         {
7159           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7160           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7161
7162           MovDelay[x][y]--;
7163           if (MovDelay[x][y])
7164             return;
7165         }
7166
7167         Feld[x][y] = EL_QUICKSAND_EMPTY;
7168         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7169         Store[x][y + 1] = Store[x][y];
7170         Store[x][y] = 0;
7171
7172         PlayLevelSoundAction(x, y, ACTION_FILLING);
7173       }
7174     }
7175     else if (element == EL_QUICKSAND_FAST_FULL)
7176     {
7177       if (IS_FREE(x, y + 1))
7178       {
7179         InitMovingField(x, y, MV_DOWN);
7180         started_moving = TRUE;
7181
7182         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7183 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7184         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7185           Store[x][y] = EL_ROCK;
7186 #else
7187         Store[x][y] = EL_ROCK;
7188 #endif
7189
7190         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7191       }
7192       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7193       {
7194         if (!MovDelay[x][y])
7195         {
7196           MovDelay[x][y] = TILEY + 1;
7197
7198           ResetGfxAnimation(x, y);
7199           ResetGfxAnimation(x, y + 1);
7200         }
7201
7202         if (MovDelay[x][y])
7203         {
7204           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7205           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7206
7207           MovDelay[x][y]--;
7208           if (MovDelay[x][y])
7209             return;
7210         }
7211
7212         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7213         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7214         Store[x][y + 1] = Store[x][y];
7215         Store[x][y] = 0;
7216
7217         PlayLevelSoundAction(x, y, ACTION_FILLING);
7218       }
7219       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7220       {
7221         if (!MovDelay[x][y])
7222         {
7223           MovDelay[x][y] = TILEY + 1;
7224
7225           ResetGfxAnimation(x, y);
7226           ResetGfxAnimation(x, y + 1);
7227         }
7228
7229         if (MovDelay[x][y])
7230         {
7231           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7232           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7233
7234           MovDelay[x][y]--;
7235           if (MovDelay[x][y])
7236             return;
7237         }
7238
7239         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7240         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7241         Store[x][y + 1] = Store[x][y];
7242         Store[x][y] = 0;
7243
7244         PlayLevelSoundAction(x, y, ACTION_FILLING);
7245       }
7246     }
7247     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7248              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7249     {
7250       InitMovingField(x, y, MV_DOWN);
7251       started_moving = TRUE;
7252
7253       Feld[x][y] = EL_QUICKSAND_FILLING;
7254       Store[x][y] = element;
7255
7256       PlayLevelSoundAction(x, y, ACTION_FILLING);
7257     }
7258     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7259              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7260     {
7261       InitMovingField(x, y, MV_DOWN);
7262       started_moving = TRUE;
7263
7264       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7265       Store[x][y] = element;
7266
7267       PlayLevelSoundAction(x, y, ACTION_FILLING);
7268     }
7269     else if (element == EL_MAGIC_WALL_FULL)
7270     {
7271       if (IS_FREE(x, y + 1))
7272       {
7273         InitMovingField(x, y, MV_DOWN);
7274         started_moving = TRUE;
7275
7276         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7277         Store[x][y] = EL_CHANGED(Store[x][y]);
7278       }
7279       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7280       {
7281         if (!MovDelay[x][y])
7282           MovDelay[x][y] = TILEY / 4 + 1;
7283
7284         if (MovDelay[x][y])
7285         {
7286           MovDelay[x][y]--;
7287           if (MovDelay[x][y])
7288             return;
7289         }
7290
7291         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7292         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7293         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7294         Store[x][y] = 0;
7295       }
7296     }
7297     else if (element == EL_BD_MAGIC_WALL_FULL)
7298     {
7299       if (IS_FREE(x, y + 1))
7300       {
7301         InitMovingField(x, y, MV_DOWN);
7302         started_moving = TRUE;
7303
7304         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7305         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7306       }
7307       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7308       {
7309         if (!MovDelay[x][y])
7310           MovDelay[x][y] = TILEY / 4 + 1;
7311
7312         if (MovDelay[x][y])
7313         {
7314           MovDelay[x][y]--;
7315           if (MovDelay[x][y])
7316             return;
7317         }
7318
7319         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7320         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7321         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7322         Store[x][y] = 0;
7323       }
7324     }
7325     else if (element == EL_DC_MAGIC_WALL_FULL)
7326     {
7327       if (IS_FREE(x, y + 1))
7328       {
7329         InitMovingField(x, y, MV_DOWN);
7330         started_moving = TRUE;
7331
7332         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7333         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7334       }
7335       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7336       {
7337         if (!MovDelay[x][y])
7338           MovDelay[x][y] = TILEY / 4 + 1;
7339
7340         if (MovDelay[x][y])
7341         {
7342           MovDelay[x][y]--;
7343           if (MovDelay[x][y])
7344             return;
7345         }
7346
7347         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7348         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7349         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7350         Store[x][y] = 0;
7351       }
7352     }
7353     else if ((CAN_PASS_MAGIC_WALL(element) &&
7354               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7355                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7356              (CAN_PASS_DC_MAGIC_WALL(element) &&
7357               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7358
7359     {
7360       InitMovingField(x, y, MV_DOWN);
7361       started_moving = TRUE;
7362
7363       Feld[x][y] =
7364         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7365          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7366          EL_DC_MAGIC_WALL_FILLING);
7367       Store[x][y] = element;
7368     }
7369     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7370     {
7371       SplashAcid(x, y + 1);
7372
7373       InitMovingField(x, y, MV_DOWN);
7374       started_moving = TRUE;
7375
7376       Store[x][y] = EL_ACID;
7377     }
7378     else if (
7379              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7380               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7381              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7382               CAN_FALL(element) && WasJustFalling[x][y] &&
7383               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7384
7385              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7386               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7387               (Feld[x][y + 1] == EL_BLOCKED)))
7388     {
7389       /* this is needed for a special case not covered by calling "Impact()"
7390          from "ContinueMoving()": if an element moves to a tile directly below
7391          another element which was just falling on that tile (which was empty
7392          in the previous frame), the falling element above would just stop
7393          instead of smashing the element below (in previous version, the above
7394          element was just checked for "moving" instead of "falling", resulting
7395          in incorrect smashes caused by horizontal movement of the above
7396          element; also, the case of the player being the element to smash was
7397          simply not covered here... :-/ ) */
7398
7399       CheckCollision[x][y] = 0;
7400       CheckImpact[x][y] = 0;
7401
7402       Impact(x, y);
7403     }
7404     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7405     {
7406       if (MovDir[x][y] == MV_NONE)
7407       {
7408         InitMovingField(x, y, MV_DOWN);
7409         started_moving = TRUE;
7410       }
7411     }
7412     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7413     {
7414       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7415         MovDir[x][y] = MV_DOWN;
7416
7417       InitMovingField(x, y, MV_DOWN);
7418       started_moving = TRUE;
7419     }
7420     else if (element == EL_AMOEBA_DROP)
7421     {
7422       Feld[x][y] = EL_AMOEBA_GROWING;
7423       Store[x][y] = EL_AMOEBA_WET;
7424     }
7425     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7426               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7427              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7428              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7429     {
7430       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7431                                 (IS_FREE(x - 1, y + 1) ||
7432                                  Feld[x - 1][y + 1] == EL_ACID));
7433       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7434                                 (IS_FREE(x + 1, y + 1) ||
7435                                  Feld[x + 1][y + 1] == EL_ACID));
7436       boolean can_fall_any  = (can_fall_left || can_fall_right);
7437       boolean can_fall_both = (can_fall_left && can_fall_right);
7438       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7439
7440       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7441       {
7442         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7443           can_fall_right = FALSE;
7444         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7445           can_fall_left = FALSE;
7446         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7447           can_fall_right = FALSE;
7448         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7449           can_fall_left = FALSE;
7450
7451         can_fall_any  = (can_fall_left || can_fall_right);
7452         can_fall_both = FALSE;
7453       }
7454
7455       if (can_fall_both)
7456       {
7457         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7458           can_fall_right = FALSE;       /* slip down on left side */
7459         else
7460           can_fall_left = !(can_fall_right = RND(2));
7461
7462         can_fall_both = FALSE;
7463       }
7464
7465       if (can_fall_any)
7466       {
7467         /* if not determined otherwise, prefer left side for slipping down */
7468         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7469         started_moving = TRUE;
7470       }
7471     }
7472     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7473     {
7474       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7475       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7476       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7477       int belt_dir = game.belt_dir[belt_nr];
7478
7479       if ((belt_dir == MV_LEFT  && left_is_free) ||
7480           (belt_dir == MV_RIGHT && right_is_free))
7481       {
7482         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7483
7484         InitMovingField(x, y, belt_dir);
7485         started_moving = TRUE;
7486
7487         Pushed[x][y] = TRUE;
7488         Pushed[nextx][y] = TRUE;
7489
7490         GfxAction[x][y] = ACTION_DEFAULT;
7491       }
7492       else
7493       {
7494         MovDir[x][y] = 0;       /* if element was moving, stop it */
7495       }
7496     }
7497   }
7498
7499   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7500   if (CAN_MOVE(element) && !started_moving)
7501   {
7502     int move_pattern = element_info[element].move_pattern;
7503     int newx, newy;
7504
7505     Moving2Blocked(x, y, &newx, &newy);
7506
7507     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7508       return;
7509
7510     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7511         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7512     {
7513       WasJustMoving[x][y] = 0;
7514       CheckCollision[x][y] = 0;
7515
7516       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7517
7518       if (Feld[x][y] != element)        /* element has changed */
7519         return;
7520     }
7521
7522     if (!MovDelay[x][y])        /* start new movement phase */
7523     {
7524       /* all objects that can change their move direction after each step
7525          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7526
7527       if (element != EL_YAMYAM &&
7528           element != EL_DARK_YAMYAM &&
7529           element != EL_PACMAN &&
7530           !(move_pattern & MV_ANY_DIRECTION) &&
7531           move_pattern != MV_TURNING_LEFT &&
7532           move_pattern != MV_TURNING_RIGHT &&
7533           move_pattern != MV_TURNING_LEFT_RIGHT &&
7534           move_pattern != MV_TURNING_RIGHT_LEFT &&
7535           move_pattern != MV_TURNING_RANDOM)
7536       {
7537         TurnRound(x, y);
7538
7539         if (MovDelay[x][y] && (element == EL_BUG ||
7540                                element == EL_SPACESHIP ||
7541                                element == EL_SP_SNIKSNAK ||
7542                                element == EL_SP_ELECTRON ||
7543                                element == EL_MOLE))
7544           TEST_DrawLevelField(x, y);
7545       }
7546     }
7547
7548     if (MovDelay[x][y])         /* wait some time before next movement */
7549     {
7550       MovDelay[x][y]--;
7551
7552       if (element == EL_ROBOT ||
7553           element == EL_YAMYAM ||
7554           element == EL_DARK_YAMYAM)
7555       {
7556         DrawLevelElementAnimationIfNeeded(x, y, element);
7557         PlayLevelSoundAction(x, y, ACTION_WAITING);
7558       }
7559       else if (element == EL_SP_ELECTRON)
7560         DrawLevelElementAnimationIfNeeded(x, y, element);
7561       else if (element == EL_DRAGON)
7562       {
7563         int i;
7564         int dir = MovDir[x][y];
7565         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7566         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7567         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7568                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7569                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7570                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7571         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7572
7573         GfxAction[x][y] = ACTION_ATTACKING;
7574
7575         if (IS_PLAYER(x, y))
7576           DrawPlayerField(x, y);
7577         else
7578           TEST_DrawLevelField(x, y);
7579
7580         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7581
7582         for (i = 1; i <= 3; i++)
7583         {
7584           int xx = x + i * dx;
7585           int yy = y + i * dy;
7586           int sx = SCREENX(xx);
7587           int sy = SCREENY(yy);
7588           int flame_graphic = graphic + (i - 1);
7589
7590           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7591             break;
7592
7593           if (MovDelay[x][y])
7594           {
7595             int flamed = MovingOrBlocked2Element(xx, yy);
7596
7597             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7598               Bang(xx, yy);
7599             else
7600               RemoveMovingField(xx, yy);
7601
7602             ChangeDelay[xx][yy] = 0;
7603
7604             Feld[xx][yy] = EL_FLAMES;
7605
7606             if (IN_SCR_FIELD(sx, sy))
7607             {
7608               TEST_DrawLevelFieldCrumbled(xx, yy);
7609               DrawGraphic(sx, sy, flame_graphic, frame);
7610             }
7611           }
7612           else
7613           {
7614             if (Feld[xx][yy] == EL_FLAMES)
7615               Feld[xx][yy] = EL_EMPTY;
7616             TEST_DrawLevelField(xx, yy);
7617           }
7618         }
7619       }
7620
7621       if (MovDelay[x][y])       /* element still has to wait some time */
7622       {
7623         PlayLevelSoundAction(x, y, ACTION_WAITING);
7624
7625         return;
7626       }
7627     }
7628
7629     /* now make next step */
7630
7631     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7632
7633     if (DONT_COLLIDE_WITH(element) &&
7634         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7635         !PLAYER_ENEMY_PROTECTED(newx, newy))
7636     {
7637       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7638
7639       return;
7640     }
7641
7642     else if (CAN_MOVE_INTO_ACID(element) &&
7643              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7644              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7645              (MovDir[x][y] == MV_DOWN ||
7646               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7647     {
7648       SplashAcid(newx, newy);
7649       Store[x][y] = EL_ACID;
7650     }
7651     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7652     {
7653       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7654           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7655           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7656           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7657       {
7658         RemoveField(x, y);
7659         TEST_DrawLevelField(x, y);
7660
7661         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7662         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7663           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7664
7665         local_player->friends_still_needed--;
7666         if (!local_player->friends_still_needed &&
7667             !local_player->GameOver && AllPlayersGone)
7668           PlayerWins(local_player);
7669
7670         return;
7671       }
7672       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7673       {
7674         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7675           TEST_DrawLevelField(newx, newy);
7676         else
7677           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7678       }
7679       else if (!IS_FREE(newx, newy))
7680       {
7681         GfxAction[x][y] = ACTION_WAITING;
7682
7683         if (IS_PLAYER(x, y))
7684           DrawPlayerField(x, y);
7685         else
7686           TEST_DrawLevelField(x, y);
7687
7688         return;
7689       }
7690     }
7691     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7692     {
7693       if (IS_FOOD_PIG(Feld[newx][newy]))
7694       {
7695         if (IS_MOVING(newx, newy))
7696           RemoveMovingField(newx, newy);
7697         else
7698         {
7699           Feld[newx][newy] = EL_EMPTY;
7700           TEST_DrawLevelField(newx, newy);
7701         }
7702
7703         PlayLevelSound(x, y, SND_PIG_DIGGING);
7704       }
7705       else if (!IS_FREE(newx, newy))
7706       {
7707         if (IS_PLAYER(x, y))
7708           DrawPlayerField(x, y);
7709         else
7710           TEST_DrawLevelField(x, y);
7711
7712         return;
7713       }
7714     }
7715     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7716     {
7717       if (Store[x][y] != EL_EMPTY)
7718       {
7719         boolean can_clone = FALSE;
7720         int xx, yy;
7721
7722         /* check if element to clone is still there */
7723         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7724         {
7725           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7726           {
7727             can_clone = TRUE;
7728
7729             break;
7730           }
7731         }
7732
7733         /* cannot clone or target field not free anymore -- do not clone */
7734         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7735           Store[x][y] = EL_EMPTY;
7736       }
7737
7738       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7739       {
7740         if (IS_MV_DIAGONAL(MovDir[x][y]))
7741         {
7742           int diagonal_move_dir = MovDir[x][y];
7743           int stored = Store[x][y];
7744           int change_delay = 8;
7745           int graphic;
7746
7747           /* android is moving diagonally */
7748
7749           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7750
7751           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7752           GfxElement[x][y] = EL_EMC_ANDROID;
7753           GfxAction[x][y] = ACTION_SHRINKING;
7754           GfxDir[x][y] = diagonal_move_dir;
7755           ChangeDelay[x][y] = change_delay;
7756
7757           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7758                                    GfxDir[x][y]);
7759
7760           DrawLevelGraphicAnimation(x, y, graphic);
7761           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7762
7763           if (Feld[newx][newy] == EL_ACID)
7764           {
7765             SplashAcid(newx, newy);
7766
7767             return;
7768           }
7769
7770           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7771
7772           Store[newx][newy] = EL_EMC_ANDROID;
7773           GfxElement[newx][newy] = EL_EMC_ANDROID;
7774           GfxAction[newx][newy] = ACTION_GROWING;
7775           GfxDir[newx][newy] = diagonal_move_dir;
7776           ChangeDelay[newx][newy] = change_delay;
7777
7778           graphic = el_act_dir2img(GfxElement[newx][newy],
7779                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7780
7781           DrawLevelGraphicAnimation(newx, newy, graphic);
7782           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7783
7784           return;
7785         }
7786         else
7787         {
7788           Feld[newx][newy] = EL_EMPTY;
7789           TEST_DrawLevelField(newx, newy);
7790
7791           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7792         }
7793       }
7794       else if (!IS_FREE(newx, newy))
7795       {
7796         return;
7797       }
7798     }
7799     else if (IS_CUSTOM_ELEMENT(element) &&
7800              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7801     {
7802       if (!DigFieldByCE(newx, newy, element))
7803         return;
7804
7805       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7806       {
7807         RunnerVisit[x][y] = FrameCounter;
7808         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7809       }
7810     }
7811     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7812     {
7813       if (!IS_FREE(newx, newy))
7814       {
7815         if (IS_PLAYER(x, y))
7816           DrawPlayerField(x, y);
7817         else
7818           TEST_DrawLevelField(x, y);
7819
7820         return;
7821       }
7822       else
7823       {
7824         boolean wanna_flame = !RND(10);
7825         int dx = newx - x, dy = newy - y;
7826         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7827         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7828         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7829                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7830         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7831                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7832
7833         if ((wanna_flame ||
7834              IS_CLASSIC_ENEMY(element1) ||
7835              IS_CLASSIC_ENEMY(element2)) &&
7836             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7837             element1 != EL_FLAMES && element2 != EL_FLAMES)
7838         {
7839           ResetGfxAnimation(x, y);
7840           GfxAction[x][y] = ACTION_ATTACKING;
7841
7842           if (IS_PLAYER(x, y))
7843             DrawPlayerField(x, y);
7844           else
7845             TEST_DrawLevelField(x, y);
7846
7847           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7848
7849           MovDelay[x][y] = 50;
7850
7851           Feld[newx][newy] = EL_FLAMES;
7852           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7853             Feld[newx1][newy1] = EL_FLAMES;
7854           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7855             Feld[newx2][newy2] = EL_FLAMES;
7856
7857           return;
7858         }
7859       }
7860     }
7861     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7862              Feld[newx][newy] == EL_DIAMOND)
7863     {
7864       if (IS_MOVING(newx, newy))
7865         RemoveMovingField(newx, newy);
7866       else
7867       {
7868         Feld[newx][newy] = EL_EMPTY;
7869         TEST_DrawLevelField(newx, newy);
7870       }
7871
7872       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7873     }
7874     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7875              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7876     {
7877       if (AmoebaNr[newx][newy])
7878       {
7879         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7880         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7881             Feld[newx][newy] == EL_BD_AMOEBA)
7882           AmoebaCnt[AmoebaNr[newx][newy]]--;
7883       }
7884
7885       if (IS_MOVING(newx, newy))
7886       {
7887         RemoveMovingField(newx, newy);
7888       }
7889       else
7890       {
7891         Feld[newx][newy] = EL_EMPTY;
7892         TEST_DrawLevelField(newx, newy);
7893       }
7894
7895       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7896     }
7897     else if ((element == EL_PACMAN || element == EL_MOLE)
7898              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7899     {
7900       if (AmoebaNr[newx][newy])
7901       {
7902         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7903         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7904             Feld[newx][newy] == EL_BD_AMOEBA)
7905           AmoebaCnt[AmoebaNr[newx][newy]]--;
7906       }
7907
7908       if (element == EL_MOLE)
7909       {
7910         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7911         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7912
7913         ResetGfxAnimation(x, y);
7914         GfxAction[x][y] = ACTION_DIGGING;
7915         TEST_DrawLevelField(x, y);
7916
7917         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7918
7919         return;                         /* wait for shrinking amoeba */
7920       }
7921       else      /* element == EL_PACMAN */
7922       {
7923         Feld[newx][newy] = EL_EMPTY;
7924         TEST_DrawLevelField(newx, newy);
7925         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7926       }
7927     }
7928     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7929              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7930               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7931     {
7932       /* wait for shrinking amoeba to completely disappear */
7933       return;
7934     }
7935     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7936     {
7937       /* object was running against a wall */
7938
7939       TurnRound(x, y);
7940
7941       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7942         DrawLevelElementAnimation(x, y, element);
7943
7944       if (DONT_TOUCH(element))
7945         TestIfBadThingTouchesPlayer(x, y);
7946
7947       return;
7948     }
7949
7950     InitMovingField(x, y, MovDir[x][y]);
7951
7952     PlayLevelSoundAction(x, y, ACTION_MOVING);
7953   }
7954
7955   if (MovDir[x][y])
7956     ContinueMoving(x, y);
7957 }
7958
7959 void ContinueMoving(int x, int y)
7960 {
7961   int element = Feld[x][y];
7962   struct ElementInfo *ei = &element_info[element];
7963   int direction = MovDir[x][y];
7964   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7965   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7966   int newx = x + dx, newy = y + dy;
7967   int stored = Store[x][y];
7968   int stored_new = Store[newx][newy];
7969   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7970   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7971   boolean last_line = (newy == lev_fieldy - 1);
7972
7973   MovPos[x][y] += getElementMoveStepsize(x, y);
7974
7975   if (pushed_by_player) /* special case: moving object pushed by player */
7976     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7977
7978   if (ABS(MovPos[x][y]) < TILEX)
7979   {
7980     TEST_DrawLevelField(x, y);
7981
7982     return;     /* element is still moving */
7983   }
7984
7985   /* element reached destination field */
7986
7987   Feld[x][y] = EL_EMPTY;
7988   Feld[newx][newy] = element;
7989   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7990
7991   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7992   {
7993     element = Feld[newx][newy] = EL_ACID;
7994   }
7995   else if (element == EL_MOLE)
7996   {
7997     Feld[x][y] = EL_SAND;
7998
7999     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8000   }
8001   else if (element == EL_QUICKSAND_FILLING)
8002   {
8003     element = Feld[newx][newy] = get_next_element(element);
8004     Store[newx][newy] = Store[x][y];
8005   }
8006   else if (element == EL_QUICKSAND_EMPTYING)
8007   {
8008     Feld[x][y] = get_next_element(element);
8009     element = Feld[newx][newy] = Store[x][y];
8010   }
8011   else if (element == EL_QUICKSAND_FAST_FILLING)
8012   {
8013     element = Feld[newx][newy] = get_next_element(element);
8014     Store[newx][newy] = Store[x][y];
8015   }
8016   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8017   {
8018     Feld[x][y] = get_next_element(element);
8019     element = Feld[newx][newy] = Store[x][y];
8020   }
8021   else if (element == EL_MAGIC_WALL_FILLING)
8022   {
8023     element = Feld[newx][newy] = get_next_element(element);
8024     if (!game.magic_wall_active)
8025       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8026     Store[newx][newy] = Store[x][y];
8027   }
8028   else if (element == EL_MAGIC_WALL_EMPTYING)
8029   {
8030     Feld[x][y] = get_next_element(element);
8031     if (!game.magic_wall_active)
8032       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8033     element = Feld[newx][newy] = Store[x][y];
8034
8035     InitField(newx, newy, FALSE);
8036   }
8037   else if (element == EL_BD_MAGIC_WALL_FILLING)
8038   {
8039     element = Feld[newx][newy] = get_next_element(element);
8040     if (!game.magic_wall_active)
8041       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8042     Store[newx][newy] = Store[x][y];
8043   }
8044   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8045   {
8046     Feld[x][y] = get_next_element(element);
8047     if (!game.magic_wall_active)
8048       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8049     element = Feld[newx][newy] = Store[x][y];
8050
8051     InitField(newx, newy, FALSE);
8052   }
8053   else if (element == EL_DC_MAGIC_WALL_FILLING)
8054   {
8055     element = Feld[newx][newy] = get_next_element(element);
8056     if (!game.magic_wall_active)
8057       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8058     Store[newx][newy] = Store[x][y];
8059   }
8060   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8061   {
8062     Feld[x][y] = get_next_element(element);
8063     if (!game.magic_wall_active)
8064       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8065     element = Feld[newx][newy] = Store[x][y];
8066
8067     InitField(newx, newy, FALSE);
8068   }
8069   else if (element == EL_AMOEBA_DROPPING)
8070   {
8071     Feld[x][y] = get_next_element(element);
8072     element = Feld[newx][newy] = Store[x][y];
8073   }
8074   else if (element == EL_SOKOBAN_OBJECT)
8075   {
8076     if (Back[x][y])
8077       Feld[x][y] = Back[x][y];
8078
8079     if (Back[newx][newy])
8080       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8081
8082     Back[x][y] = Back[newx][newy] = 0;
8083   }
8084
8085   Store[x][y] = EL_EMPTY;
8086   MovPos[x][y] = 0;
8087   MovDir[x][y] = 0;
8088   MovDelay[x][y] = 0;
8089
8090   MovDelay[newx][newy] = 0;
8091
8092   if (CAN_CHANGE_OR_HAS_ACTION(element))
8093   {
8094     /* copy element change control values to new field */
8095     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8096     ChangePage[newx][newy]  = ChangePage[x][y];
8097     ChangeCount[newx][newy] = ChangeCount[x][y];
8098     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8099   }
8100
8101   CustomValue[newx][newy] = CustomValue[x][y];
8102
8103   ChangeDelay[x][y] = 0;
8104   ChangePage[x][y] = -1;
8105   ChangeCount[x][y] = 0;
8106   ChangeEvent[x][y] = -1;
8107
8108   CustomValue[x][y] = 0;
8109
8110   /* copy animation control values to new field */
8111   GfxFrame[newx][newy]  = GfxFrame[x][y];
8112   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8113   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8114   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8115
8116   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8117
8118   /* some elements can leave other elements behind after moving */
8119   if (ei->move_leave_element != EL_EMPTY &&
8120       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8121       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8122   {
8123     int move_leave_element = ei->move_leave_element;
8124
8125     /* this makes it possible to leave the removed element again */
8126     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8127       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8128
8129     Feld[x][y] = move_leave_element;
8130
8131     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8132       MovDir[x][y] = direction;
8133
8134     InitField(x, y, FALSE);
8135
8136     if (GFX_CRUMBLED(Feld[x][y]))
8137       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8138
8139     if (ELEM_IS_PLAYER(move_leave_element))
8140       RelocatePlayer(x, y, move_leave_element);
8141   }
8142
8143   /* do this after checking for left-behind element */
8144   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8145
8146   if (!CAN_MOVE(element) ||
8147       (CAN_FALL(element) && direction == MV_DOWN &&
8148        (element == EL_SPRING ||
8149         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8150         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8151     GfxDir[x][y] = MovDir[newx][newy] = 0;
8152
8153   TEST_DrawLevelField(x, y);
8154   TEST_DrawLevelField(newx, newy);
8155
8156   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8157
8158   /* prevent pushed element from moving on in pushed direction */
8159   if (pushed_by_player && CAN_MOVE(element) &&
8160       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8161       !(element_info[element].move_pattern & direction))
8162     TurnRound(newx, newy);
8163
8164   /* prevent elements on conveyor belt from moving on in last direction */
8165   if (pushed_by_conveyor && CAN_FALL(element) &&
8166       direction & MV_HORIZONTAL)
8167     MovDir[newx][newy] = 0;
8168
8169   if (!pushed_by_player)
8170   {
8171     int nextx = newx + dx, nexty = newy + dy;
8172     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8173
8174     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8175
8176     if (CAN_FALL(element) && direction == MV_DOWN)
8177       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8178
8179     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8180       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8181
8182     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8183       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8184   }
8185
8186   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8187   {
8188     TestIfBadThingTouchesPlayer(newx, newy);
8189     TestIfBadThingTouchesFriend(newx, newy);
8190
8191     if (!IS_CUSTOM_ELEMENT(element))
8192       TestIfBadThingTouchesOtherBadThing(newx, newy);
8193   }
8194   else if (element == EL_PENGUIN)
8195     TestIfFriendTouchesBadThing(newx, newy);
8196
8197   if (DONT_GET_HIT_BY(element))
8198   {
8199     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8200   }
8201
8202   /* give the player one last chance (one more frame) to move away */
8203   if (CAN_FALL(element) && direction == MV_DOWN &&
8204       (last_line || (!IS_FREE(x, newy + 1) &&
8205                      (!IS_PLAYER(x, newy + 1) ||
8206                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8207     Impact(x, newy);
8208
8209   if (pushed_by_player && !game.use_change_when_pushing_bug)
8210   {
8211     int push_side = MV_DIR_OPPOSITE(direction);
8212     struct PlayerInfo *player = PLAYERINFO(x, y);
8213
8214     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8215                                player->index_bit, push_side);
8216     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8217                                         player->index_bit, push_side);
8218   }
8219
8220   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8221     MovDelay[newx][newy] = 1;
8222
8223   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8224
8225   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8226   TestIfElementHitsCustomElement(newx, newy, direction);
8227   TestIfPlayerTouchesCustomElement(newx, newy);
8228   TestIfElementTouchesCustomElement(newx, newy);
8229
8230   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8231       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8232     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8233                              MV_DIR_OPPOSITE(direction));
8234 }
8235
8236 int AmoebeNachbarNr(int ax, int ay)
8237 {
8238   int i;
8239   int element = Feld[ax][ay];
8240   int group_nr = 0;
8241   static int xy[4][2] =
8242   {
8243     { 0, -1 },
8244     { -1, 0 },
8245     { +1, 0 },
8246     { 0, +1 }
8247   };
8248
8249   for (i = 0; i < NUM_DIRECTIONS; i++)
8250   {
8251     int x = ax + xy[i][0];
8252     int y = ay + xy[i][1];
8253
8254     if (!IN_LEV_FIELD(x, y))
8255       continue;
8256
8257     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8258       group_nr = AmoebaNr[x][y];
8259   }
8260
8261   return group_nr;
8262 }
8263
8264 void AmoebenVereinigen(int ax, int ay)
8265 {
8266   int i, x, y, xx, yy;
8267   int new_group_nr = AmoebaNr[ax][ay];
8268   static int xy[4][2] =
8269   {
8270     { 0, -1 },
8271     { -1, 0 },
8272     { +1, 0 },
8273     { 0, +1 }
8274   };
8275
8276   if (new_group_nr == 0)
8277     return;
8278
8279   for (i = 0; i < NUM_DIRECTIONS; i++)
8280   {
8281     x = ax + xy[i][0];
8282     y = ay + xy[i][1];
8283
8284     if (!IN_LEV_FIELD(x, y))
8285       continue;
8286
8287     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8288          Feld[x][y] == EL_BD_AMOEBA ||
8289          Feld[x][y] == EL_AMOEBA_DEAD) &&
8290         AmoebaNr[x][y] != new_group_nr)
8291     {
8292       int old_group_nr = AmoebaNr[x][y];
8293
8294       if (old_group_nr == 0)
8295         return;
8296
8297       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8298       AmoebaCnt[old_group_nr] = 0;
8299       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8300       AmoebaCnt2[old_group_nr] = 0;
8301
8302       SCAN_PLAYFIELD(xx, yy)
8303       {
8304         if (AmoebaNr[xx][yy] == old_group_nr)
8305           AmoebaNr[xx][yy] = new_group_nr;
8306       }
8307     }
8308   }
8309 }
8310
8311 void AmoebeUmwandeln(int ax, int ay)
8312 {
8313   int i, x, y;
8314
8315   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8316   {
8317     int group_nr = AmoebaNr[ax][ay];
8318
8319 #ifdef DEBUG
8320     if (group_nr == 0)
8321     {
8322       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8323       printf("AmoebeUmwandeln(): This should never happen!\n");
8324       return;
8325     }
8326 #endif
8327
8328     SCAN_PLAYFIELD(x, y)
8329     {
8330       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8331       {
8332         AmoebaNr[x][y] = 0;
8333         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8334       }
8335     }
8336
8337     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8338                             SND_AMOEBA_TURNING_TO_GEM :
8339                             SND_AMOEBA_TURNING_TO_ROCK));
8340     Bang(ax, ay);
8341   }
8342   else
8343   {
8344     static int xy[4][2] =
8345     {
8346       { 0, -1 },
8347       { -1, 0 },
8348       { +1, 0 },
8349       { 0, +1 }
8350     };
8351
8352     for (i = 0; i < NUM_DIRECTIONS; i++)
8353     {
8354       x = ax + xy[i][0];
8355       y = ay + xy[i][1];
8356
8357       if (!IN_LEV_FIELD(x, y))
8358         continue;
8359
8360       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8361       {
8362         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8363                               SND_AMOEBA_TURNING_TO_GEM :
8364                               SND_AMOEBA_TURNING_TO_ROCK));
8365         Bang(x, y);
8366       }
8367     }
8368   }
8369 }
8370
8371 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8372 {
8373   int x, y;
8374   int group_nr = AmoebaNr[ax][ay];
8375   boolean done = FALSE;
8376
8377 #ifdef DEBUG
8378   if (group_nr == 0)
8379   {
8380     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8381     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8382     return;
8383   }
8384 #endif
8385
8386   SCAN_PLAYFIELD(x, y)
8387   {
8388     if (AmoebaNr[x][y] == group_nr &&
8389         (Feld[x][y] == EL_AMOEBA_DEAD ||
8390          Feld[x][y] == EL_BD_AMOEBA ||
8391          Feld[x][y] == EL_AMOEBA_GROWING))
8392     {
8393       AmoebaNr[x][y] = 0;
8394       Feld[x][y] = new_element;
8395       InitField(x, y, FALSE);
8396       TEST_DrawLevelField(x, y);
8397       done = TRUE;
8398     }
8399   }
8400
8401   if (done)
8402     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8403                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8404                             SND_BD_AMOEBA_TURNING_TO_GEM));
8405 }
8406
8407 void AmoebeWaechst(int x, int y)
8408 {
8409   static unsigned int sound_delay = 0;
8410   static unsigned int sound_delay_value = 0;
8411
8412   if (!MovDelay[x][y])          /* start new growing cycle */
8413   {
8414     MovDelay[x][y] = 7;
8415
8416     if (DelayReached(&sound_delay, sound_delay_value))
8417     {
8418       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8419       sound_delay_value = 30;
8420     }
8421   }
8422
8423   if (MovDelay[x][y])           /* wait some time before growing bigger */
8424   {
8425     MovDelay[x][y]--;
8426     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8427     {
8428       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8429                                            6 - MovDelay[x][y]);
8430
8431       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8432     }
8433
8434     if (!MovDelay[x][y])
8435     {
8436       Feld[x][y] = Store[x][y];
8437       Store[x][y] = 0;
8438       TEST_DrawLevelField(x, y);
8439     }
8440   }
8441 }
8442
8443 void AmoebaDisappearing(int x, int y)
8444 {
8445   static unsigned int sound_delay = 0;
8446   static unsigned int sound_delay_value = 0;
8447
8448   if (!MovDelay[x][y])          /* start new shrinking cycle */
8449   {
8450     MovDelay[x][y] = 7;
8451
8452     if (DelayReached(&sound_delay, sound_delay_value))
8453       sound_delay_value = 30;
8454   }
8455
8456   if (MovDelay[x][y])           /* wait some time before shrinking */
8457   {
8458     MovDelay[x][y]--;
8459     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8460     {
8461       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8462                                            6 - MovDelay[x][y]);
8463
8464       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8465     }
8466
8467     if (!MovDelay[x][y])
8468     {
8469       Feld[x][y] = EL_EMPTY;
8470       TEST_DrawLevelField(x, y);
8471
8472       /* don't let mole enter this field in this cycle;
8473          (give priority to objects falling to this field from above) */
8474       Stop[x][y] = TRUE;
8475     }
8476   }
8477 }
8478
8479 void AmoebeAbleger(int ax, int ay)
8480 {
8481   int i;
8482   int element = Feld[ax][ay];
8483   int graphic = el2img(element);
8484   int newax = ax, neway = ay;
8485   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8486   static int xy[4][2] =
8487   {
8488     { 0, -1 },
8489     { -1, 0 },
8490     { +1, 0 },
8491     { 0, +1 }
8492   };
8493
8494   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8495   {
8496     Feld[ax][ay] = EL_AMOEBA_DEAD;
8497     TEST_DrawLevelField(ax, ay);
8498     return;
8499   }
8500
8501   if (IS_ANIMATED(graphic))
8502     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8503
8504   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8505     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8506
8507   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8508   {
8509     MovDelay[ax][ay]--;
8510     if (MovDelay[ax][ay])
8511       return;
8512   }
8513
8514   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8515   {
8516     int start = RND(4);
8517     int x = ax + xy[start][0];
8518     int y = ay + xy[start][1];
8519
8520     if (!IN_LEV_FIELD(x, y))
8521       return;
8522
8523     if (IS_FREE(x, y) ||
8524         CAN_GROW_INTO(Feld[x][y]) ||
8525         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8526         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8527     {
8528       newax = x;
8529       neway = y;
8530     }
8531
8532     if (newax == ax && neway == ay)
8533       return;
8534   }
8535   else                          /* normal or "filled" (BD style) amoeba */
8536   {
8537     int start = RND(4);
8538     boolean waiting_for_player = FALSE;
8539
8540     for (i = 0; i < NUM_DIRECTIONS; i++)
8541     {
8542       int j = (start + i) % 4;
8543       int x = ax + xy[j][0];
8544       int y = ay + xy[j][1];
8545
8546       if (!IN_LEV_FIELD(x, y))
8547         continue;
8548
8549       if (IS_FREE(x, y) ||
8550           CAN_GROW_INTO(Feld[x][y]) ||
8551           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8552           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8553       {
8554         newax = x;
8555         neway = y;
8556         break;
8557       }
8558       else if (IS_PLAYER(x, y))
8559         waiting_for_player = TRUE;
8560     }
8561
8562     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8563     {
8564       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8565       {
8566         Feld[ax][ay] = EL_AMOEBA_DEAD;
8567         TEST_DrawLevelField(ax, ay);
8568         AmoebaCnt[AmoebaNr[ax][ay]]--;
8569
8570         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8571         {
8572           if (element == EL_AMOEBA_FULL)
8573             AmoebeUmwandeln(ax, ay);
8574           else if (element == EL_BD_AMOEBA)
8575             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8576         }
8577       }
8578       return;
8579     }
8580     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8581     {
8582       /* amoeba gets larger by growing in some direction */
8583
8584       int new_group_nr = AmoebaNr[ax][ay];
8585
8586 #ifdef DEBUG
8587   if (new_group_nr == 0)
8588   {
8589     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8590     printf("AmoebeAbleger(): This should never happen!\n");
8591     return;
8592   }
8593 #endif
8594
8595       AmoebaNr[newax][neway] = new_group_nr;
8596       AmoebaCnt[new_group_nr]++;
8597       AmoebaCnt2[new_group_nr]++;
8598
8599       /* if amoeba touches other amoeba(s) after growing, unify them */
8600       AmoebenVereinigen(newax, neway);
8601
8602       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8603       {
8604         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8605         return;
8606       }
8607     }
8608   }
8609
8610   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8611       (neway == lev_fieldy - 1 && newax != ax))
8612   {
8613     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8614     Store[newax][neway] = element;
8615   }
8616   else if (neway == ay || element == EL_EMC_DRIPPER)
8617   {
8618     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8619
8620     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8621   }
8622   else
8623   {
8624     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8625     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8626     Store[ax][ay] = EL_AMOEBA_DROP;
8627     ContinueMoving(ax, ay);
8628     return;
8629   }
8630
8631   TEST_DrawLevelField(newax, neway);
8632 }
8633
8634 void Life(int ax, int ay)
8635 {
8636   int x1, y1, x2, y2;
8637   int life_time = 40;
8638   int element = Feld[ax][ay];
8639   int graphic = el2img(element);
8640   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8641                          level.biomaze);
8642   boolean changed = FALSE;
8643
8644   if (IS_ANIMATED(graphic))
8645     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8646
8647   if (Stop[ax][ay])
8648     return;
8649
8650   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8651     MovDelay[ax][ay] = life_time;
8652
8653   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8654   {
8655     MovDelay[ax][ay]--;
8656     if (MovDelay[ax][ay])
8657       return;
8658   }
8659
8660   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8661   {
8662     int xx = ax+x1, yy = ay+y1;
8663     int nachbarn = 0;
8664
8665     if (!IN_LEV_FIELD(xx, yy))
8666       continue;
8667
8668     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8669     {
8670       int x = xx+x2, y = yy+y2;
8671
8672       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8673         continue;
8674
8675       if (((Feld[x][y] == element ||
8676             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8677            !Stop[x][y]) ||
8678           (IS_FREE(x, y) && Stop[x][y]))
8679         nachbarn++;
8680     }
8681
8682     if (xx == ax && yy == ay)           /* field in the middle */
8683     {
8684       if (nachbarn < life_parameter[0] ||
8685           nachbarn > life_parameter[1])
8686       {
8687         Feld[xx][yy] = EL_EMPTY;
8688         if (!Stop[xx][yy])
8689           TEST_DrawLevelField(xx, yy);
8690         Stop[xx][yy] = TRUE;
8691         changed = TRUE;
8692       }
8693     }
8694     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8695     {                                   /* free border field */
8696       if (nachbarn >= life_parameter[2] &&
8697           nachbarn <= life_parameter[3])
8698       {
8699         Feld[xx][yy] = element;
8700         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8701         if (!Stop[xx][yy])
8702           TEST_DrawLevelField(xx, yy);
8703         Stop[xx][yy] = TRUE;
8704         changed = TRUE;
8705       }
8706     }
8707   }
8708
8709   if (changed)
8710     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8711                    SND_GAME_OF_LIFE_GROWING);
8712 }
8713
8714 static void InitRobotWheel(int x, int y)
8715 {
8716   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8717 }
8718
8719 static void RunRobotWheel(int x, int y)
8720 {
8721   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8722 }
8723
8724 static void StopRobotWheel(int x, int y)
8725 {
8726   if (ZX == x && ZY == y)
8727   {
8728     ZX = ZY = -1;
8729
8730     game.robot_wheel_active = FALSE;
8731   }
8732 }
8733
8734 static void InitTimegateWheel(int x, int y)
8735 {
8736   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8737 }
8738
8739 static void RunTimegateWheel(int x, int y)
8740 {
8741   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8742 }
8743
8744 static void InitMagicBallDelay(int x, int y)
8745 {
8746   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8747 }
8748
8749 static void ActivateMagicBall(int bx, int by)
8750 {
8751   int x, y;
8752
8753   if (level.ball_random)
8754   {
8755     int pos_border = RND(8);    /* select one of the eight border elements */
8756     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8757     int xx = pos_content % 3;
8758     int yy = pos_content / 3;
8759
8760     x = bx - 1 + xx;
8761     y = by - 1 + yy;
8762
8763     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8764       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8765   }
8766   else
8767   {
8768     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8769     {
8770       int xx = x - bx + 1;
8771       int yy = y - by + 1;
8772
8773       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8774         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8775     }
8776   }
8777
8778   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8779 }
8780
8781 void CheckExit(int x, int y)
8782 {
8783   if (local_player->gems_still_needed > 0 ||
8784       local_player->sokobanfields_still_needed > 0 ||
8785       local_player->lights_still_needed > 0)
8786   {
8787     int element = Feld[x][y];
8788     int graphic = el2img(element);
8789
8790     if (IS_ANIMATED(graphic))
8791       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8792
8793     return;
8794   }
8795
8796   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8797     return;
8798
8799   Feld[x][y] = EL_EXIT_OPENING;
8800
8801   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8802 }
8803
8804 void CheckExitEM(int x, int y)
8805 {
8806   if (local_player->gems_still_needed > 0 ||
8807       local_player->sokobanfields_still_needed > 0 ||
8808       local_player->lights_still_needed > 0)
8809   {
8810     int element = Feld[x][y];
8811     int graphic = el2img(element);
8812
8813     if (IS_ANIMATED(graphic))
8814       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8815
8816     return;
8817   }
8818
8819   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8820     return;
8821
8822   Feld[x][y] = EL_EM_EXIT_OPENING;
8823
8824   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8825 }
8826
8827 void CheckExitSteel(int x, int y)
8828 {
8829   if (local_player->gems_still_needed > 0 ||
8830       local_player->sokobanfields_still_needed > 0 ||
8831       local_player->lights_still_needed > 0)
8832   {
8833     int element = Feld[x][y];
8834     int graphic = el2img(element);
8835
8836     if (IS_ANIMATED(graphic))
8837       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8838
8839     return;
8840   }
8841
8842   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8843     return;
8844
8845   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8846
8847   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8848 }
8849
8850 void CheckExitSteelEM(int x, int y)
8851 {
8852   if (local_player->gems_still_needed > 0 ||
8853       local_player->sokobanfields_still_needed > 0 ||
8854       local_player->lights_still_needed > 0)
8855   {
8856     int element = Feld[x][y];
8857     int graphic = el2img(element);
8858
8859     if (IS_ANIMATED(graphic))
8860       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8861
8862     return;
8863   }
8864
8865   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8866     return;
8867
8868   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8869
8870   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8871 }
8872
8873 void CheckExitSP(int x, int y)
8874 {
8875   if (local_player->gems_still_needed > 0)
8876   {
8877     int element = Feld[x][y];
8878     int graphic = el2img(element);
8879
8880     if (IS_ANIMATED(graphic))
8881       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8882
8883     return;
8884   }
8885
8886   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8887     return;
8888
8889   Feld[x][y] = EL_SP_EXIT_OPENING;
8890
8891   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8892 }
8893
8894 static void CloseAllOpenTimegates()
8895 {
8896   int x, y;
8897
8898   SCAN_PLAYFIELD(x, y)
8899   {
8900     int element = Feld[x][y];
8901
8902     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8903     {
8904       Feld[x][y] = EL_TIMEGATE_CLOSING;
8905
8906       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8907     }
8908   }
8909 }
8910
8911 void DrawTwinkleOnField(int x, int y)
8912 {
8913   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8914     return;
8915
8916   if (Feld[x][y] == EL_BD_DIAMOND)
8917     return;
8918
8919   if (MovDelay[x][y] == 0)      /* next animation frame */
8920     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8921
8922   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8923   {
8924     MovDelay[x][y]--;
8925
8926     DrawLevelElementAnimation(x, y, Feld[x][y]);
8927
8928     if (MovDelay[x][y] != 0)
8929     {
8930       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8931                                            10 - MovDelay[x][y]);
8932
8933       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8934     }
8935   }
8936 }
8937
8938 void MauerWaechst(int x, int y)
8939 {
8940   int delay = 6;
8941
8942   if (!MovDelay[x][y])          /* next animation frame */
8943     MovDelay[x][y] = 3 * delay;
8944
8945   if (MovDelay[x][y])           /* wait some time before next frame */
8946   {
8947     MovDelay[x][y]--;
8948
8949     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8950     {
8951       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8952       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8953
8954       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8955     }
8956
8957     if (!MovDelay[x][y])
8958     {
8959       if (MovDir[x][y] == MV_LEFT)
8960       {
8961         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8962           TEST_DrawLevelField(x - 1, y);
8963       }
8964       else if (MovDir[x][y] == MV_RIGHT)
8965       {
8966         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8967           TEST_DrawLevelField(x + 1, y);
8968       }
8969       else if (MovDir[x][y] == MV_UP)
8970       {
8971         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8972           TEST_DrawLevelField(x, y - 1);
8973       }
8974       else
8975       {
8976         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8977           TEST_DrawLevelField(x, y + 1);
8978       }
8979
8980       Feld[x][y] = Store[x][y];
8981       Store[x][y] = 0;
8982       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8983       TEST_DrawLevelField(x, y);
8984     }
8985   }
8986 }
8987
8988 void MauerAbleger(int ax, int ay)
8989 {
8990   int element = Feld[ax][ay];
8991   int graphic = el2img(element);
8992   boolean oben_frei = FALSE, unten_frei = FALSE;
8993   boolean links_frei = FALSE, rechts_frei = FALSE;
8994   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8995   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8996   boolean new_wall = FALSE;
8997
8998   if (IS_ANIMATED(graphic))
8999     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9000
9001   if (!MovDelay[ax][ay])        /* start building new wall */
9002     MovDelay[ax][ay] = 6;
9003
9004   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9005   {
9006     MovDelay[ax][ay]--;
9007     if (MovDelay[ax][ay])
9008       return;
9009   }
9010
9011   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9012     oben_frei = TRUE;
9013   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9014     unten_frei = TRUE;
9015   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9016     links_frei = TRUE;
9017   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9018     rechts_frei = TRUE;
9019
9020   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9021       element == EL_EXPANDABLE_WALL_ANY)
9022   {
9023     if (oben_frei)
9024     {
9025       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9026       Store[ax][ay-1] = element;
9027       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9028       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9029         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9030                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9031       new_wall = TRUE;
9032     }
9033     if (unten_frei)
9034     {
9035       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9036       Store[ax][ay+1] = element;
9037       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9038       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9039         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9040                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9041       new_wall = TRUE;
9042     }
9043   }
9044
9045   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9046       element == EL_EXPANDABLE_WALL_ANY ||
9047       element == EL_EXPANDABLE_WALL ||
9048       element == EL_BD_EXPANDABLE_WALL)
9049   {
9050     if (links_frei)
9051     {
9052       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9053       Store[ax-1][ay] = element;
9054       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9055       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9056         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9057                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9058       new_wall = TRUE;
9059     }
9060
9061     if (rechts_frei)
9062     {
9063       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9064       Store[ax+1][ay] = element;
9065       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9066       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9067         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9068                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9069       new_wall = TRUE;
9070     }
9071   }
9072
9073   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9074     TEST_DrawLevelField(ax, ay);
9075
9076   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9077     oben_massiv = TRUE;
9078   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9079     unten_massiv = TRUE;
9080   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9081     links_massiv = TRUE;
9082   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9083     rechts_massiv = TRUE;
9084
9085   if (((oben_massiv && unten_massiv) ||
9086        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9087        element == EL_EXPANDABLE_WALL) &&
9088       ((links_massiv && rechts_massiv) ||
9089        element == EL_EXPANDABLE_WALL_VERTICAL))
9090     Feld[ax][ay] = EL_WALL;
9091
9092   if (new_wall)
9093     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9094 }
9095
9096 void MauerAblegerStahl(int ax, int ay)
9097 {
9098   int element = Feld[ax][ay];
9099   int graphic = el2img(element);
9100   boolean oben_frei = FALSE, unten_frei = FALSE;
9101   boolean links_frei = FALSE, rechts_frei = FALSE;
9102   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9103   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9104   boolean new_wall = FALSE;
9105
9106   if (IS_ANIMATED(graphic))
9107     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9108
9109   if (!MovDelay[ax][ay])        /* start building new wall */
9110     MovDelay[ax][ay] = 6;
9111
9112   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9113   {
9114     MovDelay[ax][ay]--;
9115     if (MovDelay[ax][ay])
9116       return;
9117   }
9118
9119   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9120     oben_frei = TRUE;
9121   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9122     unten_frei = TRUE;
9123   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9124     links_frei = TRUE;
9125   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9126     rechts_frei = TRUE;
9127
9128   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9129       element == EL_EXPANDABLE_STEELWALL_ANY)
9130   {
9131     if (oben_frei)
9132     {
9133       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9134       Store[ax][ay-1] = element;
9135       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9136       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9137         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9138                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9139       new_wall = TRUE;
9140     }
9141     if (unten_frei)
9142     {
9143       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9144       Store[ax][ay+1] = element;
9145       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9146       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9147         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9148                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9149       new_wall = TRUE;
9150     }
9151   }
9152
9153   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9154       element == EL_EXPANDABLE_STEELWALL_ANY)
9155   {
9156     if (links_frei)
9157     {
9158       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9159       Store[ax-1][ay] = element;
9160       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9161       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9162         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9163                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9164       new_wall = TRUE;
9165     }
9166
9167     if (rechts_frei)
9168     {
9169       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9170       Store[ax+1][ay] = element;
9171       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9172       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9173         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9174                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9175       new_wall = TRUE;
9176     }
9177   }
9178
9179   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9180     oben_massiv = TRUE;
9181   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9182     unten_massiv = TRUE;
9183   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9184     links_massiv = TRUE;
9185   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9186     rechts_massiv = TRUE;
9187
9188   if (((oben_massiv && unten_massiv) ||
9189        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9190       ((links_massiv && rechts_massiv) ||
9191        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9192     Feld[ax][ay] = EL_STEELWALL;
9193
9194   if (new_wall)
9195     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9196 }
9197
9198 void CheckForDragon(int x, int y)
9199 {
9200   int i, j;
9201   boolean dragon_found = FALSE;
9202   static int xy[4][2] =
9203   {
9204     { 0, -1 },
9205     { -1, 0 },
9206     { +1, 0 },
9207     { 0, +1 }
9208   };
9209
9210   for (i = 0; i < NUM_DIRECTIONS; i++)
9211   {
9212     for (j = 0; j < 4; j++)
9213     {
9214       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9215
9216       if (IN_LEV_FIELD(xx, yy) &&
9217           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9218       {
9219         if (Feld[xx][yy] == EL_DRAGON)
9220           dragon_found = TRUE;
9221       }
9222       else
9223         break;
9224     }
9225   }
9226
9227   if (!dragon_found)
9228   {
9229     for (i = 0; i < NUM_DIRECTIONS; i++)
9230     {
9231       for (j = 0; j < 3; j++)
9232       {
9233         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9234   
9235         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9236         {
9237           Feld[xx][yy] = EL_EMPTY;
9238           TEST_DrawLevelField(xx, yy);
9239         }
9240         else
9241           break;
9242       }
9243     }
9244   }
9245 }
9246
9247 static void InitBuggyBase(int x, int y)
9248 {
9249   int element = Feld[x][y];
9250   int activating_delay = FRAMES_PER_SECOND / 4;
9251
9252   ChangeDelay[x][y] =
9253     (element == EL_SP_BUGGY_BASE ?
9254      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9255      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9256      activating_delay :
9257      element == EL_SP_BUGGY_BASE_ACTIVE ?
9258      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9259 }
9260
9261 static void WarnBuggyBase(int x, int y)
9262 {
9263   int i;
9264   static int xy[4][2] =
9265   {
9266     { 0, -1 },
9267     { -1, 0 },
9268     { +1, 0 },
9269     { 0, +1 }
9270   };
9271
9272   for (i = 0; i < NUM_DIRECTIONS; i++)
9273   {
9274     int xx = x + xy[i][0];
9275     int yy = y + xy[i][1];
9276
9277     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9278     {
9279       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9280
9281       break;
9282     }
9283   }
9284 }
9285
9286 static void InitTrap(int x, int y)
9287 {
9288   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9289 }
9290
9291 static void ActivateTrap(int x, int y)
9292 {
9293   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9294 }
9295
9296 static void ChangeActiveTrap(int x, int y)
9297 {
9298   int graphic = IMG_TRAP_ACTIVE;
9299
9300   /* if new animation frame was drawn, correct crumbled sand border */
9301   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9302     TEST_DrawLevelFieldCrumbled(x, y);
9303 }
9304
9305 static int getSpecialActionElement(int element, int number, int base_element)
9306 {
9307   return (element != EL_EMPTY ? element :
9308           number != -1 ? base_element + number - 1 :
9309           EL_EMPTY);
9310 }
9311
9312 static int getModifiedActionNumber(int value_old, int operator, int operand,
9313                                    int value_min, int value_max)
9314 {
9315   int value_new = (operator == CA_MODE_SET      ? operand :
9316                    operator == CA_MODE_ADD      ? value_old + operand :
9317                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9318                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9319                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9320                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9321                    value_old);
9322
9323   return (value_new < value_min ? value_min :
9324           value_new > value_max ? value_max :
9325           value_new);
9326 }
9327
9328 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9329 {
9330   struct ElementInfo *ei = &element_info[element];
9331   struct ElementChangeInfo *change = &ei->change_page[page];
9332   int target_element = change->target_element;
9333   int action_type = change->action_type;
9334   int action_mode = change->action_mode;
9335   int action_arg = change->action_arg;
9336   int action_element = change->action_element;
9337   int i;
9338
9339   if (!change->has_action)
9340     return;
9341
9342   /* ---------- determine action paramater values -------------------------- */
9343
9344   int level_time_value =
9345     (level.time > 0 ? TimeLeft :
9346      TimePlayed);
9347
9348   int action_arg_element_raw =
9349     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9350      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9351      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9352      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9353      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9354      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9355      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9356      EL_EMPTY);
9357   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9358
9359   int action_arg_direction =
9360     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9361      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9362      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9363      change->actual_trigger_side :
9364      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9365      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9366      MV_NONE);
9367
9368   int action_arg_number_min =
9369     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9370      CA_ARG_MIN);
9371
9372   int action_arg_number_max =
9373     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9374      action_type == CA_SET_LEVEL_GEMS ? 999 :
9375      action_type == CA_SET_LEVEL_TIME ? 9999 :
9376      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9377      action_type == CA_SET_CE_VALUE ? 9999 :
9378      action_type == CA_SET_CE_SCORE ? 9999 :
9379      CA_ARG_MAX);
9380
9381   int action_arg_number_reset =
9382     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9383      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9384      action_type == CA_SET_LEVEL_TIME ? level.time :
9385      action_type == CA_SET_LEVEL_SCORE ? 0 :
9386      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9387      action_type == CA_SET_CE_SCORE ? 0 :
9388      0);
9389
9390   int action_arg_number =
9391     (action_arg <= CA_ARG_MAX ? action_arg :
9392      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9393      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9394      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9395      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9396      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9397      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9398      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9399      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9400      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9401      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9402      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9403      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9404      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9405      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9406      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9407      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9408      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9409      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9410      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9411      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9412      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9413      -1);
9414
9415   int action_arg_number_old =
9416     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9417      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9418      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9419      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9420      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9421      0);
9422
9423   int action_arg_number_new =
9424     getModifiedActionNumber(action_arg_number_old,
9425                             action_mode, action_arg_number,
9426                             action_arg_number_min, action_arg_number_max);
9427
9428   int trigger_player_bits =
9429     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9430      change->actual_trigger_player_bits : change->trigger_player);
9431
9432   int action_arg_player_bits =
9433     (action_arg >= CA_ARG_PLAYER_1 &&
9434      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9435      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9436      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9437      PLAYER_BITS_ANY);
9438
9439   /* ---------- execute action  -------------------------------------------- */
9440
9441   switch (action_type)
9442   {
9443     case CA_NO_ACTION:
9444     {
9445       return;
9446     }
9447
9448     /* ---------- level actions  ------------------------------------------- */
9449
9450     case CA_RESTART_LEVEL:
9451     {
9452       game.restart_level = TRUE;
9453
9454       break;
9455     }
9456
9457     case CA_SHOW_ENVELOPE:
9458     {
9459       int element = getSpecialActionElement(action_arg_element,
9460                                             action_arg_number, EL_ENVELOPE_1);
9461
9462       if (IS_ENVELOPE(element))
9463         local_player->show_envelope = element;
9464
9465       break;
9466     }
9467
9468     case CA_SET_LEVEL_TIME:
9469     {
9470       if (level.time > 0)       /* only modify limited time value */
9471       {
9472         TimeLeft = action_arg_number_new;
9473
9474         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9475
9476         DisplayGameControlValues();
9477
9478         if (!TimeLeft && setup.time_limit)
9479           for (i = 0; i < MAX_PLAYERS; i++)
9480             KillPlayer(&stored_player[i]);
9481       }
9482
9483       break;
9484     }
9485
9486     case CA_SET_LEVEL_SCORE:
9487     {
9488       local_player->score = action_arg_number_new;
9489
9490       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9491
9492       DisplayGameControlValues();
9493
9494       break;
9495     }
9496
9497     case CA_SET_LEVEL_GEMS:
9498     {
9499       local_player->gems_still_needed = action_arg_number_new;
9500
9501       game.snapshot.collected_item = TRUE;
9502
9503       game_panel_controls[GAME_PANEL_GEMS].value =
9504         local_player->gems_still_needed;
9505
9506       DisplayGameControlValues();
9507
9508       break;
9509     }
9510
9511     case CA_SET_LEVEL_WIND:
9512     {
9513       game.wind_direction = action_arg_direction;
9514
9515       break;
9516     }
9517
9518     case CA_SET_LEVEL_RANDOM_SEED:
9519     {
9520       /* ensure that setting a new random seed while playing is predictable */
9521       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9522
9523       break;
9524     }
9525
9526     /* ---------- player actions  ------------------------------------------ */
9527
9528     case CA_MOVE_PLAYER:
9529     {
9530       /* automatically move to the next field in specified direction */
9531       for (i = 0; i < MAX_PLAYERS; i++)
9532         if (trigger_player_bits & (1 << i))
9533           stored_player[i].programmed_action = action_arg_direction;
9534
9535       break;
9536     }
9537
9538     case CA_EXIT_PLAYER:
9539     {
9540       for (i = 0; i < MAX_PLAYERS; i++)
9541         if (action_arg_player_bits & (1 << i))
9542           PlayerWins(&stored_player[i]);
9543
9544       break;
9545     }
9546
9547     case CA_KILL_PLAYER:
9548     {
9549       for (i = 0; i < MAX_PLAYERS; i++)
9550         if (action_arg_player_bits & (1 << i))
9551           KillPlayer(&stored_player[i]);
9552
9553       break;
9554     }
9555
9556     case CA_SET_PLAYER_KEYS:
9557     {
9558       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9559       int element = getSpecialActionElement(action_arg_element,
9560                                             action_arg_number, EL_KEY_1);
9561
9562       if (IS_KEY(element))
9563       {
9564         for (i = 0; i < MAX_PLAYERS; i++)
9565         {
9566           if (trigger_player_bits & (1 << i))
9567           {
9568             stored_player[i].key[KEY_NR(element)] = key_state;
9569
9570             DrawGameDoorValues();
9571           }
9572         }
9573       }
9574
9575       break;
9576     }
9577
9578     case CA_SET_PLAYER_SPEED:
9579     {
9580       for (i = 0; i < MAX_PLAYERS; i++)
9581       {
9582         if (trigger_player_bits & (1 << i))
9583         {
9584           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9585
9586           if (action_arg == CA_ARG_SPEED_FASTER &&
9587               stored_player[i].cannot_move)
9588           {
9589             action_arg_number = STEPSIZE_VERY_SLOW;
9590           }
9591           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9592                    action_arg == CA_ARG_SPEED_FASTER)
9593           {
9594             action_arg_number = 2;
9595             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9596                            CA_MODE_MULTIPLY);
9597           }
9598           else if (action_arg == CA_ARG_NUMBER_RESET)
9599           {
9600             action_arg_number = level.initial_player_stepsize[i];
9601           }
9602
9603           move_stepsize =
9604             getModifiedActionNumber(move_stepsize,
9605                                     action_mode,
9606                                     action_arg_number,
9607                                     action_arg_number_min,
9608                                     action_arg_number_max);
9609
9610           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9611         }
9612       }
9613
9614       break;
9615     }
9616
9617     case CA_SET_PLAYER_SHIELD:
9618     {
9619       for (i = 0; i < MAX_PLAYERS; i++)
9620       {
9621         if (trigger_player_bits & (1 << i))
9622         {
9623           if (action_arg == CA_ARG_SHIELD_OFF)
9624           {
9625             stored_player[i].shield_normal_time_left = 0;
9626             stored_player[i].shield_deadly_time_left = 0;
9627           }
9628           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9629           {
9630             stored_player[i].shield_normal_time_left = 999999;
9631           }
9632           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9633           {
9634             stored_player[i].shield_normal_time_left = 999999;
9635             stored_player[i].shield_deadly_time_left = 999999;
9636           }
9637         }
9638       }
9639
9640       break;
9641     }
9642
9643     case CA_SET_PLAYER_GRAVITY:
9644     {
9645       for (i = 0; i < MAX_PLAYERS; i++)
9646       {
9647         if (trigger_player_bits & (1 << i))
9648         {
9649           stored_player[i].gravity =
9650             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9651              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9652              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9653              stored_player[i].gravity);
9654         }
9655       }
9656
9657       break;
9658     }
9659
9660     case CA_SET_PLAYER_ARTWORK:
9661     {
9662       for (i = 0; i < MAX_PLAYERS; i++)
9663       {
9664         if (trigger_player_bits & (1 << i))
9665         {
9666           int artwork_element = action_arg_element;
9667
9668           if (action_arg == CA_ARG_ELEMENT_RESET)
9669             artwork_element =
9670               (level.use_artwork_element[i] ? level.artwork_element[i] :
9671                stored_player[i].element_nr);
9672
9673           if (stored_player[i].artwork_element != artwork_element)
9674             stored_player[i].Frame = 0;
9675
9676           stored_player[i].artwork_element = artwork_element;
9677
9678           SetPlayerWaiting(&stored_player[i], FALSE);
9679
9680           /* set number of special actions for bored and sleeping animation */
9681           stored_player[i].num_special_action_bored =
9682             get_num_special_action(artwork_element,
9683                                    ACTION_BORING_1, ACTION_BORING_LAST);
9684           stored_player[i].num_special_action_sleeping =
9685             get_num_special_action(artwork_element,
9686                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9687         }
9688       }
9689
9690       break;
9691     }
9692
9693     case CA_SET_PLAYER_INVENTORY:
9694     {
9695       for (i = 0; i < MAX_PLAYERS; i++)
9696       {
9697         struct PlayerInfo *player = &stored_player[i];
9698         int j, k;
9699
9700         if (trigger_player_bits & (1 << i))
9701         {
9702           int inventory_element = action_arg_element;
9703
9704           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9705               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9706               action_arg == CA_ARG_ELEMENT_ACTION)
9707           {
9708             int element = inventory_element;
9709             int collect_count = element_info[element].collect_count_initial;
9710
9711             if (!IS_CUSTOM_ELEMENT(element))
9712               collect_count = 1;
9713
9714             if (collect_count == 0)
9715               player->inventory_infinite_element = element;
9716             else
9717               for (k = 0; k < collect_count; k++)
9718                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9719                   player->inventory_element[player->inventory_size++] =
9720                     element;
9721           }
9722           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9723                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9724                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9725           {
9726             if (player->inventory_infinite_element != EL_UNDEFINED &&
9727                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9728                                      action_arg_element_raw))
9729               player->inventory_infinite_element = EL_UNDEFINED;
9730
9731             for (k = 0, j = 0; j < player->inventory_size; j++)
9732             {
9733               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9734                                         action_arg_element_raw))
9735                 player->inventory_element[k++] = player->inventory_element[j];
9736             }
9737
9738             player->inventory_size = k;
9739           }
9740           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9741           {
9742             if (player->inventory_size > 0)
9743             {
9744               for (j = 0; j < player->inventory_size - 1; j++)
9745                 player->inventory_element[j] = player->inventory_element[j + 1];
9746
9747               player->inventory_size--;
9748             }
9749           }
9750           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9751           {
9752             if (player->inventory_size > 0)
9753               player->inventory_size--;
9754           }
9755           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9756           {
9757             player->inventory_infinite_element = EL_UNDEFINED;
9758             player->inventory_size = 0;
9759           }
9760           else if (action_arg == CA_ARG_INVENTORY_RESET)
9761           {
9762             player->inventory_infinite_element = EL_UNDEFINED;
9763             player->inventory_size = 0;
9764
9765             if (level.use_initial_inventory[i])
9766             {
9767               for (j = 0; j < level.initial_inventory_size[i]; j++)
9768               {
9769                 int element = level.initial_inventory_content[i][j];
9770                 int collect_count = element_info[element].collect_count_initial;
9771
9772                 if (!IS_CUSTOM_ELEMENT(element))
9773                   collect_count = 1;
9774
9775                 if (collect_count == 0)
9776                   player->inventory_infinite_element = element;
9777                 else
9778                   for (k = 0; k < collect_count; k++)
9779                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9780                       player->inventory_element[player->inventory_size++] =
9781                         element;
9782               }
9783             }
9784           }
9785         }
9786       }
9787
9788       break;
9789     }
9790
9791     /* ---------- CE actions  ---------------------------------------------- */
9792
9793     case CA_SET_CE_VALUE:
9794     {
9795       int last_ce_value = CustomValue[x][y];
9796
9797       CustomValue[x][y] = action_arg_number_new;
9798
9799       if (CustomValue[x][y] != last_ce_value)
9800       {
9801         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9802         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9803
9804         if (CustomValue[x][y] == 0)
9805         {
9806           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9807           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9808         }
9809       }
9810
9811       break;
9812     }
9813
9814     case CA_SET_CE_SCORE:
9815     {
9816       int last_ce_score = ei->collect_score;
9817
9818       ei->collect_score = action_arg_number_new;
9819
9820       if (ei->collect_score != last_ce_score)
9821       {
9822         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9823         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9824
9825         if (ei->collect_score == 0)
9826         {
9827           int xx, yy;
9828
9829           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9830           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9831
9832           /*
9833             This is a very special case that seems to be a mixture between
9834             CheckElementChange() and CheckTriggeredElementChange(): while
9835             the first one only affects single elements that are triggered
9836             directly, the second one affects multiple elements in the playfield
9837             that are triggered indirectly by another element. This is a third
9838             case: Changing the CE score always affects multiple identical CEs,
9839             so every affected CE must be checked, not only the single CE for
9840             which the CE score was changed in the first place (as every instance
9841             of that CE shares the same CE score, and therefore also can change)!
9842           */
9843           SCAN_PLAYFIELD(xx, yy)
9844           {
9845             if (Feld[xx][yy] == element)
9846               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9847                                  CE_SCORE_GETS_ZERO);
9848           }
9849         }
9850       }
9851
9852       break;
9853     }
9854
9855     case CA_SET_CE_ARTWORK:
9856     {
9857       int artwork_element = action_arg_element;
9858       boolean reset_frame = FALSE;
9859       int xx, yy;
9860
9861       if (action_arg == CA_ARG_ELEMENT_RESET)
9862         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9863                            element);
9864
9865       if (ei->gfx_element != artwork_element)
9866         reset_frame = TRUE;
9867
9868       ei->gfx_element = artwork_element;
9869
9870       SCAN_PLAYFIELD(xx, yy)
9871       {
9872         if (Feld[xx][yy] == element)
9873         {
9874           if (reset_frame)
9875           {
9876             ResetGfxAnimation(xx, yy);
9877             ResetRandomAnimationValue(xx, yy);
9878           }
9879
9880           TEST_DrawLevelField(xx, yy);
9881         }
9882       }
9883
9884       break;
9885     }
9886
9887     /* ---------- engine actions  ------------------------------------------ */
9888
9889     case CA_SET_ENGINE_SCAN_MODE:
9890     {
9891       InitPlayfieldScanMode(action_arg);
9892
9893       break;
9894     }
9895
9896     default:
9897       break;
9898   }
9899 }
9900
9901 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9902 {
9903   int old_element = Feld[x][y];
9904   int new_element = GetElementFromGroupElement(element);
9905   int previous_move_direction = MovDir[x][y];
9906   int last_ce_value = CustomValue[x][y];
9907   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9908   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9909   boolean add_player_onto_element = (new_element_is_player &&
9910                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9911                                      IS_WALKABLE(old_element));
9912
9913   if (!add_player_onto_element)
9914   {
9915     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9916       RemoveMovingField(x, y);
9917     else
9918       RemoveField(x, y);
9919
9920     Feld[x][y] = new_element;
9921
9922     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9923       MovDir[x][y] = previous_move_direction;
9924
9925     if (element_info[new_element].use_last_ce_value)
9926       CustomValue[x][y] = last_ce_value;
9927
9928     InitField_WithBug1(x, y, FALSE);
9929
9930     new_element = Feld[x][y];   /* element may have changed */
9931
9932     ResetGfxAnimation(x, y);
9933     ResetRandomAnimationValue(x, y);
9934
9935     TEST_DrawLevelField(x, y);
9936
9937     if (GFX_CRUMBLED(new_element))
9938       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9939   }
9940
9941   /* check if element under the player changes from accessible to unaccessible
9942      (needed for special case of dropping element which then changes) */
9943   /* (must be checked after creating new element for walkable group elements) */
9944   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9945       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9946   {
9947     Bang(x, y);
9948
9949     return;
9950   }
9951
9952   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9953   if (new_element_is_player)
9954     RelocatePlayer(x, y, new_element);
9955
9956   if (is_change)
9957     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9958
9959   TestIfBadThingTouchesPlayer(x, y);
9960   TestIfPlayerTouchesCustomElement(x, y);
9961   TestIfElementTouchesCustomElement(x, y);
9962 }
9963
9964 static void CreateField(int x, int y, int element)
9965 {
9966   CreateFieldExt(x, y, element, FALSE);
9967 }
9968
9969 static void CreateElementFromChange(int x, int y, int element)
9970 {
9971   element = GET_VALID_RUNTIME_ELEMENT(element);
9972
9973   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9974   {
9975     int old_element = Feld[x][y];
9976
9977     /* prevent changed element from moving in same engine frame
9978        unless both old and new element can either fall or move */
9979     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9980         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9981       Stop[x][y] = TRUE;
9982   }
9983
9984   CreateFieldExt(x, y, element, TRUE);
9985 }
9986
9987 static boolean ChangeElement(int x, int y, int element, int page)
9988 {
9989   struct ElementInfo *ei = &element_info[element];
9990   struct ElementChangeInfo *change = &ei->change_page[page];
9991   int ce_value = CustomValue[x][y];
9992   int ce_score = ei->collect_score;
9993   int target_element;
9994   int old_element = Feld[x][y];
9995
9996   /* always use default change event to prevent running into a loop */
9997   if (ChangeEvent[x][y] == -1)
9998     ChangeEvent[x][y] = CE_DELAY;
9999
10000   if (ChangeEvent[x][y] == CE_DELAY)
10001   {
10002     /* reset actual trigger element, trigger player and action element */
10003     change->actual_trigger_element = EL_EMPTY;
10004     change->actual_trigger_player = EL_EMPTY;
10005     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10006     change->actual_trigger_side = CH_SIDE_NONE;
10007     change->actual_trigger_ce_value = 0;
10008     change->actual_trigger_ce_score = 0;
10009   }
10010
10011   /* do not change elements more than a specified maximum number of changes */
10012   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10013     return FALSE;
10014
10015   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10016
10017   if (change->explode)
10018   {
10019     Bang(x, y);
10020
10021     return TRUE;
10022   }
10023
10024   if (change->use_target_content)
10025   {
10026     boolean complete_replace = TRUE;
10027     boolean can_replace[3][3];
10028     int xx, yy;
10029
10030     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10031     {
10032       boolean is_empty;
10033       boolean is_walkable;
10034       boolean is_diggable;
10035       boolean is_collectible;
10036       boolean is_removable;
10037       boolean is_destructible;
10038       int ex = x + xx - 1;
10039       int ey = y + yy - 1;
10040       int content_element = change->target_content.e[xx][yy];
10041       int e;
10042
10043       can_replace[xx][yy] = TRUE;
10044
10045       if (ex == x && ey == y)   /* do not check changing element itself */
10046         continue;
10047
10048       if (content_element == EL_EMPTY_SPACE)
10049       {
10050         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10051
10052         continue;
10053       }
10054
10055       if (!IN_LEV_FIELD(ex, ey))
10056       {
10057         can_replace[xx][yy] = FALSE;
10058         complete_replace = FALSE;
10059
10060         continue;
10061       }
10062
10063       e = Feld[ex][ey];
10064
10065       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10066         e = MovingOrBlocked2Element(ex, ey);
10067
10068       is_empty = (IS_FREE(ex, ey) ||
10069                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10070
10071       is_walkable     = (is_empty || IS_WALKABLE(e));
10072       is_diggable     = (is_empty || IS_DIGGABLE(e));
10073       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10074       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10075       is_removable    = (is_diggable || is_collectible);
10076
10077       can_replace[xx][yy] =
10078         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10079           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10080           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10081           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10082           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10083           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10084          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10085
10086       if (!can_replace[xx][yy])
10087         complete_replace = FALSE;
10088     }
10089
10090     if (!change->only_if_complete || complete_replace)
10091     {
10092       boolean something_has_changed = FALSE;
10093
10094       if (change->only_if_complete && change->use_random_replace &&
10095           RND(100) < change->random_percentage)
10096         return FALSE;
10097
10098       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10099       {
10100         int ex = x + xx - 1;
10101         int ey = y + yy - 1;
10102         int content_element;
10103
10104         if (can_replace[xx][yy] && (!change->use_random_replace ||
10105                                     RND(100) < change->random_percentage))
10106         {
10107           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10108             RemoveMovingField(ex, ey);
10109
10110           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10111
10112           content_element = change->target_content.e[xx][yy];
10113           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10114                                               ce_value, ce_score);
10115
10116           CreateElementFromChange(ex, ey, target_element);
10117
10118           something_has_changed = TRUE;
10119
10120           /* for symmetry reasons, freeze newly created border elements */
10121           if (ex != x || ey != y)
10122             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10123         }
10124       }
10125
10126       if (something_has_changed)
10127       {
10128         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10129         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10130       }
10131     }
10132   }
10133   else
10134   {
10135     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10136                                         ce_value, ce_score);
10137
10138     if (element == EL_DIAGONAL_GROWING ||
10139         element == EL_DIAGONAL_SHRINKING)
10140     {
10141       target_element = Store[x][y];
10142
10143       Store[x][y] = EL_EMPTY;
10144     }
10145
10146     CreateElementFromChange(x, y, target_element);
10147
10148     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10149     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10150   }
10151
10152   /* this uses direct change before indirect change */
10153   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10154
10155   return TRUE;
10156 }
10157
10158 static void HandleElementChange(int x, int y, int page)
10159 {
10160   int element = MovingOrBlocked2Element(x, y);
10161   struct ElementInfo *ei = &element_info[element];
10162   struct ElementChangeInfo *change = &ei->change_page[page];
10163   boolean handle_action_before_change = FALSE;
10164
10165 #ifdef DEBUG
10166   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10167       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10168   {
10169     printf("\n\n");
10170     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10171            x, y, element, element_info[element].token_name);
10172     printf("HandleElementChange(): This should never happen!\n");
10173     printf("\n\n");
10174   }
10175 #endif
10176
10177   /* this can happen with classic bombs on walkable, changing elements */
10178   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10179   {
10180     return;
10181   }
10182
10183   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10184   {
10185     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10186
10187     if (change->can_change)
10188     {
10189       /* !!! not clear why graphic animation should be reset at all here !!! */
10190       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10191       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10192
10193       /*
10194         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10195
10196         When using an animation frame delay of 1 (this only happens with
10197         "sp_zonk.moving.left/right" in the classic graphics), the default
10198         (non-moving) animation shows wrong animation frames (while the
10199         moving animation, like "sp_zonk.moving.left/right", is correct,
10200         so this graphical bug never shows up with the classic graphics).
10201         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10202         be drawn instead of the correct frames 0,1,2,3. This is caused by
10203         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10204         an element change: First when the change delay ("ChangeDelay[][]")
10205         counter has reached zero after decrementing, then a second time in
10206         the next frame (after "GfxFrame[][]" was already incremented) when
10207         "ChangeDelay[][]" is reset to the initial delay value again.
10208
10209         This causes frame 0 to be drawn twice, while the last frame won't
10210         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10211
10212         As some animations may already be cleverly designed around this bug
10213         (at least the "Snake Bite" snake tail animation does this), it cannot
10214         simply be fixed here without breaking such existing animations.
10215         Unfortunately, it cannot easily be detected if a graphics set was
10216         designed "before" or "after" the bug was fixed. As a workaround,
10217         a new graphics set option "game.graphics_engine_version" was added
10218         to be able to specify the game's major release version for which the
10219         graphics set was designed, which can then be used to decide if the
10220         bugfix should be used (version 4 and above) or not (version 3 or
10221         below, or if no version was specified at all, as with old sets).
10222
10223         (The wrong/fixed animation frames can be tested with the test level set
10224         "test_gfxframe" and level "000", which contains a specially prepared
10225         custom element at level position (x/y) == (11/9) which uses the zonk
10226         animation mentioned above. Using "game.graphics_engine_version: 4"
10227         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10228         This can also be seen from the debug output for this test element.)
10229       */
10230
10231       /* when a custom element is about to change (for example by change delay),
10232          do not reset graphic animation when the custom element is moving */
10233       if (game.graphics_engine_version < 4 &&
10234           !IS_MOVING(x, y))
10235       {
10236         ResetGfxAnimation(x, y);
10237         ResetRandomAnimationValue(x, y);
10238       }
10239
10240       if (change->pre_change_function)
10241         change->pre_change_function(x, y);
10242     }
10243   }
10244
10245   ChangeDelay[x][y]--;
10246
10247   if (ChangeDelay[x][y] != 0)           /* continue element change */
10248   {
10249     if (change->can_change)
10250     {
10251       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10252
10253       if (IS_ANIMATED(graphic))
10254         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10255
10256       if (change->change_function)
10257         change->change_function(x, y);
10258     }
10259   }
10260   else                                  /* finish element change */
10261   {
10262     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10263     {
10264       page = ChangePage[x][y];
10265       ChangePage[x][y] = -1;
10266
10267       change = &ei->change_page[page];
10268     }
10269
10270     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10271     {
10272       ChangeDelay[x][y] = 1;            /* try change after next move step */
10273       ChangePage[x][y] = page;          /* remember page to use for change */
10274
10275       return;
10276     }
10277
10278     /* special case: set new level random seed before changing element */
10279     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10280       handle_action_before_change = TRUE;
10281
10282     if (change->has_action && handle_action_before_change)
10283       ExecuteCustomElementAction(x, y, element, page);
10284
10285     if (change->can_change)
10286     {
10287       if (ChangeElement(x, y, element, page))
10288       {
10289         if (change->post_change_function)
10290           change->post_change_function(x, y);
10291       }
10292     }
10293
10294     if (change->has_action && !handle_action_before_change)
10295       ExecuteCustomElementAction(x, y, element, page);
10296   }
10297 }
10298
10299 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10300                                               int trigger_element,
10301                                               int trigger_event,
10302                                               int trigger_player,
10303                                               int trigger_side,
10304                                               int trigger_page)
10305 {
10306   boolean change_done_any = FALSE;
10307   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10308   int i;
10309
10310   if (!(trigger_events[trigger_element][trigger_event]))
10311     return FALSE;
10312
10313   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10314
10315   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10316   {
10317     int element = EL_CUSTOM_START + i;
10318     boolean change_done = FALSE;
10319     int p;
10320
10321     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10322         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10323       continue;
10324
10325     for (p = 0; p < element_info[element].num_change_pages; p++)
10326     {
10327       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10328
10329       if (change->can_change_or_has_action &&
10330           change->has_event[trigger_event] &&
10331           change->trigger_side & trigger_side &&
10332           change->trigger_player & trigger_player &&
10333           change->trigger_page & trigger_page_bits &&
10334           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10335       {
10336         change->actual_trigger_element = trigger_element;
10337         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10338         change->actual_trigger_player_bits = trigger_player;
10339         change->actual_trigger_side = trigger_side;
10340         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10341         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10342
10343         if ((change->can_change && !change_done) || change->has_action)
10344         {
10345           int x, y;
10346
10347           SCAN_PLAYFIELD(x, y)
10348           {
10349             if (Feld[x][y] == element)
10350             {
10351               if (change->can_change && !change_done)
10352               {
10353                 /* if element already changed in this frame, not only prevent
10354                    another element change (checked in ChangeElement()), but
10355                    also prevent additional element actions for this element */
10356
10357                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10358                     !level.use_action_after_change_bug)
10359                   continue;
10360
10361                 ChangeDelay[x][y] = 1;
10362                 ChangeEvent[x][y] = trigger_event;
10363
10364                 HandleElementChange(x, y, p);
10365               }
10366               else if (change->has_action)
10367               {
10368                 /* if element already changed in this frame, not only prevent
10369                    another element change (checked in ChangeElement()), but
10370                    also prevent additional element actions for this element */
10371
10372                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10373                     !level.use_action_after_change_bug)
10374                   continue;
10375
10376                 ExecuteCustomElementAction(x, y, element, p);
10377                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10378               }
10379             }
10380           }
10381
10382           if (change->can_change)
10383           {
10384             change_done = TRUE;
10385             change_done_any = TRUE;
10386           }
10387         }
10388       }
10389     }
10390   }
10391
10392   RECURSION_LOOP_DETECTION_END();
10393
10394   return change_done_any;
10395 }
10396
10397 static boolean CheckElementChangeExt(int x, int y,
10398                                      int element,
10399                                      int trigger_element,
10400                                      int trigger_event,
10401                                      int trigger_player,
10402                                      int trigger_side)
10403 {
10404   boolean change_done = FALSE;
10405   int p;
10406
10407   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10408       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10409     return FALSE;
10410
10411   if (Feld[x][y] == EL_BLOCKED)
10412   {
10413     Blocked2Moving(x, y, &x, &y);
10414     element = Feld[x][y];
10415   }
10416
10417   /* check if element has already changed or is about to change after moving */
10418   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10419        Feld[x][y] != element) ||
10420
10421       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10422        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10423         ChangePage[x][y] != -1)))
10424     return FALSE;
10425
10426   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10427
10428   for (p = 0; p < element_info[element].num_change_pages; p++)
10429   {
10430     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10431
10432     /* check trigger element for all events where the element that is checked
10433        for changing interacts with a directly adjacent element -- this is
10434        different to element changes that affect other elements to change on the
10435        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10436     boolean check_trigger_element =
10437       (trigger_event == CE_TOUCHING_X ||
10438        trigger_event == CE_HITTING_X ||
10439        trigger_event == CE_HIT_BY_X ||
10440        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10441
10442     if (change->can_change_or_has_action &&
10443         change->has_event[trigger_event] &&
10444         change->trigger_side & trigger_side &&
10445         change->trigger_player & trigger_player &&
10446         (!check_trigger_element ||
10447          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10448     {
10449       change->actual_trigger_element = trigger_element;
10450       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10451       change->actual_trigger_player_bits = trigger_player;
10452       change->actual_trigger_side = trigger_side;
10453       change->actual_trigger_ce_value = CustomValue[x][y];
10454       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10455
10456       /* special case: trigger element not at (x,y) position for some events */
10457       if (check_trigger_element)
10458       {
10459         static struct
10460         {
10461           int dx, dy;
10462         } move_xy[] =
10463           {
10464             {  0,  0 },
10465             { -1,  0 },
10466             { +1,  0 },
10467             {  0,  0 },
10468             {  0, -1 },
10469             {  0,  0 }, { 0, 0 }, { 0, 0 },
10470             {  0, +1 }
10471           };
10472
10473         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10474         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10475
10476         change->actual_trigger_ce_value = CustomValue[xx][yy];
10477         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10478       }
10479
10480       if (change->can_change && !change_done)
10481       {
10482         ChangeDelay[x][y] = 1;
10483         ChangeEvent[x][y] = trigger_event;
10484
10485         HandleElementChange(x, y, p);
10486
10487         change_done = TRUE;
10488       }
10489       else if (change->has_action)
10490       {
10491         ExecuteCustomElementAction(x, y, element, p);
10492         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10493       }
10494     }
10495   }
10496
10497   RECURSION_LOOP_DETECTION_END();
10498
10499   return change_done;
10500 }
10501
10502 static void PlayPlayerSound(struct PlayerInfo *player)
10503 {
10504   int jx = player->jx, jy = player->jy;
10505   int sound_element = player->artwork_element;
10506   int last_action = player->last_action_waiting;
10507   int action = player->action_waiting;
10508
10509   if (player->is_waiting)
10510   {
10511     if (action != last_action)
10512       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10513     else
10514       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10515   }
10516   else
10517   {
10518     if (action != last_action)
10519       StopSound(element_info[sound_element].sound[last_action]);
10520
10521     if (last_action == ACTION_SLEEPING)
10522       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10523   }
10524 }
10525
10526 static void PlayAllPlayersSound()
10527 {
10528   int i;
10529
10530   for (i = 0; i < MAX_PLAYERS; i++)
10531     if (stored_player[i].active)
10532       PlayPlayerSound(&stored_player[i]);
10533 }
10534
10535 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10536 {
10537   boolean last_waiting = player->is_waiting;
10538   int move_dir = player->MovDir;
10539
10540   player->dir_waiting = move_dir;
10541   player->last_action_waiting = player->action_waiting;
10542
10543   if (is_waiting)
10544   {
10545     if (!last_waiting)          /* not waiting -> waiting */
10546     {
10547       player->is_waiting = TRUE;
10548
10549       player->frame_counter_bored =
10550         FrameCounter +
10551         game.player_boring_delay_fixed +
10552         GetSimpleRandom(game.player_boring_delay_random);
10553       player->frame_counter_sleeping =
10554         FrameCounter +
10555         game.player_sleeping_delay_fixed +
10556         GetSimpleRandom(game.player_sleeping_delay_random);
10557
10558       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10559     }
10560
10561     if (game.player_sleeping_delay_fixed +
10562         game.player_sleeping_delay_random > 0 &&
10563         player->anim_delay_counter == 0 &&
10564         player->post_delay_counter == 0 &&
10565         FrameCounter >= player->frame_counter_sleeping)
10566       player->is_sleeping = TRUE;
10567     else if (game.player_boring_delay_fixed +
10568              game.player_boring_delay_random > 0 &&
10569              FrameCounter >= player->frame_counter_bored)
10570       player->is_bored = TRUE;
10571
10572     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10573                               player->is_bored ? ACTION_BORING :
10574                               ACTION_WAITING);
10575
10576     if (player->is_sleeping && player->use_murphy)
10577     {
10578       /* special case for sleeping Murphy when leaning against non-free tile */
10579
10580       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10581           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10582            !IS_MOVING(player->jx - 1, player->jy)))
10583         move_dir = MV_LEFT;
10584       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10585                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10586                 !IS_MOVING(player->jx + 1, player->jy)))
10587         move_dir = MV_RIGHT;
10588       else
10589         player->is_sleeping = FALSE;
10590
10591       player->dir_waiting = move_dir;
10592     }
10593
10594     if (player->is_sleeping)
10595     {
10596       if (player->num_special_action_sleeping > 0)
10597       {
10598         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10599         {
10600           int last_special_action = player->special_action_sleeping;
10601           int num_special_action = player->num_special_action_sleeping;
10602           int special_action =
10603             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10604              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10605              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10606              last_special_action + 1 : ACTION_SLEEPING);
10607           int special_graphic =
10608             el_act_dir2img(player->artwork_element, special_action, move_dir);
10609
10610           player->anim_delay_counter =
10611             graphic_info[special_graphic].anim_delay_fixed +
10612             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10613           player->post_delay_counter =
10614             graphic_info[special_graphic].post_delay_fixed +
10615             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10616
10617           player->special_action_sleeping = special_action;
10618         }
10619
10620         if (player->anim_delay_counter > 0)
10621         {
10622           player->action_waiting = player->special_action_sleeping;
10623           player->anim_delay_counter--;
10624         }
10625         else if (player->post_delay_counter > 0)
10626         {
10627           player->post_delay_counter--;
10628         }
10629       }
10630     }
10631     else if (player->is_bored)
10632     {
10633       if (player->num_special_action_bored > 0)
10634       {
10635         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10636         {
10637           int special_action =
10638             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10639           int special_graphic =
10640             el_act_dir2img(player->artwork_element, special_action, move_dir);
10641
10642           player->anim_delay_counter =
10643             graphic_info[special_graphic].anim_delay_fixed +
10644             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10645           player->post_delay_counter =
10646             graphic_info[special_graphic].post_delay_fixed +
10647             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10648
10649           player->special_action_bored = special_action;
10650         }
10651
10652         if (player->anim_delay_counter > 0)
10653         {
10654           player->action_waiting = player->special_action_bored;
10655           player->anim_delay_counter--;
10656         }
10657         else if (player->post_delay_counter > 0)
10658         {
10659           player->post_delay_counter--;
10660         }
10661       }
10662     }
10663   }
10664   else if (last_waiting)        /* waiting -> not waiting */
10665   {
10666     player->is_waiting = FALSE;
10667     player->is_bored = FALSE;
10668     player->is_sleeping = FALSE;
10669
10670     player->frame_counter_bored = -1;
10671     player->frame_counter_sleeping = -1;
10672
10673     player->anim_delay_counter = 0;
10674     player->post_delay_counter = 0;
10675
10676     player->dir_waiting = player->MovDir;
10677     player->action_waiting = ACTION_DEFAULT;
10678
10679     player->special_action_bored = ACTION_DEFAULT;
10680     player->special_action_sleeping = ACTION_DEFAULT;
10681   }
10682 }
10683
10684 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10685 {
10686   if ((!player->is_moving  && player->was_moving) ||
10687       (player->MovPos == 0 && player->was_moving) ||
10688       (player->is_snapping && !player->was_snapping) ||
10689       (player->is_dropping && !player->was_dropping))
10690   {
10691     if (!CheckSaveEngineSnapshotToList())
10692       return;
10693
10694     player->was_moving = FALSE;
10695     player->was_snapping = TRUE;
10696     player->was_dropping = TRUE;
10697   }
10698   else
10699   {
10700     if (player->is_moving)
10701       player->was_moving = TRUE;
10702
10703     if (!player->is_snapping)
10704       player->was_snapping = FALSE;
10705
10706     if (!player->is_dropping)
10707       player->was_dropping = FALSE;
10708   }
10709 }
10710
10711 static void CheckSingleStepMode(struct PlayerInfo *player)
10712 {
10713   if (tape.single_step && tape.recording && !tape.pausing)
10714   {
10715     /* as it is called "single step mode", just return to pause mode when the
10716        player stopped moving after one tile (or never starts moving at all) */
10717     if (!player->is_moving && !player->is_pushing)
10718     {
10719       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10720       SnapField(player, 0, 0);                  /* stop snapping */
10721     }
10722   }
10723
10724   CheckSaveEngineSnapshot(player);
10725 }
10726
10727 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10728 {
10729   int left      = player_action & JOY_LEFT;
10730   int right     = player_action & JOY_RIGHT;
10731   int up        = player_action & JOY_UP;
10732   int down      = player_action & JOY_DOWN;
10733   int button1   = player_action & JOY_BUTTON_1;
10734   int button2   = player_action & JOY_BUTTON_2;
10735   int dx        = (left ? -1 : right ? 1 : 0);
10736   int dy        = (up   ? -1 : down  ? 1 : 0);
10737
10738   if (!player->active || tape.pausing)
10739     return 0;
10740
10741   if (player_action)
10742   {
10743     if (button1)
10744       SnapField(player, dx, dy);
10745     else
10746     {
10747       if (button2)
10748         DropElement(player);
10749
10750       MovePlayer(player, dx, dy);
10751     }
10752
10753     CheckSingleStepMode(player);
10754
10755     SetPlayerWaiting(player, FALSE);
10756
10757     return player_action;
10758   }
10759   else
10760   {
10761     /* no actions for this player (no input at player's configured device) */
10762
10763     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10764     SnapField(player, 0, 0);
10765     CheckGravityMovementWhenNotMoving(player);
10766
10767     if (player->MovPos == 0)
10768       SetPlayerWaiting(player, TRUE);
10769
10770     if (player->MovPos == 0)    /* needed for tape.playing */
10771       player->is_moving = FALSE;
10772
10773     player->is_dropping = FALSE;
10774     player->is_dropping_pressed = FALSE;
10775     player->drop_pressed_delay = 0;
10776
10777     CheckSingleStepMode(player);
10778
10779     return 0;
10780   }
10781 }
10782
10783 static void CheckLevelTime()
10784 {
10785   int i;
10786
10787   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10788   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10789   {
10790     if (level.native_em_level->lev->home == 0)  /* all players at home */
10791     {
10792       PlayerWins(local_player);
10793
10794       AllPlayersGone = TRUE;
10795
10796       level.native_em_level->lev->home = -1;
10797     }
10798
10799     if (level.native_em_level->ply[0]->alive == 0 &&
10800         level.native_em_level->ply[1]->alive == 0 &&
10801         level.native_em_level->ply[2]->alive == 0 &&
10802         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10803       AllPlayersGone = TRUE;
10804   }
10805   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10806   {
10807     if (game_sp.LevelSolved &&
10808         !game_sp.GameOver)                              /* game won */
10809     {
10810       PlayerWins(local_player);
10811
10812       game_sp.GameOver = TRUE;
10813
10814       AllPlayersGone = TRUE;
10815     }
10816
10817     if (game_sp.GameOver)                               /* game lost */
10818       AllPlayersGone = TRUE;
10819   }
10820
10821   if (TimeFrames >= FRAMES_PER_SECOND)
10822   {
10823     TimeFrames = 0;
10824     TapeTime++;
10825
10826     for (i = 0; i < MAX_PLAYERS; i++)
10827     {
10828       struct PlayerInfo *player = &stored_player[i];
10829
10830       if (SHIELD_ON(player))
10831       {
10832         player->shield_normal_time_left--;
10833
10834         if (player->shield_deadly_time_left > 0)
10835           player->shield_deadly_time_left--;
10836       }
10837     }
10838
10839     if (!local_player->LevelSolved && !level.use_step_counter)
10840     {
10841       TimePlayed++;
10842
10843       if (TimeLeft > 0)
10844       {
10845         TimeLeft--;
10846
10847         if (TimeLeft <= 10 && setup.time_limit)
10848           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10849
10850         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10851            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10852
10853         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10854
10855         if (!TimeLeft && setup.time_limit)
10856         {
10857           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10858             level.native_em_level->lev->killed_out_of_time = TRUE;
10859           else
10860             for (i = 0; i < MAX_PLAYERS; i++)
10861               KillPlayer(&stored_player[i]);
10862         }
10863       }
10864       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10865       {
10866         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10867       }
10868
10869       level.native_em_level->lev->time =
10870         (game.no_time_limit ? TimePlayed : TimeLeft);
10871     }
10872
10873     if (tape.recording || tape.playing)
10874       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10875   }
10876
10877   if (tape.recording || tape.playing)
10878     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10879
10880   UpdateAndDisplayGameControlValues();
10881 }
10882
10883 void AdvanceFrameAndPlayerCounters(int player_nr)
10884 {
10885   int i;
10886
10887   /* advance frame counters (global frame counter and time frame counter) */
10888   FrameCounter++;
10889   TimeFrames++;
10890
10891   /* advance player counters (counters for move delay, move animation etc.) */
10892   for (i = 0; i < MAX_PLAYERS; i++)
10893   {
10894     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10895     int move_delay_value = stored_player[i].move_delay_value;
10896     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10897
10898     if (!advance_player_counters)       /* not all players may be affected */
10899       continue;
10900
10901     if (move_frames == 0)       /* less than one move per game frame */
10902     {
10903       int stepsize = TILEX / move_delay_value;
10904       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10905       int count = (stored_player[i].is_moving ?
10906                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10907
10908       if (count % delay == 0)
10909         move_frames = 1;
10910     }
10911
10912     stored_player[i].Frame += move_frames;
10913
10914     if (stored_player[i].MovPos != 0)
10915       stored_player[i].StepFrame += move_frames;
10916
10917     if (stored_player[i].move_delay > 0)
10918       stored_player[i].move_delay--;
10919
10920     /* due to bugs in previous versions, counter must count up, not down */
10921     if (stored_player[i].push_delay != -1)
10922       stored_player[i].push_delay++;
10923
10924     if (stored_player[i].drop_delay > 0)
10925       stored_player[i].drop_delay--;
10926
10927     if (stored_player[i].is_dropping_pressed)
10928       stored_player[i].drop_pressed_delay++;
10929   }
10930 }
10931
10932 void StartGameActions(boolean init_network_game, boolean record_tape,
10933                       int random_seed)
10934 {
10935   unsigned int new_random_seed = InitRND(random_seed);
10936
10937   if (record_tape)
10938     TapeStartRecording(new_random_seed);
10939
10940 #if defined(NETWORK_AVALIABLE)
10941   if (init_network_game)
10942   {
10943     SendToServer_StartPlaying();
10944
10945     return;
10946   }
10947 #endif
10948
10949   InitGame();
10950 }
10951
10952 void GameActionsExt()
10953 {
10954 #if 0
10955   static unsigned int game_frame_delay = 0;
10956 #endif
10957   unsigned int game_frame_delay_value;
10958   byte *recorded_player_action;
10959   byte summarized_player_action = 0;
10960   byte tape_action[MAX_PLAYERS];
10961   int i;
10962
10963   /* detect endless loops, caused by custom element programming */
10964   if (recursion_loop_detected && recursion_loop_depth == 0)
10965   {
10966     char *message = getStringCat3("Internal Error! Element ",
10967                                   EL_NAME(recursion_loop_element),
10968                                   " caused endless loop! Quit the game?");
10969
10970     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10971           EL_NAME(recursion_loop_element));
10972
10973     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10974
10975     recursion_loop_detected = FALSE;    /* if game should be continued */
10976
10977     free(message);
10978
10979     return;
10980   }
10981
10982   if (game.restart_level)
10983     StartGameActions(options.network, setup.autorecord, level.random_seed);
10984
10985   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10986   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10987   {
10988     if (level.native_em_level->lev->home == 0)  /* all players at home */
10989     {
10990       PlayerWins(local_player);
10991
10992       AllPlayersGone = TRUE;
10993
10994       level.native_em_level->lev->home = -1;
10995     }
10996
10997     if (level.native_em_level->ply[0]->alive == 0 &&
10998         level.native_em_level->ply[1]->alive == 0 &&
10999         level.native_em_level->ply[2]->alive == 0 &&
11000         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11001       AllPlayersGone = TRUE;
11002   }
11003   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11004   {
11005     if (game_sp.LevelSolved &&
11006         !game_sp.GameOver)                              /* game won */
11007     {
11008       PlayerWins(local_player);
11009
11010       game_sp.GameOver = TRUE;
11011
11012       AllPlayersGone = TRUE;
11013     }
11014
11015     if (game_sp.GameOver)                               /* game lost */
11016       AllPlayersGone = TRUE;
11017   }
11018
11019   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11020     GameWon();
11021
11022   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11023     TapeStop();
11024
11025   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11026     return;
11027
11028   game_frame_delay_value =
11029     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11030
11031   if (tape.playing && tape.warp_forward && !tape.pausing)
11032     game_frame_delay_value = 0;
11033
11034   SetVideoFrameDelay(game_frame_delay_value);
11035
11036 #if 0
11037 #if 0
11038   /* ---------- main game synchronization point ---------- */
11039
11040   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11041
11042   printf("::: skip == %d\n", skip);
11043
11044 #else
11045   /* ---------- main game synchronization point ---------- */
11046
11047   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11048 #endif
11049 #endif
11050
11051   if (network_playing && !network_player_action_received)
11052   {
11053     /* try to get network player actions in time */
11054
11055 #if defined(NETWORK_AVALIABLE)
11056     /* last chance to get network player actions without main loop delay */
11057     HandleNetworking();
11058 #endif
11059
11060     /* game was quit by network peer */
11061     if (game_status != GAME_MODE_PLAYING)
11062       return;
11063
11064     if (!network_player_action_received)
11065       return;           /* failed to get network player actions in time */
11066
11067     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11068   }
11069
11070   if (tape.pausing)
11071     return;
11072
11073   /* at this point we know that we really continue executing the game */
11074
11075   network_player_action_received = FALSE;
11076
11077   /* when playing tape, read previously recorded player input from tape data */
11078   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11079
11080   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11081   if (tape.pausing)
11082     return;
11083
11084   if (tape.set_centered_player)
11085   {
11086     game.centered_player_nr_next = tape.centered_player_nr_next;
11087     game.set_centered_player = TRUE;
11088   }
11089
11090   for (i = 0; i < MAX_PLAYERS; i++)
11091   {
11092     summarized_player_action |= stored_player[i].action;
11093
11094     if (!network_playing && (game.team_mode || tape.playing))
11095       stored_player[i].effective_action = stored_player[i].action;
11096   }
11097
11098 #if defined(NETWORK_AVALIABLE)
11099   if (network_playing)
11100     SendToServer_MovePlayer(summarized_player_action);
11101 #endif
11102
11103   // summarize all actions at local players mapped input device position
11104   // (this allows using different input devices in single player mode)
11105   if (!options.network && !game.team_mode)
11106     stored_player[map_player_action[local_player->index_nr]].effective_action =
11107       summarized_player_action;
11108
11109   if (tape.recording &&
11110       setup.team_mode &&
11111       setup.input_on_focus &&
11112       game.centered_player_nr != -1)
11113   {
11114     for (i = 0; i < MAX_PLAYERS; i++)
11115       stored_player[i].effective_action =
11116         (i == game.centered_player_nr ? summarized_player_action : 0);
11117   }
11118
11119   if (recorded_player_action != NULL)
11120     for (i = 0; i < MAX_PLAYERS; i++)
11121       stored_player[i].effective_action = recorded_player_action[i];
11122
11123   for (i = 0; i < MAX_PLAYERS; i++)
11124   {
11125     tape_action[i] = stored_player[i].effective_action;
11126
11127     /* (this may happen in the RND game engine if a player was not present on
11128        the playfield on level start, but appeared later from a custom element */
11129     if (setup.team_mode &&
11130         tape.recording &&
11131         tape_action[i] &&
11132         !tape.player_participates[i])
11133       tape.player_participates[i] = TRUE;
11134   }
11135
11136   /* only record actions from input devices, but not programmed actions */
11137   if (tape.recording)
11138     TapeRecordAction(tape_action);
11139
11140 #if USE_NEW_PLAYER_ASSIGNMENTS
11141   // !!! also map player actions in single player mode !!!
11142   // if (game.team_mode)
11143   if (1)
11144   {
11145     byte mapped_action[MAX_PLAYERS];
11146
11147 #if DEBUG_PLAYER_ACTIONS
11148     printf(":::");
11149     for (i = 0; i < MAX_PLAYERS; i++)
11150       printf(" %d, ", stored_player[i].effective_action);
11151 #endif
11152
11153     for (i = 0; i < MAX_PLAYERS; i++)
11154       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11155
11156     for (i = 0; i < MAX_PLAYERS; i++)
11157       stored_player[i].effective_action = mapped_action[i];
11158
11159 #if DEBUG_PLAYER_ACTIONS
11160     printf(" =>");
11161     for (i = 0; i < MAX_PLAYERS; i++)
11162       printf(" %d, ", stored_player[i].effective_action);
11163     printf("\n");
11164 #endif
11165   }
11166 #if DEBUG_PLAYER_ACTIONS
11167   else
11168   {
11169     printf(":::");
11170     for (i = 0; i < MAX_PLAYERS; i++)
11171       printf(" %d, ", stored_player[i].effective_action);
11172     printf("\n");
11173   }
11174 #endif
11175 #endif
11176
11177   for (i = 0; i < MAX_PLAYERS; i++)
11178   {
11179     // allow engine snapshot in case of changed movement attempt
11180     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11181         (stored_player[i].effective_action & KEY_MOTION))
11182       game.snapshot.changed_action = TRUE;
11183
11184     // allow engine snapshot in case of snapping/dropping attempt
11185     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11186         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11187       game.snapshot.changed_action = TRUE;
11188
11189     game.snapshot.last_action[i] = stored_player[i].effective_action;
11190   }
11191
11192   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11193   {
11194     GameActions_EM_Main();
11195   }
11196   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11197   {
11198     GameActions_SP_Main();
11199   }
11200   else
11201   {
11202     GameActions_RND_Main();
11203   }
11204
11205   BlitScreenToBitmap(backbuffer);
11206
11207   CheckLevelTime();
11208
11209   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11210
11211   if (options.debug)                    /* calculate frames per second */
11212   {
11213     static unsigned int fps_counter = 0;
11214     static int fps_frames = 0;
11215     unsigned int fps_delay_ms = Counter() - fps_counter;
11216
11217     fps_frames++;
11218
11219     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11220     {
11221       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11222
11223       fps_frames = 0;
11224       fps_counter = Counter();
11225     }
11226
11227     redraw_mask |= REDRAW_FPS;
11228   }
11229 }
11230
11231 static void GameActions_CheckSaveEngineSnapshot()
11232 {
11233   if (!game.snapshot.save_snapshot)
11234     return;
11235
11236   // clear flag for saving snapshot _before_ saving snapshot
11237   game.snapshot.save_snapshot = FALSE;
11238
11239   SaveEngineSnapshotToList();
11240 }
11241
11242 void GameActions()
11243 {
11244   GameActionsExt();
11245
11246   GameActions_CheckSaveEngineSnapshot();
11247 }
11248
11249 void GameActions_EM_Main()
11250 {
11251   byte effective_action[MAX_PLAYERS];
11252   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11253   int i;
11254
11255   for (i = 0; i < MAX_PLAYERS; i++)
11256     effective_action[i] = stored_player[i].effective_action;
11257
11258   GameActions_EM(effective_action, warp_mode);
11259 }
11260
11261 void GameActions_SP_Main()
11262 {
11263   byte effective_action[MAX_PLAYERS];
11264   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11265   int i;
11266
11267   for (i = 0; i < MAX_PLAYERS; i++)
11268     effective_action[i] = stored_player[i].effective_action;
11269
11270   GameActions_SP(effective_action, warp_mode);
11271 }
11272
11273 void GameActions_RND_Main()
11274 {
11275   GameActions_RND();
11276 }
11277
11278 void GameActions_RND()
11279 {
11280   int magic_wall_x = 0, magic_wall_y = 0;
11281   int i, x, y, element, graphic, last_gfx_frame;
11282
11283   InitPlayfieldScanModeVars();
11284
11285   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11286   {
11287     SCAN_PLAYFIELD(x, y)
11288     {
11289       ChangeCount[x][y] = 0;
11290       ChangeEvent[x][y] = -1;
11291     }
11292   }
11293
11294   if (game.set_centered_player)
11295   {
11296     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11297
11298     /* switching to "all players" only possible if all players fit to screen */
11299     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11300     {
11301       game.centered_player_nr_next = game.centered_player_nr;
11302       game.set_centered_player = FALSE;
11303     }
11304
11305     /* do not switch focus to non-existing (or non-active) player */
11306     if (game.centered_player_nr_next >= 0 &&
11307         !stored_player[game.centered_player_nr_next].active)
11308     {
11309       game.centered_player_nr_next = game.centered_player_nr;
11310       game.set_centered_player = FALSE;
11311     }
11312   }
11313
11314   if (game.set_centered_player &&
11315       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11316   {
11317     int sx, sy;
11318
11319     if (game.centered_player_nr_next == -1)
11320     {
11321       setScreenCenteredToAllPlayers(&sx, &sy);
11322     }
11323     else
11324     {
11325       sx = stored_player[game.centered_player_nr_next].jx;
11326       sy = stored_player[game.centered_player_nr_next].jy;
11327     }
11328
11329     game.centered_player_nr = game.centered_player_nr_next;
11330     game.set_centered_player = FALSE;
11331
11332     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11333     DrawGameDoorValues();
11334   }
11335
11336   for (i = 0; i < MAX_PLAYERS; i++)
11337   {
11338     int actual_player_action = stored_player[i].effective_action;
11339
11340 #if 1
11341     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11342        - rnd_equinox_tetrachloride 048
11343        - rnd_equinox_tetrachloride_ii 096
11344        - rnd_emanuel_schmieg 002
11345        - doctor_sloan_ww 001, 020
11346     */
11347     if (stored_player[i].MovPos == 0)
11348       CheckGravityMovement(&stored_player[i]);
11349 #endif
11350
11351     /* overwrite programmed action with tape action */
11352     if (stored_player[i].programmed_action)
11353       actual_player_action = stored_player[i].programmed_action;
11354
11355     PlayerActions(&stored_player[i], actual_player_action);
11356
11357     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11358   }
11359
11360   ScrollScreen(NULL, SCROLL_GO_ON);
11361
11362   /* for backwards compatibility, the following code emulates a fixed bug that
11363      occured when pushing elements (causing elements that just made their last
11364      pushing step to already (if possible) make their first falling step in the
11365      same game frame, which is bad); this code is also needed to use the famous
11366      "spring push bug" which is used in older levels and might be wanted to be
11367      used also in newer levels, but in this case the buggy pushing code is only
11368      affecting the "spring" element and no other elements */
11369
11370   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11371   {
11372     for (i = 0; i < MAX_PLAYERS; i++)
11373     {
11374       struct PlayerInfo *player = &stored_player[i];
11375       int x = player->jx;
11376       int y = player->jy;
11377
11378       if (player->active && player->is_pushing && player->is_moving &&
11379           IS_MOVING(x, y) &&
11380           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11381            Feld[x][y] == EL_SPRING))
11382       {
11383         ContinueMoving(x, y);
11384
11385         /* continue moving after pushing (this is actually a bug) */
11386         if (!IS_MOVING(x, y))
11387           Stop[x][y] = FALSE;
11388       }
11389     }
11390   }
11391
11392   SCAN_PLAYFIELD(x, y)
11393   {
11394     ChangeCount[x][y] = 0;
11395     ChangeEvent[x][y] = -1;
11396
11397     /* this must be handled before main playfield loop */
11398     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11399     {
11400       MovDelay[x][y]--;
11401       if (MovDelay[x][y] <= 0)
11402         RemoveField(x, y);
11403     }
11404
11405     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11406     {
11407       MovDelay[x][y]--;
11408       if (MovDelay[x][y] <= 0)
11409       {
11410         RemoveField(x, y);
11411         TEST_DrawLevelField(x, y);
11412
11413         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11414       }
11415     }
11416
11417 #if DEBUG
11418     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11419     {
11420       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11421       printf("GameActions(): This should never happen!\n");
11422
11423       ChangePage[x][y] = -1;
11424     }
11425 #endif
11426
11427     Stop[x][y] = FALSE;
11428     if (WasJustMoving[x][y] > 0)
11429       WasJustMoving[x][y]--;
11430     if (WasJustFalling[x][y] > 0)
11431       WasJustFalling[x][y]--;
11432     if (CheckCollision[x][y] > 0)
11433       CheckCollision[x][y]--;
11434     if (CheckImpact[x][y] > 0)
11435       CheckImpact[x][y]--;
11436
11437     GfxFrame[x][y]++;
11438
11439     /* reset finished pushing action (not done in ContinueMoving() to allow
11440        continuous pushing animation for elements with zero push delay) */
11441     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11442     {
11443       ResetGfxAnimation(x, y);
11444       TEST_DrawLevelField(x, y);
11445     }
11446
11447 #if DEBUG
11448     if (IS_BLOCKED(x, y))
11449     {
11450       int oldx, oldy;
11451
11452       Blocked2Moving(x, y, &oldx, &oldy);
11453       if (!IS_MOVING(oldx, oldy))
11454       {
11455         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11456         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11457         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11458         printf("GameActions(): This should never happen!\n");
11459       }
11460     }
11461 #endif
11462   }
11463
11464   SCAN_PLAYFIELD(x, y)
11465   {
11466     element = Feld[x][y];
11467     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11468     last_gfx_frame = GfxFrame[x][y];
11469
11470     ResetGfxFrame(x, y);
11471
11472     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11473       DrawLevelGraphicAnimation(x, y, graphic);
11474
11475     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11476         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11477       ResetRandomAnimationValue(x, y);
11478
11479     SetRandomAnimationValue(x, y);
11480
11481     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11482
11483     if (IS_INACTIVE(element))
11484     {
11485       if (IS_ANIMATED(graphic))
11486         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11487
11488       continue;
11489     }
11490
11491     /* this may take place after moving, so 'element' may have changed */
11492     if (IS_CHANGING(x, y) &&
11493         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11494     {
11495       int page = element_info[element].event_page_nr[CE_DELAY];
11496
11497       HandleElementChange(x, y, page);
11498
11499       element = Feld[x][y];
11500       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11501     }
11502
11503     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11504     {
11505       StartMoving(x, y);
11506
11507       element = Feld[x][y];
11508       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11509
11510       if (IS_ANIMATED(graphic) &&
11511           !IS_MOVING(x, y) &&
11512           !Stop[x][y])
11513         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11514
11515       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11516         TEST_DrawTwinkleOnField(x, y);
11517     }
11518     else if (element == EL_ACID)
11519     {
11520       if (!Stop[x][y])
11521         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11522     }
11523     else if ((element == EL_EXIT_OPEN ||
11524               element == EL_EM_EXIT_OPEN ||
11525               element == EL_SP_EXIT_OPEN ||
11526               element == EL_STEEL_EXIT_OPEN ||
11527               element == EL_EM_STEEL_EXIT_OPEN ||
11528               element == EL_SP_TERMINAL ||
11529               element == EL_SP_TERMINAL_ACTIVE ||
11530               element == EL_EXTRA_TIME ||
11531               element == EL_SHIELD_NORMAL ||
11532               element == EL_SHIELD_DEADLY) &&
11533              IS_ANIMATED(graphic))
11534       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11535     else if (IS_MOVING(x, y))
11536       ContinueMoving(x, y);
11537     else if (IS_ACTIVE_BOMB(element))
11538       CheckDynamite(x, y);
11539     else if (element == EL_AMOEBA_GROWING)
11540       AmoebeWaechst(x, y);
11541     else if (element == EL_AMOEBA_SHRINKING)
11542       AmoebaDisappearing(x, y);
11543
11544 #if !USE_NEW_AMOEBA_CODE
11545     else if (IS_AMOEBALIVE(element))
11546       AmoebeAbleger(x, y);
11547 #endif
11548
11549     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11550       Life(x, y);
11551     else if (element == EL_EXIT_CLOSED)
11552       CheckExit(x, y);
11553     else if (element == EL_EM_EXIT_CLOSED)
11554       CheckExitEM(x, y);
11555     else if (element == EL_STEEL_EXIT_CLOSED)
11556       CheckExitSteel(x, y);
11557     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11558       CheckExitSteelEM(x, y);
11559     else if (element == EL_SP_EXIT_CLOSED)
11560       CheckExitSP(x, y);
11561     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11562              element == EL_EXPANDABLE_STEELWALL_GROWING)
11563       MauerWaechst(x, y);
11564     else if (element == EL_EXPANDABLE_WALL ||
11565              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11566              element == EL_EXPANDABLE_WALL_VERTICAL ||
11567              element == EL_EXPANDABLE_WALL_ANY ||
11568              element == EL_BD_EXPANDABLE_WALL)
11569       MauerAbleger(x, y);
11570     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11571              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11572              element == EL_EXPANDABLE_STEELWALL_ANY)
11573       MauerAblegerStahl(x, y);
11574     else if (element == EL_FLAMES)
11575       CheckForDragon(x, y);
11576     else if (element == EL_EXPLOSION)
11577       ; /* drawing of correct explosion animation is handled separately */
11578     else if (element == EL_ELEMENT_SNAPPING ||
11579              element == EL_DIAGONAL_SHRINKING ||
11580              element == EL_DIAGONAL_GROWING)
11581     {
11582       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11583
11584       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11585     }
11586     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11587       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11588
11589     if (IS_BELT_ACTIVE(element))
11590       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11591
11592     if (game.magic_wall_active)
11593     {
11594       int jx = local_player->jx, jy = local_player->jy;
11595
11596       /* play the element sound at the position nearest to the player */
11597       if ((element == EL_MAGIC_WALL_FULL ||
11598            element == EL_MAGIC_WALL_ACTIVE ||
11599            element == EL_MAGIC_WALL_EMPTYING ||
11600            element == EL_BD_MAGIC_WALL_FULL ||
11601            element == EL_BD_MAGIC_WALL_ACTIVE ||
11602            element == EL_BD_MAGIC_WALL_EMPTYING ||
11603            element == EL_DC_MAGIC_WALL_FULL ||
11604            element == EL_DC_MAGIC_WALL_ACTIVE ||
11605            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11606           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11607       {
11608         magic_wall_x = x;
11609         magic_wall_y = y;
11610       }
11611     }
11612   }
11613
11614 #if USE_NEW_AMOEBA_CODE
11615   /* new experimental amoeba growth stuff */
11616   if (!(FrameCounter % 8))
11617   {
11618     static unsigned int random = 1684108901;
11619
11620     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11621     {
11622       x = RND(lev_fieldx);
11623       y = RND(lev_fieldy);
11624       element = Feld[x][y];
11625
11626       if (!IS_PLAYER(x,y) &&
11627           (element == EL_EMPTY ||
11628            CAN_GROW_INTO(element) ||
11629            element == EL_QUICKSAND_EMPTY ||
11630            element == EL_QUICKSAND_FAST_EMPTY ||
11631            element == EL_ACID_SPLASH_LEFT ||
11632            element == EL_ACID_SPLASH_RIGHT))
11633       {
11634         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11635             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11636             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11637             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11638           Feld[x][y] = EL_AMOEBA_DROP;
11639       }
11640
11641       random = random * 129 + 1;
11642     }
11643   }
11644 #endif
11645
11646   game.explosions_delayed = FALSE;
11647
11648   SCAN_PLAYFIELD(x, y)
11649   {
11650     element = Feld[x][y];
11651
11652     if (ExplodeField[x][y])
11653       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11654     else if (element == EL_EXPLOSION)
11655       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11656
11657     ExplodeField[x][y] = EX_TYPE_NONE;
11658   }
11659
11660   game.explosions_delayed = TRUE;
11661
11662   if (game.magic_wall_active)
11663   {
11664     if (!(game.magic_wall_time_left % 4))
11665     {
11666       int element = Feld[magic_wall_x][magic_wall_y];
11667
11668       if (element == EL_BD_MAGIC_WALL_FULL ||
11669           element == EL_BD_MAGIC_WALL_ACTIVE ||
11670           element == EL_BD_MAGIC_WALL_EMPTYING)
11671         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11672       else if (element == EL_DC_MAGIC_WALL_FULL ||
11673                element == EL_DC_MAGIC_WALL_ACTIVE ||
11674                element == EL_DC_MAGIC_WALL_EMPTYING)
11675         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11676       else
11677         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11678     }
11679
11680     if (game.magic_wall_time_left > 0)
11681     {
11682       game.magic_wall_time_left--;
11683
11684       if (!game.magic_wall_time_left)
11685       {
11686         SCAN_PLAYFIELD(x, y)
11687         {
11688           element = Feld[x][y];
11689
11690           if (element == EL_MAGIC_WALL_ACTIVE ||
11691               element == EL_MAGIC_WALL_FULL)
11692           {
11693             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11694             TEST_DrawLevelField(x, y);
11695           }
11696           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11697                    element == EL_BD_MAGIC_WALL_FULL)
11698           {
11699             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11700             TEST_DrawLevelField(x, y);
11701           }
11702           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11703                    element == EL_DC_MAGIC_WALL_FULL)
11704           {
11705             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11706             TEST_DrawLevelField(x, y);
11707           }
11708         }
11709
11710         game.magic_wall_active = FALSE;
11711       }
11712     }
11713   }
11714
11715   if (game.light_time_left > 0)
11716   {
11717     game.light_time_left--;
11718
11719     if (game.light_time_left == 0)
11720       RedrawAllLightSwitchesAndInvisibleElements();
11721   }
11722
11723   if (game.timegate_time_left > 0)
11724   {
11725     game.timegate_time_left--;
11726
11727     if (game.timegate_time_left == 0)
11728       CloseAllOpenTimegates();
11729   }
11730
11731   if (game.lenses_time_left > 0)
11732   {
11733     game.lenses_time_left--;
11734
11735     if (game.lenses_time_left == 0)
11736       RedrawAllInvisibleElementsForLenses();
11737   }
11738
11739   if (game.magnify_time_left > 0)
11740   {
11741     game.magnify_time_left--;
11742
11743     if (game.magnify_time_left == 0)
11744       RedrawAllInvisibleElementsForMagnifier();
11745   }
11746
11747   for (i = 0; i < MAX_PLAYERS; i++)
11748   {
11749     struct PlayerInfo *player = &stored_player[i];
11750
11751     if (SHIELD_ON(player))
11752     {
11753       if (player->shield_deadly_time_left)
11754         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11755       else if (player->shield_normal_time_left)
11756         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11757     }
11758   }
11759
11760 #if USE_DELAYED_GFX_REDRAW
11761   SCAN_PLAYFIELD(x, y)
11762   {
11763     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11764     {
11765       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11766          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11767
11768       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11769         DrawLevelField(x, y);
11770
11771       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11772         DrawLevelFieldCrumbled(x, y);
11773
11774       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11775         DrawLevelFieldCrumbledNeighbours(x, y);
11776
11777       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11778         DrawTwinkleOnField(x, y);
11779     }
11780
11781     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11782   }
11783 #endif
11784
11785   DrawAllPlayers();
11786   PlayAllPlayersSound();
11787
11788   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11789   {
11790     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11791
11792     local_player->show_envelope = 0;
11793   }
11794
11795   /* use random number generator in every frame to make it less predictable */
11796   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11797     RND(1);
11798 }
11799
11800 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11801 {
11802   int min_x = x, min_y = y, max_x = x, max_y = y;
11803   int i;
11804
11805   for (i = 0; i < MAX_PLAYERS; i++)
11806   {
11807     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11808
11809     if (!stored_player[i].active || &stored_player[i] == player)
11810       continue;
11811
11812     min_x = MIN(min_x, jx);
11813     min_y = MIN(min_y, jy);
11814     max_x = MAX(max_x, jx);
11815     max_y = MAX(max_y, jy);
11816   }
11817
11818   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11819 }
11820
11821 static boolean AllPlayersInVisibleScreen()
11822 {
11823   int i;
11824
11825   for (i = 0; i < MAX_PLAYERS; i++)
11826   {
11827     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11828
11829     if (!stored_player[i].active)
11830       continue;
11831
11832     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11833       return FALSE;
11834   }
11835
11836   return TRUE;
11837 }
11838
11839 void ScrollLevel(int dx, int dy)
11840 {
11841   int scroll_offset = 2 * TILEX_VAR;
11842   int x, y;
11843
11844   BlitBitmap(drawto_field, drawto_field,
11845              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11846              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11847              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11848              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11849              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11850              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11851
11852   if (dx != 0)
11853   {
11854     x = (dx == 1 ? BX1 : BX2);
11855     for (y = BY1; y <= BY2; y++)
11856       DrawScreenField(x, y);
11857   }
11858
11859   if (dy != 0)
11860   {
11861     y = (dy == 1 ? BY1 : BY2);
11862     for (x = BX1; x <= BX2; x++)
11863       DrawScreenField(x, y);
11864   }
11865
11866   redraw_mask |= REDRAW_FIELD;
11867 }
11868
11869 static boolean canFallDown(struct PlayerInfo *player)
11870 {
11871   int jx = player->jx, jy = player->jy;
11872
11873   return (IN_LEV_FIELD(jx, jy + 1) &&
11874           (IS_FREE(jx, jy + 1) ||
11875            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11876           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11877           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11878 }
11879
11880 static boolean canPassField(int x, int y, int move_dir)
11881 {
11882   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11883   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11884   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11885   int nextx = x + dx;
11886   int nexty = y + dy;
11887   int element = Feld[x][y];
11888
11889   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11890           !CAN_MOVE(element) &&
11891           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11892           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11893           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11894 }
11895
11896 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11897 {
11898   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11899   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11900   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11901   int newx = x + dx;
11902   int newy = y + dy;
11903
11904   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11905           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11906           (IS_DIGGABLE(Feld[newx][newy]) ||
11907            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11908            canPassField(newx, newy, move_dir)));
11909 }
11910
11911 static void CheckGravityMovement(struct PlayerInfo *player)
11912 {
11913   if (player->gravity && !player->programmed_action)
11914   {
11915     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11916     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11917     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11918     int jx = player->jx, jy = player->jy;
11919     boolean player_is_moving_to_valid_field =
11920       (!player_is_snapping &&
11921        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11922         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11923     boolean player_can_fall_down = canFallDown(player);
11924
11925     if (player_can_fall_down &&
11926         !player_is_moving_to_valid_field)
11927       player->programmed_action = MV_DOWN;
11928   }
11929 }
11930
11931 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11932 {
11933   return CheckGravityMovement(player);
11934
11935   if (player->gravity && !player->programmed_action)
11936   {
11937     int jx = player->jx, jy = player->jy;
11938     boolean field_under_player_is_free =
11939       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11940     boolean player_is_standing_on_valid_field =
11941       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11942        (IS_WALKABLE(Feld[jx][jy]) &&
11943         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11944
11945     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11946       player->programmed_action = MV_DOWN;
11947   }
11948 }
11949
11950 /*
11951   MovePlayerOneStep()
11952   -----------------------------------------------------------------------------
11953   dx, dy:               direction (non-diagonal) to try to move the player to
11954   real_dx, real_dy:     direction as read from input device (can be diagonal)
11955 */
11956
11957 boolean MovePlayerOneStep(struct PlayerInfo *player,
11958                           int dx, int dy, int real_dx, int real_dy)
11959 {
11960   int jx = player->jx, jy = player->jy;
11961   int new_jx = jx + dx, new_jy = jy + dy;
11962   int can_move;
11963   boolean player_can_move = !player->cannot_move;
11964
11965   if (!player->active || (!dx && !dy))
11966     return MP_NO_ACTION;
11967
11968   player->MovDir = (dx < 0 ? MV_LEFT :
11969                     dx > 0 ? MV_RIGHT :
11970                     dy < 0 ? MV_UP :
11971                     dy > 0 ? MV_DOWN :  MV_NONE);
11972
11973   if (!IN_LEV_FIELD(new_jx, new_jy))
11974     return MP_NO_ACTION;
11975
11976   if (!player_can_move)
11977   {
11978     if (player->MovPos == 0)
11979     {
11980       player->is_moving = FALSE;
11981       player->is_digging = FALSE;
11982       player->is_collecting = FALSE;
11983       player->is_snapping = FALSE;
11984       player->is_pushing = FALSE;
11985     }
11986   }
11987
11988   if (!options.network && game.centered_player_nr == -1 &&
11989       !AllPlayersInSight(player, new_jx, new_jy))
11990     return MP_NO_ACTION;
11991
11992   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11993   if (can_move != MP_MOVING)
11994     return can_move;
11995
11996   /* check if DigField() has caused relocation of the player */
11997   if (player->jx != jx || player->jy != jy)
11998     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11999
12000   StorePlayer[jx][jy] = 0;
12001   player->last_jx = jx;
12002   player->last_jy = jy;
12003   player->jx = new_jx;
12004   player->jy = new_jy;
12005   StorePlayer[new_jx][new_jy] = player->element_nr;
12006
12007   if (player->move_delay_value_next != -1)
12008   {
12009     player->move_delay_value = player->move_delay_value_next;
12010     player->move_delay_value_next = -1;
12011   }
12012
12013   player->MovPos =
12014     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12015
12016   player->step_counter++;
12017
12018   PlayerVisit[jx][jy] = FrameCounter;
12019
12020   player->is_moving = TRUE;
12021
12022 #if 1
12023   /* should better be called in MovePlayer(), but this breaks some tapes */
12024   ScrollPlayer(player, SCROLL_INIT);
12025 #endif
12026
12027   return MP_MOVING;
12028 }
12029
12030 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12031 {
12032   int jx = player->jx, jy = player->jy;
12033   int old_jx = jx, old_jy = jy;
12034   int moved = MP_NO_ACTION;
12035
12036   if (!player->active)
12037     return FALSE;
12038
12039   if (!dx && !dy)
12040   {
12041     if (player->MovPos == 0)
12042     {
12043       player->is_moving = FALSE;
12044       player->is_digging = FALSE;
12045       player->is_collecting = FALSE;
12046       player->is_snapping = FALSE;
12047       player->is_pushing = FALSE;
12048     }
12049
12050     return FALSE;
12051   }
12052
12053   if (player->move_delay > 0)
12054     return FALSE;
12055
12056   player->move_delay = -1;              /* set to "uninitialized" value */
12057
12058   /* store if player is automatically moved to next field */
12059   player->is_auto_moving = (player->programmed_action != MV_NONE);
12060
12061   /* remove the last programmed player action */
12062   player->programmed_action = 0;
12063
12064   if (player->MovPos)
12065   {
12066     /* should only happen if pre-1.2 tape recordings are played */
12067     /* this is only for backward compatibility */
12068
12069     int original_move_delay_value = player->move_delay_value;
12070
12071 #if DEBUG
12072     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12073            tape.counter);
12074 #endif
12075
12076     /* scroll remaining steps with finest movement resolution */
12077     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12078
12079     while (player->MovPos)
12080     {
12081       ScrollPlayer(player, SCROLL_GO_ON);
12082       ScrollScreen(NULL, SCROLL_GO_ON);
12083
12084       AdvanceFrameAndPlayerCounters(player->index_nr);
12085
12086       DrawAllPlayers();
12087       BackToFront_WithFrameDelay(0);
12088     }
12089
12090     player->move_delay_value = original_move_delay_value;
12091   }
12092
12093   player->is_active = FALSE;
12094
12095   if (player->last_move_dir & MV_HORIZONTAL)
12096   {
12097     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12098       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12099   }
12100   else
12101   {
12102     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12103       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12104   }
12105
12106   if (!moved && !player->is_active)
12107   {
12108     player->is_moving = FALSE;
12109     player->is_digging = FALSE;
12110     player->is_collecting = FALSE;
12111     player->is_snapping = FALSE;
12112     player->is_pushing = FALSE;
12113   }
12114
12115   jx = player->jx;
12116   jy = player->jy;
12117
12118   if (moved & MP_MOVING && !ScreenMovPos &&
12119       (player->index_nr == game.centered_player_nr ||
12120        game.centered_player_nr == -1))
12121   {
12122     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12123     int offset = game.scroll_delay_value;
12124
12125     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12126     {
12127       /* actual player has left the screen -- scroll in that direction */
12128       if (jx != old_jx)         /* player has moved horizontally */
12129         scroll_x += (jx - old_jx);
12130       else                      /* player has moved vertically */
12131         scroll_y += (jy - old_jy);
12132     }
12133     else
12134     {
12135       if (jx != old_jx)         /* player has moved horizontally */
12136       {
12137         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12138             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12139           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12140
12141         /* don't scroll over playfield boundaries */
12142         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12143           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12144
12145         /* don't scroll more than one field at a time */
12146         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12147
12148         /* don't scroll against the player's moving direction */
12149         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12150             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12151           scroll_x = old_scroll_x;
12152       }
12153       else                      /* player has moved vertically */
12154       {
12155         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12156             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12157           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12158
12159         /* don't scroll over playfield boundaries */
12160         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12161           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12162
12163         /* don't scroll more than one field at a time */
12164         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12165
12166         /* don't scroll against the player's moving direction */
12167         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12168             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12169           scroll_y = old_scroll_y;
12170       }
12171     }
12172
12173     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12174     {
12175       if (!options.network && game.centered_player_nr == -1 &&
12176           !AllPlayersInVisibleScreen())
12177       {
12178         scroll_x = old_scroll_x;
12179         scroll_y = old_scroll_y;
12180       }
12181       else
12182       {
12183         ScrollScreen(player, SCROLL_INIT);
12184         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12185       }
12186     }
12187   }
12188
12189   player->StepFrame = 0;
12190
12191   if (moved & MP_MOVING)
12192   {
12193     if (old_jx != jx && old_jy == jy)
12194       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12195     else if (old_jx == jx && old_jy != jy)
12196       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12197
12198     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12199
12200     player->last_move_dir = player->MovDir;
12201     player->is_moving = TRUE;
12202     player->is_snapping = FALSE;
12203     player->is_switching = FALSE;
12204     player->is_dropping = FALSE;
12205     player->is_dropping_pressed = FALSE;
12206     player->drop_pressed_delay = 0;
12207
12208 #if 0
12209     /* should better be called here than above, but this breaks some tapes */
12210     ScrollPlayer(player, SCROLL_INIT);
12211 #endif
12212   }
12213   else
12214   {
12215     CheckGravityMovementWhenNotMoving(player);
12216
12217     player->is_moving = FALSE;
12218
12219     /* at this point, the player is allowed to move, but cannot move right now
12220        (e.g. because of something blocking the way) -- ensure that the player
12221        is also allowed to move in the next frame (in old versions before 3.1.1,
12222        the player was forced to wait again for eight frames before next try) */
12223
12224     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12225       player->move_delay = 0;   /* allow direct movement in the next frame */
12226   }
12227
12228   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12229     player->move_delay = player->move_delay_value;
12230
12231   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12232   {
12233     TestIfPlayerTouchesBadThing(jx, jy);
12234     TestIfPlayerTouchesCustomElement(jx, jy);
12235   }
12236
12237   if (!player->active)
12238     RemovePlayer(player);
12239
12240   return moved;
12241 }
12242
12243 void ScrollPlayer(struct PlayerInfo *player, int mode)
12244 {
12245   int jx = player->jx, jy = player->jy;
12246   int last_jx = player->last_jx, last_jy = player->last_jy;
12247   int move_stepsize = TILEX / player->move_delay_value;
12248
12249   if (!player->active)
12250     return;
12251
12252   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12253     return;
12254
12255   if (mode == SCROLL_INIT)
12256   {
12257     player->actual_frame_counter = FrameCounter;
12258     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12259
12260     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12261         Feld[last_jx][last_jy] == EL_EMPTY)
12262     {
12263       int last_field_block_delay = 0;   /* start with no blocking at all */
12264       int block_delay_adjustment = player->block_delay_adjustment;
12265
12266       /* if player blocks last field, add delay for exactly one move */
12267       if (player->block_last_field)
12268       {
12269         last_field_block_delay += player->move_delay_value;
12270
12271         /* when blocking enabled, prevent moving up despite gravity */
12272         if (player->gravity && player->MovDir == MV_UP)
12273           block_delay_adjustment = -1;
12274       }
12275
12276       /* add block delay adjustment (also possible when not blocking) */
12277       last_field_block_delay += block_delay_adjustment;
12278
12279       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12280       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12281     }
12282
12283     if (player->MovPos != 0)    /* player has not yet reached destination */
12284       return;
12285   }
12286   else if (!FrameReached(&player->actual_frame_counter, 1))
12287     return;
12288
12289   if (player->MovPos != 0)
12290   {
12291     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12292     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12293
12294     /* before DrawPlayer() to draw correct player graphic for this case */
12295     if (player->MovPos == 0)
12296       CheckGravityMovement(player);
12297   }
12298
12299   if (player->MovPos == 0)      /* player reached destination field */
12300   {
12301     if (player->move_delay_reset_counter > 0)
12302     {
12303       player->move_delay_reset_counter--;
12304
12305       if (player->move_delay_reset_counter == 0)
12306       {
12307         /* continue with normal speed after quickly moving through gate */
12308         HALVE_PLAYER_SPEED(player);
12309
12310         /* be able to make the next move without delay */
12311         player->move_delay = 0;
12312       }
12313     }
12314
12315     player->last_jx = jx;
12316     player->last_jy = jy;
12317
12318     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12319         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12320         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12321         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12322         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12323         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12324         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12325         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12326     {
12327       DrawPlayer(player);       /* needed here only to cleanup last field */
12328       RemovePlayer(player);
12329
12330       if (local_player->friends_still_needed == 0 ||
12331           IS_SP_ELEMENT(Feld[jx][jy]))
12332         PlayerWins(player);
12333     }
12334
12335     /* this breaks one level: "machine", level 000 */
12336     {
12337       int move_direction = player->MovDir;
12338       int enter_side = MV_DIR_OPPOSITE(move_direction);
12339       int leave_side = move_direction;
12340       int old_jx = last_jx;
12341       int old_jy = last_jy;
12342       int old_element = Feld[old_jx][old_jy];
12343       int new_element = Feld[jx][jy];
12344
12345       if (IS_CUSTOM_ELEMENT(old_element))
12346         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12347                                    CE_LEFT_BY_PLAYER,
12348                                    player->index_bit, leave_side);
12349
12350       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12351                                           CE_PLAYER_LEAVES_X,
12352                                           player->index_bit, leave_side);
12353
12354       if (IS_CUSTOM_ELEMENT(new_element))
12355         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12356                                    player->index_bit, enter_side);
12357
12358       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12359                                           CE_PLAYER_ENTERS_X,
12360                                           player->index_bit, enter_side);
12361
12362       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12363                                         CE_MOVE_OF_X, move_direction);
12364     }
12365
12366     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12367     {
12368       TestIfPlayerTouchesBadThing(jx, jy);
12369       TestIfPlayerTouchesCustomElement(jx, jy);
12370
12371       /* needed because pushed element has not yet reached its destination,
12372          so it would trigger a change event at its previous field location */
12373       if (!player->is_pushing)
12374         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12375
12376       if (!player->active)
12377         RemovePlayer(player);
12378     }
12379
12380     if (!local_player->LevelSolved && level.use_step_counter)
12381     {
12382       int i;
12383
12384       TimePlayed++;
12385
12386       if (TimeLeft > 0)
12387       {
12388         TimeLeft--;
12389
12390         if (TimeLeft <= 10 && setup.time_limit)
12391           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12392
12393         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12394
12395         DisplayGameControlValues();
12396
12397         if (!TimeLeft && setup.time_limit)
12398           for (i = 0; i < MAX_PLAYERS; i++)
12399             KillPlayer(&stored_player[i]);
12400       }
12401       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12402       {
12403         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12404
12405         DisplayGameControlValues();
12406       }
12407     }
12408
12409     if (tape.single_step && tape.recording && !tape.pausing &&
12410         !player->programmed_action)
12411       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12412
12413     if (!player->programmed_action)
12414       CheckSaveEngineSnapshot(player);
12415   }
12416 }
12417
12418 void ScrollScreen(struct PlayerInfo *player, int mode)
12419 {
12420   static unsigned int screen_frame_counter = 0;
12421
12422   if (mode == SCROLL_INIT)
12423   {
12424     /* set scrolling step size according to actual player's moving speed */
12425     ScrollStepSize = TILEX / player->move_delay_value;
12426
12427     screen_frame_counter = FrameCounter;
12428     ScreenMovDir = player->MovDir;
12429     ScreenMovPos = player->MovPos;
12430     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12431     return;
12432   }
12433   else if (!FrameReached(&screen_frame_counter, 1))
12434     return;
12435
12436   if (ScreenMovPos)
12437   {
12438     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12439     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12440     redraw_mask |= REDRAW_FIELD;
12441   }
12442   else
12443     ScreenMovDir = MV_NONE;
12444 }
12445
12446 void TestIfPlayerTouchesCustomElement(int x, int y)
12447 {
12448   static int xy[4][2] =
12449   {
12450     { 0, -1 },
12451     { -1, 0 },
12452     { +1, 0 },
12453     { 0, +1 }
12454   };
12455   static int trigger_sides[4][2] =
12456   {
12457     /* center side       border side */
12458     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12459     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12460     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12461     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12462   };
12463   static int touch_dir[4] =
12464   {
12465     MV_LEFT | MV_RIGHT,
12466     MV_UP   | MV_DOWN,
12467     MV_UP   | MV_DOWN,
12468     MV_LEFT | MV_RIGHT
12469   };
12470   int center_element = Feld[x][y];      /* should always be non-moving! */
12471   int i;
12472
12473   for (i = 0; i < NUM_DIRECTIONS; i++)
12474   {
12475     int xx = x + xy[i][0];
12476     int yy = y + xy[i][1];
12477     int center_side = trigger_sides[i][0];
12478     int border_side = trigger_sides[i][1];
12479     int border_element;
12480
12481     if (!IN_LEV_FIELD(xx, yy))
12482       continue;
12483
12484     if (IS_PLAYER(x, y))                /* player found at center element */
12485     {
12486       struct PlayerInfo *player = PLAYERINFO(x, y);
12487
12488       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12489         border_element = Feld[xx][yy];          /* may be moving! */
12490       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12491         border_element = Feld[xx][yy];
12492       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12493         border_element = MovingOrBlocked2Element(xx, yy);
12494       else
12495         continue;               /* center and border element do not touch */
12496
12497       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12498                                  player->index_bit, border_side);
12499       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12500                                           CE_PLAYER_TOUCHES_X,
12501                                           player->index_bit, border_side);
12502
12503       {
12504         /* use player element that is initially defined in the level playfield,
12505            not the player element that corresponds to the runtime player number
12506            (example: a level that contains EL_PLAYER_3 as the only player would
12507            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12508         int player_element = PLAYERINFO(x, y)->initial_element;
12509
12510         CheckElementChangeBySide(xx, yy, border_element, player_element,
12511                                  CE_TOUCHING_X, border_side);
12512       }
12513     }
12514     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12515     {
12516       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12517
12518       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12519       {
12520         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12521           continue;             /* center and border element do not touch */
12522       }
12523
12524       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12525                                  player->index_bit, center_side);
12526       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12527                                           CE_PLAYER_TOUCHES_X,
12528                                           player->index_bit, center_side);
12529
12530       {
12531         /* use player element that is initially defined in the level playfield,
12532            not the player element that corresponds to the runtime player number
12533            (example: a level that contains EL_PLAYER_3 as the only player would
12534            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12535         int player_element = PLAYERINFO(xx, yy)->initial_element;
12536
12537         CheckElementChangeBySide(x, y, center_element, player_element,
12538                                  CE_TOUCHING_X, center_side);
12539       }
12540
12541       break;
12542     }
12543   }
12544 }
12545
12546 void TestIfElementTouchesCustomElement(int x, int y)
12547 {
12548   static int xy[4][2] =
12549   {
12550     { 0, -1 },
12551     { -1, 0 },
12552     { +1, 0 },
12553     { 0, +1 }
12554   };
12555   static int trigger_sides[4][2] =
12556   {
12557     /* center side      border side */
12558     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12559     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12560     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12561     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12562   };
12563   static int touch_dir[4] =
12564   {
12565     MV_LEFT | MV_RIGHT,
12566     MV_UP   | MV_DOWN,
12567     MV_UP   | MV_DOWN,
12568     MV_LEFT | MV_RIGHT
12569   };
12570   boolean change_center_element = FALSE;
12571   int center_element = Feld[x][y];      /* should always be non-moving! */
12572   int border_element_old[NUM_DIRECTIONS];
12573   int i;
12574
12575   for (i = 0; i < NUM_DIRECTIONS; i++)
12576   {
12577     int xx = x + xy[i][0];
12578     int yy = y + xy[i][1];
12579     int border_element;
12580
12581     border_element_old[i] = -1;
12582
12583     if (!IN_LEV_FIELD(xx, yy))
12584       continue;
12585
12586     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12587       border_element = Feld[xx][yy];    /* may be moving! */
12588     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12589       border_element = Feld[xx][yy];
12590     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12591       border_element = MovingOrBlocked2Element(xx, yy);
12592     else
12593       continue;                 /* center and border element do not touch */
12594
12595     border_element_old[i] = border_element;
12596   }
12597
12598   for (i = 0; i < NUM_DIRECTIONS; i++)
12599   {
12600     int xx = x + xy[i][0];
12601     int yy = y + xy[i][1];
12602     int center_side = trigger_sides[i][0];
12603     int border_element = border_element_old[i];
12604
12605     if (border_element == -1)
12606       continue;
12607
12608     /* check for change of border element */
12609     CheckElementChangeBySide(xx, yy, border_element, center_element,
12610                              CE_TOUCHING_X, center_side);
12611
12612     /* (center element cannot be player, so we dont have to check this here) */
12613   }
12614
12615   for (i = 0; i < NUM_DIRECTIONS; i++)
12616   {
12617     int xx = x + xy[i][0];
12618     int yy = y + xy[i][1];
12619     int border_side = trigger_sides[i][1];
12620     int border_element = border_element_old[i];
12621
12622     if (border_element == -1)
12623       continue;
12624
12625     /* check for change of center element (but change it only once) */
12626     if (!change_center_element)
12627       change_center_element =
12628         CheckElementChangeBySide(x, y, center_element, border_element,
12629                                  CE_TOUCHING_X, border_side);
12630
12631     if (IS_PLAYER(xx, yy))
12632     {
12633       /* use player element that is initially defined in the level playfield,
12634          not the player element that corresponds to the runtime player number
12635          (example: a level that contains EL_PLAYER_3 as the only player would
12636          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12637       int player_element = PLAYERINFO(xx, yy)->initial_element;
12638
12639       CheckElementChangeBySide(x, y, center_element, player_element,
12640                                CE_TOUCHING_X, border_side);
12641     }
12642   }
12643 }
12644
12645 void TestIfElementHitsCustomElement(int x, int y, int direction)
12646 {
12647   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12648   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12649   int hitx = x + dx, hity = y + dy;
12650   int hitting_element = Feld[x][y];
12651   int touched_element;
12652
12653   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12654     return;
12655
12656   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12657                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12658
12659   if (IN_LEV_FIELD(hitx, hity))
12660   {
12661     int opposite_direction = MV_DIR_OPPOSITE(direction);
12662     int hitting_side = direction;
12663     int touched_side = opposite_direction;
12664     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12665                           MovDir[hitx][hity] != direction ||
12666                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12667
12668     object_hit = TRUE;
12669
12670     if (object_hit)
12671     {
12672       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12673                                CE_HITTING_X, touched_side);
12674
12675       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12676                                CE_HIT_BY_X, hitting_side);
12677
12678       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12679                                CE_HIT_BY_SOMETHING, opposite_direction);
12680
12681       if (IS_PLAYER(hitx, hity))
12682       {
12683         /* use player element that is initially defined in the level playfield,
12684            not the player element that corresponds to the runtime player number
12685            (example: a level that contains EL_PLAYER_3 as the only player would
12686            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12687         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12688
12689         CheckElementChangeBySide(x, y, hitting_element, player_element,
12690                                  CE_HITTING_X, touched_side);
12691       }
12692     }
12693   }
12694
12695   /* "hitting something" is also true when hitting the playfield border */
12696   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12697                            CE_HITTING_SOMETHING, direction);
12698 }
12699
12700 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12701 {
12702   int i, kill_x = -1, kill_y = -1;
12703
12704   int bad_element = -1;
12705   static int test_xy[4][2] =
12706   {
12707     { 0, -1 },
12708     { -1, 0 },
12709     { +1, 0 },
12710     { 0, +1 }
12711   };
12712   static int test_dir[4] =
12713   {
12714     MV_UP,
12715     MV_LEFT,
12716     MV_RIGHT,
12717     MV_DOWN
12718   };
12719
12720   for (i = 0; i < NUM_DIRECTIONS; i++)
12721   {
12722     int test_x, test_y, test_move_dir, test_element;
12723
12724     test_x = good_x + test_xy[i][0];
12725     test_y = good_y + test_xy[i][1];
12726
12727     if (!IN_LEV_FIELD(test_x, test_y))
12728       continue;
12729
12730     test_move_dir =
12731       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12732
12733     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12734
12735     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12736        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12737     */
12738     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12739         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12740     {
12741       kill_x = test_x;
12742       kill_y = test_y;
12743       bad_element = test_element;
12744
12745       break;
12746     }
12747   }
12748
12749   if (kill_x != -1 || kill_y != -1)
12750   {
12751     if (IS_PLAYER(good_x, good_y))
12752     {
12753       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12754
12755       if (player->shield_deadly_time_left > 0 &&
12756           !IS_INDESTRUCTIBLE(bad_element))
12757         Bang(kill_x, kill_y);
12758       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12759         KillPlayer(player);
12760     }
12761     else
12762       Bang(good_x, good_y);
12763   }
12764 }
12765
12766 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12767 {
12768   int i, kill_x = -1, kill_y = -1;
12769   int bad_element = Feld[bad_x][bad_y];
12770   static int test_xy[4][2] =
12771   {
12772     { 0, -1 },
12773     { -1, 0 },
12774     { +1, 0 },
12775     { 0, +1 }
12776   };
12777   static int touch_dir[4] =
12778   {
12779     MV_LEFT | MV_RIGHT,
12780     MV_UP   | MV_DOWN,
12781     MV_UP   | MV_DOWN,
12782     MV_LEFT | MV_RIGHT
12783   };
12784   static int test_dir[4] =
12785   {
12786     MV_UP,
12787     MV_LEFT,
12788     MV_RIGHT,
12789     MV_DOWN
12790   };
12791
12792   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12793     return;
12794
12795   for (i = 0; i < NUM_DIRECTIONS; i++)
12796   {
12797     int test_x, test_y, test_move_dir, test_element;
12798
12799     test_x = bad_x + test_xy[i][0];
12800     test_y = bad_y + test_xy[i][1];
12801
12802     if (!IN_LEV_FIELD(test_x, test_y))
12803       continue;
12804
12805     test_move_dir =
12806       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12807
12808     test_element = Feld[test_x][test_y];
12809
12810     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12811        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12812     */
12813     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12814         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12815     {
12816       /* good thing is player or penguin that does not move away */
12817       if (IS_PLAYER(test_x, test_y))
12818       {
12819         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12820
12821         if (bad_element == EL_ROBOT && player->is_moving)
12822           continue;     /* robot does not kill player if he is moving */
12823
12824         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12825         {
12826           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12827             continue;           /* center and border element do not touch */
12828         }
12829
12830         kill_x = test_x;
12831         kill_y = test_y;
12832
12833         break;
12834       }
12835       else if (test_element == EL_PENGUIN)
12836       {
12837         kill_x = test_x;
12838         kill_y = test_y;
12839
12840         break;
12841       }
12842     }
12843   }
12844
12845   if (kill_x != -1 || kill_y != -1)
12846   {
12847     if (IS_PLAYER(kill_x, kill_y))
12848     {
12849       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12850
12851       if (player->shield_deadly_time_left > 0 &&
12852           !IS_INDESTRUCTIBLE(bad_element))
12853         Bang(bad_x, bad_y);
12854       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12855         KillPlayer(player);
12856     }
12857     else
12858       Bang(kill_x, kill_y);
12859   }
12860 }
12861
12862 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12863 {
12864   int bad_element = Feld[bad_x][bad_y];
12865   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12866   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12867   int test_x = bad_x + dx, test_y = bad_y + dy;
12868   int test_move_dir, test_element;
12869   int kill_x = -1, kill_y = -1;
12870
12871   if (!IN_LEV_FIELD(test_x, test_y))
12872     return;
12873
12874   test_move_dir =
12875     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12876
12877   test_element = Feld[test_x][test_y];
12878
12879   if (test_move_dir != bad_move_dir)
12880   {
12881     /* good thing can be player or penguin that does not move away */
12882     if (IS_PLAYER(test_x, test_y))
12883     {
12884       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12885
12886       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12887          player as being hit when he is moving towards the bad thing, because
12888          the "get hit by" condition would be lost after the player stops) */
12889       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12890         return;         /* player moves away from bad thing */
12891
12892       kill_x = test_x;
12893       kill_y = test_y;
12894     }
12895     else if (test_element == EL_PENGUIN)
12896     {
12897       kill_x = test_x;
12898       kill_y = test_y;
12899     }
12900   }
12901
12902   if (kill_x != -1 || kill_y != -1)
12903   {
12904     if (IS_PLAYER(kill_x, kill_y))
12905     {
12906       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12907
12908       if (player->shield_deadly_time_left > 0 &&
12909           !IS_INDESTRUCTIBLE(bad_element))
12910         Bang(bad_x, bad_y);
12911       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12912         KillPlayer(player);
12913     }
12914     else
12915       Bang(kill_x, kill_y);
12916   }
12917 }
12918
12919 void TestIfPlayerTouchesBadThing(int x, int y)
12920 {
12921   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12922 }
12923
12924 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12925 {
12926   TestIfGoodThingHitsBadThing(x, y, move_dir);
12927 }
12928
12929 void TestIfBadThingTouchesPlayer(int x, int y)
12930 {
12931   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12932 }
12933
12934 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12935 {
12936   TestIfBadThingHitsGoodThing(x, y, move_dir);
12937 }
12938
12939 void TestIfFriendTouchesBadThing(int x, int y)
12940 {
12941   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12942 }
12943
12944 void TestIfBadThingTouchesFriend(int x, int y)
12945 {
12946   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12947 }
12948
12949 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12950 {
12951   int i, kill_x = bad_x, kill_y = bad_y;
12952   static int xy[4][2] =
12953   {
12954     { 0, -1 },
12955     { -1, 0 },
12956     { +1, 0 },
12957     { 0, +1 }
12958   };
12959
12960   for (i = 0; i < NUM_DIRECTIONS; i++)
12961   {
12962     int x, y, element;
12963
12964     x = bad_x + xy[i][0];
12965     y = bad_y + xy[i][1];
12966     if (!IN_LEV_FIELD(x, y))
12967       continue;
12968
12969     element = Feld[x][y];
12970     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12971         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12972     {
12973       kill_x = x;
12974       kill_y = y;
12975       break;
12976     }
12977   }
12978
12979   if (kill_x != bad_x || kill_y != bad_y)
12980     Bang(bad_x, bad_y);
12981 }
12982
12983 void KillPlayer(struct PlayerInfo *player)
12984 {
12985   int jx = player->jx, jy = player->jy;
12986
12987   if (!player->active)
12988     return;
12989
12990 #if 0
12991   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12992          player->killed, player->active, player->reanimated);
12993 #endif
12994
12995   /* the following code was introduced to prevent an infinite loop when calling
12996      -> Bang()
12997      -> CheckTriggeredElementChangeExt()
12998      -> ExecuteCustomElementAction()
12999      -> KillPlayer()
13000      -> (infinitely repeating the above sequence of function calls)
13001      which occurs when killing the player while having a CE with the setting
13002      "kill player X when explosion of <player X>"; the solution using a new
13003      field "player->killed" was chosen for backwards compatibility, although
13004      clever use of the fields "player->active" etc. would probably also work */
13005 #if 1
13006   if (player->killed)
13007     return;
13008 #endif
13009
13010   player->killed = TRUE;
13011
13012   /* remove accessible field at the player's position */
13013   Feld[jx][jy] = EL_EMPTY;
13014
13015   /* deactivate shield (else Bang()/Explode() would not work right) */
13016   player->shield_normal_time_left = 0;
13017   player->shield_deadly_time_left = 0;
13018
13019 #if 0
13020   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13021          player->killed, player->active, player->reanimated);
13022 #endif
13023
13024   Bang(jx, jy);
13025
13026 #if 0
13027   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13028          player->killed, player->active, player->reanimated);
13029 #endif
13030
13031   if (player->reanimated)       /* killed player may have been reanimated */
13032     player->killed = player->reanimated = FALSE;
13033   else
13034     BuryPlayer(player);
13035 }
13036
13037 static void KillPlayerUnlessEnemyProtected(int x, int y)
13038 {
13039   if (!PLAYER_ENEMY_PROTECTED(x, y))
13040     KillPlayer(PLAYERINFO(x, y));
13041 }
13042
13043 static void KillPlayerUnlessExplosionProtected(int x, int y)
13044 {
13045   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13046     KillPlayer(PLAYERINFO(x, y));
13047 }
13048
13049 void BuryPlayer(struct PlayerInfo *player)
13050 {
13051   int jx = player->jx, jy = player->jy;
13052
13053   if (!player->active)
13054     return;
13055
13056   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13057   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13058
13059   player->GameOver = TRUE;
13060   RemovePlayer(player);
13061 }
13062
13063 void RemovePlayer(struct PlayerInfo *player)
13064 {
13065   int jx = player->jx, jy = player->jy;
13066   int i, found = FALSE;
13067
13068   player->present = FALSE;
13069   player->active = FALSE;
13070
13071   if (!ExplodeField[jx][jy])
13072     StorePlayer[jx][jy] = 0;
13073
13074   if (player->is_moving)
13075     TEST_DrawLevelField(player->last_jx, player->last_jy);
13076
13077   for (i = 0; i < MAX_PLAYERS; i++)
13078     if (stored_player[i].active)
13079       found = TRUE;
13080
13081   if (!found)
13082     AllPlayersGone = TRUE;
13083
13084   ExitX = ZX = jx;
13085   ExitY = ZY = jy;
13086 }
13087
13088 static void setFieldForSnapping(int x, int y, int element, int direction)
13089 {
13090   struct ElementInfo *ei = &element_info[element];
13091   int direction_bit = MV_DIR_TO_BIT(direction);
13092   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13093   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13094                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13095
13096   Feld[x][y] = EL_ELEMENT_SNAPPING;
13097   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13098
13099   ResetGfxAnimation(x, y);
13100
13101   GfxElement[x][y] = element;
13102   GfxAction[x][y] = action;
13103   GfxDir[x][y] = direction;
13104   GfxFrame[x][y] = -1;
13105 }
13106
13107 /*
13108   =============================================================================
13109   checkDiagonalPushing()
13110   -----------------------------------------------------------------------------
13111   check if diagonal input device direction results in pushing of object
13112   (by checking if the alternative direction is walkable, diggable, ...)
13113   =============================================================================
13114 */
13115
13116 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13117                                     int x, int y, int real_dx, int real_dy)
13118 {
13119   int jx, jy, dx, dy, xx, yy;
13120
13121   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13122     return TRUE;
13123
13124   /* diagonal direction: check alternative direction */
13125   jx = player->jx;
13126   jy = player->jy;
13127   dx = x - jx;
13128   dy = y - jy;
13129   xx = jx + (dx == 0 ? real_dx : 0);
13130   yy = jy + (dy == 0 ? real_dy : 0);
13131
13132   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13133 }
13134
13135 /*
13136   =============================================================================
13137   DigField()
13138   -----------------------------------------------------------------------------
13139   x, y:                 field next to player (non-diagonal) to try to dig to
13140   real_dx, real_dy:     direction as read from input device (can be diagonal)
13141   =============================================================================
13142 */
13143
13144 static int DigField(struct PlayerInfo *player,
13145                     int oldx, int oldy, int x, int y,
13146                     int real_dx, int real_dy, int mode)
13147 {
13148   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13149   boolean player_was_pushing = player->is_pushing;
13150   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13151   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13152   int jx = oldx, jy = oldy;
13153   int dx = x - jx, dy = y - jy;
13154   int nextx = x + dx, nexty = y + dy;
13155   int move_direction = (dx == -1 ? MV_LEFT  :
13156                         dx == +1 ? MV_RIGHT :
13157                         dy == -1 ? MV_UP    :
13158                         dy == +1 ? MV_DOWN  : MV_NONE);
13159   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13160   int dig_side = MV_DIR_OPPOSITE(move_direction);
13161   int old_element = Feld[jx][jy];
13162   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13163   int collect_count;
13164
13165   if (is_player)                /* function can also be called by EL_PENGUIN */
13166   {
13167     if (player->MovPos == 0)
13168     {
13169       player->is_digging = FALSE;
13170       player->is_collecting = FALSE;
13171     }
13172
13173     if (player->MovPos == 0)    /* last pushing move finished */
13174       player->is_pushing = FALSE;
13175
13176     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13177     {
13178       player->is_switching = FALSE;
13179       player->push_delay = -1;
13180
13181       return MP_NO_ACTION;
13182     }
13183   }
13184
13185   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13186     old_element = Back[jx][jy];
13187
13188   /* in case of element dropped at player position, check background */
13189   else if (Back[jx][jy] != EL_EMPTY &&
13190            game.engine_version >= VERSION_IDENT(2,2,0,0))
13191     old_element = Back[jx][jy];
13192
13193   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13194     return MP_NO_ACTION;        /* field has no opening in this direction */
13195
13196   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13197     return MP_NO_ACTION;        /* field has no opening in this direction */
13198
13199   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13200   {
13201     SplashAcid(x, y);
13202
13203     Feld[jx][jy] = player->artwork_element;
13204     InitMovingField(jx, jy, MV_DOWN);
13205     Store[jx][jy] = EL_ACID;
13206     ContinueMoving(jx, jy);
13207     BuryPlayer(player);
13208
13209     return MP_DONT_RUN_INTO;
13210   }
13211
13212   if (player_can_move && DONT_RUN_INTO(element))
13213   {
13214     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13215
13216     return MP_DONT_RUN_INTO;
13217   }
13218
13219   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13220     return MP_NO_ACTION;
13221
13222   collect_count = element_info[element].collect_count_initial;
13223
13224   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13225     return MP_NO_ACTION;
13226
13227   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13228     player_can_move = player_can_move_or_snap;
13229
13230   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13231       game.engine_version >= VERSION_IDENT(2,2,0,0))
13232   {
13233     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13234                                player->index_bit, dig_side);
13235     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13236                                         player->index_bit, dig_side);
13237
13238     if (element == EL_DC_LANDMINE)
13239       Bang(x, y);
13240
13241     if (Feld[x][y] != element)          /* field changed by snapping */
13242       return MP_ACTION;
13243
13244     return MP_NO_ACTION;
13245   }
13246
13247   if (player->gravity && is_player && !player->is_auto_moving &&
13248       canFallDown(player) && move_direction != MV_DOWN &&
13249       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13250     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13251
13252   if (player_can_move &&
13253       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13254   {
13255     int sound_element = SND_ELEMENT(element);
13256     int sound_action = ACTION_WALKING;
13257
13258     if (IS_RND_GATE(element))
13259     {
13260       if (!player->key[RND_GATE_NR(element)])
13261         return MP_NO_ACTION;
13262     }
13263     else if (IS_RND_GATE_GRAY(element))
13264     {
13265       if (!player->key[RND_GATE_GRAY_NR(element)])
13266         return MP_NO_ACTION;
13267     }
13268     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13269     {
13270       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13271         return MP_NO_ACTION;
13272     }
13273     else if (element == EL_EXIT_OPEN ||
13274              element == EL_EM_EXIT_OPEN ||
13275              element == EL_EM_EXIT_OPENING ||
13276              element == EL_STEEL_EXIT_OPEN ||
13277              element == EL_EM_STEEL_EXIT_OPEN ||
13278              element == EL_EM_STEEL_EXIT_OPENING ||
13279              element == EL_SP_EXIT_OPEN ||
13280              element == EL_SP_EXIT_OPENING)
13281     {
13282       sound_action = ACTION_PASSING;    /* player is passing exit */
13283     }
13284     else if (element == EL_EMPTY)
13285     {
13286       sound_action = ACTION_MOVING;             /* nothing to walk on */
13287     }
13288
13289     /* play sound from background or player, whatever is available */
13290     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13291       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13292     else
13293       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13294   }
13295   else if (player_can_move &&
13296            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13297   {
13298     if (!ACCESS_FROM(element, opposite_direction))
13299       return MP_NO_ACTION;      /* field not accessible from this direction */
13300
13301     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13302       return MP_NO_ACTION;
13303
13304     if (IS_EM_GATE(element))
13305     {
13306       if (!player->key[EM_GATE_NR(element)])
13307         return MP_NO_ACTION;
13308     }
13309     else if (IS_EM_GATE_GRAY(element))
13310     {
13311       if (!player->key[EM_GATE_GRAY_NR(element)])
13312         return MP_NO_ACTION;
13313     }
13314     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13315     {
13316       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13317         return MP_NO_ACTION;
13318     }
13319     else if (IS_EMC_GATE(element))
13320     {
13321       if (!player->key[EMC_GATE_NR(element)])
13322         return MP_NO_ACTION;
13323     }
13324     else if (IS_EMC_GATE_GRAY(element))
13325     {
13326       if (!player->key[EMC_GATE_GRAY_NR(element)])
13327         return MP_NO_ACTION;
13328     }
13329     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13330     {
13331       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13332         return MP_NO_ACTION;
13333     }
13334     else if (element == EL_DC_GATE_WHITE ||
13335              element == EL_DC_GATE_WHITE_GRAY ||
13336              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13337     {
13338       if (player->num_white_keys == 0)
13339         return MP_NO_ACTION;
13340
13341       player->num_white_keys--;
13342     }
13343     else if (IS_SP_PORT(element))
13344     {
13345       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13346           element == EL_SP_GRAVITY_PORT_RIGHT ||
13347           element == EL_SP_GRAVITY_PORT_UP ||
13348           element == EL_SP_GRAVITY_PORT_DOWN)
13349         player->gravity = !player->gravity;
13350       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13351                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13352                element == EL_SP_GRAVITY_ON_PORT_UP ||
13353                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13354         player->gravity = TRUE;
13355       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13356                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13357                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13358                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13359         player->gravity = FALSE;
13360     }
13361
13362     /* automatically move to the next field with double speed */
13363     player->programmed_action = move_direction;
13364
13365     if (player->move_delay_reset_counter == 0)
13366     {
13367       player->move_delay_reset_counter = 2;     /* two double speed steps */
13368
13369       DOUBLE_PLAYER_SPEED(player);
13370     }
13371
13372     PlayLevelSoundAction(x, y, ACTION_PASSING);
13373   }
13374   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13375   {
13376     RemoveField(x, y);
13377
13378     if (mode != DF_SNAP)
13379     {
13380       GfxElement[x][y] = GFX_ELEMENT(element);
13381       player->is_digging = TRUE;
13382     }
13383
13384     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13385
13386     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13387                                         player->index_bit, dig_side);
13388
13389     if (mode == DF_SNAP)
13390     {
13391       if (level.block_snap_field)
13392         setFieldForSnapping(x, y, element, move_direction);
13393       else
13394         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13395
13396       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13397                                           player->index_bit, dig_side);
13398     }
13399   }
13400   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13401   {
13402     RemoveField(x, y);
13403
13404     if (is_player && mode != DF_SNAP)
13405     {
13406       GfxElement[x][y] = element;
13407       player->is_collecting = TRUE;
13408     }
13409
13410     if (element == EL_SPEED_PILL)
13411     {
13412       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13413     }
13414     else if (element == EL_EXTRA_TIME && level.time > 0)
13415     {
13416       TimeLeft += level.extra_time;
13417
13418       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13419
13420       DisplayGameControlValues();
13421     }
13422     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13423     {
13424       player->shield_normal_time_left += level.shield_normal_time;
13425       if (element == EL_SHIELD_DEADLY)
13426         player->shield_deadly_time_left += level.shield_deadly_time;
13427     }
13428     else if (element == EL_DYNAMITE ||
13429              element == EL_EM_DYNAMITE ||
13430              element == EL_SP_DISK_RED)
13431     {
13432       if (player->inventory_size < MAX_INVENTORY_SIZE)
13433         player->inventory_element[player->inventory_size++] = element;
13434
13435       DrawGameDoorValues();
13436     }
13437     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13438     {
13439       player->dynabomb_count++;
13440       player->dynabombs_left++;
13441     }
13442     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13443     {
13444       player->dynabomb_size++;
13445     }
13446     else if (element == EL_DYNABOMB_INCREASE_POWER)
13447     {
13448       player->dynabomb_xl = TRUE;
13449     }
13450     else if (IS_KEY(element))
13451     {
13452       player->key[KEY_NR(element)] = TRUE;
13453
13454       DrawGameDoorValues();
13455     }
13456     else if (element == EL_DC_KEY_WHITE)
13457     {
13458       player->num_white_keys++;
13459
13460       /* display white keys? */
13461       /* DrawGameDoorValues(); */
13462     }
13463     else if (IS_ENVELOPE(element))
13464     {
13465       player->show_envelope = element;
13466     }
13467     else if (element == EL_EMC_LENSES)
13468     {
13469       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13470
13471       RedrawAllInvisibleElementsForLenses();
13472     }
13473     else if (element == EL_EMC_MAGNIFIER)
13474     {
13475       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13476
13477       RedrawAllInvisibleElementsForMagnifier();
13478     }
13479     else if (IS_DROPPABLE(element) ||
13480              IS_THROWABLE(element))     /* can be collected and dropped */
13481     {
13482       int i;
13483
13484       if (collect_count == 0)
13485         player->inventory_infinite_element = element;
13486       else
13487         for (i = 0; i < collect_count; i++)
13488           if (player->inventory_size < MAX_INVENTORY_SIZE)
13489             player->inventory_element[player->inventory_size++] = element;
13490
13491       DrawGameDoorValues();
13492     }
13493     else if (collect_count > 0)
13494     {
13495       local_player->gems_still_needed -= collect_count;
13496       if (local_player->gems_still_needed < 0)
13497         local_player->gems_still_needed = 0;
13498
13499       game.snapshot.collected_item = TRUE;
13500
13501       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13502
13503       DisplayGameControlValues();
13504     }
13505
13506     RaiseScoreElement(element);
13507     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13508
13509     if (is_player)
13510       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13511                                           player->index_bit, dig_side);
13512
13513     if (mode == DF_SNAP)
13514     {
13515       if (level.block_snap_field)
13516         setFieldForSnapping(x, y, element, move_direction);
13517       else
13518         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13519
13520       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13521                                           player->index_bit, dig_side);
13522     }
13523   }
13524   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13525   {
13526     if (mode == DF_SNAP && element != EL_BD_ROCK)
13527       return MP_NO_ACTION;
13528
13529     if (CAN_FALL(element) && dy)
13530       return MP_NO_ACTION;
13531
13532     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13533         !(element == EL_SPRING && level.use_spring_bug))
13534       return MP_NO_ACTION;
13535
13536     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13537         ((move_direction & MV_VERTICAL &&
13538           ((element_info[element].move_pattern & MV_LEFT &&
13539             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13540            (element_info[element].move_pattern & MV_RIGHT &&
13541             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13542          (move_direction & MV_HORIZONTAL &&
13543           ((element_info[element].move_pattern & MV_UP &&
13544             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13545            (element_info[element].move_pattern & MV_DOWN &&
13546             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13547       return MP_NO_ACTION;
13548
13549     /* do not push elements already moving away faster than player */
13550     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13551         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13552       return MP_NO_ACTION;
13553
13554     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13555     {
13556       if (player->push_delay_value == -1 || !player_was_pushing)
13557         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13558     }
13559     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13560     {
13561       if (player->push_delay_value == -1)
13562         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13563     }
13564     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13565     {
13566       if (!player->is_pushing)
13567         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13568     }
13569
13570     player->is_pushing = TRUE;
13571     player->is_active = TRUE;
13572
13573     if (!(IN_LEV_FIELD(nextx, nexty) &&
13574           (IS_FREE(nextx, nexty) ||
13575            (IS_SB_ELEMENT(element) &&
13576             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13577            (IS_CUSTOM_ELEMENT(element) &&
13578             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13579       return MP_NO_ACTION;
13580
13581     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13582       return MP_NO_ACTION;
13583
13584     if (player->push_delay == -1)       /* new pushing; restart delay */
13585       player->push_delay = 0;
13586
13587     if (player->push_delay < player->push_delay_value &&
13588         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13589         element != EL_SPRING && element != EL_BALLOON)
13590     {
13591       /* make sure that there is no move delay before next try to push */
13592       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13593         player->move_delay = 0;
13594
13595       return MP_NO_ACTION;
13596     }
13597
13598     if (IS_CUSTOM_ELEMENT(element) &&
13599         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13600     {
13601       if (!DigFieldByCE(nextx, nexty, element))
13602         return MP_NO_ACTION;
13603     }
13604
13605     if (IS_SB_ELEMENT(element))
13606     {
13607       if (element == EL_SOKOBAN_FIELD_FULL)
13608       {
13609         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13610         local_player->sokobanfields_still_needed++;
13611       }
13612
13613       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13614       {
13615         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13616         local_player->sokobanfields_still_needed--;
13617       }
13618
13619       Feld[x][y] = EL_SOKOBAN_OBJECT;
13620
13621       if (Back[x][y] == Back[nextx][nexty])
13622         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13623       else if (Back[x][y] != 0)
13624         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13625                                     ACTION_EMPTYING);
13626       else
13627         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13628                                     ACTION_FILLING);
13629
13630       if (local_player->sokobanfields_still_needed == 0 &&
13631           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13632       {
13633         PlayerWins(player);
13634
13635         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13636       }
13637     }
13638     else
13639       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13640
13641     InitMovingField(x, y, move_direction);
13642     GfxAction[x][y] = ACTION_PUSHING;
13643
13644     if (mode == DF_SNAP)
13645       ContinueMoving(x, y);
13646     else
13647       MovPos[x][y] = (dx != 0 ? dx : dy);
13648
13649     Pushed[x][y] = TRUE;
13650     Pushed[nextx][nexty] = TRUE;
13651
13652     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13653       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13654     else
13655       player->push_delay_value = -1;    /* get new value later */
13656
13657     /* check for element change _after_ element has been pushed */
13658     if (game.use_change_when_pushing_bug)
13659     {
13660       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13661                                  player->index_bit, dig_side);
13662       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13663                                           player->index_bit, dig_side);
13664     }
13665   }
13666   else if (IS_SWITCHABLE(element))
13667   {
13668     if (PLAYER_SWITCHING(player, x, y))
13669     {
13670       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13671                                           player->index_bit, dig_side);
13672
13673       return MP_ACTION;
13674     }
13675
13676     player->is_switching = TRUE;
13677     player->switch_x = x;
13678     player->switch_y = y;
13679
13680     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13681
13682     if (element == EL_ROBOT_WHEEL)
13683     {
13684       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13685       ZX = x;
13686       ZY = y;
13687
13688       game.robot_wheel_active = TRUE;
13689
13690       TEST_DrawLevelField(x, y);
13691     }
13692     else if (element == EL_SP_TERMINAL)
13693     {
13694       int xx, yy;
13695
13696       SCAN_PLAYFIELD(xx, yy)
13697       {
13698         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13699         {
13700           Bang(xx, yy);
13701         }
13702         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13703         {
13704           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13705
13706           ResetGfxAnimation(xx, yy);
13707           TEST_DrawLevelField(xx, yy);
13708         }
13709       }
13710     }
13711     else if (IS_BELT_SWITCH(element))
13712     {
13713       ToggleBeltSwitch(x, y);
13714     }
13715     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13716              element == EL_SWITCHGATE_SWITCH_DOWN ||
13717              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13718              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13719     {
13720       ToggleSwitchgateSwitch(x, y);
13721     }
13722     else if (element == EL_LIGHT_SWITCH ||
13723              element == EL_LIGHT_SWITCH_ACTIVE)
13724     {
13725       ToggleLightSwitch(x, y);
13726     }
13727     else if (element == EL_TIMEGATE_SWITCH ||
13728              element == EL_DC_TIMEGATE_SWITCH)
13729     {
13730       ActivateTimegateSwitch(x, y);
13731     }
13732     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13733              element == EL_BALLOON_SWITCH_RIGHT ||
13734              element == EL_BALLOON_SWITCH_UP    ||
13735              element == EL_BALLOON_SWITCH_DOWN  ||
13736              element == EL_BALLOON_SWITCH_NONE  ||
13737              element == EL_BALLOON_SWITCH_ANY)
13738     {
13739       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13740                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13741                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13742                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13743                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13744                              move_direction);
13745     }
13746     else if (element == EL_LAMP)
13747     {
13748       Feld[x][y] = EL_LAMP_ACTIVE;
13749       local_player->lights_still_needed--;
13750
13751       ResetGfxAnimation(x, y);
13752       TEST_DrawLevelField(x, y);
13753     }
13754     else if (element == EL_TIME_ORB_FULL)
13755     {
13756       Feld[x][y] = EL_TIME_ORB_EMPTY;
13757
13758       if (level.time > 0 || level.use_time_orb_bug)
13759       {
13760         TimeLeft += level.time_orb_time;
13761         game.no_time_limit = FALSE;
13762
13763         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13764
13765         DisplayGameControlValues();
13766       }
13767
13768       ResetGfxAnimation(x, y);
13769       TEST_DrawLevelField(x, y);
13770     }
13771     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13772              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13773     {
13774       int xx, yy;
13775
13776       game.ball_state = !game.ball_state;
13777
13778       SCAN_PLAYFIELD(xx, yy)
13779       {
13780         int e = Feld[xx][yy];
13781
13782         if (game.ball_state)
13783         {
13784           if (e == EL_EMC_MAGIC_BALL)
13785             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13786           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13787             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13788         }
13789         else
13790         {
13791           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13792             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13793           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13794             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13795         }
13796       }
13797     }
13798
13799     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13800                                         player->index_bit, dig_side);
13801
13802     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13803                                         player->index_bit, dig_side);
13804
13805     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13806                                         player->index_bit, dig_side);
13807
13808     return MP_ACTION;
13809   }
13810   else
13811   {
13812     if (!PLAYER_SWITCHING(player, x, y))
13813     {
13814       player->is_switching = TRUE;
13815       player->switch_x = x;
13816       player->switch_y = y;
13817
13818       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13819                                  player->index_bit, dig_side);
13820       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13821                                           player->index_bit, dig_side);
13822
13823       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13824                                  player->index_bit, dig_side);
13825       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13826                                           player->index_bit, dig_side);
13827     }
13828
13829     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13830                                player->index_bit, dig_side);
13831     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13832                                         player->index_bit, dig_side);
13833
13834     return MP_NO_ACTION;
13835   }
13836
13837   player->push_delay = -1;
13838
13839   if (is_player)                /* function can also be called by EL_PENGUIN */
13840   {
13841     if (Feld[x][y] != element)          /* really digged/collected something */
13842     {
13843       player->is_collecting = !player->is_digging;
13844       player->is_active = TRUE;
13845     }
13846   }
13847
13848   return MP_MOVING;
13849 }
13850
13851 static boolean DigFieldByCE(int x, int y, int digging_element)
13852 {
13853   int element = Feld[x][y];
13854
13855   if (!IS_FREE(x, y))
13856   {
13857     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13858                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13859                   ACTION_BREAKING);
13860
13861     /* no element can dig solid indestructible elements */
13862     if (IS_INDESTRUCTIBLE(element) &&
13863         !IS_DIGGABLE(element) &&
13864         !IS_COLLECTIBLE(element))
13865       return FALSE;
13866
13867     if (AmoebaNr[x][y] &&
13868         (element == EL_AMOEBA_FULL ||
13869          element == EL_BD_AMOEBA ||
13870          element == EL_AMOEBA_GROWING))
13871     {
13872       AmoebaCnt[AmoebaNr[x][y]]--;
13873       AmoebaCnt2[AmoebaNr[x][y]]--;
13874     }
13875
13876     if (IS_MOVING(x, y))
13877       RemoveMovingField(x, y);
13878     else
13879     {
13880       RemoveField(x, y);
13881       TEST_DrawLevelField(x, y);
13882     }
13883
13884     /* if digged element was about to explode, prevent the explosion */
13885     ExplodeField[x][y] = EX_TYPE_NONE;
13886
13887     PlayLevelSoundAction(x, y, action);
13888   }
13889
13890   Store[x][y] = EL_EMPTY;
13891
13892   /* this makes it possible to leave the removed element again */
13893   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13894     Store[x][y] = element;
13895
13896   return TRUE;
13897 }
13898
13899 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13900 {
13901   int jx = player->jx, jy = player->jy;
13902   int x = jx + dx, y = jy + dy;
13903   int snap_direction = (dx == -1 ? MV_LEFT  :
13904                         dx == +1 ? MV_RIGHT :
13905                         dy == -1 ? MV_UP    :
13906                         dy == +1 ? MV_DOWN  : MV_NONE);
13907   boolean can_continue_snapping = (level.continuous_snapping &&
13908                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13909
13910   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13911     return FALSE;
13912
13913   if (!player->active || !IN_LEV_FIELD(x, y))
13914     return FALSE;
13915
13916   if (dx && dy)
13917     return FALSE;
13918
13919   if (!dx && !dy)
13920   {
13921     if (player->MovPos == 0)
13922       player->is_pushing = FALSE;
13923
13924     player->is_snapping = FALSE;
13925
13926     if (player->MovPos == 0)
13927     {
13928       player->is_moving = FALSE;
13929       player->is_digging = FALSE;
13930       player->is_collecting = FALSE;
13931     }
13932
13933     return FALSE;
13934   }
13935
13936   /* prevent snapping with already pressed snap key when not allowed */
13937   if (player->is_snapping && !can_continue_snapping)
13938     return FALSE;
13939
13940   player->MovDir = snap_direction;
13941
13942   if (player->MovPos == 0)
13943   {
13944     player->is_moving = FALSE;
13945     player->is_digging = FALSE;
13946     player->is_collecting = FALSE;
13947   }
13948
13949   player->is_dropping = FALSE;
13950   player->is_dropping_pressed = FALSE;
13951   player->drop_pressed_delay = 0;
13952
13953   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13954     return FALSE;
13955
13956   player->is_snapping = TRUE;
13957   player->is_active = TRUE;
13958
13959   if (player->MovPos == 0)
13960   {
13961     player->is_moving = FALSE;
13962     player->is_digging = FALSE;
13963     player->is_collecting = FALSE;
13964   }
13965
13966   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13967     TEST_DrawLevelField(player->last_jx, player->last_jy);
13968
13969   TEST_DrawLevelField(x, y);
13970
13971   return TRUE;
13972 }
13973
13974 static boolean DropElement(struct PlayerInfo *player)
13975 {
13976   int old_element, new_element;
13977   int dropx = player->jx, dropy = player->jy;
13978   int drop_direction = player->MovDir;
13979   int drop_side = drop_direction;
13980   int drop_element = get_next_dropped_element(player);
13981
13982   player->is_dropping_pressed = TRUE;
13983
13984   /* do not drop an element on top of another element; when holding drop key
13985      pressed without moving, dropped element must move away before the next
13986      element can be dropped (this is especially important if the next element
13987      is dynamite, which can be placed on background for historical reasons) */
13988   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13989     return MP_ACTION;
13990
13991   if (IS_THROWABLE(drop_element))
13992   {
13993     dropx += GET_DX_FROM_DIR(drop_direction);
13994     dropy += GET_DY_FROM_DIR(drop_direction);
13995
13996     if (!IN_LEV_FIELD(dropx, dropy))
13997       return FALSE;
13998   }
13999
14000   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14001   new_element = drop_element;           /* default: no change when dropping */
14002
14003   /* check if player is active, not moving and ready to drop */
14004   if (!player->active || player->MovPos || player->drop_delay > 0)
14005     return FALSE;
14006
14007   /* check if player has anything that can be dropped */
14008   if (new_element == EL_UNDEFINED)
14009     return FALSE;
14010
14011   /* check if drop key was pressed long enough for EM style dynamite */
14012   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14013     return FALSE;
14014
14015   /* check if anything can be dropped at the current position */
14016   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14017     return FALSE;
14018
14019   /* collected custom elements can only be dropped on empty fields */
14020   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14021     return FALSE;
14022
14023   if (old_element != EL_EMPTY)
14024     Back[dropx][dropy] = old_element;   /* store old element on this field */
14025
14026   ResetGfxAnimation(dropx, dropy);
14027   ResetRandomAnimationValue(dropx, dropy);
14028
14029   if (player->inventory_size > 0 ||
14030       player->inventory_infinite_element != EL_UNDEFINED)
14031   {
14032     if (player->inventory_size > 0)
14033     {
14034       player->inventory_size--;
14035
14036       DrawGameDoorValues();
14037
14038       if (new_element == EL_DYNAMITE)
14039         new_element = EL_DYNAMITE_ACTIVE;
14040       else if (new_element == EL_EM_DYNAMITE)
14041         new_element = EL_EM_DYNAMITE_ACTIVE;
14042       else if (new_element == EL_SP_DISK_RED)
14043         new_element = EL_SP_DISK_RED_ACTIVE;
14044     }
14045
14046     Feld[dropx][dropy] = new_element;
14047
14048     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14049       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14050                           el2img(Feld[dropx][dropy]), 0);
14051
14052     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14053
14054     /* needed if previous element just changed to "empty" in the last frame */
14055     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14056
14057     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14058                                player->index_bit, drop_side);
14059     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14060                                         CE_PLAYER_DROPS_X,
14061                                         player->index_bit, drop_side);
14062
14063     TestIfElementTouchesCustomElement(dropx, dropy);
14064   }
14065   else          /* player is dropping a dyna bomb */
14066   {
14067     player->dynabombs_left--;
14068
14069     Feld[dropx][dropy] = new_element;
14070
14071     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14072       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14073                           el2img(Feld[dropx][dropy]), 0);
14074
14075     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14076   }
14077
14078   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14079     InitField_WithBug1(dropx, dropy, FALSE);
14080
14081   new_element = Feld[dropx][dropy];     /* element might have changed */
14082
14083   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14084       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14085   {
14086     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14087       MovDir[dropx][dropy] = drop_direction;
14088
14089     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14090
14091     /* do not cause impact style collision by dropping elements that can fall */
14092     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14093   }
14094
14095   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14096   player->is_dropping = TRUE;
14097
14098   player->drop_pressed_delay = 0;
14099   player->is_dropping_pressed = FALSE;
14100
14101   player->drop_x = dropx;
14102   player->drop_y = dropy;
14103
14104   return TRUE;
14105 }
14106
14107 /* ------------------------------------------------------------------------- */
14108 /* game sound playing functions                                              */
14109 /* ------------------------------------------------------------------------- */
14110
14111 static int *loop_sound_frame = NULL;
14112 static int *loop_sound_volume = NULL;
14113
14114 void InitPlayLevelSound()
14115 {
14116   int num_sounds = getSoundListSize();
14117
14118   checked_free(loop_sound_frame);
14119   checked_free(loop_sound_volume);
14120
14121   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14122   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14123 }
14124
14125 static void PlayLevelSound(int x, int y, int nr)
14126 {
14127   int sx = SCREENX(x), sy = SCREENY(y);
14128   int volume, stereo_position;
14129   int max_distance = 8;
14130   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14131
14132   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14133       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14134     return;
14135
14136   if (!IN_LEV_FIELD(x, y) ||
14137       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14138       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14139     return;
14140
14141   volume = SOUND_MAX_VOLUME;
14142
14143   if (!IN_SCR_FIELD(sx, sy))
14144   {
14145     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14146     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14147
14148     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14149   }
14150
14151   stereo_position = (SOUND_MAX_LEFT +
14152                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14153                      (SCR_FIELDX + 2 * max_distance));
14154
14155   if (IS_LOOP_SOUND(nr))
14156   {
14157     /* This assures that quieter loop sounds do not overwrite louder ones,
14158        while restarting sound volume comparison with each new game frame. */
14159
14160     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14161       return;
14162
14163     loop_sound_volume[nr] = volume;
14164     loop_sound_frame[nr] = FrameCounter;
14165   }
14166
14167   PlaySoundExt(nr, volume, stereo_position, type);
14168 }
14169
14170 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14171 {
14172   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14173                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14174                  y < LEVELY(BY1) ? LEVELY(BY1) :
14175                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14176                  sound_action);
14177 }
14178
14179 static void PlayLevelSoundAction(int x, int y, int action)
14180 {
14181   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14182 }
14183
14184 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14185 {
14186   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14187
14188   if (sound_effect != SND_UNDEFINED)
14189     PlayLevelSound(x, y, sound_effect);
14190 }
14191
14192 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14193                                               int action)
14194 {
14195   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14196
14197   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14198     PlayLevelSound(x, y, sound_effect);
14199 }
14200
14201 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14202 {
14203   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14204
14205   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14206     PlayLevelSound(x, y, sound_effect);
14207 }
14208
14209 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14210 {
14211   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14212
14213   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14214     StopSound(sound_effect);
14215 }
14216
14217 static void PlayLevelMusic()
14218 {
14219   if (levelset.music[level_nr] != MUS_UNDEFINED)
14220     PlayMusic(levelset.music[level_nr]);        /* from config file */
14221   else
14222     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14223 }
14224
14225 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14226 {
14227   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14228   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14229   int x = xx - 1 - offset;
14230   int y = yy - 1 - offset;
14231
14232   switch (sample)
14233   {
14234     case SAMPLE_blank:
14235       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14236       break;
14237
14238     case SAMPLE_roll:
14239       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14240       break;
14241
14242     case SAMPLE_stone:
14243       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14244       break;
14245
14246     case SAMPLE_nut:
14247       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14248       break;
14249
14250     case SAMPLE_crack:
14251       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14252       break;
14253
14254     case SAMPLE_bug:
14255       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14256       break;
14257
14258     case SAMPLE_tank:
14259       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14260       break;
14261
14262     case SAMPLE_android_clone:
14263       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14264       break;
14265
14266     case SAMPLE_android_move:
14267       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14268       break;
14269
14270     case SAMPLE_spring:
14271       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14272       break;
14273
14274     case SAMPLE_slurp:
14275       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14276       break;
14277
14278     case SAMPLE_eater:
14279       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14280       break;
14281
14282     case SAMPLE_eater_eat:
14283       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14284       break;
14285
14286     case SAMPLE_alien:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14288       break;
14289
14290     case SAMPLE_collect:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14292       break;
14293
14294     case SAMPLE_diamond:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14296       break;
14297
14298     case SAMPLE_squash:
14299       /* !!! CHECK THIS !!! */
14300 #if 1
14301       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14302 #else
14303       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14304 #endif
14305       break;
14306
14307     case SAMPLE_wonderfall:
14308       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14309       break;
14310
14311     case SAMPLE_drip:
14312       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14313       break;
14314
14315     case SAMPLE_push:
14316       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14317       break;
14318
14319     case SAMPLE_dirt:
14320       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14321       break;
14322
14323     case SAMPLE_acid:
14324       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14325       break;
14326
14327     case SAMPLE_ball:
14328       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14329       break;
14330
14331     case SAMPLE_grow:
14332       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14333       break;
14334
14335     case SAMPLE_wonder:
14336       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14337       break;
14338
14339     case SAMPLE_door:
14340       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14341       break;
14342
14343     case SAMPLE_exit_open:
14344       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14345       break;
14346
14347     case SAMPLE_exit_leave:
14348       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14349       break;
14350
14351     case SAMPLE_dynamite:
14352       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14353       break;
14354
14355     case SAMPLE_tick:
14356       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14357       break;
14358
14359     case SAMPLE_press:
14360       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14361       break;
14362
14363     case SAMPLE_wheel:
14364       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14365       break;
14366
14367     case SAMPLE_boom:
14368       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14369       break;
14370
14371     case SAMPLE_die:
14372       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14373       break;
14374
14375     case SAMPLE_time:
14376       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14377       break;
14378
14379     default:
14380       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14381       break;
14382   }
14383 }
14384
14385 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14386 {
14387   int element = map_element_SP_to_RND(element_sp);
14388   int action = map_action_SP_to_RND(action_sp);
14389   int offset = (setup.sp_show_border_elements ? 0 : 1);
14390   int x = xx - offset;
14391   int y = yy - offset;
14392
14393   PlayLevelSoundElementAction(x, y, element, action);
14394 }
14395
14396 void RaiseScore(int value)
14397 {
14398   local_player->score += value;
14399
14400   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14401
14402   DisplayGameControlValues();
14403 }
14404
14405 void RaiseScoreElement(int element)
14406 {
14407   switch (element)
14408   {
14409     case EL_EMERALD:
14410     case EL_BD_DIAMOND:
14411     case EL_EMERALD_YELLOW:
14412     case EL_EMERALD_RED:
14413     case EL_EMERALD_PURPLE:
14414     case EL_SP_INFOTRON:
14415       RaiseScore(level.score[SC_EMERALD]);
14416       break;
14417     case EL_DIAMOND:
14418       RaiseScore(level.score[SC_DIAMOND]);
14419       break;
14420     case EL_CRYSTAL:
14421       RaiseScore(level.score[SC_CRYSTAL]);
14422       break;
14423     case EL_PEARL:
14424       RaiseScore(level.score[SC_PEARL]);
14425       break;
14426     case EL_BUG:
14427     case EL_BD_BUTTERFLY:
14428     case EL_SP_ELECTRON:
14429       RaiseScore(level.score[SC_BUG]);
14430       break;
14431     case EL_SPACESHIP:
14432     case EL_BD_FIREFLY:
14433     case EL_SP_SNIKSNAK:
14434       RaiseScore(level.score[SC_SPACESHIP]);
14435       break;
14436     case EL_YAMYAM:
14437     case EL_DARK_YAMYAM:
14438       RaiseScore(level.score[SC_YAMYAM]);
14439       break;
14440     case EL_ROBOT:
14441       RaiseScore(level.score[SC_ROBOT]);
14442       break;
14443     case EL_PACMAN:
14444       RaiseScore(level.score[SC_PACMAN]);
14445       break;
14446     case EL_NUT:
14447       RaiseScore(level.score[SC_NUT]);
14448       break;
14449     case EL_DYNAMITE:
14450     case EL_EM_DYNAMITE:
14451     case EL_SP_DISK_RED:
14452     case EL_DYNABOMB_INCREASE_NUMBER:
14453     case EL_DYNABOMB_INCREASE_SIZE:
14454     case EL_DYNABOMB_INCREASE_POWER:
14455       RaiseScore(level.score[SC_DYNAMITE]);
14456       break;
14457     case EL_SHIELD_NORMAL:
14458     case EL_SHIELD_DEADLY:
14459       RaiseScore(level.score[SC_SHIELD]);
14460       break;
14461     case EL_EXTRA_TIME:
14462       RaiseScore(level.extra_time_score);
14463       break;
14464     case EL_KEY_1:
14465     case EL_KEY_2:
14466     case EL_KEY_3:
14467     case EL_KEY_4:
14468     case EL_EM_KEY_1:
14469     case EL_EM_KEY_2:
14470     case EL_EM_KEY_3:
14471     case EL_EM_KEY_4:
14472     case EL_EMC_KEY_5:
14473     case EL_EMC_KEY_6:
14474     case EL_EMC_KEY_7:
14475     case EL_EMC_KEY_8:
14476     case EL_DC_KEY_WHITE:
14477       RaiseScore(level.score[SC_KEY]);
14478       break;
14479     default:
14480       RaiseScore(element_info[element].collect_score);
14481       break;
14482   }
14483 }
14484
14485 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14486 {
14487   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14488   {
14489     /* closing door required in case of envelope style request dialogs */
14490     if (!skip_request)
14491       CloseDoor(DOOR_CLOSE_1);
14492
14493 #if defined(NETWORK_AVALIABLE)
14494     if (options.network)
14495       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14496     else
14497 #endif
14498     {
14499       if (quick_quit)
14500         FadeSkipNextFadeIn();
14501
14502       SetGameStatus(GAME_MODE_MAIN);
14503
14504       DrawMainMenu();
14505     }
14506   }
14507   else          /* continue playing the game */
14508   {
14509     if (tape.playing && tape.deactivate_display)
14510       TapeDeactivateDisplayOff(TRUE);
14511
14512     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14513
14514     if (tape.playing && tape.deactivate_display)
14515       TapeDeactivateDisplayOn();
14516   }
14517 }
14518
14519 void RequestQuitGame(boolean ask_if_really_quit)
14520 {
14521   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14522   boolean skip_request = AllPlayersGone || quick_quit;
14523
14524   RequestQuitGameExt(skip_request, quick_quit,
14525                      "Do you really want to quit the game?");
14526 }
14527
14528
14529 /* ------------------------------------------------------------------------- */
14530 /* random generator functions                                                */
14531 /* ------------------------------------------------------------------------- */
14532
14533 unsigned int InitEngineRandom_RND(int seed)
14534 {
14535   game.num_random_calls = 0;
14536
14537   return InitEngineRandom(seed);
14538 }
14539
14540 unsigned int RND(int max)
14541 {
14542   if (max > 0)
14543   {
14544     game.num_random_calls++;
14545
14546     return GetEngineRandom(max);
14547   }
14548
14549   return 0;
14550 }
14551
14552
14553 /* ------------------------------------------------------------------------- */
14554 /* game engine snapshot handling functions                                   */
14555 /* ------------------------------------------------------------------------- */
14556
14557 struct EngineSnapshotInfo
14558 {
14559   /* runtime values for custom element collect score */
14560   int collect_score[NUM_CUSTOM_ELEMENTS];
14561
14562   /* runtime values for group element choice position */
14563   int choice_pos[NUM_GROUP_ELEMENTS];
14564
14565   /* runtime values for belt position animations */
14566   int belt_graphic[4][NUM_BELT_PARTS];
14567   int belt_anim_mode[4][NUM_BELT_PARTS];
14568 };
14569
14570 static struct EngineSnapshotInfo engine_snapshot_rnd;
14571 static char *snapshot_level_identifier = NULL;
14572 static int snapshot_level_nr = -1;
14573
14574 static void SaveEngineSnapshotValues_RND()
14575 {
14576   static int belt_base_active_element[4] =
14577   {
14578     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14579     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14580     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14581     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14582   };
14583   int i, j;
14584
14585   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14586   {
14587     int element = EL_CUSTOM_START + i;
14588
14589     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14590   }
14591
14592   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14593   {
14594     int element = EL_GROUP_START + i;
14595
14596     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14597   }
14598
14599   for (i = 0; i < 4; i++)
14600   {
14601     for (j = 0; j < NUM_BELT_PARTS; j++)
14602     {
14603       int element = belt_base_active_element[i] + j;
14604       int graphic = el2img(element);
14605       int anim_mode = graphic_info[graphic].anim_mode;
14606
14607       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14608       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14609     }
14610   }
14611 }
14612
14613 static void LoadEngineSnapshotValues_RND()
14614 {
14615   unsigned int num_random_calls = game.num_random_calls;
14616   int i, j;
14617
14618   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14619   {
14620     int element = EL_CUSTOM_START + i;
14621
14622     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14623   }
14624
14625   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14626   {
14627     int element = EL_GROUP_START + i;
14628
14629     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14630   }
14631
14632   for (i = 0; i < 4; i++)
14633   {
14634     for (j = 0; j < NUM_BELT_PARTS; j++)
14635     {
14636       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14637       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14638
14639       graphic_info[graphic].anim_mode = anim_mode;
14640     }
14641   }
14642
14643   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14644   {
14645     InitRND(tape.random_seed);
14646     for (i = 0; i < num_random_calls; i++)
14647       RND(1);
14648   }
14649
14650   if (game.num_random_calls != num_random_calls)
14651   {
14652     Error(ERR_INFO, "number of random calls out of sync");
14653     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14654     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14655     Error(ERR_EXIT, "this should not happen -- please debug");
14656   }
14657 }
14658
14659 void FreeEngineSnapshotSingle()
14660 {
14661   FreeSnapshotSingle();
14662
14663   setString(&snapshot_level_identifier, NULL);
14664   snapshot_level_nr = -1;
14665 }
14666
14667 void FreeEngineSnapshotList()
14668 {
14669   FreeSnapshotList();
14670 }
14671
14672 ListNode *SaveEngineSnapshotBuffers()
14673 {
14674   ListNode *buffers = NULL;
14675
14676   /* copy some special values to a structure better suited for the snapshot */
14677
14678   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14679     SaveEngineSnapshotValues_RND();
14680   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14681     SaveEngineSnapshotValues_EM();
14682   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14683     SaveEngineSnapshotValues_SP(&buffers);
14684
14685   /* save values stored in special snapshot structure */
14686
14687   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14688     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14689   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14690     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14691   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14692     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14693
14694   /* save further RND engine values */
14695
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14699
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14704
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14710
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14714
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14716
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14718
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14721
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14740
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14743
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14747
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14750
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14756
14757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14759
14760 #if 0
14761   ListNode *node = engine_snapshot_list_rnd;
14762   int num_bytes = 0;
14763
14764   while (node != NULL)
14765   {
14766     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14767
14768     node = node->next;
14769   }
14770
14771   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14772 #endif
14773
14774   return buffers;
14775 }
14776
14777 void SaveEngineSnapshotSingle()
14778 {
14779   ListNode *buffers = SaveEngineSnapshotBuffers();
14780
14781   /* finally save all snapshot buffers to single snapshot */
14782   SaveSnapshotSingle(buffers);
14783
14784   /* save level identification information */
14785   setString(&snapshot_level_identifier, leveldir_current->identifier);
14786   snapshot_level_nr = level_nr;
14787 }
14788
14789 boolean CheckSaveEngineSnapshotToList()
14790 {
14791   boolean save_snapshot =
14792     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14793      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14794       game.snapshot.changed_action) ||
14795      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14796       game.snapshot.collected_item));
14797
14798   game.snapshot.changed_action = FALSE;
14799   game.snapshot.collected_item = FALSE;
14800   game.snapshot.save_snapshot = save_snapshot;
14801
14802   return save_snapshot;
14803 }
14804
14805 void SaveEngineSnapshotToList()
14806 {
14807   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14808       tape.quick_resume)
14809     return;
14810
14811   ListNode *buffers = SaveEngineSnapshotBuffers();
14812
14813   /* finally save all snapshot buffers to snapshot list */
14814   SaveSnapshotToList(buffers);
14815 }
14816
14817 void SaveEngineSnapshotToListInitial()
14818 {
14819   FreeEngineSnapshotList();
14820
14821   SaveEngineSnapshotToList();
14822 }
14823
14824 void LoadEngineSnapshotValues()
14825 {
14826   /* restore special values from snapshot structure */
14827
14828   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14829     LoadEngineSnapshotValues_RND();
14830   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14831     LoadEngineSnapshotValues_EM();
14832   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14833     LoadEngineSnapshotValues_SP();
14834 }
14835
14836 void LoadEngineSnapshotSingle()
14837 {
14838   LoadSnapshotSingle();
14839
14840   LoadEngineSnapshotValues();
14841 }
14842
14843 void LoadEngineSnapshot_Undo(int steps)
14844 {
14845   LoadSnapshotFromList_Older(steps);
14846
14847   LoadEngineSnapshotValues();
14848 }
14849
14850 void LoadEngineSnapshot_Redo(int steps)
14851 {
14852   LoadSnapshotFromList_Newer(steps);
14853
14854   LoadEngineSnapshotValues();
14855 }
14856
14857 boolean CheckEngineSnapshotSingle()
14858 {
14859   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14860           snapshot_level_nr == level_nr);
14861 }
14862
14863 boolean CheckEngineSnapshotList()
14864 {
14865   return CheckSnapshotList();
14866 }
14867
14868
14869 /* ---------- new game button stuff ---------------------------------------- */
14870
14871 static struct
14872 {
14873   int graphic;
14874   struct XY *pos;
14875   int gadget_id;
14876   char *infotext;
14877 } gamebutton_info[NUM_GAME_BUTTONS] =
14878 {
14879   {
14880     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14881     GAME_CTRL_ID_STOP,                  "stop game"
14882   },
14883   {
14884     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14885     GAME_CTRL_ID_PAUSE,                 "pause game"
14886   },
14887   {
14888     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14889     GAME_CTRL_ID_PLAY,                  "play game"
14890   },
14891   {
14892     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14893     GAME_CTRL_ID_UNDO,                  "undo step"
14894   },
14895   {
14896     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14897     GAME_CTRL_ID_REDO,                  "redo step"
14898   },
14899   {
14900     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14901     GAME_CTRL_ID_SAVE,                  "save game"
14902   },
14903   {
14904     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14905     GAME_CTRL_ID_PAUSE2,                "pause game"
14906   },
14907   {
14908     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14909     GAME_CTRL_ID_LOAD,                  "load game"
14910   },
14911   {
14912     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14913     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14914   },
14915   {
14916     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14917     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14918   },
14919   {
14920     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14921     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14922   }
14923 };
14924
14925 void CreateGameButtons()
14926 {
14927   int i;
14928
14929   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14930   {
14931     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14932     struct XY *pos = gamebutton_info[i].pos;
14933     struct GadgetInfo *gi;
14934     int button_type;
14935     boolean checked;
14936     unsigned int event_mask;
14937     int base_x = (tape.show_game_buttons ? VX : DX);
14938     int base_y = (tape.show_game_buttons ? VY : DY);
14939     int gd_x   = gfx->src_x;
14940     int gd_y   = gfx->src_y;
14941     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14942     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14943     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14944     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14945     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14946     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14947     int id = i;
14948
14949     if (gfx->bitmap == NULL)
14950     {
14951       game_gadget[id] = NULL;
14952
14953       continue;
14954     }
14955
14956     if (id == GAME_CTRL_ID_STOP ||
14957         id == GAME_CTRL_ID_PLAY ||
14958         id == GAME_CTRL_ID_SAVE ||
14959         id == GAME_CTRL_ID_LOAD)
14960     {
14961       button_type = GD_TYPE_NORMAL_BUTTON;
14962       checked = FALSE;
14963       event_mask = GD_EVENT_RELEASED;
14964     }
14965     else if (id == GAME_CTRL_ID_UNDO ||
14966              id == GAME_CTRL_ID_REDO)
14967     {
14968       button_type = GD_TYPE_NORMAL_BUTTON;
14969       checked = FALSE;
14970       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14971     }
14972     else
14973     {
14974       button_type = GD_TYPE_CHECK_BUTTON;
14975       checked =
14976         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14977          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14978          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14979       event_mask = GD_EVENT_PRESSED;
14980     }
14981
14982     gi = CreateGadget(GDI_CUSTOM_ID, id,
14983                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14984                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14985                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14986                       GDI_WIDTH, gfx->width,
14987                       GDI_HEIGHT, gfx->height,
14988                       GDI_TYPE, button_type,
14989                       GDI_STATE, GD_BUTTON_UNPRESSED,
14990                       GDI_CHECKED, checked,
14991                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14992                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14993                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14994                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14995                       GDI_DIRECT_DRAW, FALSE,
14996                       GDI_EVENT_MASK, event_mask,
14997                       GDI_CALLBACK_ACTION, HandleGameButtons,
14998                       GDI_END);
14999
15000     if (gi == NULL)
15001       Error(ERR_EXIT, "cannot create gadget");
15002
15003     game_gadget[id] = gi;
15004   }
15005 }
15006
15007 void FreeGameButtons()
15008 {
15009   int i;
15010
15011   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15012     FreeGadget(game_gadget[i]);
15013 }
15014
15015 static void UnmapGameButtonsAtSamePosition(int id)
15016 {
15017   int i;
15018
15019   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15020     if (i != id &&
15021         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15022         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15023       UnmapGadget(game_gadget[i]);
15024 }
15025
15026 static void UnmapGameButtonsAtSamePosition_All()
15027 {
15028   if (setup.show_snapshot_buttons)
15029   {
15030     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15031     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15032     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15033   }
15034   else
15035   {
15036     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15037     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15038     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15039   }
15040 }
15041
15042 static void MapGameButtonsAtSamePosition(int id)
15043 {
15044   int i;
15045
15046   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15047     if (i != id &&
15048         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15049         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15050       MapGadget(game_gadget[i]);
15051
15052   UnmapGameButtonsAtSamePosition_All();
15053 }
15054
15055 void MapUndoRedoButtons()
15056 {
15057   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15058   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15059
15060   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15061   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15062
15063   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15064 }
15065
15066 void UnmapUndoRedoButtons()
15067 {
15068   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15069   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15070
15071   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15072   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15073
15074   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15075 }
15076
15077 void MapGameButtons()
15078 {
15079   int i;
15080
15081   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15082     if (i != GAME_CTRL_ID_UNDO &&
15083         i != GAME_CTRL_ID_REDO)
15084       MapGadget(game_gadget[i]);
15085
15086   UnmapGameButtonsAtSamePosition_All();
15087
15088   RedrawGameButtons();
15089 }
15090
15091 void UnmapGameButtons()
15092 {
15093   int i;
15094
15095   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15096     UnmapGadget(game_gadget[i]);
15097 }
15098
15099 void RedrawGameButtons()
15100 {
15101   int i;
15102
15103   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15104     RedrawGadget(game_gadget[i]);
15105
15106   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15107   redraw_mask &= ~REDRAW_ALL;
15108 }
15109
15110 void GameUndoRedoExt()
15111 {
15112   ClearPlayerAction();
15113
15114   tape.pausing = TRUE;
15115
15116   RedrawPlayfield();
15117   UpdateAndDisplayGameControlValues();
15118
15119   DrawCompleteVideoDisplay();
15120   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15121   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15122   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15123
15124   BackToFront();
15125 }
15126
15127 void GameUndo(int steps)
15128 {
15129   if (!CheckEngineSnapshotList())
15130     return;
15131
15132   LoadEngineSnapshot_Undo(steps);
15133
15134   GameUndoRedoExt();
15135 }
15136
15137 void GameRedo(int steps)
15138 {
15139   if (!CheckEngineSnapshotList())
15140     return;
15141
15142   LoadEngineSnapshot_Redo(steps);
15143
15144   GameUndoRedoExt();
15145 }
15146
15147 static void HandleGameButtonsExt(int id, int button)
15148 {
15149   static boolean game_undo_executed = FALSE;
15150   int steps = BUTTON_STEPSIZE(button);
15151   boolean handle_game_buttons =
15152     (game_status == GAME_MODE_PLAYING ||
15153      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15154
15155   if (!handle_game_buttons)
15156     return;
15157
15158   switch (id)
15159   {
15160     case GAME_CTRL_ID_STOP:
15161       if (game_status == GAME_MODE_MAIN)
15162         break;
15163
15164       if (tape.playing)
15165         TapeStop();
15166       else
15167         RequestQuitGame(TRUE);
15168
15169       break;
15170
15171     case GAME_CTRL_ID_PAUSE:
15172     case GAME_CTRL_ID_PAUSE2:
15173       if (options.network && game_status == GAME_MODE_PLAYING)
15174       {
15175 #if defined(NETWORK_AVALIABLE)
15176         if (tape.pausing)
15177           SendToServer_ContinuePlaying();
15178         else
15179           SendToServer_PausePlaying();
15180 #endif
15181       }
15182       else
15183         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15184
15185       game_undo_executed = FALSE;
15186
15187       break;
15188
15189     case GAME_CTRL_ID_PLAY:
15190       if (game_status == GAME_MODE_MAIN)
15191       {
15192         StartGameActions(options.network, setup.autorecord, level.random_seed);
15193       }
15194       else if (tape.pausing)
15195       {
15196 #if defined(NETWORK_AVALIABLE)
15197         if (options.network)
15198           SendToServer_ContinuePlaying();
15199         else
15200 #endif
15201           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15202       }
15203       break;
15204
15205     case GAME_CTRL_ID_UNDO:
15206       // Important: When using "save snapshot when collecting an item" mode,
15207       // load last (current) snapshot for first "undo" after pressing "pause"
15208       // (else the last-but-one snapshot would be loaded, because the snapshot
15209       // pointer already points to the last snapshot when pressing "pause",
15210       // which is fine for "every step/move" mode, but not for "every collect")
15211       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15212           !game_undo_executed)
15213         steps--;
15214
15215       game_undo_executed = TRUE;
15216
15217       GameUndo(steps);
15218       break;
15219
15220     case GAME_CTRL_ID_REDO:
15221       GameRedo(steps);
15222       break;
15223
15224     case GAME_CTRL_ID_SAVE:
15225       TapeQuickSave();
15226       break;
15227
15228     case GAME_CTRL_ID_LOAD:
15229       TapeQuickLoad();
15230       break;
15231
15232     case SOUND_CTRL_ID_MUSIC:
15233       if (setup.sound_music)
15234       { 
15235         setup.sound_music = FALSE;
15236
15237         FadeMusic();
15238       }
15239       else if (audio.music_available)
15240       { 
15241         setup.sound = setup.sound_music = TRUE;
15242
15243         SetAudioMode(setup.sound);
15244
15245         PlayLevelMusic();
15246       }
15247       break;
15248
15249     case SOUND_CTRL_ID_LOOPS:
15250       if (setup.sound_loops)
15251         setup.sound_loops = FALSE;
15252       else if (audio.loops_available)
15253       {
15254         setup.sound = setup.sound_loops = TRUE;
15255
15256         SetAudioMode(setup.sound);
15257       }
15258       break;
15259
15260     case SOUND_CTRL_ID_SIMPLE:
15261       if (setup.sound_simple)
15262         setup.sound_simple = FALSE;
15263       else if (audio.sound_available)
15264       {
15265         setup.sound = setup.sound_simple = TRUE;
15266
15267         SetAudioMode(setup.sound);
15268       }
15269       break;
15270
15271     default:
15272       break;
15273   }
15274 }
15275
15276 static void HandleGameButtons(struct GadgetInfo *gi)
15277 {
15278   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15279 }
15280
15281 void HandleSoundButtonKeys(Key key)
15282 {
15283
15284   if (key == setup.shortcut.sound_simple)
15285     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15286   else if (key == setup.shortcut.sound_loops)
15287     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15288   else if (key == setup.shortcut.sound_music)
15289     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15290 }